Skip to content
LIBRARY
Experimental.LinearMPCScheduledObserver.md

Experimental.LinearMPCScheduledObserver ​

Gain-scheduled state observer of a bank of LinearMPC.jl controllers: the steady-state Kalman filters (or offset-free observers) of the members, blended with the member weights w of LinearMPCScheduler. With F_j, G_j, ... the estimator matrices of member j, the recursion of LinearMPCObserver is run with the blended matrices Σ_j w_j F_j etc.:

Misplaced &

The members' matrices are stacked side by side into final parameters at model-build time and multiplied with the Kronecker product of the weights and the vectors, so the component remains plain matrix arithmetic and is exported to C like LinearMPCObserver. The blended Kalman gain is the usual gain-scheduled observer, exact at the knots. See LinearMPCScheduledController.

This component extends from LinearMPCBankPart

Usage ​

MPCComponents.Experimental.LinearMPCScheduledObserver(xaug0=_lmpc_xaug0(mpc), d_dummy=fill(0.0, nd_eff), Fs=_lmpc_bank_obs_F(mpc), Gs=_lmpc_bank_obs_G(mpc), Gds=_lmpc_bank_obs_Gd(mpc, nd_eff), fs=_lmpc_bank_obs_f(mpc), Cs=_lmpc_bank_obs_C(mpc), Dds=_lmpc_bank_obs_Dd(mpc, nd_eff), hs=_lmpc_bank_obs_h(mpc), Ks=_lmpc_bank_obs_K(mpc))

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)
nd_effWidth of the internal disturbance vector, max(nd_in, 1) (derived)–max(nd_in, 1)
nwxWidth of the stacked state matrices and of the weight-state Kronecker products, N*nxa (derived)–N * nxa
nwuWidth of the stacked input matrices, N*nu (derived)–N * nu
nwdWidth of the stacked disturbance matrices, N*nd_eff (derived)–N * nd_eff
nwiWidth of the stacked Kalman gains, N*ny_meas (derived)–N * ny_meas
xaug0Initial augmented state estimate [x0; d0], by default the one stored in the members–_lmpc_xaug0(mpc)

Connectors ​

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

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

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

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

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

Variables ​

NameDescriptionUnits
xpredAugmented state predicted for the current tick, before correction–
xprevThe prediction made at the previous tick, i.e. xpred delayed by one tick–
xcorrAugmented state after correction with the current measurement–
innovInnovation, the measurement minus its blended prediction–
d_intDisturbance vector used by the observer equations–
wxpKronecker product of the weights and xprev–
wxKronecker product of the weights and xcorr–
wuKronecker product of the weights and u–
wdKronecker product of the weights and d_int–
wiKronecker product of the weights and innov–

Behavior ​

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

Source ​

dyad
"""
Gain-scheduled state observer of a bank of LinearMPC.jl controllers: the steady-state Kalman
filters (or offset-free observers) of the members, blended with the member weights `w` of
`LinearMPCScheduler`. With `F_j, G_j, ...` the estimator matrices of member `j`, the recursion of
`LinearMPCObserver` is run with the blended matrices `Σ_j w_j F_j` etc.:

```math
\\begin{aligned}
\\hat x_{k|k} &= \\hat x_{k|k-1} + \\textstyle\\sum_j w_j K_j\\,(y_k - \\sum_j w_j (C_j\\hat x_{k|k-1} + D_{d,j} d_k + h_j)) \\\\
\\hat x_{k+1|k} &= \\textstyle\\sum_j w_j (F_j\\hat x_{k|k} + G_j u_k + G_{d,j} d_k + f_j)
\\end{aligned}
```

The members' matrices are stacked side by side into `final` parameters at model-build time and
multiplied with the Kronecker product of the weights and the vectors, so the component remains
plain matrix arithmetic and is exported to C like `LinearMPCObserver`. The blended Kalman gain is
the usual gain-scheduled observer, exact at the knots. See `LinearMPCScheduledController`.
"""
component LinearMPCScheduledObserver
  extends LinearMPCBankPart()
  "Sampled measurements (the rows of the observers' measurement matrix)"
  y = [RealInput@[clk]() for i in 1:ny_meas] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 825, "x2": 0, "y2": 925}}}
  }
  "Control applied at the current tick, used to predict the estimate for the next tick"
  u = [RealInput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 325, "x2": 0, "y2": 425}}}
  }
  "Measured disturbances; nd_meas entries, or nd_meas*Np column-major entries with disturbance preview (only the first nd_meas are used here)"
  d = [RealInput@[clk]() for i in 1:nd_in] if has_d {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 575, "x2": 0, "y2": 675}}}
  }
  "Member weights from LinearMPCScheduler"
  w = [RealInput@[clk]() for j in 1:N] {"Dyad": {"placement": {"diagram": {"x1": -100, "y1": 75, "x2": 0, "y2": 175}}}}
  "State estimate corrected with the current measurement"
  xhat = [RealOutput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 200, "x2": 1100, "y2": 300}}}
  }
  "Estimated offset-free disturbance (only for offset-free observers)"
  dhat = [RealOutput@[clk]() for i in 1:ndo] if has_dhat {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 700, "x2": 1100, "y2": 800}}}
  }
  "Width of the internal disturbance vector, max(nd_in, 1) (derived)"
  final structural parameter nd_eff::Integer = max(nd_in, 1)
  "Width of the stacked state matrices and of the weight-state Kronecker products, N*nxa (derived)"
  final structural parameter nwx::Integer = N * nxa
  "Width of the stacked input matrices, N*nu (derived)"
  final structural parameter nwu::Integer = N * nu
  "Width of the stacked disturbance matrices, N*nd_eff (derived)"
  final structural parameter nwd::Integer = N * nd_eff
  "Width of the stacked Kalman gains, N*ny_meas (derived)"
  final structural parameter nwi::Integer = N * ny_meas
  "Initial augmented state estimate [x0; d0], by default the one stored in the members"
  parameter xaug0::Real[nxa] = _lmpc_xaug0(mpc)
  "Zero placeholder used as the disturbance vector when d is absent"
  final parameter d_dummy::Real[nd_eff] = fill(0.0, nd_eff)
  "State matrices of the estimators, stacked side by side [F_1 ... F_N]"
  final parameter Fs::Real[nxa, nwx] = _lmpc_bank_obs_F(mpc)
  "Input matrices of the estimators, stacked"
  final parameter Gs::Real[nxa, nwu] = _lmpc_bank_obs_G(mpc)
  "Measured-disturbance input matrices of the estimators, each padded to the internal disturbance width, stacked"
  final parameter Gds::Real[nxa, nwd] = _lmpc_bank_obs_Gd(mpc, nd_eff)
  "Affine offsets of the estimator state updates, one column per member"
  final parameter fs::Real[nxa, N] = _lmpc_bank_obs_f(mpc)
  "Measurement matrices of the estimators, stacked"
  final parameter Cs::Real[ny_meas, nwx] = _lmpc_bank_obs_C(mpc)
  "Measured-disturbance feedthroughs of the estimators, each padded to the internal disturbance width, stacked"
  final parameter Dds::Real[ny_meas, nwd] = _lmpc_bank_obs_Dd(mpc, nd_eff)
  "Affine offsets of the measurement equations, one column per member"
  final parameter hs::Real[ny_meas, N] = _lmpc_bank_obs_h(mpc)
  "Steady-state Kalman gains, stacked"
  final parameter Ks::Real[nxa, nwi] = _lmpc_bank_obs_K(mpc)
  "Augmented state predicted for the current tick, before correction"
  variable xpred::Real[nxa]
  "The prediction made at the previous tick, i.e. xpred delayed by one tick"
  variable xprev::Real[nxa]
  "Augmented state after correction with the current measurement"
  variable xcorr::Real[nxa]
  "Innovation, the measurement minus its blended prediction"
  variable innov::Real[ny_meas]
  "Disturbance vector used by the observer equations"
  variable d_int::Real[nd_eff]
  "Kronecker product of the weights and xprev"
  variable wxp::Real[nwx]
  "Kronecker product of the weights and xcorr"
  variable wx::Real[nwx]
  "Kronecker product of the weights and u"
  variable wu::Real[nwu]
  "Kronecker product of the weights and d_int"
  variable wd::Real[nwd]
  "Kronecker product of the weights and innov"
  variable wi::Real[nwi]
relations
  if has_d
    d_int = d
  else
    d_int = d_dummy
  end
  initial xpred@(clk-1) = xaug0
  xprev@clk = xpred@(clk-1)
  for j in 1:N
    for i in 1:nxa
      wxp[(j - 1) * nxa + i] = w[j] * xprev[i]
      wx[(j - 1) * nxa + i] = w[j] * xcorr[i]
    end
    for i in 1:nu
      wu[(j - 1) * nu + i] = w[j] * u[i]
    end
    for i in 1:nd_eff
      wd[(j - 1) * nd_eff + i] = w[j] * d_int[i]
    end
    for i in 1:ny_meas
      wi[(j - 1) * ny_meas + i] = w[j] * innov[i]
    end
  end
  innov = y - Cs * wxp - Dds * wd - hs * w
  xcorr = xprev + Ks * wi
  xpred = Fs * wx + Gs * wu + Gds * wd + fs * w
  # The augmented state is [x; d_offset]
  for i in 1:nx
    xhat[i] = xcorr[i]
  end
  if has_dhat
    for i in 1:ndo
      dhat[i] = xcorr[nx + i]
    end
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/LinearMPCScheduledObserver.svg"},
    "doc": {"behavior": false}
  }
}
end
Flattened Source
dyad
"""
Gain-scheduled state observer of a bank of LinearMPC.jl controllers: the steady-state Kalman
filters (or offset-free observers) of the members, blended with the member weights `w` of
`LinearMPCScheduler`. With `F_j, G_j, ...` the estimator matrices of member `j`, the recursion of
`LinearMPCObserver` is run with the blended matrices `Σ_j w_j F_j` etc.:

```math
\\begin{aligned}
\\hat x_{k|k} &= \\hat x_{k|k-1} + \\textstyle\\sum_j w_j K_j\\,(y_k - \\sum_j w_j (C_j\\hat x_{k|k-1} + D_{d,j} d_k + h_j)) \\\\
\\hat x_{k+1|k} &= \\textstyle\\sum_j w_j (F_j\\hat x_{k|k} + G_j u_k + G_{d,j} d_k + f_j)
\\end{aligned}
```

The members' matrices are stacked side by side into `final` parameters at model-build time and
multiplied with the Kronecker product of the weights and the vectors, so the component remains
plain matrix arithmetic and is exported to C like `LinearMPCObserver`. The blended Kalman gain is
the usual gain-scheduled observer, exact at the knots. See `LinearMPCScheduledController`.
"""
component LinearMPCScheduledObserver
  "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)
  "Sampled measurements (the rows of the observers' measurement matrix)"
  y = [RealInput@[clk]() for i in 1:ny_meas] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 825, "x2": 0, "y2": 925}}}
  }
  "Control applied at the current tick, used to predict the estimate for the next tick"
  u = [RealInput@[clk]() for i in 1:nu] {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 325, "x2": 0, "y2": 425}}}
  }
  "Measured disturbances; nd_meas entries, or nd_meas*Np column-major entries with disturbance preview (only the first nd_meas are used here)"
  d = [RealInput@[clk]() for i in 1:nd_in] if has_d {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 575, "x2": 0, "y2": 675}}}
  }
  "Member weights from LinearMPCScheduler"
  w = [RealInput@[clk]() for j in 1:N] {"Dyad": {"placement": {"diagram": {"x1": -100, "y1": 75, "x2": 0, "y2": 175}}}}
  "State estimate corrected with the current measurement"
  xhat = [RealOutput@[clk]() for i in 1:nx] {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 200, "x2": 1100, "y2": 300}}}
  }
  "Estimated offset-free disturbance (only for offset-free observers)"
  dhat = [RealOutput@[clk]() for i in 1:ndo] if has_dhat {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "y1": 700, "x2": 1100, "y2": 800}}}
  }
  "Width of the internal disturbance vector, max(nd_in, 1) (derived)"
  final structural parameter nd_eff::Integer = max(nd_in, 1)
  "Width of the stacked state matrices and of the weight-state Kronecker products, N*nxa (derived)"
  final structural parameter nwx::Integer = N * nxa
  "Width of the stacked input matrices, N*nu (derived)"
  final structural parameter nwu::Integer = N * nu
  "Width of the stacked disturbance matrices, N*nd_eff (derived)"
  final structural parameter nwd::Integer = N * nd_eff
  "Width of the stacked Kalman gains, N*ny_meas (derived)"
  final structural parameter nwi::Integer = N * ny_meas
  "Initial augmented state estimate [x0; d0], by default the one stored in the members"
  parameter xaug0::Real[nxa] = _lmpc_xaug0(mpc)
  "Zero placeholder used as the disturbance vector when d is absent"
  final parameter d_dummy::Real[nd_eff] = fill(0.0, nd_eff)
  "State matrices of the estimators, stacked side by side [F_1 ... F_N]"
  final parameter Fs::Real[nxa, nwx] = _lmpc_bank_obs_F(mpc)
  "Input matrices of the estimators, stacked"
  final parameter Gs::Real[nxa, nwu] = _lmpc_bank_obs_G(mpc)
  "Measured-disturbance input matrices of the estimators, each padded to the internal disturbance width, stacked"
  final parameter Gds::Real[nxa, nwd] = _lmpc_bank_obs_Gd(mpc, nd_eff)
  "Affine offsets of the estimator state updates, one column per member"
  final parameter fs::Real[nxa, N] = _lmpc_bank_obs_f(mpc)
  "Measurement matrices of the estimators, stacked"
  final parameter Cs::Real[ny_meas, nwx] = _lmpc_bank_obs_C(mpc)
  "Measured-disturbance feedthroughs of the estimators, each padded to the internal disturbance width, stacked"
  final parameter Dds::Real[ny_meas, nwd] = _lmpc_bank_obs_Dd(mpc, nd_eff)
  "Affine offsets of the measurement equations, one column per member"
  final parameter hs::Real[ny_meas, N] = _lmpc_bank_obs_h(mpc)
  "Steady-state Kalman gains, stacked"
  final parameter Ks::Real[nxa, nwi] = _lmpc_bank_obs_K(mpc)
  "Augmented state predicted for the current tick, before correction"
  variable xpred::Real[nxa]
  "The prediction made at the previous tick, i.e. xpred delayed by one tick"
  variable xprev::Real[nxa]
  "Augmented state after correction with the current measurement"
  variable xcorr::Real[nxa]
  "Innovation, the measurement minus its blended prediction"
  variable innov::Real[ny_meas]
  "Disturbance vector used by the observer equations"
  variable d_int::Real[nd_eff]
  "Kronecker product of the weights and xprev"
  variable wxp::Real[nwx]
  "Kronecker product of the weights and xcorr"
  variable wx::Real[nwx]
  "Kronecker product of the weights and u"
  variable wu::Real[nwu]
  "Kronecker product of the weights and d_int"
  variable wd::Real[nwd]
  "Kronecker product of the weights and innov"
  variable wi::Real[nwi]
relations
  if has_d
    d_int = d
  else
    d_int = d_dummy
  end
  initial xpred@(clk-1) = xaug0
  xprev@clk = xpred@(clk-1)
  for j in 1:N
    for i in 1:nxa
      wxp[(j - 1) * nxa + i] = w[j] * xprev[i]
      wx[(j - 1) * nxa + i] = w[j] * xcorr[i]
    end
    for i in 1:nu
      wu[(j - 1) * nu + i] = w[j] * u[i]
    end
    for i in 1:nd_eff
      wd[(j - 1) * nd_eff + i] = w[j] * d_int[i]
    end
    for i in 1:ny_meas
      wi[(j - 1) * ny_meas + i] = w[j] * innov[i]
    end
  end
  innov = y - Cs * wxp - Dds * wd - hs * w
  xcorr = xprev + Ks * wi
  xpred = Fs * wx + Gs * wu + Gds * wd + fs * w
  # The augmented state is [x; d_offset]
  for i in 1:nx
    xhat[i] = xcorr[i]
  end
  if has_dhat
    for i in 1:ndo
      dhat[i] = xcorr[nx + i]
    end
  end
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MPCComponents/LinearMPCScheduledObserver.svg"},
    "doc": {"behavior": false}
  }
}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses