Skip to content
LIBRARY
Experimental.ACADOSScheduledController.md

Experimental.ACADOSScheduledController ​

Gain-scheduled (LPV) model-predictive controller over a bank of linear models, solved by acados as a single nonlinear program instead of a bank of quadratic programs.

The prediction model is the interpolation of the bank's continuous-time members,

with the member weights w of the scheduling variable rho (the hat functions of LinearMPCScheduler, see MPCScheduling). One solver is built for the whole operating range, so the cost, the constraints and the model need not be duplicated per knot, and the scheduling variable may enter the prediction as well as the current model.

Relation to LinearMPCScheduledController ​

LinearMPCScheduledController solves the QPs of the members enclosing rho and interpolates their controls, which is exact at the knots and first-order accurate between them; the model is frozen at the current weights over the whole horizon, and up to two QPs are solved per tick. This component interpolates the model and solves one optimal-control problem for it. Both take the same bank as mpc and the same sampled inputs, so they can be exchanged and compared (see benchmark_scheduled_controllers).

Modes ​

With predict_scheduling = false (the default) the weights are the online parameters of the prediction model, held at their current values over the horizon: the model is affine in the state and control, the optimal-control problem is a quadratic program, and one real-time iteration (nlp_solver = SQP_RTI) solves it exactly. This is the same information the bank uses.

With predict_scheduling = true the scheduling variable is a linear function of the state,       (rho_C), so the weights are functions of the predicted state and the solver anticipates how the model changes along the horizon – appropriate when the scheduling variable is a state, as for a tank whose outflow depends on its level. The problem is then a genuine nonlinear program, and the rho input is still used for the observer.

The weights of that model are piecewise linear, so its Jacobian jumps where the predicted scheduling variable crosses a knot. An SQP iterated to convergence can then cycle between the two linearizations when its horizon terminates on a knot, which is one reason the default solver is one real-time iteration per tick (SQP_RTI); a finer grid or a horizon that does not end on a knot also helps.

Signals and timing ​

At every tick the measurements y are corrected into the state estimate by the weight-blended observers of the members (LinearMPCScheduledObserver), the optimal-control problem is solved for that estimate, the reference r and the weights, and the first control of the optimal sequence is output as u. The control must be held (ZeroOrderHold) outside this component, the measurements and the scheduling variable must be sampled, and the members' sample time must equal the period of the clock. w are the weights of the tick and exitflag is the acados status of the last solve (0 on success, see ACADOSMPC).

Restrictions ​

The bank must be built from continuous-time models (build_linear_mpc_bank(...; continuous = true, Ts = ...)), since the model is integrated by acados rather than discretized once. The control bounds and the weights Q1, Q2, Q3 and Qf are taken from the members, which must share them. The members' linear control cost eu is carried over as well: a common one as eu, and one that differs between the knots as Eu on the knot weights, which then interpolate it as they interpolate the model (not available with predict_scheduling). They are the defaults of the parameters optimizer.eu and optimizer.Eu of the inner ACADOSMPC, so they can be tuned like the weights. Members with an offset-free observer, measured disturbances, previews, generalized parameters, a general constraint block (including output bounds: LinearMPC enforces those at the predicted states 2:Np and acados at the stages 0..Np-1, so the block cannot be carried over as it stands – state it for this controller with the keywords of build_acados_scheduled_mpc instead), a control horizon shorter than the prediction horizon, binary controls or a prestabilizing feedback are rejected: ACADOSMPC has no counterpart for them.

This component extends from LinearMPCBankPart

Usage ​

MPCComponents.Experimental.ACADOSScheduledController()

Parameters: ​

