For the purpose of giving a robotic serial manipulator the ability to automatically avoid obstacles and plan its own path, it is desirable to interface Matlab with the Motion Control API (MCAPI). While I would personally prefer to use something other than Matlab for this task, we have a large amount of applicable software already written in Matlab, and it will hopefully take less time to work around Matlab than it would take to re-write everything. Everything is being done under Ubuntu 10.04 at the moment, but we also need to make everything work with Windows (stay tuned).
The MCAPI is a software library produced by Precision MicroControl Corp. (PMC) to make it easier to write programs for PMC’s multi-axis motion control cards. The API works with several languages and is supported on both Linux and Windows. Unfortunately, Matlab is not one of these languages…hence the problem of making Matlab work with the MCAPI.
Thankfully, C is one of the languages supported by the MCAPI and Matlab allows the subsumption of C programs through MEX-files. The goal here is to take a snippet of sample code included in the MCAPI, port it for use in a MEX-file, and finally build the MEX-file as a Matlab executable with all the proper library linking.
Here’s what we already have done and working:
- Motion control card installed and connected to some equipment
- g++ installed, plus whatever developer tools are necessary to compile standard C and C++ programs
- MCAPI installed and working properly (this is not so straightforward on a Linux platform…see here)
- Matlab installed with the mex compiler configured properly (see here)
The simplest sample program included in the MCAPI is the Console program. This is a simple command line program written in C++, which connects to the motion control card, reads the number of active axes, and prints the encoder value for each active axis. Very straight-forward, not a lot of code.
Here’s the C++ source code for the file console.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| //
// Copyright (c) 2000-2009 by Precision MicroControl Corp. All rights reserved.
//
#include <stdio.h>
#include <iostream>
#include "mcapi.h"
int main(int argc, char* argv[], char* envp[])
{
HCTRLR hCtlr;
// Get a handle (open) the motion controller binary interface
if ((hCtlr = MCOpen(0, MC_OPEN_BINARY, "")) > 0)
{
// Obtain the current configuration from the controller
MCPARAM Param;
MCGetConfiguration(hCtlr, &Param);
std::cout << "There are " << Param.NumberAxes << " axes installed" << std::endl << std::endl;
// Display position info
for (short int i = 1; i <= Param.NumberAxes; i++)
{
double posit;
MCGetPositionEx(hCtlr, i, &posit);
std::cout << " Axis " << i << " Position = " << posit << std::endl;
}
MCClose(hCtlr); // done - close handle!
}
else
{
char buffer[256];
MCTranslateErrorEx(hCtlr, buffer, sizeof(buffer));
std::cout << "Error attempting to open controller: " << buffer << std::endl;
}
return 0;
} |
I don’t really understand the process of compiling and linking a C++ program to produce an executable, which is probably what presented me with so much trouble in finally getting things to work. Either way, this code is built with the following makefile (this will be handy later on):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #
# Make sure the loader flag -L is set to the directory where you installed
# the MCAPI libraries (/usr/local/lib by default)
#
CPPFLAGS = -g -Wall -I../
LDFLAGS = -L/usr/local/lib
PROGRAM = console
OBJECTS = console.o
# implementation
.SUFFIXES: .o .cpp
.cpp.o :
g++ -c $(CPPFLAGS) -o $@ $<
all: $(PROGRAM)
$(PROGRAM): $(OBJECTS)
libtool --mode=link g++ $(LDFLAGS) -o $(PROGRAM) $(OBJECTS) -lmcapi
clean:
rm -f *.o $(PROGRAM) |
While converting console.cpp to a MEX-file, I also ported the code from C++ to C as well. I’m more familiar with C than I am with C++, so I felt that changing it to C would reduce the number of unknowns I’d have to deal with. The differences are minor. Replace line 5 with the following (allow the MEX-file access to the MEX library):
Replace line 8 with the following (declare the main function according to the MEX standard):
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
Replace lines 18, 25, and 34 respectively with the following (converting C++ print statements to C equivalent):
printf("There are %d axes installed\n\n", Param.NumberAxes);
printf(" Axis %d Position = %lf\n", i, posit);
printf("Error attempting to open controler: %s\n", buffer);
Rename the file as console.c and we’re almost done! On line 21, we shouldn’t be declaring a new variable within the for loop statement. So, delete the short int from line 21 and declare it instead at line 11:
While this shouldn’t make a difference, I’ve found the mex compiler to be finicky about comments. The //-style comments are technically C++, even though they are supported by many C compilers. All comments will have to be re-written as /* blah */-style comments.
Here’s what the final MEX-ified code should look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| /*
* Copyright (c) 2000-2009 by Precision MicroControl Corp. All rights reserved.
*/
#include <stdio.h>
#include <mex.h>
#include "mcapi.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
HCTRLR hCtlr;
short int i;
/* Get a handle (open) the motion controller binary interface */
if ((hCtlr = MCOpen(0, MC_OPEN_BINARY, "")) > 0)
{
/* Obtain the current configuration from the controller */
MCPARAM Param;
MCGetConfiguration(hCtlr, &Param);
printf("There are %d axes installed.\n\n", Param.NumberAxes);
/* Display position info */
for (i = 1; i <= Param.NumberAxes; i++)
{
double posit;
MCGetPositionEx(hCtlr, i, &posit);
printf(" Axis %d Position = %lf\n", i, posit);
}
MCClose(hCtlr); /* done - close handle! */
}
else
{
char buffer[256];
MCTranslateErrorEx(hCtlr, buffer, sizeof(buffer));
printf("Error attempting to open controller: %s\n", buffer);
}
} |
Now the trick is to actually get this to build using the mex compiler. As far as I know, there’s no equivalent of running make in Matlab, so we need a mex command to do the entire building process in one shot. There are a number of files which need to be accessible to the compiler. After having installed MCAPI, the directory src in the MCAPI directory contains files such as mcapi.h, interface_lib.h, and mfx_status.h. You need to tell the compiler to look in this directory so the include statement #include "mcapi.h" can actually find what it’s looking for. For the sake of easiness, I placed the source console.c in a subdirectory of the MCAPI src directory, so it’s only necessary to refer to the parent directory. Under Linux, the MCAPI libraries themselves lie in the /usr/local/lib directory. We need to link the compiler to this directory so that it can find the libraries it needs to use. Here’s what the mex compile command looks like in my case:
mex CFLAGS='$CFLAGS -g -Wall -I../' -L/usr/local/lib -lmcapi console.c
To break that down, CFLAGS='$CFLAGS -g -Wall -I../' tells the compiler where to look for the #include "mcapi.h" statement (plus a few other things), and -L/usr/local/lib -lmcapi links the build to the MCAPI library itself in the /usr/local/lib directory. I figured this out based on lines 15 and 20 of the makefile, and reading the documentation for mex to see how to translate it into a single statement. Obviously, the console.c part is what specifies which file you’d like to build. It should also go without saying that you need to run this command from Matlab while your current directory is the directory containing console.c.
If all went well (as it did for me, eventually), you should now have a Matlab function which allows you to communicate with a PMC motion control card. Extending the functionality of console.c is relatively simple…just use standard commands from the MCAPI (see reference document here).