Main Content

nlmpcmoveCodeGeneration

Compute nonlinear MPC control moves with code generation support

Since R2020a

Description

example

[mv,newOnlineData] = nlmpcmoveCodeGeneration(coreData,x,lastMV,onlineData) computes optimal nonlinear MPC control moves and supports code generation for deployment to real-time targets. Control moves are calculated using the current prediction model states (x), the control moves from the previous control interval (lastMV), and input data structures (coreData and nlOnlineData) generated using getCodeGenerationData.

nlmpcmoveCodeGeneration does not check input arguments for correct dimensions and data types.

[___,info] = nlmpcmoveCodeGeneration(___) returns additional information about the optimization result, including the number of iterations and the objective function cost.

Examples

collapse all

Create a nonlinear MPC controller with four states, two outputs, and one input.

nlobj = nlmpc(4,2,1);
Zero weights are applied to one or more OVs because there are fewer MVs than OVs.

Specify the sample time and horizons of the controller.

Ts = 0.1;
nlobj.Ts = Ts;
nlobj.PredictionHorizon = 10;
nlobj.ControlHorizon = 5;

Specify the state function for the controller, which is in the file pendulumDT0.m. This discrete-time model integrates the continuous time model defined in pendulumCT0.m using a multistep forward Euler method.

nlobj.Model.StateFcn = "pendulumDT0";
nlobj.Model.IsContinuousTime = false;

The prediction model uses an optional parameter, Ts, to represent the sample time. Specify the number of parameters and create a parameter vector.

nlobj.Model.NumberOfParameters = 1;
params = {Ts};

Specify the output function of the model, passing the sample time parameter as an input argument.

nlobj.Model.OutputFcn = "pendulumOutputFcn";

Define standard constraints for the controller.

nlobj.Weights.OutputVariables = [3 3];
nlobj.Weights.ManipulatedVariablesRate = 0.1;
nlobj.OV(1).Min = -10;
nlobj.OV(1).Max = 10;
nlobj.MV.Min = -100;
nlobj.MV.Max = 100;

Validate the prediction model functions.

x0 = [0.1;0.2;-pi/2;0.3];
u0 = 0.4;
validateFcns(nlobj,x0,u0,[],params);
Model.StateFcn is OK.
Model.OutputFcn is OK.
Analysis of user-provided model, cost, and constraint functions complete.

Only two of the plant states are measurable. Therefore, create an extended Kalman filter for estimating the four plant states. Its state transition function is defined in pendulumStateFcn.m and its measurement function is defined in pendulumMeasurementFcn.m.

EKF = extendedKalmanFilter(@pendulumStateFcn,@pendulumMeasurementFcn);

Define initial conditions for the simulation, initialize the extended Kalman filter state, and specify a zero initial manipulated variable value.

x0 = [0;0;-pi;0];
y0 = [x0(1);x0(3)];
EKF.State = x0;
mv0 = 0;

Create code generation data structures for the controller, specifying the initial conditions and parameters.

[coreData,onlineData] = getCodeGenerationData(nlobj,x0,mv0,params);

Specify the output reference value in the online data structure.

onlineData.ref = [0 0];

To verify the controller operation, run a simulation for 10 seconds. During each control interval:

  1. Correct the previous prediction using the current measurement.

  2. Compute optimal control moves using nlmpcmoveCodeGeneration. This function returns the computed optimal sequences in onlineData. Passing the updated data structure to nlmpcmoveCodeGeneration in the next control interval provides initial guesses for the optimal sequences.

  3. Predict the model states.

  4. Apply the first computed optimal control move to the plant, updating the plant states.

  5. Generate sensor data with white noise.

  6. Save the plant states.

mv = mv0;
y = y0;
x = x0;
Duration = 10;
xHistory = x0;
for ct = 1:(Duration/Ts)
    % Correct previous prediction
    xk = correct(EKF,y);
    % Compute optimal control move
    [mv,onlineData] = nlmpcmoveCodeGeneration(coreData,xk,mv,onlineData);
    % Predict prediction model states for the next iteration
    predict(EKF,[mv; Ts]);
    % Implement first optimal control move
    x = pendulumDT0(x,mv,Ts);
    % Generate sensor data
    y = x([1 3]) + randn(2,1)*0.01;
    % Save plant states
    xHistory = [xHistory x];
end

Generate a MEX function with MATLAB® Coder™.

cfg = coder.config('mex');
cfg.EnableDynamicMemoryAllocation = false;
mexfun = buildMEX(nlobj, 'nlmpcmoveMEX', coreData, onlineData, cfg);
Generating MEX function "nlmpcmoveMEX" from nonlinear MPC to speed up simulation.
Code generation successful.

MEX function "nlmpcmoveMEX" successfully generated.

Input Arguments

collapse all

Nonlinear MPC configuration parameters that are constant at run time, specified as a structure generated using getCodeGenerationData.

Note

When using codegen (MATLAB Coder), coreData must be defined as coder.Constant (MATLAB Coder).

Current prediction model states, specified as a vector of lengthNx, where Nx is the number of prediction model states. The prediction model state function is defined in nlobj.Model.StateFcn.

Since the nonlinear MPC controller does not perform state estimation, you must either measure or estimate the current prediction model states at each control interval. For more information on nonlinear MPC prediction models, see Specify Prediction Model for Nonlinear MPC.

Control signals used in plant at previous control interval, specified as a column vector of lengthNmv, where Nmv is the number of manipulated variables.

Note

Specify lastMV as the manipulated variable signals applied to the plant in the previous control interval. Typically, these signals are the values generated by the controller (mv). However, this is not always the case. For example, if your controller is offline and running in tracking mode; that is, the controller output is not driving the plant, then feeding the actual control signal to last_mv can help achieve bumpless transfer when the controller is switched back online.

Online controller data that you must update at run time, specified as a structure with the following fields. Generate the initial structure using getCodeGenerationData. Some structure fields are not required, depending on the configuration of the controller and what weights or constraints vary at run time.

Plant output reference values, specified as a row vector of length Ny or an array with Ny columns, where Ny is the number of output variables.

To use the same reference values across the prediction horizon, specify a row vector.

To vary the reference values over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the reference values for one prediction horizon step. If you specify fewer than p rows, the values in the final row are used for the remaining steps of the prediction horizon.

If your controller cost function does not use ref, leave ref at its default value.

Manipulated variable targets, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables.

To use the same manipulated variable targets across the prediction horizon, specify a row vector.

To vary the targets over the prediction horizon (previewing) from time k to time k+p-1, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the targets for one prediction horizon step. If you specify fewer than p rows, the final targets are used for the remaining steps of the prediction horizon.

If your controller cost function does not use mvTarget, leave mvTarget at its default value.

Initial guesses for the optimal state solutions, specified as a row vector of length Nx or an array with Nx columns, where Nx is the number of states.

To use the same initial guesses across the prediction horizon, specify a row vector.

To vary the initial guesses over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the initial guesses for one prediction horizon step. If you specify fewer than p rows, the final guesses are used for the remaining steps of the prediction horizon.

In general, during closed-loop simulation, you do not specify X0 yourself. Instead, when calling nlmpcmoveCodeGeneration, return the newOnlineData output argument, which contains updated X0 estimates. You can then pass newOnlineData in as the onlineData input argument to nlmpcmoveCodeGeneration for the next control interval.

Initial guesses for the optimal manipulated variable solutions, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables.

To use the same initial guesses across the prediction horizon, specify a row vector.

To vary the initial guesses over the prediction horizon from time k to time k+p-1, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the initial guesses for one prediction horizon step. If you specify fewer than p rows, the final guesses are used for the remaining steps of the prediction horizon.

In general, during closed-loop simulation, you do not specify MV0 yourself. Instead, when calling nlmpcmoveCodeGeneration, return the newOnlineData output argument, which contains updated MV0 estimates. You can then pass newOnlineData in as the onlineData input argument to nlmpcmoveCodeGeneration for the next control interval.

Initial guess for the slack variable at the solution, specified as a nonnegative scalar.

In general, during closed-loop simulation, you do not specify Slack0 yourself. Instead, when calling nlmpcmoveCodeGeneration, return the newOnlineData output argument, which contains updated Slack0 estimates. You can then pass newOnlineData in as the onlineData input argument to nlmpcmoveCodeGeneration for the next control interval.

Measured disturbance values, specified as a row vector of length Nmd or an array with Nmd columns, where Nmd is the number of measured disturbances. If your controller has measured disturbances, you must specify md. If your controller has no measured disturbances, then getCodeGenerationData omits this field.

To use the same disturbance values across the prediction horizon, specify a row vector.

To vary the disturbance values over the prediction horizon from time k to time k+p, specify an array with up to p+1 rows. Here, k is the current time and p is the prediction horizon. Each row contains the disturbance values for one prediction horizon step. If you specify fewer than p rows, the values in the final row are used for the remaining steps of the prediction horizon.

Parameter values used by the prediction model, custom cost function, and custom constraints, specified as a cell vector with length equal to the Model.NumberOfParameters property of the controller. If the controller has no parameters, then getCodeGenerationData omits this field.

The order of the parameters must match the order defined for the prediction model, custom cost function, and custom constraints.

Output variable tuning weights that replace the default tuning weights at run time, specified as a row vector of length Ny or an array with Ny columns, where Ny is the number of output variables. If you expect your output variable weights to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same weights across the prediction horizon, specify a row vector.

To vary the weights over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the weights for one prediction horizon step. If you specify fewer than p rows, the final weights are used for the remaining steps of the prediction horizon.

Manipulated variable tuning weights that replace the default tuning weights at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable weights to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same weights across the prediction horizon, specify a row vector.

To vary the weights over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the weights for one prediction horizon step. If you specify fewer than p rows, the final weights are used for the remaining steps of the prediction horizon.

Manipulated variable rate tuning weights that replace the default tuning weights at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable rate weights to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same weights across the prediction horizon, specify a row vector.

