Skip to content

Commit c36fc27

Browse files
authored
Merge pull request #118 from JuliaControl/custom_constraints
Added: support for custom nonlinear constraints `gc` in `NonLinMPC`
2 parents 1a451a9 + 58191fd commit c36fc27

File tree

12 files changed

+476
-222
lines changed

12 files changed

+476
-222
lines changed

CITATION.cff

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cff-version: 1.2.0
2+
message: "If you use this software, please cite the article below."
3+
authors:
4+
- name: "ModelPredictiveControl.jl contributors"
5+
title: "ModelPredictiveControl.jl - An open source model predictive control package for Julia."
6+
url: "https://github.com/JuliaControl/ModelPredictiveControl.jl"
7+
preferred-citation:
8+
type: generic
9+
authors:
10+
- family-names: "Gagnon"
11+
given-names: "Francis"
12+
- family-names: "Thivierge"
13+
given-names: "Alex"
14+
- family-names: "Desbiens"
15+
given-names: "André"
16+
- family-names: "Bagge Carlson"
17+
given-names: "Fredrik"
18+
title: "ModelPredictiveControl.jl: advanced process control made easy in Julia"
19+
year: 2024
20+
doi: "10.1007/978-3-030-68928-5"
21+
url: "https://arxiv.org/abs/2411.09764"

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ for more detailed examples.
8484
- [x] move suppression
8585
- [x] input setpoint tracking
8686
- [x] terminal costs
87-
- [x] economic costs (economic model predictive control)
87+
- [x] custom economic costs (economic model predictive control)
8888
- [x] adaptive linear model predictive controller
8989
- [x] manual model modification
9090
- [x] automatic successive linearization of a nonlinear model
@@ -95,7 +95,7 @@ for more detailed examples.
9595
- [x] manipulated inputs
9696
- [x] manipulated inputs increments
9797
- [x] terminal states to ensure nominal stability
98-
- [ ] custom manipulated input constraints that are a function of the predictions
98+
- [x] custom economic inequality constraints (soft or hard)
9999
- [x] supported feedback strategy:
100100
- [x] state estimator (see State Estimation features)
101101
- [x] internal model structure with a custom stochastic model

docs/src/manual/nonlinmpc.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ output vector of `plant` argument corresponds to the model output vector in `mpc
194194
We can now define the ``J_E`` function and the `empc` controller:
195195

196196
```@example 1
197-
function JE(UE, ŶE, _ , p)
197+
function JE(Ue, Ŷe, _ , p)
198198
Ts = p
199-
τ, ω = UE[1:end-1], ŶE[2:2:end-1]
199+
τ, ω = Ue[1:end-1], Ŷe[2:2:end-1]
200200
return Ts*sum(τ.*ω)
201201
end
202202
empc = NonLinMPC(estim2; Hp, Hc, Nwt, Mwt=[0.5, 0], Cwt=Inf, Ewt=3.5e3, JE=JE, p=Ts)

src/controller/construct.jl

+44-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"Include all the data for the constraints of [`PredictiveController`](@ref)"
2-
struct ControllerConstraint{NT<:Real}
2+
struct ControllerConstraint{NT<:Real, GCfunc<:Function}
33
ẽx̂ ::Matrix{NT}
44
fx̂ ::Vector{NT}
55
gx̂ ::Matrix{NT}
@@ -31,12 +31,14 @@ struct ControllerConstraint{NT<:Real}
3131
c_x̂min ::Vector{NT}
3232
c_x̂max ::Vector{NT}
3333
i_g ::BitVector
34+
gc! ::GCfunc
35+
nc ::Int
3436
end
3537

3638
@doc raw"""
3739
setconstraint!(mpc::PredictiveController; <keyword arguments>) -> mpc
3840
39-
Set the constraint parameters of the [`PredictiveController`](@ref) `mpc`.
41+
Set the bound constraint parameters of the [`PredictiveController`](@ref) `mpc`.
4042
4143
The predictive controllers support both soft and hard constraints, defined by:
4244
```math
@@ -146,7 +148,8 @@ function setconstraint!(
146148
C_Δumin = C_Deltaumin, C_Δumax = C_Deltaumax,
147149
)
148150
model, con, optim = mpc.estim.model, mpc.con, mpc.optim
149-
nu, ny, nx̂, Hp, Hc, nϵ = model.nu, model.ny, mpc.estim.nx̂, mpc.Hp, mpc.Hc, mpc.
151+
nu, ny, nx̂, Hp, Hc = model.nu, model.ny, mpc.estim.nx̂, mpc.Hp, mpc.Hc
152+
nϵ, nc = mpc.nϵ, con.nc
150153
notSolvedYet = (JuMP.termination_status(optim) == JuMP.OPTIMIZE_NOT_CALLED)
151154
if isnothing(Umin) && !isnothing(umin)
152155
size(umin) == (nu,) || throw(ArgumentError("umin size must be $((nu,))"))
@@ -275,7 +278,8 @@ function setconstraint!(
275278
i_Ymin, i_Ymax = .!isinf.(con.Y0min), .!isinf.(con.Y0max)
276279
i_x̂min, i_x̂max = .!isinf.(con.x̂0min), .!isinf.(con.x̂0max)
277280
if notSolvedYet
278-
con.i_b[:], con.i_g[:], con.A[:] = init_matconstraint_mpc(model,
281+
con.i_b[:], con.i_g[:], con.A[:] = init_matconstraint_mpc(
282+
model, nc,
279283
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
280284
i_Ymin, i_Ymax, i_x̂min, i_x̂max,
281285
con.A_Umin, con.A_Umax, con.A_ΔŨmin, con.A_ΔŨmax,
@@ -287,9 +291,10 @@ function setconstraint!(
287291
JuMP.delete(optim, optim[:linconstraint])
288292
JuMP.unregister(optim, :linconstraint)
289293
@constraint(optim, linconstraint, A*ΔŨvar .≤ b)
290-
setnonlincon!(mpc, model, optim)
294+
set_nonlincon!(mpc, model, optim)
291295
else
292-
i_b, i_g = init_matconstraint_mpc(model,
296+
i_b, i_g = init_matconstraint_mpc(
297+
model, nc,
293298
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
294299
i_Ymin, i_Ymax, i_x̂min, i_x̂max
295300
)
@@ -302,8 +307,10 @@ end
302307

303308

304309
@doc raw"""
305-
init_matconstraint_mpc(model::LinModel,
306-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max, args...
310+
init_matconstraint_mpc(
311+
model::LinModel, nc::Int,
312+
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
313+
args...
307314
) -> i_b, i_g, A
308315
309316
Init `i_b`, `i_g` and `A` matrices for the linear and nonlinear inequality constraints.
@@ -315,19 +322,22 @@ The linear and nonlinear inequality constraints are respectively defined as:
315322
\mathbf{g(ΔŨ)} &≤ \mathbf{0}
316323
\end{aligned}
317324
```
318-
`i_b` is a `BitVector` including the indices of ``\mathbf{b}`` that are finite numbers.
319-
`i_g` is a similar vector but for the indices of ``\mathbf{g}`` (empty if `model` is a
320-
[`LinModel`](@ref)). The method also returns the ``\mathbf{A}`` matrix if `args` is
321-
provided. In such a case, `args` needs to contain all the inequality constraint matrices:
325+
The argument `nc` is the number of custom nonlinear constraints in ``\mathbf{g_c}``. `i_b`
326+
is a `BitVector` including the indices of ``\mathbf{b}`` that are finite numbers. `i_g` is a
327+
similar vector but for the indices of ``\mathbf{g}``. The method also returns the
328+
``\mathbf{A}`` matrix if `args` is provided. In such a case, `args` needs to contain all
329+
the inequality constraint matrices:
322330
`A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max`.
323331
"""
324-
function init_matconstraint_mpc(::LinModel{NT},
325-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max, args...
332+
function init_matconstraint_mpc(
333+
::LinModel{NT}, nc::Int,
334+
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
335+
args...
326336
) where {NT<:Real}
327337
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax; i_Ymin; i_Ymax; i_x̂min; i_x̂max]
328-
i_g = BitVector()
338+
i_g = trues(nc)
329339
if isempty(args)
330-
A = zeros(NT, length(i_b), 0)
340+
A = nothing
331341
else
332342
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max = args
333343
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
@@ -336,13 +346,15 @@ function init_matconstraint_mpc(::LinModel{NT},
336346
end
337347

338348
"Init `i_b, A` without outputs and terminal constraints if `model` is not a [`LinModel`](@ref)."
339-
function init_matconstraint_mpc(::SimModel{NT},
340-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max, args...
349+
function init_matconstraint_mpc(
350+
::SimModel{NT}, nc::Int,
351+
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
352+
args...
341353
) where {NT<:Real}
342354
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax]
343-
i_g = [i_Ymin; i_Ymax; i_x̂min; i_x̂max]
355+
i_g = [i_Ymin; i_Ymax; i_x̂min; i_x̂max; trues(nc)]
344356
if isempty(args)
345-
A = zeros(NT, length(i_b), 0)
357+
A = nothing
346358
else
347359
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, _ , _ , _ , _ = args
348360
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax]
@@ -351,7 +363,7 @@ function init_matconstraint_mpc(::SimModel{NT},
351363
end
352364

353365
"By default, there is no nonlinear constraint, thus do nothing."
354-
setnonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
366+
set_nonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
355367

356368
"""
357369
default_Hp(model::LinModel)
@@ -625,16 +637,20 @@ function init_quadprog(::SimModel{NT}, Ẽ, S̃, M_Hp, Ñ_Hc, L_Hp) where {NT<:
625637
end
626638

627639
"""
628-
init_defaultcon_mpc(estim, C, S, N_Hc, E, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂) -> con, S̃, Ñ_Hc, Ẽ
640+
init_defaultcon_mpc(
641+
estim, C, S, N_Hc, E, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂,
642+
gc!=(_,_,_,_,_,_)->nothing, nc=0
643+
) -> con, S̃, Ñ_Hc, Ẽ
629644
630645
Init `ControllerConstraint` struct with default parameters based on estimator `estim`.
631646
632647
Also return `S̃`, `Ñ_Hc` and `Ẽ` matrices for the the augmented decision vector `ΔŨ`.
633648
"""
634649
function init_defaultcon_mpc(
635650
estim::StateEstimator{NT},
636-
Hp, Hc, C, S, N_Hc, E, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂
637-
) where {NT<:Real}
651+
Hp, Hc, C, S, N_Hc, E, ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂,
652+
gc!::GCfunc=(_,_,_,_,_,_)->nothing, nc=0
653+
) where {NT<:Real, GCfunc<:Function}
638654
model = estim.model
639655
nu, ny, nx̂ = model.nu, model.ny, estim.nx̂
640656
= isinf(C) ? 0 : 1
@@ -661,16 +677,17 @@ function init_defaultcon_mpc(
661677
i_Ymin, i_Ymax = .!isinf.(Y0min), .!isinf.(Y0max)
662678
i_x̂min, i_x̂max = .!isinf.(x̂0min), .!isinf.(x̂0max)
663679
i_b, i_g, A = init_matconstraint_mpc(
664-
model,
680+
model, nc,
665681
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
666682
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂max, A_x̂min
667683
)
668684
b = zeros(NT, size(A, 1)) # dummy b vector (updated just before optimization)
669-
con = ControllerConstraint{NT}(
685+
con = ControllerConstraint{NT, GCfunc}(
670686
ẽx̂ , fx̂ , gx̂ , jx̂ , kx̂ , vx̂ , bx̂ ,
671687
U0min , U0max , ΔŨmin , ΔŨmax , Y0min , Y0max , x̂0min , x̂0max,
672688
A_Umin , A_Umax, A_ΔŨmin, A_ΔŨmax , A_Ymin , A_Ymax , A_x̂min , A_x̂max,
673-
A , b , i_b , C_ymin , C_ymax , c_x̂min , c_x̂max , i_g
689+
A , b , i_b , C_ymin , C_ymax , c_x̂min , c_x̂max , i_g,
690+
gc! , nc
674691
)
675692
return con, nϵ, S̃, Ñ_Hc, Ẽ
676693
end

0 commit comments

Comments
 (0)