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: ​
| Name | Description | Units | Default value |
|---|---|---|---|
wbar | Bound of the load on the input, |w| <= wbar; the amplitude of the square wave | – | 0.2 |
kappa | Uncertainty of the state estimate of the robust controller, in standard deviations of the stationary estimation error | – | 3.0 |
ymax | Upper output bound of both controllers, also the reference | – | 1.0 |
Np | Prediction horizon of both controllers | – | 20 |
Ts | Sample interval; fixed by the periodic clock inside DoubleIntegratorExamplePlant | – | 0.1 |
A | State matrix of the plant model, the double integrator discretized at Ts (derived) | – | [1.0 Ts; 0.0 1.0] |
B | Input matrix of the plant model (derived) | – | [Ts * Ts / 2; Ts;;] |
C | Output matrix of the plant model: the plant measures and tracks the position (derived) | – | [1.0 0.0] |
R1 | Process-noise covariance of both observers, B B' wbar^2 plus 1e-6 I (derived) | – | [Ts * Ts * ...bar + 1e-6] |
Variables ​
| Name | Description | Units |
|---|---|---|
y1 | Position, nominal controller | – |
u1 | Control, nominal controller | – |
y2 | Position, robust controller | – |
u2 | Control, robust controller | – |
Behavior ​
Behavior of this component cannot be rendered because it includes path variables.
Source ​
"""
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"
}
}
]
}
}
endFlattened Source
"""
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"
}
}
]
}
}
endTest Cases ​
Test Case case1 ​
Related ​
Examples
Experiments
Analyses