UI /

MPT3 allows to export parametric solutions in two ways:

- Export of generic parametric solutions to pure Matlab code
- Export of binary trees to pure Matlab code
- Export to C code

Arbitrary parametric solutions can be exported to a standalone m-file by the `PolyUnion/toMatlab`

method (see `help PolyUnion/toMatlab`

for more information). The exported code is completely MPT-independent and, more importantly, much faster to execute compared to built-in MPT function-evaluation code (which needs to perform lots of sanity checks which slow down the evaluation process).

The general syntax is as follows:

solution.toMatlab('name_of_file.m', 'function_to_export', 'tiebreak_function')

The method takes any `PolyUnion`

object (or an array thereof) and exports the function `function_to_export`

to file `name_of_file`

, resolving tiebreak (when a particular point is contained in multiple regions) using the function `tiebreak_function`

. If you have a single optimizer that is continuous, you can use set `tiebreak_function='first-region'`

. Here, the search for a region that contains a particular point is finished upon hitting the first such region.

The exported parametric solution can then be evaluated by

[z, region] = name_of_file(x)

where `x`

is the vector of parameters at which to evaluate, `z`

is the value of the optimizer at `x`

, and `region`

is the index of the region that constains `x`

. If there is no region containing `x`

, then `z=NaN`

and `region=0`

.

To export explicit MPC feedbacks, use the following syntax:

controller.optimizer.toMatlab('name_of_file.m', 'primal', 'obj')

This tells MPT to export the primal optimizer (stored as the `primal`

function of the `optimizer`

field of the controller), resolving tiebreaks using the cost function of the controller (stored as the `obj`

function of `controller.optimizer`

).

Note that, by default, the `toMatlab`

method exports the full open-loop optimizer. In the case of explicit MPC this implies that the output of `z=name_of_file(x)`

will be an {$ N n_u \times 1$} vector, where {$ N $} is the prediction horizon and {$ n_u $} is the number of control inputs. Then the closed-loop control action is given by `u = z(1:nu)`

. Alternatively, you can ask `toMatlab()`

to only export the closed-loop optimizer. This can be achieved by:

solution.trimFunction('primal', nu);

solution.toMatlab('name_of_file.m', 'primal', 'obj')

solution.toMatlab('name_of_file.m', 'primal', 'obj')

Here, `trimFunction('primal', nu)`

will change the `primal`

function such that it only returns the closed-loop optimizer (i.e., the first `nu`

components of the open-loop optimizer). The added benefit is in decreased memory consumption.

Full example:

% prediction model

model = LTISystem('A', [1 1; 0 1], 'B', [1; 0.5]);

model.x.min = [-5; -5];

model.x.max = [5; 5];

model.u.min = -1;

model.u.max = 1;

model.x.penalty = QuadFunction(eye(2));

model.u.penalty = QuadFunction(1);

% prediction horizon

N = 5;

% construct the explicit MPC controller

ctrl = MPCController(model, N).toExplicit();

% export the explicit solution to a standalone m-file

ctrl.optimizer.toMatlab('mycontroller.m', 'primal', 'obj');

% evaluate the exported solution for a particular state

x0 = [2; 1];

[U, region] = mycontroller(x0)

if region==0

error('No region for state %s.', mat2str(x0));

end

% note that U contains the open-loop optimizer. to get the closed-loop control action, use

nu = 1; % number of control inputs

uopt = U(1:nu)

model = LTISystem('A', [1 1; 0 1], 'B', [1; 0.5]);

model.x.min = [-5; -5];

model.x.max = [5; 5];

model.u.min = -1;

model.u.max = 1;

model.x.penalty = QuadFunction(eye(2));

model.u.penalty = QuadFunction(1);

% prediction horizon

N = 5;

% construct the explicit MPC controller

ctrl = MPCController(model, N).toExplicit();

% export the explicit solution to a standalone m-file

