Skip to content
LIBRARY
Examples.PIDEquivalentMPCConstrainedExample.md

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: ​

NameDescriptionUnitsDefault value
NpPrediction horizon for the MPC controllers–20
output_trajectoriesRecord the predicted trajectories and the QP statistics of the controller(s), for mpc_gui–false

Variables ​

NameDescriptionUnits
y1Position under the PID–
u1Control of the PID–
y2Position under the equivalent MPC–
u2Control of the equivalent MPC–
y3Position under the output-constrained MPC–
u3Control of the output-constrained MPC–

Behavior ​

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

Source ​

dyad
"""
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"
        }
      }
    ]
  }
}
end
Flattened Source
dyad
"""
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"
        }
      }
    ]
  }
}
end


Test Cases ​

Test Case case1 ​

  • Examples

  • Experiments

  • Analyses