@@ -62,18 +62,18 @@ plant simulator to test the design. Its sampling time is 2 s thus the control pe
6262## Linear Model Predictive Controller
6363
6464A 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 :
6666
6767``` math
68- y_L ≥ 45
68+ y_L ≥ 48
6969```
7070
7171We design our [ ` LinMPC ` ] ( @ref ) controllers by including the linear level constraint with
7272[ ` setconstraint! ` ] ( @ref ) (` ±Inf ` values should be used when there is no bound):
7373
7474``` @example 1
7575mpc = 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])
7777```
7878
7979in which ` Hp ` and ` Hc ` keyword arguments are respectively the predictive and control
@@ -116,10 +116,11 @@ function test_mpc(mpc, model)
116116 i == 101 && (ry = [54, 30])
117117 i == 151 && (ul = -20)
118118 y = model() # simulated measurements
119+ preparestate!(mpc, y) # prepare mpc state estimate for current iteration
119120 u = mpc(ry) # or equivalently : u = moveinput!(mpc, ry)
120121 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
123124 end
124125 return u_data, y_data, ry_data
125126end
@@ -131,10 +132,10 @@ nothing # hide
131132The [ ` LinMPC ` ] ( @ref ) objects are also callable as an alternative syntax for
132133[ ` moveinput! ` ] ( @ref ) . It is worth mentioning that additional information like the optimal
133134output 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 .
138139
139140Lastly, we plot the closed-loop test with the ` Plots ` package:
140141
@@ -143,7 +144,7 @@ using Plots
143144function plot_data(t_data, u_data, y_data, ry_data)
144145 p1 = plot(t_data, y_data[1,:], label="meas.", ylabel="level")
145146 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)
147148 p2 = plot(t_data, y_data[2,:], label="meas.", legend=:topleft, ylabel="temp.")
148149 plot!(p2, t_data, ry_data[2,:],label="setpoint", linestyle=:dash, linetype=:steppost)
149150 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
162163
163164``` @example 1
164165mpc2 = 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])
166167```
167168
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:
170171
171172``` @example 1
172173setstate!(model, zeros(model.nx))
@@ -202,7 +203,7 @@ mpc_mhe = LinMPC(estim, Hp=10, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
202203mpc_mhe = setconstraint!(mpc_mhe, ymin=[45, -Inf])
203204```
204205
205- The rejection is slightly improved:
206+ The rejection is not improved here :
206207
207208``` @example 1
208209setstate!(model, zeros(model.nx))
@@ -215,6 +216,10 @@ savefig("plot3_LinMPC.svg"); nothing # hide
215216
216217![ plot3_LinMPC] ( plot3_LinMPC.svg )
217218
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+
218223## Adding Feedforward Compensation
219224
220225Suppose 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:
246251
247252``` @example 1
248253mpc_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])
250255```
251256
252257A 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)
264269 i == 151 && (ul = -20)
265270 d = ul .+ dop # simulated measured disturbance
266271 y = model() # simulated measurements
272+ preparestate!(mpc_d, y, d) # prepare estimate with the measured disturbance d
267273 u = mpc_d(ry, d) # also feed the measured disturbance d to the controller
268274 u_data[:,i], y_data[:,i], ry_data[:,i] = u, y, ry
269275 updatestate!(mpc_d, u, y, d) # update estimate with the measured disturbance d
0 commit comments