The standard model-predictive control example presented for the low-level and the high-level interface can be more naturally formulated using Y2F. To see how, consider the following linear MPC problem with lower and upper bounds on state and inputs, and a terminal cost term:

$$ \begin{align} \text{minimize} \ & x_N^T P x_N + \sum_{i=0}^{N-1} x_i^T Q x_i + u_i^T R u_i \\ \text{subject to} \ & x_0 = \color{#d22d33}{x} \\ & x_{i+1} = Ax_i + Bu_i \\ & \underline{x} \leq x_i \leq \bar{x} \\ & \underline{u} \leq u_i \leq \bar{u} \end{align} $$

As usual, this problem is parametric in the initial state \(\color{#d22d33}{x}\) and the first input \(u_0\) is typically applied to the system after a solution has been obtained. Here we present the corresponding problem formulation with YALMIP, how you can use Y2F to easily generate a solver with FORCES Pro, and how you can use the resulting controller for simulation. You can also download the example to try it out for yourself.

Let's dive in right into the problem formulation, assuming that all matrices and vectors that are not a parameter have been defined in the workspace (see the section below on how to do this). Also, make sure to have Yalmip installed correctly (run `yalmiptest` to verify this).

**%% Build MPC problem in YALMIP**

X = sdpvar(nx,N+1,'full'); % state trajectory: x0,x1,...,xN (columns of X)

U = sdpvar(nu,N,'full'); % input trajectory: u0,...,u_{N-1} (columns of U)

% Initialize objective and constraints of the problem

cost = 0; const = [];

% Assemble MPC formulation

for i = 1:N

% cost

if( i < N ), cost = cost + 0.5*X(:,i+1)'*Q*X(:,i+1) + 0.5*U(:,i)'*R*U(:,i);

else, cost = cost + 0.5*X(:,N+1)'*P*X(:,N+1) + 0.5*U(:,N)'*R*U(:,N); end

% model

const = [const, X(:,i+1) == A*X(:,i) + B*U(:,i)];

% bounds

const = [const, umin <= U(:,i) <= umax];

const = [const, xmin <= X(:,i+1) <= xmax];

end

Thanks to YALMIP, defining the mathematical problem is very much like writing down the mathematical equations in code.

We have now incrementally built up the `obj` and `const` object, which are both YALMIP objects. Now comes the magic: use the function optimizerFORCES to generate a solver for the problem defined by `const` and `obj` with the initial state as a parameter, and the first input move \(u_0\) as an output:

%% Create controller object (generates code)

% for a complete list of codeoptions, see

% https://www.embotech.com/FORCES-Pro/User-Manual/Low-level-Interface/Solver-Options

codeoptions = getOptions('simpleMPC_solver'); % give solver a name

controller = **optimizerFORCES**(const, cost, codeoptions, X(:,1), U(:,1), {'xinit'}, {'u0'});

That's it! Y2F automatically figures out the structure of the problem and generates a solver. Continue reading to learn how to call the solver.

You can now use the `controller` object to call the solver, or call the generated MEX code directly:

% Evaluate controller function for parameters

[U(:,k),exitflag,info] = controller{ X(:,k) };

% This is an equivalent call, if the controller object is deleted from the workspace

[output,exitflag,info] = simpleMPC_solver({ X(:,k) });

U(:,k) = output{1}

See `help [solvername]` to get more information about how to call the solver.

This section is just for completeness, and it should come first to define the known data of the MPC problem, i.e. the system matrices \(A\) and \(B\), the prediction horizon \(N\), the stage cost matrices \(Q\) and \(R\), the terminal cost matrix \(P\), and the state and input bounds:

% system matrices

A = [1.1 1; 0 1];

B = [1; 0.5];

[nx,nu] = size(B);

% horizon

N = 10;

% cost matrices

Q = eye(2);

R = eye(1);

if exist('dlqr', 'file')

[~,P] = dlqr(A,B,Q,R);

else

disp('Cannot find dlqr (Control Systems Toolbox). Using 10*Q for the terminal cost matrix.');

P = 10*Q;

end

% constraints

umin = -0.5; umax = 0.5;

xmin = [-5; -5]; xmax = [5; 5];