@@ -18,7 +18,7 @@ At the steady-state operating points:
1818
1919``` math
2020\begin{aligned}
21- \mathbf{u_{op}} &= \begin{bmatrix} 10 \\ 10 \end{bmatrix} \\
21+ \mathbf{u_{op}} &= \begin{bmatrix} 20 \\ 20 \end{bmatrix} \\
2222 \mathbf{y_{op}} &= \begin{bmatrix} 50 \\ 30 \end{bmatrix}
2323\end{aligned}
2424```
@@ -46,7 +46,7 @@ using ModelPredictiveControl, ControlSystemsBase
4646sys = [ tf(1.90, [18, 1]) tf(1.90, [18, 1]);
4747 tf(-0.74,[8, 1]) tf(0.74, [8, 1]) ]
4848Ts = 2.0
49- model = setop!(LinModel(sys, Ts), uop=[10, 10 ], yop=[50, 30])
49+ model = setop!(LinModel(sys, Ts), uop=[20, 20 ], yop=[50, 30])
5050```
5151
5252The ` model ` object will be used for two purposes : to construct our controller, and as a
@@ -66,7 +66,8 @@ We design our [`LinMPC`](@ref) controllers by including the linear level constra
6666[ ` setconstraint! ` ] ( @ref ) (` ±Inf ` values should be used when there is no bound):
6767
6868``` @example 1
69- mpc = setconstraint!(LinMPC(model, Hp=15, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1]), ymin=[45, -Inf])
69+ mpc = LinMPC(model, Hp=15, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
70+ mpc = setconstraint!(mpc, ymin=[45, -Inf])
7071```
7172
7273in which ` Hp ` and ` Hc ` keyword arguments are respectively the predictive and control
@@ -85,33 +86,32 @@ output will initialize the states. [`LinModel`](@ref) objects are callable for t
8586(an alias for [ ` evaloutput ` ] ( @ref ) ):
8687
8788``` @example 1
88- u = model.uop
89- y = model() # or equivalently : y = evaloutput(model)
89+ u, y = model.uop, model() # or equivalently : y = evaloutput(model)
9090initstate!(mpc, u, y)
9191nothing # hide
9292```
9393
9494We can then close the loop and test ` mpc ` performance on the simulator by imposing step
95- changes on output setpoints `` \mathbf{r_y} `` and on a load disturbance `` \mathbf{u_d} `` :
95+ changes on output setpoints `` \mathbf{r_y} `` and on a load disturbance `` u_l `` :
9696
9797``` @example 1
9898function test_mpc(mpc, model)
9999 N = 200
100- ry, ud = [50, 30], [0, 0]
100+ ry, ul = [50, 30], 0
101101 u_data = zeros(model.nu, N)
102102 y_data = zeros(model.ny, N)
103103 ry_data = zeros(model.ny, N)
104104 for k = 0:N-1
105- y = model() # simulated measurements
106105 k == 50 && (ry = [50, 35])
107106 k == 100 && (ry = [54, 30])
108- k == 150 && (ud = [0, -20])
107+ k == 150 && (ul = -20)
108+ y = model() # simulated measurements
109109 u = mpc(ry) # or equivalently : u = moveinput!(mpc, ry)
110110 u_data[:,k+1] = u
111111 y_data[:,k+1] = y
112112 ry_data[:,k+1] = ry
113113 updatestate!(mpc, u, y) # update mpc state estimate
114- updatestate!(model, u + ud ) # update simulator with disturbance
114+ updatestate!(model, u + [0; ul] ) # update simulator with the load disturbance
115115 end
116116 return u_data, y_data, ry_data
117117end
@@ -158,15 +158,85 @@ real-life control problems. Constructing a [`LinMPC`](@ref) with `DAQP` and inpu
158158using JuMP, DAQP
159159daqp = Model(DAQP.Optimizer)
160160estim = SteadyKalmanFilter(model, nint_u=[1, 1], nint_ym=[0, 0])
161- mpc2 = setconstraint!(LinMPC(estim, Hp=15, Hc=2, optim=daqp), ymin=[45, -Inf])
161+ mpc2 = LinMPC(estim, Hp=15, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1], optim=daqp)
162+ mpc2 = setconstraint!(mpc2, ymin=[45, -Inf])
162163```
163164
164165leads to similar computational times, but it does accelerate the rejection of the load
165166disturbance and eliminates the level constraint violation:
166167
167168``` @example 1
168169setstate!(model, zeros(model.nx))
169- initstate!(mpc2, model.uop, model())
170- u_data2, y_data2, ry_data2 = test_mpc(mpc2, model)
171- plot_data(t_data, u_data2, y_data2, ry_data2)
170+ u, y = model.uop, model()
171+ initstate!(mpc2, u, y)
172+ u_data, y_data, ry_data = test_mpc(mpc2, model)
173+ plot_data(t_data, u_data, y_data, ry_data)
174+ ```
175+
176+ ## Adding Feedforward Compensation
177+
178+ Suppose that the load disturbance `` u_l `` of the last section is in fact caused by a
179+ separate hot water pipe that discharges into the tank. Measuring this flow rate allows us to
180+ incorporate feedforward compensation in the controller. The new plant model is:
181+
182+ ``` math
183+ \begin{bmatrix}
184+ y_L(s) \\ y_T(s)
185+ \end{bmatrix} =
186+ \begin{bmatrix}
187+ \frac{1.90}{18s + 1} & \frac{1.90}{18s + 1} & \frac{1.90}{18s + 1} \\[3pt]
188+ \frac{-0.74}{8s + 1} & \frac{0.74}{8s + 1} & \frac{0.74}{8s + 1}
189+ \end{bmatrix}
190+ \begin{bmatrix}
191+ u_c(s) \\ u_h(s) \\ u_l(s)
192+ \end{bmatrix}
193+ ```
194+
195+ We construct a new [ ` LinModel ` ] ( @ref ) that includes the measured disturbance
196+ `` \mathbf{d} = [u_l] `` and the operating point `` \mathbf{d_{op}} = [10] `` :
197+
198+ ``` @example 1
199+ sys_ff = [sys sys[1:2, 2]]
200+ model_ff = setop!(LinModel(sys_ff, Ts, i_d=[3]), uop=[10, 10], yop=[50, 30], dop=[10])
201+ mpc_ff = LinMPC(model_ff, Hp=15, Hc=2, Mwt=[1, 1], Nwt=[0.1, 0.1])
202+ mpc_ff = setconstraint!(mpc_ff, ymin=[45, -Inf])
203+ ```
204+
205+ Also, a new test function that feeds the measured disturbance `` \mathbf{d} `` to the
206+ controller is defined:
207+
208+ ``` @example 1
209+ function test_mpc_ff(mpc_ff, model)
210+ N = 200
211+ ry, ul = [50, 30], 0
212+ dop = mpc_ff.estim.model.dop
213+ u_data = zeros(model.nu, N)
214+ y_data = zeros(model.ny, N)
215+ ry_data = zeros(model.ny, N)
216+ for k = 0:N-1
217+ k == 50 && (ry = [50, 35])
218+ k == 100 && (ry = [54, 30])
219+ k == 150 && (ul = -20)
220+ d = ul .+ dop # simulated measured disturbance
221+ y = model() # simulated measurements
222+ u = mpc_ff(ry, d) # also feed the measured disturbance d to the controller
223+ u_data[:,k+1] = u
224+ y_data[:,k+1] = y
225+ ry_data[:,k+1] = ry
226+ updatestate!(mpc_ff, u, y, d) # update estimate with the measured disturbance d
227+ updatestate!(model, u + [0; ul]) # update simulator
228+ end
229+ return u_data, y_data, ry_data
230+ end
231+ nothing # hide
232+ ```
233+
234+ The new feedforward compensation is able to almost perfectly rejet the load disturbance:
235+
236+ ``` @example 1
237+ setstate!(model, zeros(model.nx))
238+ u, y, d = model.uop, model(), mpc_ff.estim.model.dop
239+ initstate!(mpc_ff, u, y, d)
240+ u_data, y_data, ry_data = test_mpc_ff(mpc_ff, model)
241+ plot_data(t_data, u_data, y_data, ry_data)
172242```
0 commit comments