NameDescriptionUnitsDefault value
mpcSet-up LinearMPC.jl MPC object wrapped in an MPCRef (see build_linear_mpc)–
nxState dimension of the model (derived from mpc)–MPCComponen...mpc_nx(mpc)
nuNumber of controls (derived from mpc)–MPCComponen...mpc_nu(mpc)
ny_measNumber of measured outputs, the rows of the observer's measurement matrix; 0 without an observer (derived from mpc)–MPCComponen...y_meas(mpc)
ndoNumber of estimated offset-free disturbances, 0 for a plain Kalman filter (derived from mpc)–MPCComponen...pc_ndo(mpc)
nxaLength of the augmented observer state, nx + ndo (derived)–nx + ndo
nr_inWidth of the reference input: 0, ny or ny*Np (derived from mpc)–MPCComponen..._nr_in(mpc)
nd_inWidth of the measured-disturbance input: 0, nd_meas or nd_meas*Np (derived from mpc)–MPCComponen..._nd_in(mpc)
np_inWidth of the generalized-parameter input: 0, np or np*Np (derived from mpc)–MPCComponen..._np_in(mpc)
has_rWhether the r connector exists (derived)–nr_in > 0
has_dWhether the d connector exists (derived)–nd_in > 0
has_dhatWhether the dhat connector exists (derived)–ndo > 0
has_pWhether the p connector exists (derived)–np_in > 0
NNumber of controllers in the bank (derived from mpc)–_lmpc_bank_N(mpc)
schedulingScheduling law of the observer, and of the prediction model without predict_scheduling–MPCScheduling.Linear()
predict_schedulingLet the prediction model follow the scheduling variable along the horizon, with rho = rho_C x (quasi-LPV); requires rho_C–false
rho_CRow mapping the state to the scheduling variable, rho = rho_C x (nx entries); required with predict_scheduling–fill(0.0, 0)
NpPrediction horizon of the optimal-control problem; -1 takes the horizon of the members–-1
nlp_solverNLP solver: one real-time iteration per tick (exact without predict_scheduling), or SQP to convergence–MPCComponen...r.SQP_RTI()
qp_solverQP solver: HPIPM after full condensing (the default here: the models are small and the horizon short, where condensing the optimal-control QP into the control moves is the cheaper problem, as it is for the LinearMPC bank) or after partial condensing to qp_cond_N stages–MPCComponen...singHPIPM()
qp_cond_NHorizon of the partially condensed QP; -1 keeps the full horizon (no condensing). Only with PartialCondensingHPIPM–-1
discreteInterpolate the members' zero-order-hold discretizations and hand acados the one-step map, so that nothing is integrated at solve time; false interpolates the continuous models and lets acados discretize them at every solve, which needs a bank built with continuous = true–true
integratorIntegrator of the shooting intervals, used only without discrete–MPCComponen...rator.IRK()
integrator_stagesRunge-Kutta stages of the integrator–4
warm_startInitial guess of the NLP at every tick–MPCComponen...art.Shift()
tolConvergence tolerance of the SQP iterations–1e-6
max_iterMaximum SQP iterations per tick (SQP only)–100
backendSolver backend; C compiles a standalone acados solver library (required for C code export)–MPCComponen...kend.Auto()
acados_mpcacados solver built from the bank and the options above; pass build_acados_scheduled_mpc(...) to bypass them–build_acado...r=max_iter)

Connectors ​

  • rho - This connector represents a real signal as an input to a component (RealInput)

  • y - This connector represents a real signal as an input to a component (RealInput)

  • r - This connector represents a real signal as an input to a component (RealInput)

  • u - This connector represents a real signal as an output from a component (RealOutput)

  • xhat - This connector represents a real signal as an output from a component (RealOutput)

  • w - This connector represents a real signal as an output from a component (RealOutput)

  • exitflag - This connector represents a real signal as an output from a component (RealOutput)

Behavior ​

Behavior of this component cannot be rendered because it includes path variables.

Source ​

