@@ -62,18 +62,18 @@ plant simulator to test the design. Its sampling time is 2 s thus the control pe
62
62
## Linear Model Predictive Controller
63
63
64
64
A linear model predictive controller (MPC) will control both the water level `` y_L `` and
65
- temperature `` y_T `` in the tank. The tank level should also never fall below 45 :
65
+ temperature `` y_T `` in the tank. The tank level should also never fall below 48 :
66
66
67
67
``` math
68
- y_L ≥ 45
68
+ y_L ≥ 48
69
69
```
70
70
71
71
We design our [ ` LinMPC ` ] ( @ref ) controllers by including the linear level constraint with
72
72
[ ` setconstraint! ` ] ( @ref ) (` ±Inf ` values should be used when there is no bound):
73
73
74
74
``` @example 1
75
75
mpc = LinMPC(model, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
76
- mpc = setconstraint!(mpc, ymin=[45 , -Inf])
76
+ mpc = setconstraint!(mpc, ymin=[48 , -Inf])
77
77
```
78
78
79
79
in which ` Hp ` and ` Hc ` keyword arguments are respectively the predictive and control
@@ -116,10 +116,11 @@ function test_mpc(mpc, model)
116
116
i == 101 && (ry = [54, 30])
117
117
i == 151 && (ul = -20)
118
118
y = model() # simulated measurements
119
+ preparestate!(mpc, y) # prepare mpc state estimate for current iteration
119
120
u = mpc(ry) # or equivalently : u = moveinput!(mpc, ry)
120
121
u_data[:,i], y_data[:,i], ry_data[:,i] = u, y, ry
121
- updatestate!(mpc, u, y) # update mpc state estimate
122
- updatestate!(model, u + [0; ul]) # update simulator with the load disturbance
122
+ updatestate!(mpc, u, y) # update mpc state estimate for next iteration
123
+ updatestate!(model, u + [0; ul]) # update simulator with load disturbance
123
124
end
124
125
return u_data, y_data, ry_data
125
126
end
@@ -131,10 +132,10 @@ nothing # hide
131
132
The [ ` LinMPC ` ] ( @ref ) objects are also callable as an alternative syntax for
132
133
[ ` moveinput! ` ] ( @ref ) . It is worth mentioning that additional information like the optimal
133
134
output predictions `` \mathbf{Ŷ} `` can be retrieved by calling [ ` getinfo ` ] ( @ref ) after
134
- solving the problem. Also, calling [ ` updatestate !` ] ( @ref ) on the ` mpc ` object updates its
135
- internal state for the * NEXT * control period (this is by design, see
136
- [ Functions: State Estimators ] ( @ref ) for justifications ). That is why the call is done at the
137
- end of the ` for ` loop. The same logic applies for ` model ` .
135
+ solving the problem. Also, calling [ ` preparestate !` ] ( @ref ) on the ` mpc ` object prepares the
136
+ estimates for the current control period, and [ ` updatestate! ` ] ( @ref ) updates them for the
137
+ next one (the same logic applies for ` model ` ). This is why [ ` preparestate! ` ] ( @ref ) is called
138
+ before the controller, and [ ` updatestate! ` ] ( @ref ) , after .
138
139
139
140
Lastly, we plot the closed-loop test with the ` Plots ` package:
140
141
@@ -143,7 +144,7 @@ using Plots
143
144
function plot_data(t_data, u_data, y_data, ry_data)
144
145
p1 = plot(t_data, y_data[1,:], label="meas.", ylabel="level")
145
146
plot!(p1, t_data, ry_data[1,:], label="setpoint", linestyle=:dash, linetype=:steppost)
146
- plot!(p1, t_data, fill(45 ,size(t_data)), label="min", linestyle=:dot, linewidth=1.5)
147
+ plot!(p1, t_data, fill(48 ,size(t_data)), label="min", linestyle=:dot, linewidth=1.5)
147
148
p2 = plot(t_data, y_data[2,:], label="meas.", legend=:topleft, ylabel="temp.")
148
149
plot!(p2, t_data, ry_data[2,:],label="setpoint", linestyle=:dash, linetype=:steppost)
149
150
p3 = plot(t_data,u_data[1,:],label="cold", linetype=:steppost, ylabel="flow rate")
@@ -162,11 +163,11 @@ real-life control problems. Constructing a [`LinMPC`](@ref) with input integrato
162
163
163
164
``` @example 1
164
165
mpc2 = LinMPC(model, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1], nint_u=[1, 1])
165
- mpc2 = setconstraint!(mpc2, ymin=[45 , -Inf])
166
+ mpc2 = setconstraint!(mpc2, ymin=[48 , -Inf])
166
167
```
167
168
168
- does accelerate the rejection of the load disturbance and eliminates the level constraint
169
- violation:
169
+ does accelerate the rejection of the load disturbance and almost eliminates the level
170
+ constraint violation:
170
171
171
172
``` @example 1
172
173
setstate!(model, zeros(model.nx))
@@ -202,7 +203,7 @@ mpc_mhe = LinMPC(estim, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
202
203
mpc_mhe = setconstraint!(mpc_mhe, ymin=[45, -Inf])
203
204
```
204
205
205
- The rejection is slightly improved:
206
+ The rejection is not improved here :
206
207
207
208
``` @example 1
208
209
setstate!(model, zeros(model.nx))
@@ -215,6 +216,10 @@ savefig("plot3_LinMPC.svg"); nothing # hide
215
216
216
217
![ plot3_LinMPC] ( plot3_LinMPC.svg )
217
218
219
+ This is because the more performant ` direct=true ` version of the [ ` MovingHorizonEstimator ` ] ( @ref )
220
+ is not not implemented yet. The rejection will be improved with the ` direct=true ` version
221
+ (coming soon).
222
+
218
223
## Adding Feedforward Compensation
219
224
220
225
Suppose that the load disturbance `` u_l `` of the last section is in fact caused by a
@@ -246,7 +251,7 @@ A [`LinMPC`](@ref) controller is constructed on this model:
246
251
247
252
``` @example 1
248
253
mpc_d = LinMPC(model_d, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
249
- mpc_d = setconstraint!(mpc_d, ymin=[45 , -Inf])
254
+ mpc_d = setconstraint!(mpc_d, ymin=[48 , -Inf])
250
255
```
251
256
252
257
A new test function that feeds the measured disturbance `` \mathbf{d} `` to the controller is
@@ -264,6 +269,7 @@ function test_mpc_d(mpc_d, model)
264
269
i == 151 && (ul = -20)
265
270
d = ul .+ dop # simulated measured disturbance
266
271
y = model() # simulated measurements
272
+ preparestate!(mpc_d, y, d) # prepare estimate with the measured disturbance d
267
273
u = mpc_d(ry, d) # also feed the measured disturbance d to the controller
268
274
u_data[:,i], y_data[:,i], ry_data[:,i] = u, y, ry
269
275
updatestate!(mpc_d, u, y, d) # update estimate with the measured disturbance d
0 commit comments