To vary the weights over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the weights for one prediction horizon step. If you specify fewer than p rows, the final weights are used for the remaining steps of the prediction horizon.

Slack variable rate tuning weight that replaces the default tuning weight at run time, specified as a positive scalar. If you expect your slack variable weight to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

Output variable lower bounds that replace the default lower bounds at run time, specified as a row vector of length Ny or an array with Ny columns, where Ny is the number of output variables. If you expect your output variable lower bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Output variable upper bounds that replace the default upper bounds at run time, specified as a row vector of length Ny or an array with Ny columns, where Ny is the number of output variables. If you expect your output variable upper bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

State lower bounds that replace the default lower bounds at run time, specified as a row vector of length Nx or an array with Nx columns, where Nx is the number of states. If you expect your state lower bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

State upper bounds that replace the default upper bounds at run time, specified as a row vector of length Nx or an array with Nx columns, where Nx is the number of states. If you expect your state upper bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Manipulated variable lower bounds that replace the default lower bounds at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable lower bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Manipulated variable upper bounds that replace the default upper bounds at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable upper bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Manipulated variable rate lower bounds that replace the default lower bounds at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable rate lower bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Manipulated variable rate upper bounds that replace the default upper bounds at run time, specified as a row vector of length Nmv or an array with Nmv columns, where Nmv is the number of manipulated variables. If you expect your manipulated variable rate upper bounds to vary at run time, you must add this field to the online data structure when you call getCodeGenerationData.

To use the same bounds across the prediction horizon, specify a row vector.

To vary the bounds over the prediction horizon from time k+1 to time k+p, specify an array with up to p rows. Here, k is the current time and p is the prediction horizon. Each row contains the bounds for one prediction horizon step. If you specify fewer than p rows, the final bounds are used for the remaining steps of the prediction horizon.

Output Arguments

collapse all

Optimal manipulated variable control action, returned as a column vector of length Nmv, where Nmv is the number of manipulated variables.

If the solver converges to a local optimum solution (info.ExitFlag is positive), then mv contains the optimal solution.

If the solver reaches the maximum number of iterations, finds a feasible suboptimal solution (info.ExitFlag = 0) and:

  • coredata.usesuboptimalsolution is true, then mv contains the suboptimal solution

  • coredata.usesuboptimalsolution is false, then mv contains lastMV

If the solver fails to find a feasible solution (info.ExitFlag is negative), then mv contains lastMV.

Updated online controller data, returned as a structure. This structure is the same as onlineData, except that the decision variable initial guesses (X0, MV0, and Slack0) are updated.

For subsequent control intervals, warm start the solver by modifying the online data in newOnlineData and passing the updated structure to nlmpcmoveCodeGeneration as onlineData. Doing so allows the solver to use the decision variable initial guesses as a starting point for its solution.

Solution details, returned as a structure with the following fields.

Optimal manipulated variable sequence, returned as a (p+1)-by-Nmv array, where p is the prediction horizon and Nmv is the number of manipulated variables.

MVopt(i,:) contains the calculated optimal manipulated variable values at time k+i-1, for i = 1,...,p, where k is the current time. MVopt(1,:) contains the same manipulated variable values as output argument mv. Since the controller does not calculate optimal control moves at time k+p, MVopt(p+1,:) is equal to MVopt(p,:).

Optimal prediction model state sequence, returned as a (p+1)-by-Nx array, where p is the prediction horizon and Nx is the number of states in the prediction model.

Xopt(i,:) contains the calculated state values at time k+i-1, for i = 2,...,p+1, where k is the current time. Xopt(1,:) is the same as the current states in x.

Optimal output variable sequence, returned as a (p+1)-by-Ny array, where p is the prediction horizon and Ny is the number of outputs.

Yopt(i,:) contains the calculated output values at time k+i-1, for i = 2,...,p+1, where k is the current time. Yopt(1,:) is computed based on the current states in x and the current measured disturbances in md, if any.

Prediction horizon time sequence, returned as a column vector of length p+1, where p is the prediction horizon. Topt contains the time sequence from time k to time k+p, where k is the current time.

Topt(1) = 0 represents the current time. Subsequent time steps Topt(i) are Ts*(i-1), where Ts is the controller sample time.

Use Topt when plotting the MVopt, Xopt, or Yopt sequences.

Slack variable at optimum, ε, used in constraint softening, returned as a nonnegative scalar value.

  • ε = 0 — All soft constraints are satisfied over the entire prediction horizon.

  • ε > 0 — At least one soft constraint is violated. When more than one constraint is violated, ε represents the worst-case soft constraint violation (scaled by your ECR values for each constraint).

Optimization exit code, returned as one of the following:

  • Positive Integer — Optimal solution found

  • 0 — Feasible suboptimal solution found after the maximum number of iterations

  • Negative integer — No feasible solution found

Number of iterations used by the solver, returned as a positive integer.

Objective function cost, returned as a nonnegative scalar value. The cost quantifies the degree to which the controller has achieved its objectives.

The cost value is only meaningful when ExitFlag is nonnegative.

Extended Capabilities

Version History

Introduced in R2020a