dyad
"""
Gain-scheduled (LPV) model-predictive controller over a bank of linear models, solved by acados as a
single nonlinear program instead of a bank of quadratic programs.

The prediction model is the interpolation of the bank's continuous-time members,

```math
\\dot x = \\sum_j w_j \\, (A_j x + B_j u + f_j), \\qquad \\sum_j w_j = 1,
```

with the member weights `w` of the scheduling variable `rho` (the hat functions of
`LinearMPCScheduler`, see `MPCScheduling`). One solver is built for the whole operating range, so the
cost, the constraints and the model need not be duplicated per knot, and the scheduling variable may
enter the prediction as well as the current model.

# Relation to LinearMPCScheduledController
`LinearMPCScheduledController` solves the QPs of the members enclosing `rho` and interpolates their
*controls*, which is exact at the knots and first-order accurate between them; the model is frozen at
the current weights over the whole horizon, and up to two QPs are solved per tick. This component
interpolates the *model* and solves one optimal-control problem for it. Both take the same bank as
`mpc` and the same sampled inputs, so they can be exchanged and compared (see
`benchmark_scheduled_controllers`).

# Modes
With `predict_scheduling = false` (the default) the weights are the online parameters of the
prediction model, held at their current values over the horizon: the model is affine in the state and
control, the optimal-control problem is a quadratic program, and one real-time iteration
(`nlp_solver = SQP_RTI`) solves it exactly. This is the same information the bank uses.

With `predict_scheduling = true` the scheduling variable is a linear function of the state,
``\\rho = \\rho_C x`` (`rho_C`), so the weights are functions of the *predicted* state and the solver
anticipates how the model changes along the horizon -- appropriate when the scheduling variable is a
state, as for a tank whose outflow depends on its level. The problem is then a genuine nonlinear
program, and the `rho` input is still used for the observer.

The weights of that model are piecewise linear, so its Jacobian jumps where the predicted scheduling
variable crosses a knot. An SQP iterated to convergence can then cycle between the two linearizations
when its horizon terminates on a knot, which is one reason the default solver is one real-time
iteration per tick (`SQP_RTI`); a finer grid or a horizon that does not end on a knot also helps.

# Signals and timing
At every tick the measurements `y` are corrected into the state estimate by the weight-blended
observers of the members (`LinearMPCScheduledObserver`), the optimal-control problem is solved for
that estimate, the reference `r` and the weights, and the first control of the optimal sequence is
output as `u`. The control must be held (`ZeroOrderHold`) outside this component, the measurements and
the scheduling variable must be sampled, and the members' sample time must equal the period of the
clock. `w` are the weights of the tick and `exitflag` is the acados status of the last solve (0 on
success, see `ACADOSMPC`).

# Restrictions
The bank must be built from continuous-time models (`build_linear_mpc_bank(...; continuous = true,
Ts = ...)`), since the model is integrated by acados rather than discretized once. The control bounds
and the weights `Q1`, `Q2`, `Q3` and `Qf` are taken from the members, which must share them. The
members' linear control cost `eu` is carried over as well: a common one as `eu`, and one that differs
between the knots as `Eu` on the knot weights, which then interpolate it as they interpolate the model
(not available with `predict_scheduling`). They are the defaults of the parameters `optimizer.eu` and
`optimizer.Eu` of the inner `ACADOSMPC`, so they can be tuned like the weights. Members with an offset-free
observer, measured disturbances, previews, generalized parameters, a general constraint block
(including output bounds: LinearMPC enforces those at the predicted states `2:Np` and acados at the
stages `0..Np-1`, so the block cannot be carried over as it stands -- state it for this controller
with the keywords of `build_acados_scheduled_mpc` instead), a control horizon shorter than the
prediction horizon, binary controls or a prestabilizing feedback are rejected: `ACADOSMPC` has no
counterpart for them.
"""
component ACADOSScheduledController
  extends LinearMPCBankPart()
  "Scheduling variable (sampled)"
  rho = RealInput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 450, "x2": 0, "y2": 550}}}
  }
  "Sampled measurements"
  y = [RealInput@[clk]() for i in 1:ny_meas] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 785, "x2": 0, "y2": 885}}}
  }
  "References for the controlled outputs (ny entries; only with reference_tracking)"
  r = [RealInput@[clk]() for i in 1:nr_in] if has_r {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 115, "x2": 0, "y2": 215}}}
  }
  "Control signal (first move of the optimal sequence)"
  u = [RealOutput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 325, "x2": 1100, "y2": 425}}}
  }
  "State estimate"
  xhat = [RealOutput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 575, "x2": 1100, "y2": 675}}}
  }
  "Member weights of the current tick (sum to 1)"
  w = [RealOutput@[clk]() for j in 1:N] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 825, "x2": 1100, "y2": 925}}}
  }
  "acados status of the last solve (0 on success)"
  exitflag = RealOutput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 75, "x2": 1100, "y2": 175}}}
  }
  "Scheduler: scheduling variable to member weights"
  scheduler = LinearMPCScheduler@[clk = clk](final rho_grid = _lmpc_bank_grid(mpc), final scheduling = scheduling) {
    "Dyad": {"placement": {"diagram": {"x1": 30, "y1": 440, "x2": 160, "y2": 560}}}
  }
  "Gain-scheduled state observer"
  observer = LinearMPCScheduledObserver@[clk = clk](mpc = mpc) {
    "Dyad": {
      "placement": {"diagram": {"x1": 290, "y1": 720, "x2": 460, "y2": 890, "rot": 270}}
    }
  }
  "acados optimizer of the interpolated model"
  optimizer = MPCComponents.ACADOSMPC@[clk = clk](final mpc = acados_mpc, final backend = backend, final max_iter = max_iter) {
    "Dyad": {"placement": {"diagram": {"x1": 470, "y1": 160, "x2": 630, "y2": 320}}}
  }
  "Scheduling law of the observer, and of the prediction model without predict_scheduling"
  structural parameter scheduling::MPCScheduling = MPCScheduling.Linear()
  "Let the prediction model follow the scheduling variable along the horizon, with rho = rho_C x (quasi-LPV); requires rho_C"
  structural parameter predict_scheduling::Boolean = false
  "Row mapping the state to the scheduling variable, rho = rho_C x (nx entries); required with predict_scheduling"
  structural parameter rho_C::Real[:] = fill(0.0, 0)
  "Prediction horizon of the optimal-control problem; -1 takes the horizon of the members"
  structural parameter Np::Integer = -1
  "NLP solver: one real-time iteration per tick (exact without predict_scheduling), or SQP to convergence"
  structural parameter nlp_solver::MPCComponents.ACADOSSolver = MPCComponents.ACADOSSolver.SQP_RTI()
  "QP solver: HPIPM after full condensing (the default here: the models are small and the horizon short, where condensing the optimal-control QP into the control moves is the cheaper problem, as it is for the LinearMPC bank) or after partial condensing to qp_cond_N stages"
  structural parameter qp_solver::MPCComponents.ACADOSQPSolver = MPCComponents.ACADOSQPSolver.FullCondensingHPIPM()
  "Horizon of the partially condensed QP; -1 keeps the full horizon (no condensing). Only with PartialCondensingHPIPM"
  structural parameter qp_cond_N::Integer = -1
  "Interpolate the members' zero-order-hold discretizations and hand acados the one-step map, so that nothing is integrated at solve time; false interpolates the continuous models and lets acados discretize them at every solve, which needs a bank built with continuous = true"
  structural parameter discrete::Boolean = true
  "Integrator of the shooting intervals, used only without `discrete`"
  structural parameter integrator::MPCComponents.ACADOSIntegrator = MPCComponents.ACADOSIntegrator.IRK()
  "Runge-Kutta stages of the integrator"
  structural parameter integrator_stages::Integer = 4
  "Initial guess of the NLP at every tick"
  structural parameter warm_start::MPCComponents.ACADOSWarmStart = MPCComponents.ACADOSWarmStart.Shift()
  "Convergence tolerance of the SQP iterations"
  structural parameter tol::Real = 1e-6
  "Maximum SQP iterations per tick (SQP only)"
  structural parameter max_iter::Integer = 100
  "Solver backend; C compiles a standalone acados solver library (required for C code export)"
  structural parameter backend::MPCComponents.Backend = MPCComponents.Backend.Auto()
  "acados solver built from the bank and the options above; pass build_acados_scheduled_mpc(...) to bypass them"
  structural parameter acados_mpc::MPCComponents.MPCRef = build_acados_scheduled_mpc(mpc, predict_scheduling = predict_scheduling, rho_C = rho_C, Np = Np, discrete = discrete, nlp_solver = nlp_solver, qp_solver = qp_solver, qp_cond_N = qp_cond_N, integrator = integrator, integrator_stages = integrator_stages, warm_start = warm_start, tol = tol, max_iter = max_iter)
