@@ -108,16 +108,19 @@ As the motor torque is limited to -1.5 to 1.5 N m, we incorporate the input cons
108
108
a [ ` NonLinMPC ` ] ( @ref ) :
109
109
110
110
``` @example 1
111
- nmpc = NonLinMPC(estim, Hp=20, Hc=2, Mwt=[0.5], Nwt=[2.5], Cwt=Inf)
112
- nmpc = setconstraint!(nmpc, umin=[-1.5], umax=[+1.5])
111
+ Hp, Hc, Mwt, Nwt = 20, 2, [0.5], [2.5]
112
+ nmpc = NonLinMPC(estim; Hp, Hc, Mwt, Nwt, Cwt=Inf)
113
+ umin, umax = [-1.5], [+1.5]
114
+ nmpc = setconstraint!(nmpc; umin, umax)
113
115
```
114
116
115
- The option ` Cwt=Inf ` disables the slack variable ` ϵ ` for constraint softening. We test ` mpc ` performance on ` plant ` by imposing an angular setpoint of 180° (inverted position):
117
+ The option ` Cwt=Inf ` disables the slack variable ` ϵ ` for constraint softening. We test ` mpc `
118
+ performance on ` plant ` by imposing an angular setpoint of 180° (inverted position):
116
119
117
120
``` @example 1
118
121
using Logging; disable_logging(Warn) # hide
119
122
using JuMP; unset_time_limit_sec(nmpc.optim) # hide
120
- res_ry = sim!(nmpc, N, [180.0], plant=plant, x0 =[0, 0], x̂0 =[0, 0, 0])
123
+ res_ry = sim!(nmpc, N, [180.0], plant=plant, x_0 =[0, 0], x̂_0 =[0, 0, 0])
121
124
plot(res_ry)
122
125
savefig(ans, "plot3_NonLinMPC.svg"); nothing # hide
123
126
```
@@ -129,7 +132,7 @@ inverted position, the closed-loop response to a step disturbances of 10° is al
129
132
satisfactory:
130
133
131
134
``` @example 1
132
- res_yd = sim!(nmpc, N, [180.0], plant=plant, x0 =[π, 0], x̂0 =[π, 0, 0], y_step=[10])
135
+ res_yd = sim!(nmpc, N, [180.0], plant=plant, x_0 =[π, 0], x̂_0 =[π, 0, 0], y_step=[10])
133
136
plot(res_yd)
134
137
savefig(ans, "plot4_NonLinMPC.svg"); nothing # hide
135
138
```
@@ -184,8 +187,8 @@ function JE(UE, ŶE, _ )
184
187
τ, ω = UE[1:end-1], ŶE[2:2:end-1]
185
188
return Ts*sum(τ.*ω)
186
189
end
187
- empc = NonLinMPC(estim2, Hp=20 , Hc=2, Mwt=[0.5, 0], Nwt=[2.5 ], Cwt=Inf, Ewt=3.5e3, JE=JE)
188
- empc = setconstraint!(empc, umin=[-1.5] , umax=[+1.5] )
190
+ empc = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=[0.5, 0], Cwt=Inf, Ewt=3.5e3, JE=JE)
191
+ empc = setconstraint!(empc; umin, umax)
189
192
```
190
193
191
194
The keyword argument ` Ewt ` weights the economic costs relative to the other terms in the
@@ -196,7 +199,7 @@ setpoint is similar:
196
199
197
200
``` @example 1
198
201
unset_time_limit_sec(empc.optim) # hide
199
- res2_ry = sim!(empc, N, [180, 0], plant=plant2, x0 =[0, 0], x̂0 =[0, 0, 0])
202
+ res2_ry = sim!(empc, N, [180, 0], plant=plant2, x_0 =[0, 0], x̂_0 =[0, 0, 0])
200
203
plot(res2_ry)
201
204
savefig(ans, "plot5_NonLinMPC.svg"); nothing # hide
202
205
```
@@ -216,7 +219,7 @@ Dict(:W_nmpc => calcW(res_ry), :W_empc => calcW(res2_ry))
216
219
Also, for a 10° step disturbance:
217
220
218
221
``` @example 1
219
- res2_yd = sim!(empc, N, [180; 0]; plant=plant2, x0 =[π, 0], x̂0 =[π, 0, 0], y_step=[10, 0])
222
+ res2_yd = sim!(empc, N, [180; 0]; plant=plant2, x_0 =[π, 0], x̂_0 =[π, 0, 0], y_step=[10, 0])
220
223
plot(res2_yd)
221
224
savefig(ans, "plot6_NonLinMPC.svg"); nothing # hide
222
225
```
@@ -232,7 +235,7 @@ Dict(:W_nmpc => calcW(res_yd), :W_empc => calcW(res2_yd))
232
235
Of course, this gain is only exploitable if the motor electronic includes some kind of
233
236
regenerative circuitry.
234
237
235
- ## Linearizing the Model
238
+ ## Model Linearization
236
239
237
240
Nonlinear MPC is more computationally expensive than [ ` LinMPC ` ] ( @ref ) . Solving the problem
238
241
should always be faster than the sampling time `` T_s = 0.1 `` s for real-time operation. This
@@ -248,15 +251,15 @@ linmodel = linearize(model, x=[π, 0], u=[0])
248
251
A [ ` SteadyKalmanFilter ` ] ( @ref ) and a [ ` LinMPC ` ] ( @ref ) are designed from ` linmodel ` :
249
252
250
253
``` @example 1
251
- kf = SteadyKalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
252
- mpc = LinMPC(kf, Hp=20 , Hc=2 , Mwt=[0.5] , Nwt=[2.5] , Cwt=Inf)
254
+ skf = SteadyKalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
255
+ mpc = LinMPC(skf; Hp, Hc, Mwt, Nwt, Cwt=Inf)
253
256
mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
254
257
```
255
258
256
259
The linear controller has difficulties to reject the 10° step disturbance:
257
260
258
261
``` @example 1
259
- res_lin = sim!(mpc, N, [180.0]; plant, x0 =[π, 0], y_step=[10])
262
+ res_lin = sim!(mpc, N, [180.0]; plant, x_0 =[π, 0], y_step=[10])
260
263
plot(res_lin)
261
264
savefig(ans, "plot7_NonLinMPC.svg"); nothing # hide
262
265
```
@@ -287,22 +290,98 @@ Constructing a [`LinMPC`](@ref) with `DAQP`:
287
290
``` @example 1
288
291
using JuMP, DAQP
289
292
daqp = Model(DAQP.Optimizer, add_bridges=false)
290
- mpc2 = LinMPC(kf, Hp=20 , Hc=2 , Mwt=[0.5] , Nwt=[2.5] , Cwt=Inf, optim=daqp)
291
- mpc2 = setconstraint!(mpc2, umin=[-1.5] , umax=[+1.5] )
293
+ mpc2 = LinMPC(skf; Hp, Hc, Mwt, Nwt, Cwt=Inf, optim=daqp)
294
+ mpc2 = setconstraint!(mpc2; umin, umax)
292
295
```
293
296
294
297
does improve the rejection of the step disturbance:
295
298
296
299
``` @example 1
297
- res_lin2 = sim!(mpc2, N, [180.0]; plant, x0 =[π, 0], y_step=[10])
300
+ res_lin2 = sim!(mpc2, N, [180.0]; plant, x_0 =[π, 0], y_step=[10])
298
301
plot(res_lin2)
299
302
savefig(ans, "plot8_NonLinMPC.svg"); nothing # hide
300
303
```
301
304
302
305
![ plot8_NonLinMPC] ( plot8_NonLinMPC.svg )
303
306
304
307
The closed-loop performance is still lower than the nonlinear controller, as expected, but
305
- computations are about 2000 times faster (0.00002 s versus 0.04 s per time steps, on
306
- average). Note that ` linmodel ` is only valid for angular positions near 180°. Multiple
307
- linearized models and controllers are required for large deviations from this operating
308
- point. This is known as gain scheduling.
308
+ computations are about 210 times faster (0.000071 s versus 0.015 s per time steps, on
309
+ average). However, remember that ` linmodel ` is only valid for angular positions near 180°.
310
+ For example, the 180° setpoint response from 0° is unsatisfactory since the predictions are
311
+ poor in the first quadrant:
312
+
313
+ ``` @example 1
314
+ res_lin3 = sim!(mpc2, N, [180.0]; plant, x_0=[0, 0])
315
+ plot(res_lin3)
316
+ savefig(ans, "plot9_NonLinMPC.svg"); nothing # hide
317
+ ```
318
+
319
+ ![ plot9_NonLinMPC] ( plot9_NonLinMPC.svg )
320
+
321
+ Multiple linearized model and controller objects are required for large deviations from this
322
+ operating point. This is known as gain scheduling. Another approach is adapting the model of
323
+ the [ ` LinMPC ` ] ( @ref ) instance based on repeated online linearization.
324
+
325
+ ## Adapting the Model via Successive Linearization
326
+
327
+ The [ ` setmodel! ` ] ( @ref ) method allows online adaptation of a linear plant model. Combined
328
+ with the automatic linearization of [ ` linearize ` ] ( @ref ) , a successive linearization MPC can
329
+ be designed with minimal efforts. The [ ` SteadyKalmanFilter ` ] ( @ref ) does not support
330
+ [ ` setmodel! ` ] ( @ref ) , so we need to use the time-varying [ ` KalmanFilter ` ] ( @ref ) instead:
331
+
332
+ ``` @example 1
333
+ kf = KalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
334
+ mpc3 = LinMPC(kf; Hp, Hc, Mwt, Nwt, Cwt=Inf, optim=daqp)
335
+ mpc3 = setconstraint!(mpc3; umin, umax)
336
+ ```
337
+
338
+ We create a function that simulates the plant and the adaptive controller:
339
+
340
+ ``` @example 1
341
+ function test_slmpc(nonlinmodel, mpc, ry, plant; x_0=plant.xop, y_step=0)
342
+ N = 35
343
+ U_data, Y_data, Ry_data = zeros(plant.nu, N), zeros(plant.ny, N), zeros(plant.ny, N)
344
+ setstate!(plant, x_0)
345
+ u, y = [0.0], plant()
346
+ x̂ = initstate!(mpc, u, y)
347
+ for i = 1:N
348
+ y = plant() .+ y_step
349
+ u = moveinput!(mpc, ry)
350
+ linmodel = linearize(nonlinmodel; u, x=x̂[1:2])
351
+ setmodel!(mpc, linmodel)
352
+ U_data[:,i], Y_data[:,i], Ry_data[:,i] = u, y, ry
353
+ x̂ = updatestate!(mpc, u, y) # update mpc state estimate
354
+ updatestate!(plant, u) # update plant simulator
355
+ end
356
+ res = SimResult(mpc, U_data, Y_data; Ry_data)
357
+ return res
358
+ end
359
+ nothing # hide
360
+ ```
361
+
362
+ The [ ` setmodel! ` ] ( @ref ) method must be called after solving the optimization problem with
363
+ [ ` moveinput! ` ] ( @ref ) , and before updating the state estimate with [ ` updatestate! ` ] ( @ref ) .
364
+ The [ ` SimResult ` ] ( @ref ) object is for plotting purposes only. The adaptive [ ` LinMPC ` ] ( @ref )
365
+ performances are similar to the nonlinear MPC, both for the 180° setpoint:
366
+
367
+ ``` @example 1
368
+ res_slin = test_slmpc(model, mpc3, [180], plant, x_0=[0, 0])
369
+ plot(res_slin)
370
+ savefig(ans, "plot10_NonLinMPC.svg"); nothing # hide
371
+ ```
372
+
373
+ ![ plot10_NonLinMPC] ( plot10_NonLinMPC.svg )
374
+
375
+ and the 10° step disturbance:
376
+
377
+ ``` @example 1
378
+ res_slin = test_slmpc(model, mpc3, [180], plant, x_0=[π, 0], y_step=[10])
379
+ plot(res_slin)
380
+ savefig(ans, "plot11_NonLinMPC.svg"); nothing # hide
381
+ ```
382
+
383
+ ![ plot11_NonLinMPC] ( plot11_NonLinMPC.svg )
384
+
385
+ The computations of the successive linearization MPC are about 125 times faster than the
386
+ nonlinear MPC (0.00012 s per time steps versus 0.015 s per time steps, on average), an
387
+ impressive gain for similar closed-loop performances!
0 commit comments