Skip to content
LIBRARY
Examples.PIDEquivalentMPCExample.md

Examples.PIDEquivalentMPCExample ​

The PID-equivalent MPC next to the PID it reproduces: two identical continuous double integrators, one closed with PIDEquivalentMPCController (given the exact plant model) and one with DiscreteComponents.DiscretePIDStandard using the same gains. With the wide output limits used here neither controller saturates, so y1 = y2 and u1 = u2 up to solver tolerance.

Usage ​

MPCComponents.Examples.PIDEquivalentMPCExample()

Parameters: ​

NameDescriptionUnitsDefault value
output_trajectoriesRecord the predicted trajectories and the QP statistics of the controller(s), for mpc_gui–false

Variables ​

NameDescriptionUnits
y1Position under the MPC form–
u1Control of the MPC form–
y2Position under the PID–
u2Control of the PID–

Behavior ​

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

Source ​

dyad
"""
The PID-equivalent MPC next to the PID it reproduces: two identical continuous double integrators,
one closed with `PIDEquivalentMPCController` (given the exact plant model) and one with
`DiscreteComponents.DiscretePIDStandard` using the same gains. With the wide output limits used
here neither controller saturates, so `y1 = y2` and `u1 = u2` up to solver tolerance.
"""
example component PIDEquivalentMPCExample
  periodicclock = DiscreteComponents.PeriodicClock(dt = 0.1) {
    "Dyad": {"placement": {"diagram": {"x1": 810, "x2": 910, "y1": 730, "y2": 830}}}
  }
  periodicclock2 = DiscreteComponents.PeriodicClock(dt = 0.1) {
    "Dyad": {"placement": {"diagram": {"x1": 780, "x2": 880, "y1": 310, "y2": 410}}}
  }
  ref = BlockComponents.Sources.Step(start_time = 1.0, height = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 240, "x2": 340, "y1": 260, "y2": 360}}}
  }
  "Plant driven by the MPC form"
  vel1 = BlockComponents.Continuous.Integrator(x0 = 0.3) {
    "Dyad": {"placement": {"diagram": {"x1": 860, "x2": 960, "y1": 460, "y2": 560}}}
  }
  pos1 = BlockComponents.Continuous.Integrator(x0 = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "x2": 1100, "y1": 460, "y2": 560}}}
  }
  s1 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 910, "x2": 810, "y1": 600, "y2": 700}}}
  }
  sr1 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 400, "x2": 500, "y1": 440, "y2": 540}}}
  }
  zoh1 = DiscreteComponents.ZeroOrderHold() {
    "Dyad": {"placement": {"diagram": {"x1": 714, "x2": 814, "y1": 460, "y2": 560}}}
  }
  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, Np = 10, K = 2.0, Ti = 1.5, Td = 0.3, Nd = 8.0, wp = 0.7, wd = 0.4, y_max = 100.0, x0 = [1.0, 0.3], output_trajectories = output_trajectories) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 460, "y2": 560}}}
  }
  "Plant driven by the PID"
  vel2 = BlockComponents.Continuous.Integrator(x0 = 0.3) {
    "Dyad": {"placement": {"diagram": {"x1": 860, "x2": 960, "y1": 30, "y2": 130}}}
  }
  pos2 = BlockComponents.Continuous.Integrator(x0 = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "x2": 1100, "y1": 30, "y2": 130}}}
  }
  s2 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 880, "x2": 780, "y1": 190, "y2": 290}}}
  }
  sr2 = DiscreteComponents.Sampler() {"Dyad": {"placement": {"diagram": {"x1": 400, "x2": 500, "y1": 0, "y2": 100}}}}
  zoh2 = DiscreteComponents.ZeroOrderHold() {
    "Dyad": {"placement": {"diagram": {"x1": 720, "x2": 820, "y1": 30, "y2": 130}}}
  }
  pid = DiscreteComponents.DiscretePIDStandard(Ts = 0.1, K = 2.0, Ti = 1.5, Td = 0.3, Nd = 8.0, wp = 0.7, wd = 0.4, y_max = 100.0) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 30, "y2": 130}}}
  }
  "Record the predicted trajectories and the QP statistics of the controller(s), for mpc_gui"
  structural parameter output_trajectories::Boolean = false
  "Position under the MPC form"
  variable y1::Real
  "Control of the MPC form"
  variable u1::Real
  "Position under the PID"
  variable y2::Real
  "Control of the PID"
  variable u2::Real
relations
  y1 = pos1.y
  u1 = zoh1.y
  y2 = pos2.y
  u2 = zoh2.y
  connect(zoh1.y, vel1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(vel1.y, pos1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(pos1.y, s1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 1140, "y": 510}, {"x": 1140, "y": 650}], "E": 2}],
      "path": {}
    }
  }
  connect(s1.y, mpc.u_m, periodicclock.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 550, "y": 650}, {"x": 550, "y": 535}], "E": 2},
        {"S": -1, "M": [{"x": 670, "y": 780}], "E": 3}
      ],
      "junctions": [{"x": 670, "y": 650}],
      "path": {}
    }
  }
  connect(ref.y, sr1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 388, "y": 310}, {"x": 388, "y": 490}], "E": 2}],
      "path": {}
    }
  }
  connect(sr1.y, mpc.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 540, "y": 490}, {"x": 540, "y": 486}], "E": 2}],
      "path": {}
    }
  }
  connect(mpc.y, zoh1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 700, "y": 535}, {"x": 700, "y": 510}], "E": 2}],
      "path": {}
    }
  }
  connect(zoh2.y, vel2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(vel2.y, pos2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(pos2.y, s2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 1110, "y": 80}, {"x": 1110, "y": 240}], "E": 2}],
      "path": {}
    }
  }
  connect(s2.y, pid.u_m, periodicclock2.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 560, "y": 240}, {"x": 560, "y": 104}], "E": 2},
        {"S": 3, "M": [{"x": 670, "y": 360}], "E": -1}
      ],
      "junctions": [{"x": 670, "y": 240}],
      "path": {}
    }
  }
  connect(ref.y, sr2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 390, "y": 310}, {"x": 390, "y": 50}], "E": 2}],
      "path": {}
    }
  }
  connect(sr2.y, pid.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 543, "y": 50}, {"x": 543, "y": 52}], "E": 2}],
      "path": {}
    }
  }
  connect(pid.y, zoh2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
metadata {"Dyad": {"tests": {"case1": {"stop": 5}}, "doc": {"behavior": false}}}
end
Flattened Source
dyad
"""
The PID-equivalent MPC next to the PID it reproduces: two identical continuous double integrators,
one closed with `PIDEquivalentMPCController` (given the exact plant model) and one with
`DiscreteComponents.DiscretePIDStandard` using the same gains. With the wide output limits used
here neither controller saturates, so `y1 = y2` and `u1 = u2` up to solver tolerance.
"""
example component PIDEquivalentMPCExample
  periodicclock = DiscreteComponents.PeriodicClock(dt = 0.1) {
    "Dyad": {"placement": {"diagram": {"x1": 810, "x2": 910, "y1": 730, "y2": 830}}}
  }
  periodicclock2 = DiscreteComponents.PeriodicClock(dt = 0.1) {
    "Dyad": {"placement": {"diagram": {"x1": 780, "x2": 880, "y1": 310, "y2": 410}}}
  }
  ref = BlockComponents.Sources.Step(start_time = 1.0, height = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 240, "x2": 340, "y1": 260, "y2": 360}}}
  }
  "Plant driven by the MPC form"
  vel1 = BlockComponents.Continuous.Integrator(x0 = 0.3) {
    "Dyad": {"placement": {"diagram": {"x1": 860, "x2": 960, "y1": 460, "y2": 560}}}
  }
  pos1 = BlockComponents.Continuous.Integrator(x0 = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "x2": 1100, "y1": 460, "y2": 560}}}
  }
  s1 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 910, "x2": 810, "y1": 600, "y2": 700}}}
  }
  sr1 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 400, "x2": 500, "y1": 440, "y2": 540}}}
  }
  zoh1 = DiscreteComponents.ZeroOrderHold() {
    "Dyad": {"placement": {"diagram": {"x1": 714, "x2": 814, "y1": 460, "y2": 560}}}
  }
  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, Np = 10, K = 2.0, Ti = 1.5, Td = 0.3, Nd = 8.0, wp = 0.7, wd = 0.4, y_max = 100.0, x0 = [1.0, 0.3], output_trajectories = output_trajectories) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 460, "y2": 560}}}
  }
  "Plant driven by the PID"
  vel2 = BlockComponents.Continuous.Integrator(x0 = 0.3) {
    "Dyad": {"placement": {"diagram": {"x1": 860, "x2": 960, "y1": 30, "y2": 130}}}
  }
  pos2 = BlockComponents.Continuous.Integrator(x0 = 1.0) {
    "Dyad": {"placement": {"diagram": {"x1": 1000, "x2": 1100, "y1": 30, "y2": 130}}}
  }
  s2 = DiscreteComponents.Sampler() {
    "Dyad": {"placement": {"diagram": {"x1": 880, "x2": 780, "y1": 190, "y2": 290}}}
  }
  sr2 = DiscreteComponents.Sampler() {"Dyad": {"placement": {"diagram": {"x1": 400, "x2": 500, "y1": 0, "y2": 100}}}}
  zoh2 = DiscreteComponents.ZeroOrderHold() {
    "Dyad": {"placement": {"diagram": {"x1": 720, "x2": 820, "y1": 30, "y2": 130}}}
  }
  pid = DiscreteComponents.DiscretePIDStandard(Ts = 0.1, K = 2.0, Ti = 1.5, Td = 0.3, Nd = 8.0, wp = 0.7, wd = 0.4, y_max = 100.0) {
    "Dyad": {"placement": {"diagram": {"x1": 580, "x2": 680, "y1": 30, "y2": 130}}}
  }
  "Record the predicted trajectories and the QP statistics of the controller(s), for mpc_gui"
  structural parameter output_trajectories::Boolean = false
  "Position under the MPC form"
  variable y1::Real
  "Control of the MPC form"
  variable u1::Real
  "Position under the PID"
  variable y2::Real
  "Control of the PID"
  variable u2::Real
relations
  y1 = pos1.y
  u1 = zoh1.y
  y2 = pos2.y
  u2 = zoh2.y
  connect(zoh1.y, vel1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(vel1.y, pos1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(pos1.y, s1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 1140, "y": 510}, {"x": 1140, "y": 650}], "E": 2}],
      "path": {}
    }
  }
  connect(s1.y, mpc.u_m, periodicclock.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 550, "y": 650}, {"x": 550, "y": 535}], "E": 2},
        {"S": -1, "M": [{"x": 670, "y": 780}], "E": 3}
      ],
      "junctions": [{"x": 670, "y": 650}],
      "path": {}
    }
  }
  connect(ref.y, sr1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 388, "y": 310}, {"x": 388, "y": 490}], "E": 2}],
      "path": {}
    }
  }
  connect(sr1.y, mpc.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 540, "y": 490}, {"x": 540, "y": 486}], "E": 2}],
      "path": {}
    }
  }
  connect(mpc.y, zoh1.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 700, "y": 535}, {"x": 700, "y": 510}], "E": 2}],
      "path": {}
    }
  }
  connect(zoh2.y, vel2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(vel2.y, pos2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
  connect(pos2.y, s2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 1110, "y": 80}, {"x": 1110, "y": 240}], "E": 2}],
      "path": {}
    }
  }
  connect(s2.y, pid.u_m, periodicclock2.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 560, "y": 240}, {"x": 560, "y": 104}], "E": 2},
        {"S": 3, "M": [{"x": 670, "y": 360}], "E": -1}
      ],
      "junctions": [{"x": 670, "y": 240}],
      "path": {}
    }
  }
  connect(ref.y, sr2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 390, "y": 310}, {"x": 390, "y": 50}], "E": 2}],
      "path": {}
    }
  }
  connect(sr2.y, pid.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 543, "y": 50}, {"x": 543, "y": 52}], "E": 2}],
      "path": {}
    }
  }
  connect(pid.y, zoh2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "path": {}}}
metadata {"Dyad": {"tests": {"case1": {"stop": 5}}, "doc": {"behavior": false}}}
end


Test Cases ​

Test Case case1 ​

  • Examples

  • Experiments

  • Analyses