Examples.PIDEquivalentMPCConstrainedExample ​
Three closed loops around the same double-integrator plant (DoubleIntegratorExamplePlant), driven by the same reference and load disturbance: a DiscreteComponents.DiscretePIDStandard (loop 1), the equivalent MPCComponents.PIDEquivalentMPCController given the plant model, the measured disturbance and its preview (loop 2), and the same controller with the additional plant-output constraint y_p <= 2.9 written as the general constraint Ax = C (loop 3).
The scenario visits three regimes. The first reference step (to 2 at t = 1) saturates the control at y_max = 1.0 for about a second; the spell is too short to wind the loop up, so all three loops handle it near-identically. The second step (to 2.8 at t = 25) keeps the control inside its limits, but loops 1 and 2 overshoot to about 3.03 — through the 2.9 ceiling — while only loop 3 brakes early and follows the limit exactly at 2.9. At t = 45 a measured load pulse (-1.5 at the plant input for two seconds, known Np - 1 samples ahead through a DelayLine, as a real load schedule would be) hits all three plants. The pulse exceeds the control authority, so a large excursion is unavoidable — but preview turns reaction into preparation: the PID, blind to the schedule, is carried from 2.8 almost to 0 and rebounds through the ceiling to about 3.46, while loops 2 and 3 pre-climb and dip only to about 1.35. Loop 3 delivers the same rejection while never exceeding 2.9 — its pre-climb and its rebound are both capped by the constraint. The tracking anti-windup Ni lets both MPC loops settle cleanly after the pulse despite the long saturated spell. Away from the limits the three loops coincide.
Usage ​
MPCComponents.Examples.PIDEquivalentMPCConstrainedExample()
Parameters: ​
| Name | Description | Units | Default value |
|---|---|---|---|
Np | Prediction horizon for the MPC controllers | – | 20 |
output_trajectories | Record the predicted trajectories and the QP statistics of the controller(s), for mpc_gui | – | false |
Variables ​
| Name | Description | Units |
|---|---|---|
y1 | Position under the PID | – |
u1 | Control of the PID | – |
y2 | Position under the equivalent MPC | – |
u2 | Control of the equivalent MPC | – |
y3 | Position under the output-constrained MPC | – |
u3 | Control of the output-constrained MPC | – |
Behavior ​
Behavior of this component cannot be rendered because it includes path variables.
Source ​
"""
Three closed loops around the same double-integrator plant (`DoubleIntegratorExamplePlant`), driven by the
same reference and load disturbance: a `DiscreteComponents.DiscretePIDStandard` (loop 1), the
equivalent `MPCComponents.PIDEquivalentMPCController` given the plant model, the measured
disturbance and its preview (loop 2), and the same controller with the additional plant-output
constraint `y_p <= 2.9` written as the general constraint `Ax = C` (loop 3).
The scenario visits three regimes. The first reference step (to 2 at t = 1) saturates the
control at `y_max = 1.0` for about a second; the spell is too short to wind the loop up, so
all three loops handle it near-identically. The second step (to 2.8 at t = 25) keeps the
control inside its limits, but loops 1 and 2 overshoot to about 3.03 — through the 2.9
ceiling — while only loop 3 brakes early and follows the limit exactly at 2.9. At t = 45 a
measured load pulse (-1.5 at the plant input for two seconds, known Np - 1 samples ahead
through a `DelayLine`, as a real load schedule would be) hits all three plants. The pulse
exceeds the control authority, so a large excursion is unavoidable — but preview turns
reaction into preparation: the PID, blind to the schedule, is carried from 2.8 almost to 0
and rebounds through the ceiling to about 3.46, while loops 2 and 3 pre-climb and dip only
to about 1.35. Loop 3 delivers the same rejection while never exceeding 2.9 — its pre-climb
and its rebound are both capped by the constraint. The tracking anti-windup `Ni` lets both
MPC loops settle cleanly after the pulse despite the long saturated spell. Away from the limits the
three loops coincide.
"""
example component PIDEquivalentMPCConstrainedExample
"Reference: a large step (control saturates), then a step toward the output ceiling"
step1 = BlockComponents.Sources.Step(start_time = 10.0, height = 1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 150, "y2": 250}}}
}
step2 = BlockComponents.Sources.Step(start_time = 25.0, height = 0.8) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 280, "y2": 380}}}
}
"Load pulse schedule, generated Np - 1 samples ahead of real time: -1.5 for two seconds from t = 45, well beyond the control authority"
dist = BlockComponents.Sources.Step(start_time = 45.0 - 0.1 * (Np - 1), height = -1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 540, "y2": 640}}}
}
dist2 = BlockComponents.Sources.Step(start_time = 47.0 - 0.1 * (Np - 1), height = 1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 700, "y2": 800}}}
}
dsum = BlockComponents.Math.Add() {
"Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 620, "y2": 720}}}
}
sdk = DiscreteComponents.Sampler() {
"Dyad": {"placement": {"diagram": {"x1": 190, "x2": 290, "y1": 620, "y2": 720}}}
}
"Preview vector of the pulse: tap Np is the schedule itself, tap 1 the load that applies now"
dline = MPCComponents.DelayLine(final N = Np) {
"Dyad": {"placement": {"diagram": {"x1": 320, "x2": 420, "y1": 620, "y2": 720}}}
}
"Holds the current load (the most-delayed tap) for the continuous plants"
dhold = DiscreteComponents.ZeroOrderHold() {
"Dyad": {"placement": {"diagram": {"x1": 440, "x2": 540, "y1": 900, "y2": 1000}}}
}
"Plant driven by the PID"
plant1 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 240, "y2": 340}}}
}
sr1 = DiscreteComponents.Sampler() {
"Dyad": {"placement": {"diagram": {"x1": 200, "x2": 300, "y1": 290, "y2": 390}}}
}
pid = DiscreteComponents.DiscretePIDStandard(Ts = 0.1, K = 1.0, Ti = 10.0, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0) {
"Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 240, "y2": 340}}}
}
"Plant driven by the equivalent MPC"
plant2 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 470, "y2": 570}}}
}
mpc = MPCComponents.PIDEquivalentMPCController(A = [[0.0, 1.0], [0.0, 0.0]], B = [[0.0], [1.0]], C = [[1.0, 0.0]], continuous = true, Ts = 0.1, Bd = [[0.0], [1.0]], final disturbance_preview = true, final Np = Np, K = 1.0, Ti = 10.0, Ni = 0.9, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0, output_trajectories = output_trajectories) {
"Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 470, "y2": 570}}}
}
"Plant driven by the equivalent MPC with the output constraint"
plant3 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 710, "y2": 810}}}
}
mpcc = MPCComponents.PIDEquivalentMPCController(A = [[0.0, 1.0], [0.0, 0.0]], B = [[0.0], [1.0]], C = [[1.0, 0.0]], continuous = true, Ts = 0.1, Bd = [[0.0], [1.0]], final disturbance_preview = true, final Np = Np, K = 1.0, Ti = 10.0, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0, Ax = [[1.0, 0.0]], lb = [-1e30], ub = [2.9], Ni = 0.9, output_trajectories = output_trajectories) {
"Dyad": {"placement": {"diagram": {"x1": 590, "x2": 690, "y1": 710, "y2": 810}}}
}
ramp = BlockComponents.Sources.Ramp(duration = 3, height = 0.5, start_time = 0.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 400, "y2": 500}}}
}
radd = BlockComponents.Math.Add3() {
"Dyad": {"placement": {"diagram": {"x1": 80, "x2": 180, "y1": 290, "y2": 390}}}
}
"Prediction horizon for the MPC controllers"
structural parameter Np::Integer = 20
"Record the predicted trajectories and the QP statistics of the controller(s), for mpc_gui"
structural parameter output_trajectories::Boolean = false
"Position under the PID"
variable y1::Real
"Control of the PID"
variable u1::Real
"Position under the equivalent MPC"
variable y2::Real
"Control of the equivalent MPC"
variable u2::Real
"Position under the output-constrained MPC"
variable y3::Real
"Control of the output-constrained MPC"
variable u3::Real
relations
y1 = plant1.y
u1 = plant1.uh
y2 = plant2.y
u2 = plant2.uh
y3 = plant3.y
u3 = plant3.uh
connect(dsum.y, sdk.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(sdk.y, dline.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(dline.y, mpc.d) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 425, "y": 430}, {"x": 630, "y": 430}], "E": 2}],
"path": {}
}
}
connect(dline.y, mpcc.d) {"Dyad": {"edges": [{"S": 1, "M": [{"x": 640, "y": 670}], "E": 2}], "path": {}}}
connect(dline.y[1], dhold.u) {"Dyad": {"edges": [{"S": 1, "M": [{"x": 425, "y": 950}], "E": 2}], "path": {}}}
connect(sr1.y, pid.u_s, mpc.u_s, mpcc.u_s) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 500, "y": 262}], "E": 2},
{"S": 3, "M": [{"x": 518.75, "y": 495}, {"x": 518.75, "y": 490}], "E": -2},
{"S": -2, "M": [], "E": -1},
{"S": 4, "M": [{"x": 500, "y": 736}], "E": -2}
],
"junctions": [{"x": 500, "y": 340}, {"x": 500, "y": 490}],
"path": {}
}
}
connect(plant1.ym, pid.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 290},
{"x": 890, "y": 370},
{"x": 560, "y": 370},
{"x": 560, "y": 314}
],
"E": 2
}
],
"path": {}
}
}
connect(pid.y, plant1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(plant2.ym, mpc.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 520},
{"x": 890, "y": 610},
{"x": 560, "y": 610},
{"x": 560, "y": 545}
],
"E": 2
}
],
"path": {}
}
}
connect(mpc.y, plant2.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 720, "y": 545}, {"x": 720, "y": 520}], "E": 2}],
"path": {}
}
}
connect(plant3.ym, mpcc.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 760},
{"x": 890, "y": 890},
{"x": 560, "y": 890},
{"x": 560, "y": 785}
],
"E": 2
}
],
"path": {}
}
}
connect(mpcc.y, plant3.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 720, "y": 785}, {"x": 720, "y": 760}], "E": 2}],
"path": {}
}
}
connect(dist.y, dsum.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 40, "y": 590}, {"x": 40, "y": 640}], "E": 2}],
"path": {}
}
}
connect(dist2.y, dsum.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 40, "y": 750}, {"x": 40, "y": 700}], "E": 2}],
"path": {}
}
}
connect(dhold.y, plant3.d, plant2.d, plant1.d) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 940, "y": 950}], "E": -1},
{"S": -1, "M": [{"x": 810, "y": 670}], "E": 2},
{"S": 3, "M": [{"x": 810, "y": 430}], "E": -2},
{"S": -2, "M": [], "E": -1},
{"S": 4, "M": [{"x": 810, "y": 210}, {"x": 940, "y": 210}], "E": -2}
],
"junctions": [{"x": 940, "y": 670}, {"x": 940, "y": 430}],
"path": {}
}
}
connect(radd.y, sr1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(radd.u1, step1.y) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 310}, {"x": 52.5, "y": 200}], "E": 2}],
"path": {}
}
}
connect(step2.y, radd.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 330}, {"x": 52.5, "y": 340}], "E": 2}],
"path": {}
}
}
connect(ramp.y, radd.u3) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 450}, {"x": 52.5, "y": 370}], "E": 2}],
"path": {}
}
}
metadata {
"Dyad": {
"tests": {"case1": {"stop": 70}},
"doc": {"behavior": false},
"labels": [
{
"label": "Reference",
"x": 190,
"y": 250,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Disturbance known ahead of time",
"x": 220,
"y": 760,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 1",
"x": 990,
"y": 270,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 2",
"x": 990,
"y": 510,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 3",
"x": 990,
"y": 810,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
}
]
}
}
endFlattened Source
"""
Three closed loops around the same double-integrator plant (`DoubleIntegratorExamplePlant`), driven by the
same reference and load disturbance: a `DiscreteComponents.DiscretePIDStandard` (loop 1), the
equivalent `MPCComponents.PIDEquivalentMPCController` given the plant model, the measured
disturbance and its preview (loop 2), and the same controller with the additional plant-output
constraint `y_p <= 2.9` written as the general constraint `Ax = C` (loop 3).
The scenario visits three regimes. The first reference step (to 2 at t = 1) saturates the
control at `y_max = 1.0` for about a second; the spell is too short to wind the loop up, so
all three loops handle it near-identically. The second step (to 2.8 at t = 25) keeps the
control inside its limits, but loops 1 and 2 overshoot to about 3.03 — through the 2.9
ceiling — while only loop 3 brakes early and follows the limit exactly at 2.9. At t = 45 a
measured load pulse (-1.5 at the plant input for two seconds, known Np - 1 samples ahead
through a `DelayLine`, as a real load schedule would be) hits all three plants. The pulse
exceeds the control authority, so a large excursion is unavoidable — but preview turns
reaction into preparation: the PID, blind to the schedule, is carried from 2.8 almost to 0
and rebounds through the ceiling to about 3.46, while loops 2 and 3 pre-climb and dip only
to about 1.35. Loop 3 delivers the same rejection while never exceeding 2.9 — its pre-climb
and its rebound are both capped by the constraint. The tracking anti-windup `Ni` lets both
MPC loops settle cleanly after the pulse despite the long saturated spell. Away from the limits the
three loops coincide.
"""
example component PIDEquivalentMPCConstrainedExample
"Reference: a large step (control saturates), then a step toward the output ceiling"
step1 = BlockComponents.Sources.Step(start_time = 10.0, height = 1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 150, "y2": 250}}}
}
step2 = BlockComponents.Sources.Step(start_time = 25.0, height = 0.8) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 280, "y2": 380}}}
}
"Load pulse schedule, generated Np - 1 samples ahead of real time: -1.5 for two seconds from t = 45, well beyond the control authority"
dist = BlockComponents.Sources.Step(start_time = 45.0 - 0.1 * (Np - 1), height = -1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 540, "y2": 640}}}
}
dist2 = BlockComponents.Sources.Step(start_time = 47.0 - 0.1 * (Np - 1), height = 1.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 700, "y2": 800}}}
}
dsum = BlockComponents.Math.Add() {
"Dyad": {"placement": {"diagram": {"x1": 60, "x2": 160, "y1": 620, "y2": 720}}}
}
sdk = DiscreteComponents.Sampler() {
"Dyad": {"placement": {"diagram": {"x1": 190, "x2": 290, "y1": 620, "y2": 720}}}
}
"Preview vector of the pulse: tap Np is the schedule itself, tap 1 the load that applies now"
dline = MPCComponents.DelayLine(final N = Np) {
"Dyad": {"placement": {"diagram": {"x1": 320, "x2": 420, "y1": 620, "y2": 720}}}
}
"Holds the current load (the most-delayed tap) for the continuous plants"
dhold = DiscreteComponents.ZeroOrderHold() {
"Dyad": {"placement": {"diagram": {"x1": 440, "x2": 540, "y1": 900, "y2": 1000}}}
}
"Plant driven by the PID"
plant1 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 240, "y2": 340}}}
}
sr1 = DiscreteComponents.Sampler() {
"Dyad": {"placement": {"diagram": {"x1": 200, "x2": 300, "y1": 290, "y2": 390}}}
}
pid = DiscreteComponents.DiscretePIDStandard(Ts = 0.1, K = 1.0, Ti = 10.0, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0) {
"Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 240, "y2": 340}}}
}
"Plant driven by the equivalent MPC"
plant2 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 470, "y2": 570}}}
}
mpc = MPCComponents.PIDEquivalentMPCController(A = [[0.0, 1.0], [0.0, 0.0]], B = [[0.0], [1.0]], C = [[1.0, 0.0]], continuous = true, Ts = 0.1, Bd = [[0.0], [1.0]], final disturbance_preview = true, final Np = Np, K = 1.0, Ti = 10.0, Ni = 0.9, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0, output_trajectories = output_trajectories) {
"Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 470, "y2": 570}}}
}
"Plant driven by the equivalent MPC with the output constraint"
plant3 = MPCComponents.Examples.DoubleIntegratorExamplePlant() {
"Dyad": {"placement": {"diagram": {"x1": 760, "x2": 860, "y1": 710, "y2": 810}}}
}
mpcc = MPCComponents.PIDEquivalentMPCController(A = [[0.0, 1.0], [0.0, 0.0]], B = [[0.0], [1.0]], C = [[1.0, 0.0]], continuous = true, Ts = 0.1, Bd = [[0.0], [1.0]], final disturbance_preview = true, final Np = Np, K = 1.0, Ti = 10.0, Td = 1.0, Nd = 10.0, wd = 0.0, y_max = 1.0, Ax = [[1.0, 0.0]], lb = [-1e30], ub = [2.9], Ni = 0.9, output_trajectories = output_trajectories) {
"Dyad": {"placement": {"diagram": {"x1": 590, "x2": 690, "y1": 710, "y2": 810}}}
}
ramp = BlockComponents.Sources.Ramp(duration = 3, height = 0.5, start_time = 0.5) {
"Dyad": {"placement": {"diagram": {"x1": -80, "x2": 20, "y1": 400, "y2": 500}}}
}
radd = BlockComponents.Math.Add3() {
"Dyad": {"placement": {"diagram": {"x1": 80, "x2": 180, "y1": 290, "y2": 390}}}
}
"Prediction horizon for the MPC controllers"
structural parameter Np::Integer = 20
"Record the predicted trajectories and the QP statistics of the controller(s), for mpc_gui"
structural parameter output_trajectories::Boolean = false
"Position under the PID"
variable y1::Real
"Control of the PID"
variable u1::Real
"Position under the equivalent MPC"
variable y2::Real
"Control of the equivalent MPC"
variable u2::Real
"Position under the output-constrained MPC"
variable y3::Real
"Control of the output-constrained MPC"
variable u3::Real
relations
y1 = plant1.y
u1 = plant1.uh
y2 = plant2.y
u2 = plant2.uh
y3 = plant3.y
u3 = plant3.uh
connect(dsum.y, sdk.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(sdk.y, dline.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(dline.y, mpc.d) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 425, "y": 430}, {"x": 630, "y": 430}], "E": 2}],
"path": {}
}
}
connect(dline.y, mpcc.d) {"Dyad": {"edges": [{"S": 1, "M": [{"x": 640, "y": 670}], "E": 2}], "path": {}}}
connect(dline.y[1], dhold.u) {"Dyad": {"edges": [{"S": 1, "M": [{"x": 425, "y": 950}], "E": 2}], "path": {}}}
connect(sr1.y, pid.u_s, mpc.u_s, mpcc.u_s) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 500, "y": 262}], "E": 2},
{"S": 3, "M": [{"x": 518.75, "y": 495}, {"x": 518.75, "y": 490}], "E": -2},
{"S": -2, "M": [], "E": -1},
{"S": 4, "M": [{"x": 500, "y": 736}], "E": -2}
],
"junctions": [{"x": 500, "y": 340}, {"x": 500, "y": 490}],
"path": {}
}
}
connect(plant1.ym, pid.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 290},
{"x": 890, "y": 370},
{"x": 560, "y": 370},
{"x": 560, "y": 314}
],
"E": 2
}
],
"path": {}
}
}
connect(pid.y, plant1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(plant2.ym, mpc.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 520},
{"x": 890, "y": 610},
{"x": 560, "y": 610},
{"x": 560, "y": 545}
],
"E": 2
}
],
"path": {}
}
}
connect(mpc.y, plant2.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 720, "y": 545}, {"x": 720, "y": 520}], "E": 2}],
"path": {}
}
}
connect(plant3.ym, mpcc.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 890, "y": 760},
{"x": 890, "y": 890},
{"x": 560, "y": 890},
{"x": 560, "y": 785}
],
"E": 2
}
],
"path": {}
}
}
connect(mpcc.y, plant3.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 720, "y": 785}, {"x": 720, "y": 760}], "E": 2}],
"path": {}
}
}
connect(dist.y, dsum.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 40, "y": 590}, {"x": 40, "y": 640}], "E": 2}],
"path": {}
}
}
connect(dist2.y, dsum.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 40, "y": 750}, {"x": 40, "y": 700}], "E": 2}],
"path": {}
}
}
connect(dhold.y, plant3.d, plant2.d, plant1.d) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 940, "y": 950}], "E": -1},
{"S": -1, "M": [{"x": 810, "y": 670}], "E": 2},
{"S": 3, "M": [{"x": 810, "y": 430}], "E": -2},
{"S": -2, "M": [], "E": -1},
{"S": 4, "M": [{"x": 810, "y": 210}, {"x": 940, "y": 210}], "E": -2}
],
"junctions": [{"x": 940, "y": 670}, {"x": 940, "y": 430}],
"path": {}
}
}
connect(radd.y, sr1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
connect(radd.u1, step1.y) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 310}, {"x": 52.5, "y": 200}], "E": 2}],
"path": {}
}
}
connect(step2.y, radd.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 330}, {"x": 52.5, "y": 340}], "E": 2}],
"path": {}
}
}
connect(ramp.y, radd.u3) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 52.5, "y": 450}, {"x": 52.5, "y": 370}], "E": 2}],
"path": {}
}
}
metadata {
"Dyad": {
"tests": {"case1": {"stop": 70}},
"doc": {"behavior": false},
"labels": [
{
"label": "Reference",
"x": 190,
"y": 250,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Disturbance known ahead of time",
"x": 220,
"y": 760,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 1",
"x": 990,
"y": 270,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 2",
"x": 990,
"y": 510,
"rot": 0,
"layer": "diagram",
"attrs": {
"fill": "black",
"font-size": "20",
"dominant-baseline": "central",
"text-anchor": "middle"
}
},
{
"label": "Closed loop 3",
"x": 990,
"y": 810,
"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