Skip to content

Commit cf8bf82

Browse files
committed
added: example with a custom constraint gc function in the manual
1 parent 1cb43e8 commit cf8bf82

File tree

1 file changed

+82
-9
lines changed

1 file changed

+82
-9
lines changed

docs/src/manual/nonlinmpc.md

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,79 @@ Dict(:W_nmpc => calcW(res_yd), :W_empc => calcW(res2_yd))
248248
Of course, this gain is only exploitable if the motor electronic includes some kind of
249249
regenerative circuitry.
250250

251+
## Custom Nonlinear Constraints
252+
253+
Instead of limits on the torque, suppose that the motor can deliver a maximum of 3 watt:
254+
255+
```math
256+
P(t) = τ(t) ω(t) ≤ P_\mathrm{max}
257+
```
258+
259+
with ``P_\mathrm{max} = 3`` W. This inequality represents nonlinear constraints that can
260+
be implemented as the custom ``\mathbf{g_c}`` function of [`NonLinMPC`](@ref). The
261+
constructor expect a function in this form:
262+
263+
```math
264+
\mathbf{g_c}(\mathbf{U_e}, \mathbf{Ŷ_e}, \mathbf{D̂_e}, \mathbf{p}, ϵ) ≤ \mathbf{0}
265+
```
266+
267+
in which ``ϵ`` is the slack variable (scalar) to ensure feasibility. There is also an
268+
additional `LHS` argument for the in-place version:
269+
270+
```@example 1
271+
function gc!(LHS, Ue, Ŷe, _, p, ϵ)
272+
Pmax, Hp = p
273+
i_τ, i_ω = 1, 2
274+
for i in eachindex(LHS)
275+
τ, ω = Ue[i_τ], Ŷe[i_ω]
276+
P = τ*ω
277+
LHS[i] = P - Pmax - ϵ
278+
i_τ += 1
279+
i_ω += 2
280+
end
281+
return nothing
282+
end
283+
```
284+
285+
Here we implemented the custom nonlinear constraint function in the mutating form to reduce
286+
the computational burden (see [`NonLinMPC`](@ref) Extended Help for details). Note that
287+
similar mutating forms are also possible for the `f` and `h` functions of [`NonLinModel`](@ref).
288+
We construct the controller by enabling relaxation with the `Cwt` argument, and also by
289+
specifying the number of custom inequality constraints `nc`:
290+
291+
```@example 1
292+
Cwt, Pmax, nc = 1e5, 3, Hp + 1,
293+
p_nmpc2 = [Pmax, Hp]
294+
nmpc2 = NonLinMPC(estim2; Hp, Hc, Nwt=Nwt, Mwt=[0.5, 0], Cwt, gc!, nc, p=p_nmpc2)
295+
using JuMP; unset_time_limit_sec(nmpc2.optim) # hide
296+
```
297+
298+
We include the simulation of a 180° setpoint:
299+
300+
```@example 1
301+
res3_ry = sim!(nmpc2, N, [180; 0]; plant=plant2, x_0=[0, 0], x̂_0=[0, 0, 0])
302+
```
303+
304+
with a additional plot for the power ``P(t)``:
305+
306+
```@example 1
307+
function plotWithPower(res, Pmax)
308+
t, τ, ω = res.T_data, res.U_data[1, :], res.X_data[2, :]
309+
P, Pmax = τ.*ω, fill(Pmax, size(t))
310+
plt1 = plot(res, ploty=[1])
311+
plt2 = plot(t, P, label=raw"$P$", ylabel=raw"$P$ (W)", xlabel="Time (s)", legend=:right)
312+
plot!(plt2, t, Pmax, label=raw"$P_\mathrm{max}$", linestyle=:dot, linewidth=1.5)
313+
return plot(plt1, plt2, layout=(2,1))
314+
end
315+
plotWithPower(res3_ry, Pmax)
316+
savefig("plot7_NonLinMPC.svg"); nothing # hide
317+
```
318+
319+
![plot7_NonLinMPC](plot6_NonLinMPC.svg)
320+
321+
The small constraint violation is caused here by the modeling error in the friction
322+
coefficient ``K``.
323+
251324
## Model Linearization
252325

253326
Nonlinear MPC is more computationally expensive than [`LinMPC`](@ref). Solving the problem
@@ -275,10 +348,10 @@ The linear controller satisfactorily rejects the 10° step disturbance:
275348
```@example 1
276349
res_lin = sim!(mpc, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
277350
plot(res_lin)
278-
savefig("plot7_NonLinMPC.svg"); nothing # hide
351+
savefig("plot9_NonLinMPC.svg"); nothing # hide
279352
```
280353

281-
![plot7_NonLinMPC](plot7_NonLinMPC.svg)
354+
![plot9_NonLinMPC](plot9_NonLinMPC.svg)
282355

283356
Solving the optimization problem of `mpc` with [`DAQP`](https://darnstrom.github.io/daqp/)
284357
optimizer instead of the default `OSQP` solver can improve the performance here. It is
@@ -314,10 +387,10 @@ does slightly improve the rejection of the step disturbance:
314387
```@example 1
315388
res_lin2 = sim!(mpc2, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
316389
plot(res_lin2)
317-
savefig("plot8_NonLinMPC.svg"); nothing # hide
390+
savefig("plot10_NonLinMPC.svg"); nothing # hide
318391
```
319392

320-
![plot8_NonLinMPC](plot8_NonLinMPC.svg)
393+
![plot10_NonLinMPC](plot10_NonLinMPC.svg)
321394

322395
The closed-loop performance is still lower than the nonlinear controller, as expected, but
323396
computations are about 210 times faster (0.000071 s versus 0.015 s per time steps, on
@@ -331,7 +404,7 @@ plot(res_lin3)
331404
savefig("plot9_NonLinMPC.svg"); nothing # hide
332405
```
333406

334-
![plot9_NonLinMPC](plot9_NonLinMPC.svg)
407+
![plot11_NonLinMPC](plot11_NonLinMPC.svg)
335408

336409
Multiple linearized model and controller objects are required for large deviations from this
337410
operating point. This is known as gain scheduling. Another approach is adapting the model of
@@ -386,21 +459,21 @@ operating point. The [`SimResult`](@ref) object is for plotting purposes only. T
386459
x_0 = [0, 0]; x̂_0 = [0, 0, 0]; ry = [180]
387460
res_slin = sim_adapt!(mpc3, model, N, ry, plant, x_0, x̂_0)
388461
plot(res_slin)
389-
savefig("plot10_NonLinMPC.svg"); nothing # hide
462+
savefig("plot12_NonLinMPC.svg"); nothing # hide
390463
```
391464

392-
![plot10_NonLinMPC](plot10_NonLinMPC.svg)
465+
![plot12_NonLinMPC](plot12_NonLinMPC.svg)
393466

394467
and the 10° step disturbance:
395468

396469
```@example 1
397470
x_0 = [π, 0]; x̂_0 = [π, 0, 0]; y_step = [10]
398471
res_slin = sim_adapt!(mpc3, model, N, ry, plant, x_0, x̂_0, y_step)
399472
plot(res_slin)
400-
savefig("plot11_NonLinMPC.svg"); nothing # hide
473+
savefig("plot13_NonLinMPC.svg"); nothing # hide
401474
```
402475

403-
![plot11_NonLinMPC](plot11_NonLinMPC.svg)
476+
![plot13_NonLinMPC](plot13_NonLinMPC.svg)
404477

405478
The computations of the successive linearization MPC are about 75 times faster than the
406479
nonlinear MPC on average, an impressive gain for similar closed-loop performances!

0 commit comments

Comments
 (0)