Skip to content

Commit 9f3f3aa

Browse files
committed
doc revision by Alex
1 parent 30e6b29 commit 9f3f3aa

File tree

4 files changed

+49
-37
lines changed

4 files changed

+49
-37
lines changed

docs/src/manual/linmpc.md

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
## Linear Model
44

5-
The example considers a well-stirred tank with a cold and hot water inlet as a plant. The
6-
water flows out of an opening at the bottom of the tank. The manipulated inputs are the cold
7-
``u_c`` and hot ``u_h`` water flow rate, and the measured outputs are the liquid level
8-
``y_L`` and temperature ``y_T``:
5+
The example considers a continuously stirred-tank reactor (CSTR) with a cold and hot water
6+
inlet as a plant. The water flows out of an opening at the bottom of the tank. The
7+
manipulated inputs are the cold ``u_c`` and hot ``u_h`` water flow rates, and the measured
8+
outputs are the liquid level ``y_L`` and temperature ``y_T``:
99

1010
```math
1111
\begin{aligned}
@@ -41,41 +41,49 @@ the following linear model accurately describes the plant dynamics:
4141
We first need to construct a [`LinModel`](@ref) objet with [`setop!`](@ref) to handle the
4242
operating points:
4343

44-
```@example 1
44+
```julia
4545
using ModelPredictiveControl, ControlSystemsBase
4646
sys = [ tf(1.90, [18, 1]) tf(1.90, [18, 1]);
4747
tf(-0.74,[8, 1]) tf(0.74, [8, 1]) ]
48-
Ts = 4.0
48+
Ts = 2.0
4949
model = setop!(LinModel(sys, Ts), uop=[10, 10], yop=[50, 30])
5050
```
5151

5252
The `model` object will be used for two purposes : to construct our controller, and as a
5353
plant simulator to test the design.
5454

55-
## Linear Predictive Controller
55+
## Linear Model Predictive Controller
5656

57-
A predictive feedback will control both the water level ``y_L`` and temperature ``y_T`` in
58-
the tank, at a sampling time of 4 s. The tank level should also never fall below 45:
57+
A linear model predictive controller (MPC) will control both the water level ``y_L`` and
58+
temperature ``y_T`` in the tank, at a sampling time of 4 s. The tank level should also never
59+
fall below 45:
5960

6061
```math
6162
y_L ≥ 45
6263
```
6364

64-
We design our [`LinMPC`](@ref) controllers by including the level constraint with
65-
[`setconstraint!`](@ref):
65+
We design our [`LinMPC`](@ref) controllers by including the linear level constraint with
66+
[`setconstraint!`](@ref) (`±Inf` values should be used when there is no bound):
6667

67-
```@example 1
68+
```julia
6869
mpc = setconstraint!(LinMPC(model, Hp=15, Hc=2), ŷmin=[45, -Inf])
6970
```
7071

71-
By default, [`LinMPC`](@ref) controllers use [`OSQP`](https://osqp.org/) to solve the
72-
problem and a [`SteadyKalmanFilter`](@ref) to estimate the plant states. Before closing the
73-
loop, we call [`initstate!`](@ref) with the actual plant inputs and measurements to ensure a
74-
bumpless transfer. Since `model` simulates our plant here, its output will initialize the
75-
states. [`LinModel`](@ref) objects are callable for this purpose (an alias for
76-
[`evaloutput`](@ref)):
77-
78-
```@example 1
72+
in which `Hp` and `Hc` keyword arguments are the predictive and control horizons
73+
respectively. By default, [`LinMPC`](@ref) controllers use [`OSQP`](https://osqp.org/) to
74+
solve the problem, soft constraints on output predictions ``\mathbf{ŷ}`` to ensure
75+
feasibility, and a [`SteadyKalmanFilter`](@ref) to estimate the plant states. An attentive
76+
reader will also notice that the Kalman filter estimates two additional states compared to
77+
the plant model. These are the integrating states for the unmeasured plant disturbances, and
78+
they are automatically added the model outputs if feasible (see [`SteadyKalmanFilter`](@ref)
79+
for details).
80+
81+
Before closing the loop, we call [`initstate!`](@ref) with the actual plant inputs and
82+
measurements to ensure a bumpless transfer. Since `model` simulates our plant here, its
83+
output will initialize the states. [`LinModel`](@ref) objects are callable for this purpose
84+
(an alias for [`evaloutput`](@ref)):
85+
86+
```julia
7987
u = model.uop
8088
y = model() # or equivalently : y = evaloutput(model)
8189
initstate!(mpc, u, y)
@@ -85,24 +93,25 @@ nothing # hide
8593
We can then close the loop and test `mpc` performance on the simulator by imposing step
8694
changes on output setpoints ``\mathbf{r_y}`` and on a load disturbance ``\mathbf{u_d}``:
8795

88-
```@example 1
96+
```julia
8997
function test_mpc(mpc, model)
90-
N = 100
98+
N = 200
9199
ry, ud = [50, 30], [0, 0]
92100
u_data = zeros(model.nu, N)
93101
y_data = zeros(model.ny, N)
94102
ry_data = zeros(model.ny, N)
95103
for k = 0:N-1
96104
y = model() # simulated measurements
97-
k == 25 && (ry = [50, 35])
98-
k == 50 && (ry = [55, 30])
99-
k == 75 && (ud = [-15, 0])
105+
k == 50 && (ry = [50, 35])
106+
k == 100 && (ry = [55, 30])
107+
k == 150 && (ud = [-25, 0])
100108
u = mpc(ry) # or equivalently : u = moveinput!(mpc, ry)
101109
u_data[:,k+1] = u
102110
y_data[:,k+1] = y
103111
ry_data[:,k+1] = ry
104112
updatestate!(mpc, u, y) # update mpc state estimate
105113
updatestate!(model, u + ud) # update simulator with disturbance
114+
println(getinfo(mpc)[2][:Ŷs])
106115
end
107116
return u_data, y_data, ry_data
108117
end
@@ -119,13 +128,13 @@ end of the `for` loop. The same logic applies for `model`.
119128

120129
Lastly, we plot the closed-loop test with the `Plots` package:
121130

122-
```@example 1
131+
```julia
123132
using Plots
124133
function plot_data(t_data, u_data, y_data, ry_data)
125-
p1 = plot(t_data, y_data[1,:], label="level"); ylabel!("level")
134+
p1 = plot(t_data, y_data[1,:], label="meas."); ylabel!("level")
126135
plot!(t_data, ry_data[1,:], label="setpoint", linestyle=:dash, linetype=:steppost)
127136
plot!(t_data, fill(45,size(t_data)), label="min", linestyle=:dot, linewidth=1.5)
128-
p2 = plot(t_data, y_data[2,:], label="temp."); ylabel!("temp.")
137+
p2 = plot(t_data, y_data[2,:], label="meas."); ylabel!("temp.")
129138
plot!(t_data, ry_data[2,:],label="setpoint", linestyle=:dash, linetype=:steppost)
130139
p3 = plot(t_data,u_data[1,:],label="cold", linetype=:steppost); ylabel!("flow rate")
131140
plot!(t_data,u_data[2,:],label="hot", linetype=:steppost); xlabel!("time (s)")
@@ -141,17 +150,20 @@ optimizer may be more efficient. To install it, run:
141150
using Pkg; Pkg.add("DAQP")
142151
```
143152

144-
Constructing a [`LinMPC`](@ref) with `DAQP`:
153+
Also, compared to the default setting, adding the integrating states at the model inputs may
154+
improve the closed-loop performance. Load disturbances are indeed very frequent in real-life
155+
control problems. Constructing a [`LinMPC`](@ref) with `DAQP` and input integrators:
145156

146-
```@example 1
157+
```julia
147158
using JuMP, DAQP
148-
daqp = Model(DAQP.Optimizer)
149-
mpc2 = setconstraint!(LinMPC(model, Hp=15, Hc=2, optim=daqp), ŷmin=[45, -Inf])
159+
daqp = Model(DAQP.Optimizer)
160+
estim = SteadyKalmanFilter(model, nint_u=[1, 1])
161+
mpc2 = setconstraint!(LinMPC(estim, Hp=15, Hc=2, optim=daqp), ŷmin=[45, -Inf])
150162
```
151163

152164
leads to identical results here:
153165

154-
```@example 1
166+
```julia
155167
setstate!(model, zeros(model.nx))
156168
initstate!(mpc2, model.uop, model())
157169
u_data2, y_data2, ry_data2 = test_mpc(mpc2, model)

docs/src/manual/nonlinmpc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ u = [0.5]
5454
plot(sim!(model, 60, u), plotu=false)
5555
```
5656

57-
## Nonlinear Predictive Controller
57+
## Nonlinear Model Predictive Controller
5858

5959
An [`UnscentedKalmanFilter`](@ref) estimates the plant state :
6060

docs/src/public/predictive_control.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ predictions. The default [`LinMPC`](@ref) estimator is a [`SteadyKalmanFilter`](
99
[`NonLinMPC`](@ref) with nonlinear models, an [`UnscentedKalmanFilter`](@ref). For simpler
1010
and more classical designs, an [`InternalModel`](@ref) structure is also available, that
1111
assumes by default that the current model mismatch estimation is constant in the future
12-
(same approach than dynamic matrix control, DMC).
12+
(same approach as dynamic matrix control, DMC).
1313

1414
!!! info
1515
The nomenclature use capital letters for time series (and matrices) and hats for the

src/predictive_control.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,10 @@ matrices are computed by :
629629
\end{bmatrix}
630630
\\
631631
\mathbf{K_d} &= \begin{bmatrix}
632-
\mathbf{C}\mathbf{A}^{0} \\
633632
\mathbf{C}\mathbf{A}^{1} \\
633+
\mathbf{C}\mathbf{A}^{2} \\
634634
\vdots \\
635-
\mathbf{C}\mathbf{A}^{H_p-1}
635+
\mathbf{C}\mathbf{A}^{H_p}
636636
\end{bmatrix}
637637
\\
638638
\mathbf{Q} &= \begin{bmatrix}

0 commit comments

Comments
 (0)