ctrl.optimizer.toMatlab('mycontroller.m', 'primal', 'obj');

% evaluate the exported solution for a particular state

x0 = [2; 1];

[U, region] = mycontroller(x0)

if region==0

error('No region for state %s.', mat2str(x0));

end

% note that U contains the open-loop optimizer. to get the closed-loop control action, use

nu = 1; % number of control inputs

uopt = U(1:nu)

The `BinTreePolyUnion/toMatlab`

method exports a given binary tree to a pure Matlab code, similarly to the procedure above. Note that the method has following limitations:

- supports only a single
`BinTreePolyUnion`

object - no tiebreaking

Example:

% construct the binary tree

tree = BinTreePolyUnion(ctrl.optimizer);

% export the tree solution to a standalone m-file

tree.toMatlab('mycontroller.m', 'primal');

% evaluate the exported solution for a particular state

x0 = [2; 1];

[U, region] = mycontroller(x0)

tree = BinTreePolyUnion(ctrl.optimizer);

% export the tree solution to a standalone m-file

tree.toMatlab('mycontroller.m', 'primal');

% evaluate the exported solution for a particular state

x0 = [2; 1];

[U, region] = mycontroller(x0)

`EMPCController`

objectThe `EMPCController`

object contains a method `exportToC`

that exports PWA control law stored in the `optimizer`

property to C language. The syntax of `exportToC`

method is given as

ctrl.exportToC('output','directory')

where `output`

is the base name for the C-files to be generated in `directory`

. In the new `directory`

there will be three files located:

`output.c`

- pure C-code with PWA control law`output_mex.c`

- mex interface for fast evaluation in Matlab`output_sfunc.c`

- Simulink interface for fast evaluation in Simulink

The `output.c`

contains the pure C-code that can be ported to target application. For fast evaluation in Matlab (Simulink), one may be interested to compile the mex (Simulink) interfaces by issuing the command

mex output_mex.c

mex output_sfunc.c

mex output_sfunc.c

The compiled files can be called from Matlab (Simulink) as any other function. For the above example, the compiled function `output_mex`

evaluates to the same output as the generated Matlab files

% output from the compiled C-mex function

output_mex(x0)

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

% output from the generated matlab file

mycontroller(x0)

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

output_mex(x0)

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

% output from the generated matlab file

mycontroller(x0)

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

From version 3.0.14 we have introduced `toC()`

methods that export general PWA/PWQ functions to C language. The basic sequential search (including tie-breaking) is exported using the `toC()`

method operating over `PolyUnion`

object. The binary search trees are exported in `BinTreePolyUnion/toC()`

method. The syntax for code generation applied for the example above is given as

ctrl.optimizer.toC('primal','file','obj')

Output written to "file.c".

C-mex function written to "file_mex.c".

Output written to "file.c".

C-mex function written to "file_mex.c".

The first argument represents the PWA/PWQ function to export, the second argument is the base name for the files to be generated and the third argument is the tie-breaking function to deal with multiple valued cases. The generated mex-file can be compiled:

mex file_mex.c

and evaluated inside Matlab

file_mex(x0)

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

ans =

-1

-1

-0.76229508196722

-0.0737704918032785

1.32093882317646e-15

The result gives the same output as evaluating the Matlab file `mycontroller`

in the above example.

One straightforward way to obtain a C-code representation of parametric solutions is to use the Matlab Coder toolbox:

controller.optimizer.toMatlab('mycontroller.m', 'primal', 'obj');

coder mycontroller -args {zeros(nx, 1)}

coder mycontroller -args {zeros(nx, 1)}

where `nx`

is the number of parameters. The `coder`

function will generate the mex-function `controller_mex()`

which is much faster to execute compared to executing `controller.m`

.

For explicit MPC controllers you can obtain `nx`

by `nx=controller.xinitFormat.n_xinit`

(this number already includes any additional parameters introduced by free reference tracking and/or by the {$ \Delta u $} formulation).