relations
  connect(rho, scheduler.rho) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(scheduler.w, observer.w) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 190, "y": 500},
            {"x": 190, "y": 930},
            {"x": 310, "y": 930},
            {"x": 310, "y": 898.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(y, observer.y) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 17.5, "y": 835}, {"x": 17.5, "y": 970}, {"x": 438.75, "y": 970}],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(observer.xhat, optimizer.x, xhat) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 332.5, "y": 686.25}, {"x": 330, "y": 686.25}], "E": -1},
        {"S": -1, "M": [{"x": 330, "y": 293.6}], "E": 2},
        {"S": 3, "M": [{"x": 1050, "y": 640}], "E": -1}
      ],
      "junctions": [{"x": 330, "y": 640}],
      "path": {}
    }
  }
  if has_r
    connect(r, optimizer.r) {}
  end
  if !predict_scheduling
    connect(scheduler.w, optimizer.p) {}
  end
  connect(optimizer.u, u, observer.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 1050, "y": 280}], "E": 2},
        {"S": 3, "M": [{"x": 353.75, "y": 930}, {"x": 840, "y": 930}], "E": -1}
      ],
      "junctions": [{"x": 840, "y": 280}],
      "path": {}
    }
  }
  connect(optimizer.exitflag, exitflag) {
    "Dyad": {"edges": [{"S": 1, "M": [{"x": 1050, "y": 200}], "E": 2}], "path": {}}
  }
  connect(scheduler.w, w) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 510, "y": 500}, {"x": 510, "y": 835}, {"x": 1050, "y": 835}],
          "E": 2
        }
      ],
      "path": {}
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/ACADOSScheduledController.svg"},
    "doc": {"behavior": false},
    "panel": {
      "control": "tabs",
      "children": {
        "Scheduling": {
          "control": "fieldset",
          "contents": ["mpc", "scheduling", "predict_scheduling", "rho_C"]
        },
        "Solver options": {
          "control": "fieldset",
          "contents": [
            "Np",
            "nlp_solver",
            "qp_solver",
            "discrete",
            "integrator",
            "tol",
            "max_iter",
            "warm_start",
            "backend"
          ],
          "children": {"Advanced": {"contents": ["qp_cond_N", "integrator_stages", "acados_mpc"]}}
        },
        "Derived": {
          "control": "fieldset",
          "contents": ["N", "nx", "nu", "ny_meas", "nr_in", "has_r"]
        }
      }
    }
  }
}
end
Flattened Source
dyad
"""
Gain-scheduled (LPV) model-predictive controller over a bank of linear models, solved by acados as a
single nonlinear program instead of a bank of quadratic programs.

The prediction model is the interpolation of the bank's continuous-time members,

```math
\\dot x = \\sum_j w_j \\, (A_j x + B_j u + f_j), \\qquad \\sum_j w_j = 1,
```

with the member weights `w` of the scheduling variable `rho` (the hat functions of
`LinearMPCScheduler`, see `MPCScheduling`). One solver is built for the whole operating range, so the
cost, the constraints and the model need not be duplicated per knot, and the scheduling variable may
enter the prediction as well as the current model.

# Relation to LinearMPCScheduledController
`LinearMPCScheduledController` solves the QPs of the members enclosing `rho` and interpolates their
*controls*, which is exact at the knots and first-order accurate between them; the model is frozen at
the current weights over the whole horizon, and up to two QPs are solved per tick. This component
interpolates the *model* and solves one optimal-control problem for it. Both take the same bank as
`mpc` and the same sampled inputs, so they can be exchanged and compared (see
`benchmark_scheduled_controllers`).

# Modes
With `predict_scheduling = false` (the default) the weights are the online parameters of the
prediction model, held at their current values over the horizon: the model is affine in the state and
control, the optimal-control problem is a quadratic program, and one real-time iteration
(`nlp_solver = SQP_RTI`) solves it exactly. This is the same information the bank uses.

With `predict_scheduling = true` the scheduling variable is a linear function of the state,
``\\rho = \\rho_C x`` (`rho_C`), so the weights are functions of the *predicted* state and the solver
anticipates how the model changes along the horizon -- appropriate when the scheduling variable is a
state, as for a tank whose outflow depends on its level. The problem is then a genuine nonlinear
program, and the `rho` input is still used for the observer.

The weights of that model are piecewise linear, so its Jacobian jumps where the predicted scheduling
variable crosses a knot. An SQP iterated to convergence can then cycle between the two linearizations
when its horizon terminates on a knot, which is one reason the default solver is one real-time
iteration per tick (`SQP_RTI`); a finer grid or a horizon that does not end on a knot also helps.

# Signals and timing
At every tick the measurements `y` are corrected into the state estimate by the weight-blended
observers of the members (`LinearMPCScheduledObserver`), the optimal-control problem is solved for
that estimate, the reference `r` and the weights, and the first control of the optimal sequence is
output as `u`. The control must be held (`ZeroOrderHold`) outside this component, the measurements and
the scheduling variable must be sampled, and the members' sample time must equal the period of the
clock. `w` are the weights of the tick and `exitflag` is the acados status of the last solve (0 on
success, see `ACADOSMPC`).

# Restrictions
The bank must be built from continuous-time models (`build_linear_mpc_bank(...; continuous = true,
Ts = ...)`), since the model is integrated by acados rather than discretized once. The control bounds
and the weights `Q1`, `Q2`, `Q3` and `Qf` are taken from the members, which must share them. The
members' linear control cost `eu` is carried over as well: a common one as `eu`, and one that differs
between the knots as `Eu` on the knot weights, which then interpolate it as they interpolate the model
(not available with `predict_scheduling`). They are the defaults of the parameters `optimizer.eu` and
`optimizer.Eu` of the inner `ACADOSMPC`, so they can be tuned like the weights. Members with an offset-free
observer, measured disturbances, previews, generalized parameters, a general constraint block
(including output bounds: LinearMPC enforces those at the predicted states `2:Np` and acados at the
stages `0..Np-1`, so the block cannot be carried over as it stands -- state it for this controller
with the keywords of `build_acados_scheduled_mpc` instead), a control horizon shorter than the
prediction horizon, binary controls or a prestabilizing feedback are rejected: `ACADOSMPC` has no
counterpart for them.
"""
component ACADOSScheduledController
  "Set-up LinearMPC.jl MPC object wrapped in an MPCRef (see build_linear_mpc)"
  structural parameter mpc::MPCRef
  "State dimension of the model (derived from mpc)"
  final structural parameter nx::Integer = _lmpc_nx(mpc)
  "Number of controls (derived from mpc)"
  final structural parameter nu::Integer = _lmpc_nu(mpc)
  "Number of measured outputs, the rows of the observer's measurement matrix; 0 without an observer (derived from mpc)"
  final structural parameter ny_meas::Integer = _lmpc_ny_meas(mpc)
  "Number of estimated offset-free disturbances, 0 for a plain Kalman filter (derived from mpc)"
  final structural parameter ndo::Integer = _lmpc_ndo(mpc)
  "Length of the augmented observer state, nx + ndo (derived)"
  final structural parameter nxa::Integer = nx + ndo
  "Width of the reference input: 0, ny or ny*Np (derived from mpc)"
  final structural parameter nr_in::Integer = _lmpc_nr_in(mpc)
  "Width of the measured-disturbance input: 0, nd_meas or nd_meas*Np (derived from mpc)"
  final structural parameter nd_in::Integer = _lmpc_nd_in(mpc)
  "Width of the generalized-parameter input: 0, np or np*Np (derived from mpc)"
  final structural parameter np_in::Integer = _lmpc_np_in(mpc)
  "Whether the r connector exists (derived)"
  final structural parameter has_r::Boolean = nr_in > 0
  "Whether the d connector exists (derived)"
  final structural parameter has_d::Boolean = nd_in > 0
  "Whether the dhat connector exists (derived)"
  final structural parameter has_dhat::Boolean = ndo > 0
  "Whether the p connector exists (derived)"
  final structural parameter has_p::Boolean = np_in > 0
  "Number of controllers in the bank (derived from mpc)"
  final structural parameter N::Integer = _lmpc_bank_N(mpc)
  "Scheduling variable (sampled)"
  rho = RealInput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 450, "x2": 0, "y2": 550}}}
  }
  "Sampled measurements"
  y = [RealInput@[clk]() for i in 1:ny_meas] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 785, "x2": 0, "y2": 885}}}
  }
  "References for the controlled outputs (ny entries; only with reference_tracking)"
  r = [RealInput@[clk]() for i in 1:nr_in] if has_r {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 115, "x2": 0, "y2": 215}}}
  }
  "Control signal (first move of the optimal sequence)"
  u = [RealOutput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 325, "x2": 1100, "y2": 425}}}
  }
  "State estimate"
  xhat = [RealOutput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 575, "x2": 1100, "y2": 675}}}
  }
  "Member weights of the current tick (sum to 1)"
  w = [RealOutput@[clk]() for j in 1:N] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 825, "x2": 1100, "y2": 925}}}
  }
  "acados status of the last solve (0 on success)"
  exitflag = RealOutput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 75, "x2": 1100, "y2": 175}}}
  }
  "Scheduler: scheduling variable to member weights"
  scheduler = LinearMPCScheduler@[clk = clk](final rho_grid = _lmpc_bank_grid(mpc), final scheduling = scheduling) {
    "Dyad": {"placement": {"diagram": {"x1": 30, "y1": 440, "x2": 160, "y2": 560}}}
  }
  "Gain-scheduled state observer"
  observer = LinearMPCScheduledObserver@[clk = clk](mpc = mpc) {
    "Dyad": {
      "placement": {"diagram": {"x1": 290, "y1": 720, "x2": 460, "y2": 890, "rot": 270}}
    }
  }
  "acados optimizer of the interpolated model"
  optimizer = MPCComponents.ACADOSMPC@[clk = clk](final mpc = acados_mpc, final backend = backend, final max_iter = max_iter) {
    "Dyad": {"placement": {"diagram": {"x1": 470, "y1": 160, "x2": 630, "y2": 320}}}
  }
  "Scheduling law of the observer, and of the prediction model without predict_scheduling"
  structural parameter scheduling::MPCScheduling = MPCScheduling.Linear()
  "Let the prediction model follow the scheduling variable along the horizon, with rho = rho_C x (quasi-LPV); requires rho_C"
  structural parameter predict_scheduling::Boolean = false
  "Row mapping the state to the scheduling variable, rho = rho_C x (nx entries); required with predict_scheduling"
  structural parameter rho_C::Real[:] = fill(0.0, 0)
  "Prediction horizon of the optimal-control problem; -1 takes the horizon of the members"
  structural parameter Np::Integer = -1
  "NLP solver: one real-time iteration per tick (exact without predict_scheduling), or SQP to convergence"
  structural parameter nlp_solver::MPCComponents.ACADOSSolver = MPCComponents.ACADOSSolver.SQP_RTI()
  "QP solver: HPIPM after full condensing (the default here: the models are small and the horizon short, where condensing the optimal-control QP into the control moves is the cheaper problem, as it is for the LinearMPC bank) or after partial condensing to qp_cond_N stages"
  structural parameter qp_solver::MPCComponents.ACADOSQPSolver = MPCComponents.ACADOSQPSolver.FullCondensingHPIPM()
  "Horizon of the partially condensed QP; -1 keeps the full horizon (no condensing). Only with PartialCondensingHPIPM"
  structural parameter qp_cond_N::Integer = -1
  "Interpolate the members' zero-order-hold discretizations and hand acados the one-step map, so that nothing is integrated at solve time; false interpolates the continuous models and lets acados discretize them at every solve, which needs a bank built with continuous = true"
  structural parameter discrete::Boolean = true
  "Integrator of the shooting intervals, used only without `discrete`"
  structural parameter integrator::MPCComponents.ACADOSIntegrator = MPCComponents.ACADOSIntegrator.IRK()
  "Runge-Kutta stages of the integrator"
  structural parameter integrator_stages::Integer = 4
  "Initial guess of the NLP at every tick"
  structural parameter warm_start::MPCComponents.ACADOSWarmStart = MPCComponents.ACADOSWarmStart.Shift()
  "Convergence tolerance of the SQP iterations"
  structural parameter tol::Real = 1e-6
  "Maximum SQP iterations per tick (SQP only)"
  structural parameter max_iter::Integer = 100
  "Solver backend; C compiles a standalone acados solver library (required for C code export)"
  structural parameter backend::MPCComponents.Backend = MPCComponents.Backend.Auto()
  "acados solver built from the bank and the options above; pass build_acados_scheduled_mpc(...) to bypass them"
  structural parameter acados_mpc::MPCComponents.MPCRef = build_acados_scheduled_mpc(mpc, predict_scheduling = predict_scheduling, rho_C = rho_C, Np = Np, discrete = discrete, nlp_solver = nlp_solver, qp_solver = qp_solver, qp_cond_N = qp_cond_N, integrator = integrator, integrator_stages = integrator_stages, warm_start = warm_start, tol = tol, max_iter = max_iter)
relations
  connect(rho, scheduler.rho) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(scheduler.w, observer.w) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 190, "y": 500},
            {"x": 190, "y": 930},
            {"x": 310, "y": 930},
            {"x": 310, "y": 898.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(y, observer.y) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 17.5, "y": 835}, {"x": 17.5, "y": 970}, {"x": 438.75, "y": 970}],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(observer.xhat, optimizer.x, xhat) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 332.5, "y": 686.25}, {"x": 330, "y": 686.25}], "E": -1},
        {"S": -1, "M": [{"x": 330, "y": 293.6}], "E": 2},
        {"S": 3, "M": [{"x": 1050, "y": 640}], "E": -1}
      ],
      "junctions": [{"x": 330, "y": 640}],
      "path": {}
    }
  }
  if has_r
    connect(r, optimizer.r) {}
  end
  if !predict_scheduling
    connect(scheduler.w, optimizer.p) {}
  end
  connect(optimizer.u, u, observer.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 1050, "y": 280}], "E": 2},
        {"S": 3, "M": [{"x": 353.75, "y": 930}, {"x": 840, "y": 930}], "E": -1}
      ],
      "junctions": [{"x": 840, "y": 280}],
      "path": {}
    }
  }
  connect(optimizer.exitflag, exitflag) {
    "Dyad": {"edges": [{"S": 1, "M": [{"x": 1050, "y": 200}], "E": 2}], "path": {}}
  }
  connect(scheduler.w, w) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 510, "y": 500}, {"x": 510, "y": 835}, {"x": 1050, "y": 835}],
          "E": 2
        }
      ],
      "path": {}
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/ACADOSScheduledController.svg"},
    "doc": {"behavior": false},
    "panel": {
      "control": "tabs",
      "children": {
        "Scheduling": {
          "control": "fieldset",
          "contents": ["mpc", "scheduling", "predict_scheduling", "rho_C"]
        },
        "Solver options": {
          "control": "fieldset",
          "contents": [
            "Np",
            "nlp_solver",
            "qp_solver",
            "discrete",
            "integrator",
            "tol",
            "max_iter",
            "warm_start",
            "backend"
          ],
          "children": {"Advanced": {"contents": ["qp_cond_N", "integrator_stages", "acados_mpc"]}}
        },
        "Derived": {
          "control": "fieldset",
          "contents": ["N", "nx", "nu", "ny_meas", "nr_in", "has_r"]
        }
      }
    }
  }
}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses