Skip to content
LIBRARY
Experimental.LinearMPCScheduledOptimizer.md

Experimental.LinearMPCScheduledOptimizer ​

Gain-scheduled constrained optimizer of a bank of LinearMPC.jl controllers: at every tick the members whose weight w[j] (from LinearMPCScheduler) is positive solve their QP for the same state estimate, references, disturbances, generalized parameters and the same applied previous control, and the control output is the blend Σ_j w_j u_j of their first moves. With Nearest scheduling one member solves per tick, with Linear two (one exactly at a knot). Inputs and their widths follow the members' shared settings as for LinearMPCOptimizer.

Each member is solved by its own generated C code (or by LinearMPC.jl's Julia solver, see backend); the selection of the active members and the blending are Julia code that the synchronous code generator cross-compiles, so a model containing this component is exported to standalone C by CCodeExport with one library per member. exitflag is the composite DAQP exit flag of the active members: the first negative one when a member failed, else 2 when one reported active soft constraints, else 1, on either backend.

Blending the controls of neighbouring members is exact at the knots and first-order accurate between them; the input bounds hold for the blend (a convex combination), output and general constraints only approximately. See LinearMPCScheduledController.

This component extends from LinearMPCBankPart

Usage ​

MPCComponents.Experimental.LinearMPCScheduledOptimizer(u0=_lmpc_u0(mpc), r_dummy=fill(0.0, nr_eff), d_dummy=fill(0.0, nd_eff), dhat_dummy=fill(0.0, ndo_eff), p_dummy=fill(0.0, np_eff))

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)
backendQP solver backend; Auto uses the compiled C code when a C compiler is available–MPCComponen...kend.Auto()
precisionFloat type of the generated QP code (C backend only)–MPCComponen...on.Double()
use_cWhether the compiled C code solves the QPs (resolved from backend; building the members' libraries on first use)–lmpc_bank... precision)
nr_effWidth of the internal reference vector, max(nr_in, 1) (derived)–max(nr_in, 1)
nd_effWidth of the internal disturbance vector, max(nd_in, 1) (derived)–max(nd_in, 1)
ndo_effWidth of the internal estimated-disturbance vector, max(ndo, 1) (derived)–max(ndo, 1)
np_effWidth of the internal generalized-parameter vector, max(np_in, 1) (derived)–max(np_in, 1)
u0Control assumed applied before the first tick, by default the operating-point control of the members–_lmpc_u0(mpc)

Connectors ​

  • xhat - 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)

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

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

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

  • w - 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)

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

Variables ​

NameDescriptionUnits
r_intReference vector handed to the step function–
d_intDisturbance vector handed to the step function–
dhat_intEstimated-disturbance vector handed to the step function–
p_intGeneralized-parameter vector handed to the step function–

Behavior ​

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

Source ​

dyad
"""
Gain-scheduled constrained optimizer of a bank of LinearMPC.jl controllers: at every tick the
members whose weight `w[j]` (from `LinearMPCScheduler`) is positive solve their QP for the same
state estimate, references, disturbances, generalized parameters and the same applied previous
control, and the control output is the blend `Σ_j w_j u_j` of their first moves. With `Nearest`
scheduling one member solves per tick, with `Linear` two (one exactly at a knot). Inputs and their
widths follow the members' shared settings as for `LinearMPCOptimizer`.

Each member is solved by its own generated C code (or by LinearMPC.jl's Julia solver, see
`backend`); the selection of the active members and the blending are Julia code that the
synchronous code generator cross-compiles, so a model containing this component is exported to
standalone C by `CCodeExport` with one library per member. `exitflag` is the composite DAQP exit
flag of the active members: the first negative one when a member failed, else 2 when one reported
active soft constraints, else 1, on either backend.

Blending the controls of neighbouring members is exact at the knots and first-order accurate
between them; the input bounds hold for the blend (a convex combination), output and general
constraints only approximately. See `LinearMPCScheduledController`.
"""
component LinearMPCScheduledOptimizer
  extends LinearMPCBankPart()
  "State estimate at the current tick"
  xhat = [RealInput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 535, "x2": 0, "y2": 635}}}
  }
  "References; ny entries, or ny*Np column-major entries with reference preview (only with reference_tracking)"
  r = [RealInput@[clk]() for i in 1:nr_in] if has_r {"Dyad": {"placement": {"diagram": {"x1": -100, "y1": 35, "x2": 0, "y2": 135}}}}
  "Measured disturbances; nd_meas entries, or nd_meas*Np column-major entries with disturbance preview"
  d = [RealInput@[clk]() for i in 1:nd_in] if has_d {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 700, "x2": 0, "y2": 800}}}
  }
  "Estimated offset-free disturbances from LinearMPCScheduledObserver (only for offset-free observers)"
  dhat = [RealInput@[clk]() for i in 1:ndo] if has_dhat {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 865, "x2": 0, "y2": 965}}}
  }
  "Generalized parameters; np entries, or np*Np column-major entries with parameter preview"
  p = [RealInput@[clk]() for i in 1:np_in] if has_p {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 200, "x2": 0, "y2": 300}}}
  }
  "Member weights from LinearMPCScheduler"
  w = [RealInput@[clk]() for j in 1:N] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 365, "x2": 0, "y2": 465}}}
  }
  "Control signal (blend of the active members' first moves)"
  u = [RealOutput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 700, "x2": 1100, "y2": 800}}}
  }
  "Composite solver status of the last step (first negative member flag, else 2 with active soft constraints, else 1), on either backend"
  exitflag = RealOutput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 200, "x2": 1100, "y2": 300}}}
  }
  "QP solver backend; Auto uses the compiled C code when a C compiler is available"
  structural parameter backend::MPCComponents.Backend = MPCComponents.Backend.Auto()
  "Float type of the generated QP code (C backend only)"
  structural parameter precision::MPCComponents.MPCPrecision = MPCComponents.MPCPrecision.Double()
  "Whether the compiled C code solves the QPs (resolved from backend; building the members' libraries on first use)"
  final structural parameter use_c::Boolean = _lmpc_bank_use_c(mpc, backend, precision)
  "Width of the internal reference vector, max(nr_in, 1) (derived)"
  final structural parameter nr_eff::Integer = max(nr_in, 1)
  "Width of the internal disturbance vector, max(nd_in, 1) (derived)"
  final structural parameter nd_eff::Integer = max(nd_in, 1)
  "Width of the internal estimated-disturbance vector, max(ndo, 1) (derived)"
  final structural parameter ndo_eff::Integer = max(ndo, 1)
  "Width of the internal generalized-parameter vector, max(np_in, 1) (derived)"
  final structural parameter np_eff::Integer = max(np_in, 1)
  "Control assumed applied before the first tick, by default the operating-point control of the members"
  parameter u0::Real[nu] = _lmpc_u0(mpc)
  "Zero placeholder passed as the reference argument when r is absent"
  final parameter r_dummy::Real[nr_eff] = fill(0.0, nr_eff)
  "Zero placeholder passed as the disturbance argument when d is absent"
  final parameter d_dummy::Real[nd_eff] = fill(0.0, nd_eff)
  "Zero placeholder passed as the estimated-disturbance argument when dhat is absent"
  final parameter dhat_dummy::Real[ndo_eff] = fill(0.0, ndo_eff)
  "Zero placeholder passed as the generalized-parameter argument when p is absent"
  final parameter p_dummy::Real[np_eff] = fill(0.0, np_eff)
  "Reference vector handed to the step function"
  variable r_int::Real[nr_eff]
  "Disturbance vector handed to the step function"
  variable d_int::Real[nd_eff]
  "Estimated-disturbance vector handed to the step function"
  variable dhat_int::Real[ndo_eff]
  "Generalized-parameter vector handed to the step function"
  variable p_int::Real[np_eff]
relations
  if has_r
    r_int = r
  else
    r_int = r_dummy
  end
  if has_d
    d_int = d
  else
    d_int = d_dummy
  end
  if has_dhat
    dhat_int = dhat
  else
    dhat_int = dhat_dummy
  end
  if has_p
    p_int = p
  else
    p_int = p_dummy
  end
  initial u@(clk-1) = u0
  # One scalar solve per tick (the composite flag), the blended control read back through an
  # accessor that depends on it; see LinearMPCOptimizer. The blended control of the previous tick
  # is the previous control every member sees.
  if use_c
    exitflag@clk = lmpc_bank_solve_c(w@clk, xhat@clk, r_int@clk, d_int@clk, dhat_int@clk, p_int@clk, u@(clk-1), mpc)
    for i in 1:nu
      u[i] = lmpc_bank_u_c(exitflag, w, i, mpc)
    end
  else
    exitflag@clk = lmpc_bank_solve(w@clk, xhat@clk, r_int@clk, d_int@clk, dhat_int@clk, p_int@clk, u@(clk-1), mpc)
    for i in 1:nu
      u[i] = lmpc_bank_u(exitflag, w, i, mpc)
    end
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/LinearMPCScheduledOptimizer.svg"},
    "doc": {"behavior": false}
  }
}
end
Flattened Source
dyad
"""
Gain-scheduled constrained optimizer of a bank of LinearMPC.jl controllers: at every tick the
members whose weight `w[j]` (from `LinearMPCScheduler`) is positive solve their QP for the same
state estimate, references, disturbances, generalized parameters and the same applied previous
control, and the control output is the blend `Σ_j w_j u_j` of their first moves. With `Nearest`
scheduling one member solves per tick, with `Linear` two (one exactly at a knot). Inputs and their
widths follow the members' shared settings as for `LinearMPCOptimizer`.

Each member is solved by its own generated C code (or by LinearMPC.jl's Julia solver, see
`backend`); the selection of the active members and the blending are Julia code that the
synchronous code generator cross-compiles, so a model containing this component is exported to
standalone C by `CCodeExport` with one library per member. `exitflag` is the composite DAQP exit
flag of the active members: the first negative one when a member failed, else 2 when one reported
active soft constraints, else 1, on either backend.

Blending the controls of neighbouring members is exact at the knots and first-order accurate
between them; the input bounds hold for the blend (a convex combination), output and general
constraints only approximately. See `LinearMPCScheduledController`.
"""
component LinearMPCScheduledOptimizer
  "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)
  "State estimate at the current tick"
  xhat = [RealInput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 535, "x2": 0, "y2": 635}}}
  }
  "References; ny entries, or ny*Np column-major entries with reference preview (only with reference_tracking)"
  r = [RealInput@[clk]() for i in 1:nr_in] if has_r {"Dyad": {"placement": {"diagram": {"x1": -100, "y1": 35, "x2": 0, "y2": 135}}}}
  "Measured disturbances; nd_meas entries, or nd_meas*Np column-major entries with disturbance preview"
  d = [RealInput@[clk]() for i in 1:nd_in] if has_d {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 700, "x2": 0, "y2": 800}}}
  }
  "Estimated offset-free disturbances from LinearMPCScheduledObserver (only for offset-free observers)"
  dhat = [RealInput@[clk]() for i in 1:ndo] if has_dhat {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 865, "x2": 0, "y2": 965}}}
  }
  "Generalized parameters; np entries, or np*Np column-major entries with parameter preview"
  p = [RealInput@[clk]() for i in 1:np_in] if has_p {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 200, "x2": 0, "y2": 300}}}
  }
  "Member weights from LinearMPCScheduler"
  w = [RealInput@[clk]() for j in 1:N] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 365, "x2": 0, "y2": 465}}}
  }
  "Control signal (blend of the active members' first moves)"
  u = [RealOutput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 700, "x2": 1100, "y2": 800}}}
  }
  "Composite solver status of the last step (first negative member flag, else 2 with active soft constraints, else 1), on either backend"
  exitflag = RealOutput@[clk]() {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 200, "x2": 1100, "y2": 300}}}
  }
  "QP solver backend; Auto uses the compiled C code when a C compiler is available"
  structural parameter backend::MPCComponents.Backend = MPCComponents.Backend.Auto()
  "Float type of the generated QP code (C backend only)"
  structural parameter precision::MPCComponents.MPCPrecision = MPCComponents.MPCPrecision.Double()
  "Whether the compiled C code solves the QPs (resolved from backend; building the members' libraries on first use)"
  final structural parameter use_c::Boolean = _lmpc_bank_use_c(mpc, backend, precision)
  "Width of the internal reference vector, max(nr_in, 1) (derived)"
  final structural parameter nr_eff::Integer = max(nr_in, 1)
  "Width of the internal disturbance vector, max(nd_in, 1) (derived)"
  final structural parameter nd_eff::Integer = max(nd_in, 1)
  "Width of the internal estimated-disturbance vector, max(ndo, 1) (derived)"
  final structural parameter ndo_eff::Integer = max(ndo, 1)
  "Width of the internal generalized-parameter vector, max(np_in, 1) (derived)"
  final structural parameter np_eff::Integer = max(np_in, 1)
  "Control assumed applied before the first tick, by default the operating-point control of the members"
  parameter u0::Real[nu] = _lmpc_u0(mpc)
  "Zero placeholder passed as the reference argument when r is absent"
  final parameter r_dummy::Real[nr_eff] = fill(0.0, nr_eff)
  "Zero placeholder passed as the disturbance argument when d is absent"
  final parameter d_dummy::Real[nd_eff] = fill(0.0, nd_eff)
  "Zero placeholder passed as the estimated-disturbance argument when dhat is absent"
  final parameter dhat_dummy::Real[ndo_eff] = fill(0.0, ndo_eff)
  "Zero placeholder passed as the generalized-parameter argument when p is absent"
  final parameter p_dummy::Real[np_eff] = fill(0.0, np_eff)
  "Reference vector handed to the step function"
  variable r_int::Real[nr_eff]
  "Disturbance vector handed to the step function"
  variable d_int::Real[nd_eff]
  "Estimated-disturbance vector handed to the step function"
  variable dhat_int::Real[ndo_eff]
  "Generalized-parameter vector handed to the step function"
  variable p_int::Real[np_eff]
