Visual Servoing Platform  version 3.5.0 under development (2022-02-15)
Tutorial: Using ViSP and MATLAB

Introduction

This tutorial shows how to invoke MATLAB functions from ViSP using MATLAB Engine. The MATLAB C/C++ engine library contains routines that allow you to call MATLAB from your own programs, using MATLAB as a computation engine. This can be used to extend ViSP functionality using MATLAB.

Standalone programs written using MATLAB engine communicates with MATLAB process using pipes on UNIX system and Component Object Model (COM) interface on a Microsoft Windows system. MATLAB provides an API to start and end MATLAB process, send and receive data, and send commands to be processed in MATLAB.

Using the MATLAB engine requires an installed version of MATLAB; you cannot run the MATLAB engine on a machine that only has the MATLAB Runtime. Also, path to MATLAB runtime must be set in the PATH environment variable. For a 64bit machine running Windows, the path is path\to\MATLAB\R20XXy\bin\win64.

For this tutorial, we create a vpMatrix object containing a 3x3 matrix and pass it to MATLAB sum function to compute a column wise sum of the vpMatrix.

Note that all the material (source code and image) described in this tutorial is part of ViSP source code and could be downloaded using the following command:

$ svn export https://github.com/lagadic/visp.git/trunk/tutorial/matlab

CMakeLists.txt file

In order to build a source code that mix ViSP and MATLAB you should first create a CMakeLists.txt file that tries to find ViSP and MATLAB. In the following example we consider the case of the tutorial-matlab.cpp source file.

project(visp-matlab)
cmake_minimum_required(VERSION 3.0)
find_package(VISP REQUIRED visp_core)
include_directories(${VISP_INCLUDE_DIRS})
# Matlab
find_package(Matlab COMPONENTS MX_LIBRARY ENG_LIBRARY REQUIRED)
include_directories(${Matlab_INCLUDE_DIRS})
add_executable(tutorial-matlab tutorial-matlab.cpp)
target_link_libraries(tutorial-matlab ${VISP_LIBRARIES} ${Matlab_LIBRARIES})

To build this example when MATLAB is in your PATH, use:

$ cd tutorial/matlab
$ mkdir build
$ cmake .. -DVISP_DIR=<path to ViSP build dir>

Call MATLAB function from ViSP program

This example shows the use of MATLAB engine in a ViSP program.

/*
* Tutorial using ViSP and MATLAB
* Determine column-wise sum of ViSP matrix using MATLAB Engine
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <matrix.h>
#include <engine.h>
#include <visp3/core/vpMatrix.h>
int main()
{
// ViSP matrix containing input data
vpMatrix x(3, 3, 0);
x[0][0] = 1; x[0][1] = 2; x[0][2] = 3;
x[1][0] = 4; x[1][1] = 5; x[1][2] = 6;
x[2][0] = 7; x[2][1] = 8; x[2][2] = 9;
int xCols = x.getCols();
int xRows = x.getRows();
// MATLAB Engine
Engine *ep;
// MATLAB array to store input data to MATLAB
mxArray *T = mxCreateDoubleMatrix(xRows, xCols, mxREAL);
// MATLAB array to store output data from MATLAB
mxArray *D = NULL;
// Temporary variable to hold Output data
double res[3];
int resCols = 3;
// Display input data to the user
std::cout << "ViSP Input Matrix:" << std::endl;
for (size_t i = 0; i < xRows; i++) {
for (size_t j = 0; j < xCols; j++)
std::cout << x.data[i * xCols + j] << " ";
std::cout << std::endl;
}
// Start a MATLAB Engine process using engOpen
if (!(ep = engOpen(""))) {
fprintf(stderr, "\nCan't start MATLAB engine\n");
return EXIT_FAILURE;
}
// Copy the contents of ViSP matrix to the MATLAB matrix variable T
memcpy((void *)mxGetPr(T), (void *)x.data, xRows * xCols *sizeof(double));
// Place the variable T into the MATLAB workspace
engPutVariable(ep, "Tm", T);
// Determine the sum of each column of input matrix x
// ViSP matrix is row-major and MATLAB matrix is column-major, so transpose the matrix T before evaluation
engEvalString(ep, "Dm = sum(Tm');");
// Get the variable D from the MATLAB workspace
D = engGetVariable(ep, "Dm");
// Copy the contents of MATLAB variable D to local variable res
memcpy((void *)res, (void *)mxGetPr(D), sizeof(res));
// Display output data to the user
std::cout << std::endl << "MATLAB Output Matrix (Column-wise sum):" << std::endl;
for (size_t i = 0; i < resCols; i++)
std::cout << res[i] << " ";
std::cout << std::endl;
// Wait until user exits
std::cout << std::endl << "Hit return to quit\n" << std::endl;
fgetc(stdin);
// Free memory, close MATLAB Engine and Exit
mxDestroyArray(T);
mxDestroyArray(D);
engEvalString(ep, "close;");
engClose(ep);
return EXIT_SUCCESS;
}

The output of the program is the column-wise sum of the matrix stored in vpMatrix object computed using MATLAB.

img-visp-matlab-result.jpg

Now we explain the main lines of the source.

First, we include matrix.h and engine.h libraries from MATLAB for matrix implementation and connection to MATLAB respectively. We also include the ViSP vpMatrix.h library for ViSP based matrix implementation and operation.

#include <matrix.h>
#include <engine.h>
#include <visp3/core/vpMatrix.h>

The initial input is available in a ViSP matrix, which in our case is a 3x3 matrix. It contains numbers from 1 to 9 sequentially in a row-major order.

vpMatrix x(3, 3, 0);
x[0][0] = 1; x[0][1] = 2; x[0][2] = 3;
x[1][0] = 4; x[1][1] = 5; x[1][2] = 6;
x[2][0] = 7; x[2][1] = 8; x[2][2] = 9;

We then declare MATLAB variables i.e. Engine object reference and MATLAB matrix references for storing input and output MATLAB data. We also initialize the MATLAB matrix (of the same size as input vpMatrix) for storing input data using mxCreateDoubleMatrix().

// MATLAB Engine
Engine *ep;
// MATLAB array to store input data to MATLAB
mxArray *T = mxCreateDoubleMatrix(xRows, xCols, mxREAL);
// MATLAB array to store output data from MATLAB
mxArray *D = NULL;

Then we start a MATLAB engine process using engOpen() and assign the engine handle to the pre-declared Engine pointer ep. If the process initiation is unsuccessful, then engOpen() will return NULL and this program terminates with a failure.

if (!(ep = engOpen(""))) {
fprintf(stderr, "\nCan't start MATLAB engine\n");
return EXIT_FAILURE;
}

The contents of the vpMatrix is available in data attribute of vpMatrix class which points to a double array. This content is copied to the MATLAB matrix variable T of type double defined earlier. The mxGetPr() function returns a pointer to the first mxDouble element of the data.

memcpy((void *)mxGetPr(T), (void *)x.data, xRows * xCols *sizeof(double));

The MATLAB variable is then put onto the MATLAB workspace.

engPutVariable(ep, "Tm", T);

Once the matrix is available in the MATLAB workspace, we can use the engEvalString() function to evaluate an expression in MATLAB environment. So, we pass the MATLAB Engine the expression to determine the sum of each column of input matrix T and obtain an output matrix D which will be again stored in the MATLAB workspace. Since, ViSP matrix is row-major and MATLAB matrix is column-major, so we transpose the matrix T before evaluation.

engEvalString(ep, "Dm = sum(Tm');");

The MATLAB variable Dm is retrieved from the MATLAB workspace and stored in a local MATLAB array D.

D = engGetVariable(ep, "Dm");

We then copy the contents of MATLAB variable Dm to local double array res and print the result on the screen.

memcpy((void *)res, (void *)mxGetPr(D), sizeof(res));

Finally, we free the MATLAB variables, close MATLAB Engine and Exit

mxDestroyArray(T);
mxDestroyArray(D);
engEvalString(ep, "close;");
engClose(ep);