@@ -248,6 +248,79 @@ Dict(:W_nmpc => calcW(res_yd), :W_empc => calcW(res2_yd))
248248Of course, this gain is only exploitable if the motor electronic includes some kind of
249249regenerative 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
253326Nonlinear 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
276349res_lin = sim!(mpc, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
277350plot(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
283356Solving the optimization problem of ` mpc ` with [ ` DAQP ` ] ( https://darnstrom.github.io/daqp/ )
284357optimizer 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
315388res_lin2 = sim!(mpc2, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
316389plot(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
322395The closed-loop performance is still lower than the nonlinear controller, as expected, but
323396computations are about 210 times faster (0.000071 s versus 0.015 s per time steps, on
@@ -331,7 +404,7 @@ plot(res_lin3)
331404savefig("plot9_NonLinMPC.svg"); nothing # hide
332405```
333406
334- ![ plot9_NonLinMPC ] ( plot9_NonLinMPC .svg)
407+ ![ plot11_NonLinMPC ] ( plot11_NonLinMPC .svg)
335408
336409Multiple linearized model and controller objects are required for large deviations from this
337410operating 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
386459x_0 = [0, 0]; x̂_0 = [0, 0, 0]; ry = [180]
387460res_slin = sim_adapt!(mpc3, model, N, ry, plant, x_0, x̂_0)
388461plot(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
394467and the 10° step disturbance:
395468
396469``` @example 1
397470x_0 = [π, 0]; x̂_0 = [π, 0, 0]; y_step = [10]
398471res_slin = sim_adapt!(mpc3, model, N, ry, plant, x_0, x̂_0, y_step)
399472plot(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
405478The computations of the successive linearization MPC are about 75 times faster than the
406479nonlinear MPC on average, an impressive gain for similar closed-loop performances!
0 commit comments