relations
  if has_r
    r_int = r
  else
    r_int = r_dummy
  end
  if has_d
    d_int = d
  else
    d_int = d_dummy
  end
  if has_dhat
    dhat_int = dhat
  else
    dhat_int = dhat_dummy
  end
  if has_p
    p_int = p
  else
    p_int = p_dummy
  end
  initial u@(clk-1) = u0
  # One scalar solve per tick (the composite flag), the blended control read back through an
  # accessor that depends on it; see LinearMPCOptimizer. The blended control of the previous tick
  # is the previous control every member sees.
  if use_c
    exitflag@clk = lmpc_bank_solve_c(w@clk, xhat@clk, r_int@clk, d_int@clk, dhat_int@clk, p_int@clk, u@(clk-1), mpc)
    for i in 1:nu
      u[i] = lmpc_bank_u_c(exitflag, w, i, mpc)
    end
  else
    exitflag@clk = lmpc_bank_solve(w@clk, xhat@clk, r_int@clk, d_int@clk, dhat_int@clk, p_int@clk, u@(clk-1), mpc)
    for i in 1:nu
      u[i] = lmpc_bank_u(exitflag, w, i, mpc)
    end
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/LinearMPCScheduledOptimizer.svg"},
    "doc": {"behavior": false}
  }
}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses