Skip to content
LIBRARY
Examples.LinearMPCRobustExample.md

Examples.LinearMPCRobustExample ​

Robust constraint tightening of LinearMPCController: two identical loops around the double integrator DoubleIntegratorExamplePlant track a reference on the upper output bound ymax under the same unmeasured load on the input, a square wave of amplitude wbar. Only the position is measured; a Kalman filter estimates the state, with the process-noise covariance of the load, R1 = B B' wbar^2 plus a small regularization.

The nominal controller mpc1 predicts with the undisturbed model and plans the position onto the bound, so every half period in which the load pushes upward it exceeds the bound, by about 0.18 at the default wbar. The robust controller mpc2 tightens its constraints for a disturbance input that acts like the control (Bw = B), bounded by wbar (disturbance_input_max), and for an error of the state estimate of kappa standard deviations of the stationary estimation error (x0_uncertainty_sigma). Its position stays below the bound, which costs a margin of about 0.08 from the reference. Both controllers use prestabilizing_feedback, without which the tightening of the double integrator grows with the prediction horizon. The output bound is soft, so the nominal controller remains feasible after a violation.

Usage ​

MPCComponents.Examples.LinearMPCRobustExample()

Parameters: ​

NameDescriptionUnitsDefault value
wbarBound of the load on the input, |w| <= wbar; the amplitude of the square wave–0.2
kappaUncertainty of the state estimate of the robust controller, in standard deviations of the stationary estimation error–3.0
ymaxUpper output bound of both controllers, also the reference–1.0
NpPrediction horizon of both controllers–20
TsSample interval; fixed by the periodic clock inside DoubleIntegratorExamplePlant–0.1
AState matrix of the plant model, the double integrator discretized at Ts (derived)–[1.0 Ts; 0.0 1.0]
BInput matrix of the plant model (derived)–[Ts * Ts / 2; Ts;;]
COutput matrix of the plant model: the plant measures and tracks the position (derived)–[1.0 0.0]
R1Process-noise covariance of both observers, B B' wbar^2 plus 1e-6 I (derived)–[Ts * Ts * ...bar + 1e-6]

Variables ​

NameDescriptionUnits
y1Position, nominal controller–
u1Control, nominal controller–
y2Position, robust controller–
u2Control, robust controller–

Behavior ​

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

Source ​

dyad
"""
Robust constraint tightening of `LinearMPCController`: two identical loops around the double
integrator `DoubleIntegratorExamplePlant` track a reference on the upper output bound `ymax` under
the same unmeasured load on the input, a square wave of amplitude `wbar`. Only the position is
measured; a Kalman filter estimates the state, with the process-noise covariance of the load,
`R1 = B B' wbar^2` plus a small regularization.

The nominal controller `mpc1` predicts with the undisturbed model and plans the position onto the
bound, so every half period in which the load pushes upward it exceeds the bound, by about 0.18 at
the default `wbar`. The robust controller `mpc2` tightens its constraints for a disturbance input
that acts like the control (`Bw = B`), bounded by `wbar` (`disturbance_input_max`), and for an error
of the state estimate of `kappa` standard deviations of the stationary estimation error
(`x0_uncertainty_sigma`). Its position stays below the bound, which costs a margin of about 0.08 from
the reference. Both controllers use `prestabilizing_feedback`, without which the tightening of the
double integrator grows with the prediction horizon. The output bound is soft, so the nominal
controller remains feasible after a violation.
"""
example component LinearMPCRobustExample
  "Bound of the load on the input, |w| <= wbar; the amplitude of the square wave"
  structural parameter wbar::Real = 0.2
  "Uncertainty of the state estimate of the robust controller, in standard deviations of the stationary estimation error"
  structural parameter kappa::Real = 3.0
  "Upper output bound of both controllers, also the reference"
  structural parameter ymax::Real = 1.0
  "Prediction horizon of both controllers"
  structural parameter Np::Integer = 20
  "Sample interval; fixed by the periodic clock inside DoubleIntegratorExamplePlant"
  final structural parameter Ts::Real = 0.1
  "State matrix of the plant model, the double integrator discretized at Ts (derived)"
  final structural parameter A::Real[:, :] = [[1.0, Ts], [0.0, 1.0]]
  "Input matrix of the plant model (derived)"
  final structural parameter B::Real[:, :] = [[Ts * Ts / 2], [Ts]]
  "Output matrix of the plant model: the plant measures and tracks the position (derived)"
  final structural parameter C::Real[:, :] = [[1.0, 0.0]]
  "Process-noise covariance of both observers, B B' wbar^2 plus 1e-6 I (derived)"
  final structural parameter R1::Real[:, :] = [[Ts * Ts * Ts * Ts / 4 * wbar * wbar + 1e-6, Ts * Ts * Ts / 2 * wbar * wbar], [Ts * Ts * Ts / 2 * wbar * wbar, Ts * Ts * wbar * wbar + 1e-6]]
  "Position reference, on the output bound"
  ref = BlockComponents.Sources.Constant(k = ymax) {
    "Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 220, "y2": 320}}}
  }
  sr = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 250, "x2": 350, "y1": 220, "y2": 320}}}
  }
  "Unmeasured load on the input: a square wave between wbar and -wbar with a period of 4 s"
  load = BlockComponents.Sources.Square(amplitude = wbar, frequency = 0.25) {
    "Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 560, "y2": 660}}}
  }
  "Plant of the nominal loop"
  plant1 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
    "Dyad": {"placement": {"diagram": {"x1": 800, "x2": 900, "y1": 100, "y2": 200}}}
  }
  "Nominal controller"
  mpc1 = MPCComponents.LinearMPCController(final A = A, final B = B, final C = C, final Ts = Ts, final Np = Np, Q1 = [1.0], Q3 = [0.1], umin = [-1.0], umax = [1.0], final ymax = [ymax], prestabilizing_feedback = true, final R1 = R1, R2 = [0.0001]) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 100, "y2": 200}}}
  }
  "Plant of the robust loop"
  plant2 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
    "Dyad": {"placement": {"diagram": {"x1": 800, "x2": 900, "y1": 340, "y2": 440}}}
  }
  "Robust controller: the nominal one with the tightening for the load and the estimation error"
  mpc2 = MPCComponents.LinearMPCController(final A = A, final B = B, final C = C, final Ts = Ts, final Np = Np, Q1 = [1.0], Q3 = [0.1], umin = [-1.0], umax = [1.0], final ymax = [ymax], prestabilizing_feedback = true, final R1 = R1, R2 = [0.0001], final Bw = B, final disturbance_input_max = [wbar], final x0_uncertainty_sigma = kappa) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 340, "y2": 440}}}
  }
  "Position, nominal controller"
  variable y1::Real
  "Control, nominal controller"
  variable u1::Real
  "Position, robust controller"
  variable y2::Real
  "Control, robust controller"
  variable u2::Real
relations
  y1 = plant1.y
  u1 = mpc1.u[1]
  y2 = plant2.y
  u2 = mpc2.u[1]
  connect(ref.y, sr.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(sr.y, mpc1.r[1], mpc2.r[1]) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 480, "y": 270}], "E": -1},
        {"S": -1, "M": [{"x": 480, "y": 112.5}], "E": 2},
        {"S": -1, "M": [{"x": 480, "y": 352.5}], "E": 3}
      ],
      "junctions": [{"x": 480, "y": 270}],
      "path": {}
    }
  }
  connect(load.y, plant1.d, plant2.d) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 980, "y": 610}], "E": -2},
        {"S": -1, "M": [{"x": 850, "y": 60}], "E": 2},
        {"S": -2, "M": [{"x": 850, "y": 300}], "E": 3},
        {"S": -2, "M": [], "E": -1}
      ],
      "junctions": [{"x": 980, "y": 60}, {"x": 980, "y": 300}],
      "path": {}
    }
  }
  connect(plant1.ym, mpc1.y[1]) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 930, "y": 150},
            {"x": 930, "y": 250},
            {"x": 540, "y": 250},
            {"x": 540, "y": 187.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(mpc1.u[1], plant1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 740, "y": 137.5}, {"x": 740, "y": 150}], "E": 2}],
      "path": {}
    }
  }
  connect(plant2.ym, mpc2.y[1]) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 930, "y": 390},
            {"x": 930, "y": 490},
            {"x": 540, "y": 490},
            {"x": 540, "y": 427.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(mpc2.u[1], plant2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 740, "y": 377.5}, {"x": 740, "y": 390}], "E": 2}],
      "path": {}
    }
  }
metadata {
  "Dyad": {
    "tests": {"case1": {"stop": 20}},
    "doc": {"behavior": false},
    "labels": [
      {
        "label": "Nominal",
        "x": 1160,
        "y": 150,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Robust",
        "x": 1160,
        "y": 390,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Reference",
        "x": 200,
        "y": 180,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Unmeasured load",
        "x": 200,
        "y": 520,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      }
    ]
  }
}
end
Flattened Source
dyad
"""
Robust constraint tightening of `LinearMPCController`: two identical loops around the double
integrator `DoubleIntegratorExamplePlant` track a reference on the upper output bound `ymax` under
the same unmeasured load on the input, a square wave of amplitude `wbar`. Only the position is
measured; a Kalman filter estimates the state, with the process-noise covariance of the load,
`R1 = B B' wbar^2` plus a small regularization.

The nominal controller `mpc1` predicts with the undisturbed model and plans the position onto the
bound, so every half period in which the load pushes upward it exceeds the bound, by about 0.18 at
the default `wbar`. The robust controller `mpc2` tightens its constraints for a disturbance input
that acts like the control (`Bw = B`), bounded by `wbar` (`disturbance_input_max`), and for an error
of the state estimate of `kappa` standard deviations of the stationary estimation error
(`x0_uncertainty_sigma`). Its position stays below the bound, which costs a margin of about 0.08 from
the reference. Both controllers use `prestabilizing_feedback`, without which the tightening of the
double integrator grows with the prediction horizon. The output bound is soft, so the nominal
controller remains feasible after a violation.
"""
example component LinearMPCRobustExample
  "Bound of the load on the input, |w| <= wbar; the amplitude of the square wave"
  structural parameter wbar::Real = 0.2
  "Uncertainty of the state estimate of the robust controller, in standard deviations of the stationary estimation error"
  structural parameter kappa::Real = 3.0
  "Upper output bound of both controllers, also the reference"
  structural parameter ymax::Real = 1.0
  "Prediction horizon of both controllers"
  structural parameter Np::Integer = 20
  "Sample interval; fixed by the periodic clock inside DoubleIntegratorExamplePlant"
  final structural parameter Ts::Real = 0.1
  "State matrix of the plant model, the double integrator discretized at Ts (derived)"
  final structural parameter A::Real[:, :] = [[1.0, Ts], [0.0, 1.0]]
  "Input matrix of the plant model (derived)"
  final structural parameter B::Real[:, :] = [[Ts * Ts / 2], [Ts]]
  "Output matrix of the plant model: the plant measures and tracks the position (derived)"
  final structural parameter C::Real[:, :] = [[1.0, 0.0]]
  "Process-noise covariance of both observers, B B' wbar^2 plus 1e-6 I (derived)"
  final structural parameter R1::Real[:, :] = [[Ts * Ts * Ts * Ts / 4 * wbar * wbar + 1e-6, Ts * Ts * Ts / 2 * wbar * wbar], [Ts * Ts * Ts / 2 * wbar * wbar, Ts * Ts * wbar * wbar + 1e-6]]
  "Position reference, on the output bound"
  ref = BlockComponents.Sources.Constant(k = ymax) {
    "Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 220, "y2": 320}}}
  }
  sr = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 250, "x2": 350, "y1": 220, "y2": 320}}}
  }
  "Unmeasured load on the input: a square wave between wbar and -wbar with a period of 4 s"
  load = BlockComponents.Sources.Square(amplitude = wbar, frequency = 0.25) {
    "Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 560, "y2": 660}}}
  }
  "Plant of the nominal loop"
  plant1 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
    "Dyad": {"placement": {"diagram": {"x1": 800, "x2": 900, "y1": 100, "y2": 200}}}
  }
  "Nominal controller"
  mpc1 = MPCComponents.LinearMPCController(final A = A, final B = B, final C = C, final Ts = Ts, final Np = Np, Q1 = [1.0], Q3 = [0.1], umin = [-1.0], umax = [1.0], final ymax = [ymax], prestabilizing_feedback = true, final R1 = R1, R2 = [0.0001]) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 100, "y2": 200}}}
  }
  "Plant of the robust loop"
  plant2 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
    "Dyad": {"placement": {"diagram": {"x1": 800, "x2": 900, "y1": 340, "y2": 440}}}
  }
  "Robust controller: the nominal one with the tightening for the load and the estimation error"
  mpc2 = MPCComponents.LinearMPCController(final A = A, final B = B, final C = C, final Ts = Ts, final Np = Np, Q1 = [1.0], Q3 = [0.1], umin = [-1.0], umax = [1.0], final ymax = [ymax], prestabilizing_feedback = true, final R1 = R1, R2 = [0.0001], final Bw = B, final disturbance_input_max = [wbar], final x0_uncertainty_sigma = kappa) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 340, "y2": 440}}}
  }
  "Position, nominal controller"
  variable y1::Real
  "Control, nominal controller"
  variable u1::Real
  "Position, robust controller"
  variable y2::Real
  "Control, robust controller"
  variable u2::Real
relations
  y1 = plant1.y
  u1 = mpc1.u[1]
  y2 = plant2.y
  u2 = mpc2.u[1]
  connect(ref.y, sr.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(sr.y, mpc1.r[1], mpc2.r[1]) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 480, "y": 270}], "E": -1},
        {"S": -1, "M": [{"x": 480, "y": 112.5}], "E": 2},
        {"S": -1, "M": [{"x": 480, "y": 352.5}], "E": 3}
      ],
      "junctions": [{"x": 480, "y": 270}],
      "path": {}
    }
  }
  connect(load.y, plant1.d, plant2.d) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 980, "y": 610}], "E": -2},
        {"S": -1, "M": [{"x": 850, "y": 60}], "E": 2},
        {"S": -2, "M": [{"x": 850, "y": 300}], "E": 3},
        {"S": -2, "M": [], "E": -1}
      ],
      "junctions": [{"x": 980, "y": 60}, {"x": 980, "y": 300}],
      "path": {}
    }
  }
  connect(plant1.ym, mpc1.y[1]) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 930, "y": 150},
            {"x": 930, "y": 250},
            {"x": 540, "y": 250},
            {"x": 540, "y": 187.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(mpc1.u[1], plant1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 740, "y": 137.5}, {"x": 740, "y": 150}], "E": 2}],
      "path": {}
    }
  }
  connect(plant2.ym, mpc2.y[1]) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 930, "y": 390},
            {"x": 930, "y": 490},
            {"x": 540, "y": 490},
            {"x": 540, "y": 427.5}
          ],
          "E": 2
        }
      ],
      "path": {}
    }
  }
  connect(mpc2.u[1], plant2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 740, "y": 377.5}, {"x": 740, "y": 390}], "E": 2}],
      "path": {}
    }
  }
metadata {
  "Dyad": {
    "tests": {"case1": {"stop": 20}},
    "doc": {"behavior": false},
    "labels": [
      {
        "label": "Nominal",
        "x": 1160,
        "y": 150,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Robust",
        "x": 1160,
        "y": 390,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Reference",
        "x": 200,
        "y": 180,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "Unmeasured load",
        "x": 200,
        "y": 520,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      }
    ]
  }
}
end


Test Cases ​

Test Case case1 ​

  • Examples

  • Experiments

  • Analyses