Skip to content
LIBRARY
Examples.QuadTankMPCExample.md

Examples.QuadTankMPCExample ​

Quadruple-tank level control: the same plant under a linear MPC (LinearMPCController on the linearization of the plant at the steady state for the tutorial's pump voltages [0.260238, 0.260238], linearization_point = SteadyState(), with a Kalman filter and a control-increment penalty, as in the tutorial's linear MPC) and under a nonlinear MPC (ACADOSMPC with the plant itself as the prediction model), both driving the levels from [2, 1, 8, 3] to the tutorial's operating point [9.82, 9.82, 6.28, 6.28], the steady state whose levels are closest to [10, 10, 6, 6], with the pump voltages in [0, 0.6] and the levels softly bounded by [12, 12, 8, 8]. Translated from the DyadControlSystems quadtank tutorial. Verified in test/test_acados_tutorials.jl.

Usage ​

MPCComponents.Examples.QuadTankMPCExample()

Variables ​

NameDescriptionUnits
h1_nlTank 1 level, nonlinear MPC–
h1_linTank 1 level, linear MPC–
u1_nlPump 1 voltage, nonlinear MPC–
u1_linPump 1 voltage, linear MPC–
flagacados status–

Behavior ​

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

Source ​

dyad
"""
Quadruple-tank level control: the same plant under a linear MPC (`LinearMPCController` on the
linearization of the plant at the steady state for the tutorial's pump voltages [0.260238, 0.260238],
`linearization_point = SteadyState()`, with a Kalman filter and a control-increment penalty, as in the
tutorial's linear MPC) and under a nonlinear MPC (`ACADOSMPC` with the plant itself as the prediction
model), both driving the levels from [2, 1, 8, 3] to the tutorial's operating point
[9.82, 9.82, 6.28, 6.28], the steady state whose levels are closest to [10, 10, 6, 6], with
the pump voltages in [0, 0.6] and the levels softly bounded by [12, 12, 8, 8]. Translated from the
DyadControlSystems quadtank tutorial. Verified in `test/test_acados_tutorials.jl`.
"""
example component QuadTankMPCExample
  periodicclock = DiscreteComponents.PeriodicClock(dt = 2.0) {}
  plant_nl = MPCComponents.Examples.QuadTankPlant() {}
  plant_lin = MPCComponents.Examples.QuadTankPlant() {}
  "Level samplers of the nonlinear loop"
  s = [DiscreteComponents.Sampler() for i in 1:4] {}
  "Level samplers of the linear loop"
  sl = [DiscreteComponents.Sampler() for i in 1:4] {}
  "Pump voltage holds of the nonlinear loop"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:2] {}
  "Pump voltage holds of the linear loop"
  zohl = [DiscreteComponents.ZeroOrderHold() for i in 1:2] {}
  "Level reference of the lower tanks 1 and 2"
  r_lower = BlockComponents.Sources.Constant(k = 9.818427) {}
  "Level reference of the upper tanks 3 and 4"
  r_upper = BlockComponents.Sources.Constant(k = 6.283709) {}
  "Reference samplers"
  sr = [DiscreteComponents.Sampler() for i in 1:4] {}
  mpc_nl = MPCComponents.ACADOSMPC(dynamics = plant_nl, inputs = ["u1", "u2"], state_variables = ["h1", "h2", "h3", "h4"], outputs = ["h1", "h2", "h3", "h4"], Ts = 2.0, Np = 10, Q1 = diagonal([1.0, 1.0, 1.0, 1.0]), Q2 = diagonal([1.0, 1.0]), umin = [0.0, 0.0], umax = [0.6, 0.6], constrained = ["h1", "h2", "h3", "h4"], constrained_min = [0.0, 0.0, 0.0, 0.0], constrained_max = [12.0, 12.0, 8.0, 8.0]) {}
  mpc_lin = MPCComponents.LinearMPCController(dynamics = plant_lin, inputs = ["u1", "u2"], outputs = ["y1", "y2", "y3", "y4"], state_variables = ["h1", "h2", "h3", "h4"], linearization_point = LinearizationPoint.SteadyState(), u_op = [0.260238, 0.260238], Ts = 2.0, Np = 10, Q1 = [1.0, 1.0, 1.0, 1.0], Q2 = [0.0001, 0.0001], Q3 = [1.0, 1.0], umin = [0.0, 0.0], umax = [0.6, 0.6], ymin = [0.0, 0.0, 0.0, 0.0], ymax = [12.0, 12.0, 8.0, 8.0], R1 = [0.00001, 0.00001, 0.00001, 0.00001], R2 = [1.0, 1.0, 1.0, 1.0], x0 = [2.0, 1.0, 8.0, 3.0]) {}
  "Tank 1 level, nonlinear MPC"
  variable h1_nl::Real
  "Tank 1 level, linear MPC"
  variable h1_lin::Real
  "Pump 1 voltage, nonlinear MPC"
  variable u1_nl::Real
  "Pump 1 voltage, linear MPC"
  variable u1_lin::Real
  "acados status"
  variable flag::Real
relations
  h1_nl = plant_nl.y1
  h1_lin = plant_lin.y1
  u1_nl = mpc_nl.u[1]
  u1_lin = mpc_lin.u[1]
  flag = mpc_nl.exitflag
  connect(plant_nl.y1, s[1].u)
  connect(plant_nl.y2, s[2].u)
  connect(plant_nl.y3, s[3].u)
  connect(plant_nl.y4, s[4].u)
  connect(s[1].y, mpc_nl.x[1], periodicclock.y)
  connect(s[2].y, mpc_nl.x[2])
  connect(s[3].y, mpc_nl.x[3])
  connect(s[4].y, mpc_nl.x[4])
  connect(r_lower.y, sr[1].u, sr[2].u)
  connect(r_upper.y, sr[3].u, sr[4].u)
  # Connected entry by entry only because the compiler lowers a connect of a component array to a
  # field access on the array (JuliaComputing/dyad-lang#2247). Connections in loops are not shown in
  # the diagram, so restore the array connections connect(sr.y, mpc_nl.r, mpc_lin.r),
  # connect(mpc_nl.u, zoh.u), connect(mpc_lin.u, zohl.u) and connect(sl.y, mpc_lin.y) once it is fixed
  for i in 1:4
    connect(sr[i].y, mpc_nl.r[i], mpc_lin.r[i])
  end
  for i in 1:2
    connect(mpc_nl.u[i], zoh[i].u)
    connect(mpc_lin.u[i], zohl[i].u)
  end
  connect(zoh[1].y, plant_nl.u1)
  connect(zoh[2].y, plant_nl.u2)
  connect(plant_lin.y1, sl[1].u)
  connect(plant_lin.y2, sl[2].u)
  connect(plant_lin.y3, sl[3].u)
  connect(plant_lin.y4, sl[4].u)
  for i in 1:4
    connect(sl[i].y, mpc_lin.y[i])
  end
  connect(zohl[1].y, plant_lin.u1)
  connect(zohl[2].y, plant_lin.u2)
metadata {"Dyad": {"doc": {"behavior": false}}}
end
Flattened Source
dyad
"""
Quadruple-tank level control: the same plant under a linear MPC (`LinearMPCController` on the
linearization of the plant at the steady state for the tutorial's pump voltages [0.260238, 0.260238],
`linearization_point = SteadyState()`, with a Kalman filter and a control-increment penalty, as in the
tutorial's linear MPC) and under a nonlinear MPC (`ACADOSMPC` with the plant itself as the prediction
model), both driving the levels from [2, 1, 8, 3] to the tutorial's operating point
[9.82, 9.82, 6.28, 6.28], the steady state whose levels are closest to [10, 10, 6, 6], with
the pump voltages in [0, 0.6] and the levels softly bounded by [12, 12, 8, 8]. Translated from the
DyadControlSystems quadtank tutorial. Verified in `test/test_acados_tutorials.jl`.
"""
example component QuadTankMPCExample
  periodicclock = DiscreteComponents.PeriodicClock(dt = 2.0) {}
  plant_nl = MPCComponents.Examples.QuadTankPlant() {}
  plant_lin = MPCComponents.Examples.QuadTankPlant() {}
  "Level samplers of the nonlinear loop"
  s = [DiscreteComponents.Sampler() for i in 1:4] {}
  "Level samplers of the linear loop"
  sl = [DiscreteComponents.Sampler() for i in 1:4] {}
  "Pump voltage holds of the nonlinear loop"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:2] {}
  "Pump voltage holds of the linear loop"
  zohl = [DiscreteComponents.ZeroOrderHold() for i in 1:2] {}
  "Level reference of the lower tanks 1 and 2"
  r_lower = BlockComponents.Sources.Constant(k = 9.818427) {}
  "Level reference of the upper tanks 3 and 4"
  r_upper = BlockComponents.Sources.Constant(k = 6.283709) {}
  "Reference samplers"
  sr = [DiscreteComponents.Sampler() for i in 1:4] {}
  mpc_nl = MPCComponents.ACADOSMPC(dynamics = plant_nl, inputs = ["u1", "u2"], state_variables = ["h1", "h2", "h3", "h4"], outputs = ["h1", "h2", "h3", "h4"], Ts = 2.0, Np = 10, Q1 = diagonal([1.0, 1.0, 1.0, 1.0]), Q2 = diagonal([1.0, 1.0]), umin = [0.0, 0.0], umax = [0.6, 0.6], constrained = ["h1", "h2", "h3", "h4"], constrained_min = [0.0, 0.0, 0.0, 0.0], constrained_max = [12.0, 12.0, 8.0, 8.0]) {}
  mpc_lin = MPCComponents.LinearMPCController(dynamics = plant_lin, inputs = ["u1", "u2"], outputs = ["y1", "y2", "y3", "y4"], state_variables = ["h1", "h2", "h3", "h4"], linearization_point = LinearizationPoint.SteadyState(), u_op = [0.260238, 0.260238], Ts = 2.0, Np = 10, Q1 = [1.0, 1.0, 1.0, 1.0], Q2 = [0.0001, 0.0001], Q3 = [1.0, 1.0], umin = [0.0, 0.0], umax = [0.6, 0.6], ymin = [0.0, 0.0, 0.0, 0.0], ymax = [12.0, 12.0, 8.0, 8.0], R1 = [0.00001, 0.00001, 0.00001, 0.00001], R2 = [1.0, 1.0, 1.0, 1.0], x0 = [2.0, 1.0, 8.0, 3.0]) {}
  "Tank 1 level, nonlinear MPC"
  variable h1_nl::Real
  "Tank 1 level, linear MPC"
  variable h1_lin::Real
  "Pump 1 voltage, nonlinear MPC"
  variable u1_nl::Real
  "Pump 1 voltage, linear MPC"
  variable u1_lin::Real
  "acados status"
  variable flag::Real
relations
  h1_nl = plant_nl.y1
  h1_lin = plant_lin.y1
  u1_nl = mpc_nl.u[1]
  u1_lin = mpc_lin.u[1]
  flag = mpc_nl.exitflag
  connect(plant_nl.y1, s[1].u)
  connect(plant_nl.y2, s[2].u)
  connect(plant_nl.y3, s[3].u)
  connect(plant_nl.y4, s[4].u)
  connect(s[1].y, mpc_nl.x[1], periodicclock.y)
  connect(s[2].y, mpc_nl.x[2])
  connect(s[3].y, mpc_nl.x[3])
  connect(s[4].y, mpc_nl.x[4])
  connect(r_lower.y, sr[1].u, sr[2].u)
  connect(r_upper.y, sr[3].u, sr[4].u)
  # Connected entry by entry only because the compiler lowers a connect of a component array to a
  # field access on the array (JuliaComputing/dyad-lang#2247). Connections in loops are not shown in
  # the diagram, so restore the array connections connect(sr.y, mpc_nl.r, mpc_lin.r),
  # connect(mpc_nl.u, zoh.u), connect(mpc_lin.u, zohl.u) and connect(sl.y, mpc_lin.y) once it is fixed
  for i in 1:4
    connect(sr[i].y, mpc_nl.r[i], mpc_lin.r[i])
  end
  for i in 1:2
    connect(mpc_nl.u[i], zoh[i].u)
    connect(mpc_lin.u[i], zohl[i].u)
  end
  connect(zoh[1].y, plant_nl.u1)
  connect(zoh[2].y, plant_nl.u2)
  connect(plant_lin.y1, sl[1].u)
  connect(plant_lin.y2, sl[2].u)
  connect(plant_lin.y3, sl[3].u)
  connect(plant_lin.y4, sl[4].u)
  for i in 1:4
    connect(sl[i].y, mpc_lin.y[i])
  end
  connect(zohl[1].y, plant_lin.u1)
  connect(zohl[2].y, plant_lin.u2)
metadata {"Dyad": {"doc": {"behavior": false}}}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses