@@ -136,81 +136,6 @@ savefig(ans, "plot4_NonLinMPC.svg"); nothing # hide
136
136
137
137
![ plot4_NonLinMPC] ( plot4_NonLinMPC.svg )
138
138
139
- ## Linearizing the Model
140
-
141
- Nonlinear MPC are more computationally expensive than [ ` LinMPC ` ] ( @ref ) . Solving the problem
142
- should always be faster than the sampling time `` T_s = 0.1 `` s for real-time operation. For
143
- electronic or mechanical systems like here, this requirement is sometimes harder to achieve
144
- because of their fast dynamics. To ease the design and comparison with [ ` LinMPC ` ] ( @ref ) , the
145
- [ ` linearize ` ] ( @ref ) function allows automatic linearization of [ ` NonLinModel ` ] ( @ref ) based
146
- on [ ` ForwardDiff.jl ` ] ( https://juliadiff.org/ForwardDiff.jl/stable/ ) . We first linearize
147
- ` model ` at the operating points `` \mathbf{x} = [\begin{smallmatrix} π\\0 \end{smallmatrix}] ``
148
- and `` \mathbf{u} = 0 `` (inverted position):
149
-
150
- ``` @example 1
151
- linmodel = linearize(model, x=[π, 0], u=[0])
152
- ```
153
-
154
- It is worth mentionning that the Euler method in ` model ` object is not the best choice for
155
- linearization since its accuracy is low (i.e. approximation of a bad approximation). A
156
- [ ` SteadyKalmanFilter ` ] ( @ref ) and a [ ` LinMPC ` ] ( @ref ) are designed from ` linmodel ` :
157
-
158
- ``` @example 1
159
- kf = SteadyKalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
160
- mpc = LinMPC(kf, Hp=20, Hc=2, Mwt=[0.5], Nwt=[2.5])
161
- mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
162
- ```
163
-
164
- The linear controller has difficulties to reject the 10° step disturbance:
165
-
166
- ``` @example 1
167
- res_lin = sim!(mpc, N, [180.0]; plant, x0=[π, 0], y_step=[10])
168
- plot(res_lin)
169
- savefig(ans, "plot5_NonLinMPC.svg"); nothing # hide
170
- ```
171
-
172
- ![ plot5_NonLinMPC] ( plot5_NonLinMPC.svg )
173
-
174
- Some improvements can be achieved by solving the optimization problem of ` mpc ` with [ ` DAQP ` ] ( https://darnstrom.github.io/daqp/ )
175
- optimizer instead of the default ` OSQP ` solver. It is indeed documented that ` DAQP ` can
176
- perform better on small/medium dense matrices and unstable poles, which is obviously the
177
- case here (the absolute value of unstable poles are greater than one).:
178
-
179
- ``` @example 1
180
- using LinearAlgebra; poles = eigvals(linmodel.A)
181
- ```
182
-
183
- To install it, run:
184
-
185
- ``` text
186
- using Pkg; Pkg.add("DAQP")
187
- ```
188
-
189
- Constructing a [ ` LinMPC ` ] ( @ref ) with ` DAQP ` :
190
-
191
- ``` @example 1
192
- using JuMP, DAQP
193
- daqp = Model(DAQP.Optimizer, add_bridges=false)
194
- mpc2 = LinMPC(kf, Hp=20, Hc=2, Mwt=[0.5], Nwt=[2.5], optim=daqp)
195
- mpc2 = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
196
- ```
197
-
198
- does improve the rejection of the step disturbance:
199
-
200
- ``` @example 1
201
- res_lin2 = sim!(mpc2, N, [180.0]; plant, x0=[π, 0], y_step=[10])
202
- plot(res_lin2)
203
- savefig(ans, "plot6_NonLinMPC.svg"); nothing # hide
204
- ```
205
-
206
- ![ plot6_NonLinMPC] ( plot6_NonLinMPC.svg )
207
-
208
- The performance is still lower than the nonlinear controller, as expected, but computations
209
- are about 2000 times faster (0.00002 s versus 0.04 s per time steps on average). Note that
210
- ` linmodel ` is only valid for angular positions near 180°. Multiple linearized models and
211
- controllers are required for large deviations from this operating point. This is known as
212
- gain scheduling.
213
-
214
139
## Economic Model Predictive Controller
215
140
216
141
Economic MPC can achieve the same objective but with lower economical costs. For this case
@@ -273,10 +198,10 @@ setpoint is:
273
198
unset_time_limit_sec(empc.optim) # hide
274
199
res2_ry = sim!(empc, N, [180, 0], plant=plant2, x0=[0, 0], x̂0=[0, 0, 0])
275
200
plot(res2_ry)
276
- savefig(ans, "plot7_NonLinMPC .svg"); nothing # hide
201
+ savefig(ans, "plot5_NonLinMPC .svg"); nothing # hide
277
202
```
278
203
279
- ![ plot7_NonLinMPC ] ( plot7_NonLinMPC .svg)
204
+ ![ plot5_NonLinMPC ] ( plot5_NonLinMPC .svg)
280
205
281
206
and the energy consumption is slightly lower:
282
207
@@ -293,10 +218,10 @@ Also, for a 10° step disturbance:
293
218
``` @example 1
294
219
res2_yd = sim!(empc, N, [180; 0]; plant=plant2, x0=[π, 0], x̂0=[π, 0, 0], y_step=[10, 0])
295
220
plot(res2_yd)
296
- savefig(ans, "plot8_NonLinMPC .svg"); nothing # hide
221
+ savefig(ans, "plot6_NonLinMPC .svg"); nothing # hide
297
222
```
298
223
299
- ![ plot8_NonLinMPC ] ( plot8_NonLinMPC .svg)
224
+ ![ plot6_NonLinMPC ] ( plot6_NonLinMPC .svg)
300
225
301
226
the new controller is able to recuperate more energy from the pendulum (i.e. negative work):
302
227
@@ -306,3 +231,80 @@ Dict(:W_nmpc => calcW(res_yd), :W_empc => calcW(res2_yd))
306
231
307
232
Of course, this gain is only exploitable if the motor electronic includes some kind of
308
233
regenerative circuitry.
234
+
235
+ ## Linearizing the Model
236
+
237
+ Nonlinear MPC are more computationally expensive than [ ` LinMPC ` ] ( @ref ) . Solving the problem
238
+ should always be faster than the sampling time `` T_s = 0.1 `` s for real-time operation. This
239
+ requirement is sometimes hard to meet on electronics or mechanical systems because of fast
240
+ dynamics. To ease the design and comparison with [ ` LinMPC ` ] ( @ref ) , the [ ` linearize ` ] ( @ref )
241
+ function allows automatic linearization of [ ` NonLinModel ` ] ( @ref ) based on [ ` ForwardDiff.jl ` ] ( https://juliadiff.org/ForwardDiff.jl/stable/ ) .
242
+ We first linearize ` model ` at the point `` θ = π `` rad and `` ω = τ = 0 `` (inverted position):
243
+
244
+ ``` @example 1
245
+ linmodel = linearize(model, x=[π, 0], u=[0])
246
+ ```
247
+
248
+ It is worth mentioning that the Euler method in ` model ` object is not the best choice for
249
+ linearization since its accuracy is low (approximation of a poor approximation). A
250
+ [ ` SteadyKalmanFilter ` ] ( @ref ) and a [ ` LinMPC ` ] ( @ref ) are designed from ` linmodel ` :
251
+
252
+ ``` @example 1
253
+ kf = SteadyKalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
254
+ mpc = LinMPC(kf, Hp=20, Hc=2, Mwt=[0.5], Nwt=[2.5])
255
+ mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
256
+ ```
257
+
258
+ The linear controller has difficulties to reject the 10° step disturbance:
259
+
260
+ ``` @example 1
261
+ res_lin = sim!(mpc, N, [180.0]; plant, x0=[π, 0], y_step=[10])
262
+ plot(res_lin)
263
+ savefig(ans, "plot7_NonLinMPC.svg"); nothing # hide
264
+ ```
265
+
266
+ ![ plot7_NonLinMPC] ( plot7_NonLinMPC.svg )
267
+
268
+ Solving the optimization problem of ` mpc ` with [ ` DAQP ` ] ( https://darnstrom.github.io/daqp/ )
269
+ optimizer instead of the default ` OSQP ` solver can help here. It is indeed documented that
270
+ ` DAQP ` can perform better on small/medium dense matrices and unstable poles[ ^ 1 ] , which is
271
+ obviously the case here (absolute value of unstable poles are greater than one):
272
+
273
+ [ ^ 1 ] : Arnström, D., Bemporad, A., and Axehill, D. (2022). A dual active-set solver for
274
+ embedded quadratic programming using recursive LDLᵀ updates. IEEE Trans. Autom. Contr.,
275
+ 67(8). https://doi.org/doi:10.1109/TAC.2022.3176430 .
276
+
277
+ ``` @example 1
278
+ using LinearAlgebra; poles = eigvals(linmodel.A)
279
+ ```
280
+
281
+ To install the solver, run:
282
+
283
+ ``` text
284
+ using Pkg; Pkg.add("DAQP")
285
+ ```
286
+
287
+ Constructing a [ ` LinMPC ` ] ( @ref ) with ` DAQP ` :
288
+
289
+ ``` @example 1
290
+ using JuMP, DAQP
291
+ daqp = Model(DAQP.Optimizer, add_bridges=false)
292
+ mpc2 = LinMPC(kf, Hp=20, Hc=2, Mwt=[0.5], Nwt=[2.5], optim=daqp)
293
+ mpc2 = setconstraint!(mpc2, umin=[-1.5], umax=[+1.5])
294
+ ```
295
+
296
+ does improve the rejection of the step disturbance:
297
+
298
+ ``` @example 1
299
+ res_lin2 = sim!(mpc2, N, [180.0]; plant, x0=[π, 0], y_step=[10])
300
+ plot(res_lin2)
301
+ savefig(ans, "plot8_NonLinMPC.svg"); nothing # hide
302
+ ```
303
+
304
+ ![ plot8_NonLinMPC] ( plot8_NonLinMPC.svg )
305
+
306
+ The closed-loop performance is still lower than the nonlinear controller, as expected, but
307
+ computations are about 2000 times faster (0.00002 s versus 0.04 s per time steps, on
308
+ average). Note that ` linmodel ` is only valid for angular positions near 180°. Multiple
309
+ linearized models and controllers are required for large deviations from this operating
310
+ point. This is known as gain scheduling.
0 commit comments