Skip to content

Commit b84d375

Browse files
authored
Merge pull request #176 from JuliaControl/debug_ms_terminal_nonlinmodel
debug: terminal constraint now works with `MultipleShooting` + `NonLinModel`
2 parents 31cac69 + 85f89ff commit b84d375

9 files changed

+343
-223
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
33
authors = ["Francis Gagnon"]
4-
version = "1.4.2"
4+
version = "1.4.3"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

docs/src/internals/predictive_control.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ModelPredictiveControl.init_nonlincon!
3030

3131
```@docs
3232
ModelPredictiveControl.initpred!(::PredictiveController, ::LinModel, ::Any, ::Any, ::Any, ::Any)
33-
ModelPredictiveControl.linconstraint!(::PredictiveController, ::LinModel)
33+
ModelPredictiveControl.linconstraint!(::PredictiveController, ::LinModel, ::TranscriptionMethod)
3434
ModelPredictiveControl.linconstrainteq!
3535
```
3636

src/ModelPredictiveControl.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export SteadyKalmanFilter, KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFi
3232
export MovingHorizonEstimator
3333
export default_nint, initstate!
3434
export PredictiveController, ExplicitMPC, LinMPC, NonLinMPC, setconstraint!, moveinput!
35-
export SingleShooting, MultipleShooting
35+
export TranscriptionMethod, SingleShooting, MultipleShooting
3636
export SimResult, getinfo, sim!
3737

3838
include("general.jl")

src/controller/construct.jl

+1-89
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ function setconstraint!(
386386
JuMP.delete(optim, optim[:linconstraint])
387387
JuMP.unregister(optim, :linconstraint)
388388
@constraint(optim, linconstraint, A*Z̃var .≤ b)
389-
set_nonlincon!(mpc, model, optim)
389+
set_nonlincon!(mpc, model, transcription, optim)
390390
else
391391
i_b, i_g = init_matconstraint_mpc(
392392
model, transcription, nc,
@@ -400,94 +400,6 @@ function setconstraint!(
400400
return mpc
401401
end
402402

403-
404-
@doc raw"""
405-
init_matconstraint_mpc(
406-
model::LinModel, transcription::TranscriptionMethod, nc::Int,
407-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
408-
args...
409-
) -> i_b, i_g, A, Aeq, neq
410-
411-
Init `i_b`, `i_g`, `neq`, and `A` and `Aeq` matrices for the all the MPC constraints.
412-
413-
The linear and nonlinear constraints are respectively defined as:
414-
```math
415-
\begin{aligned}
416-
\mathbf{A Z̃ } &≤ \mathbf{b} \\
417-
\mathbf{A_{eq} Z̃} &= \mathbf{b_{eq}} \\
418-
\mathbf{g(Z̃)} &≤ \mathbf{0} \\
419-
\mathbf{g_{eq}(Z̃)} &= \mathbf{0} \\
420-
\end{aligned}
421-
```
422-
The argument `nc` is the number of custom nonlinear inequality constraints in
423-
``\mathbf{g_c}``. `i_b` is a `BitVector` including the indices of ``\mathbf{b}`` that are
424-
finite numbers. `i_g` is a similar vector but for the indices of ``\mathbf{g}``. The method
425-
also returns the ``\mathbf{A, A_{eq}}`` matrices and `neq` if `args` is provided. In such a
426-
case, `args` needs to contain all the inequality and equality constraint matrices:
427-
`A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ`. The integer `neq`
428-
is the number of nonlinear equality constraints in ``\mathbf{g_{eq}}``.
429-
"""
430-
function init_matconstraint_mpc(
431-
::LinModel{NT}, ::TranscriptionMethod, nc::Int,
432-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
433-
args...
434-
) where {NT<:Real}
435-
if isempty(args)
436-
A, Aeq, neq = nothing, nothing, nothing
437-
else
438-
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
439-
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
440-
Aeq = A_ŝ
441-
neq = 0
442-
end
443-
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax; i_Ymin; i_Ymax; i_x̂min; i_x̂max]
444-
i_g = trues(nc)
445-
return i_b, i_g, A, Aeq, neq
446-
end
447-
448-
"Init `i_b` without output constraints if [`NonLinModel`](@ref) & [`MultipleShooting`](@ref)."
449-
function init_matconstraint_mpc(
450-
::NonLinModel{NT}, ::MultipleShooting, nc::Int,
451-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
452-
args...
453-
) where {NT<:Real}
454-
if isempty(args)
455-
A, Aeq, neq = nothing, nothing, nothing
456-
else
457-
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
458-
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
459-
Aeq = A_ŝ
460-
nΔŨ, nZ̃ = size(A_ΔŨmin)
461-
neq = nZ̃ - nΔŨ
462-
end
463-
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax; i_x̂min; i_x̂max]
464-
i_g = [i_Ymin; i_Ymax; trues(nc)]
465-
return i_b, i_g, A, Aeq, neq
466-
end
467-
468-
"Init `i_b` without output & terminal constraints if [`NonLinModel`](@ref) & [`SingleShooting`](@ref)."
469-
function init_matconstraint_mpc(
470-
::NonLinModel{NT}, ::SingleShooting, nc::Int,
471-
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
472-
args...
473-
) where {NT<:Real}
474-
if isempty(args)
475-
A, Aeq, neq = nothing, nothing, nothing
476-
else
477-
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
478-
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
479-
Aeq = A_ŝ
480-
neq = 0
481-
end
482-
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax]
483-
i_g = [i_Ymin; i_Ymax; i_x̂min; i_x̂max; trues(nc)]
484-
return i_b, i_g, A, Aeq, neq
485-
end
486-
487-
488-
"By default, there is no nonlinear constraint, thus do nothing."
489-
set_nonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
490-
491403
"""
492404
default_Hp(model::LinModel)
493405

src/controller/execute.jl

+1-61
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ function moveinput!(
6666
end
6767
validate_args(mpc, ry, d, D̂, R̂y, R̂u)
6868
initpred!(mpc, mpc.estim.model, d, D̂, R̂y, R̂u)
69-
linconstraint!(mpc, mpc.estim.model)
69+
linconstraint!(mpc, mpc.estim.model, mpc.transcription)
7070
linconstrainteq!(mpc, mpc.estim.model, mpc.transcription)
7171
= optim_objective!(mpc)
7272
return getinput(mpc, Z̃)
@@ -269,66 +269,6 @@ end
269269
"Fill `Ŷs` vector with 0 values when `estim` is not an [`InternalModel`](@ref)."
270270
predictstoch!(Ŷs, mpc::PredictiveController, ::StateEstimator) = (Ŷs .= 0; nothing)
271271

272-
@doc raw"""
273-
linconstraint!(mpc::PredictiveController, model::LinModel)
274-
275-
Set `b` vector for the linear model inequality constraints (``\mathbf{A Z̃ ≤ b}``).
276-
277-
Also init ``\mathbf{f_x̂} = \mathbf{g_x̂ d_0}(k) + \mathbf{j_x̂ D̂_0} + \mathbf{k_x̂ x̂_0}(k) +
278-
\mathbf{v_x̂ u_0}(k-1) + \mathbf{b_x̂}`` vector for the terminal constraints, see
279-
[`init_predmat`](@ref).
280-
"""
281-
function linconstraint!(mpc::PredictiveController, model::LinModel)
282-
nU, nΔŨ, nY = length(mpc.con.U0min), length(mpc.con.ΔŨmin), length(mpc.con.Y0min)
283-
nx̂, fx̂ = mpc.estim.nx̂, mpc.con.fx̂
284-
fx̂ .= mpc.con.bx̂
285-
mul!(fx̂, mpc.con.kx̂, mpc.estim.x̂0, 1, 1)
286-
mul!(fx̂, mpc.con.vx̂, mpc.estim.lastu0, 1, 1)
287-
if model.nd 0
288-
mul!(fx̂, mpc.con.gx̂, mpc.d0, 1, 1)
289-
mul!(fx̂, mpc.con.jx̂, mpc.D̂0, 1, 1)
290-
end
291-
n = 0
292-
mpc.con.b[(n+1):(n+nU)] .= @. -mpc.con.U0min + mpc.Tu_lastu0
293-
n += nU
294-
mpc.con.b[(n+1):(n+nU)] .= @. +mpc.con.U0max - mpc.Tu_lastu0
295-
n += nU
296-
mpc.con.b[(n+1):(n+nΔŨ)] .= @. -mpc.con.ΔŨmin
297-
n += nΔŨ
298-
mpc.con.b[(n+1):(n+nΔŨ)] .= @. +mpc.con.ΔŨmax
299-
n += nΔŨ
300-
mpc.con.b[(n+1):(n+nY)] .= @. -mpc.con.Y0min + mpc.F
301-
n += nY
302-
mpc.con.b[(n+1):(n+nY)] .= @. +mpc.con.Y0max - mpc.F
303-
n += nY
304-
mpc.con.b[(n+1):(n+nx̂)] .= @. -mpc.con.x̂0min + fx̂
305-
n += nx̂
306-
mpc.con.b[(n+1):(n+nx̂)] .= @. +mpc.con.x̂0max - fx̂
307-
if any(mpc.con.i_b)
308-
lincon = mpc.optim[:linconstraint]
309-
JuMP.set_normalized_rhs(lincon, mpc.con.b[mpc.con.i_b])
310-
end
311-
return nothing
312-
end
313-
314-
"Set `b` excluding predicted output constraints when `model` is not a [`LinModel`](@ref)."
315-
function linconstraint!(mpc::PredictiveController, ::SimModel)
316-
nU, nΔŨ = length(mpc.con.U0min), length(mpc.con.ΔŨmin)
317-
n = 0
318-
mpc.con.b[(n+1):(n+nU)] .= @. -mpc.con.U0min + mpc.Tu_lastu0
319-
n += nU
320-
mpc.con.b[(n+1):(n+nU)] .= @. +mpc.con.U0max - mpc.Tu_lastu0
321-
n += nU
322-
mpc.con.b[(n+1):(n+nΔŨ)] .= @. -mpc.con.ΔŨmin
323-
n += nΔŨ
324-
mpc.con.b[(n+1):(n+nΔŨ)] .= @. +mpc.con.ΔŨmax
325-
if any(mpc.con.i_b)
326-
lincon = mpc.optim[:linconstraint]
327-
@views JuMP.set_normalized_rhs(lincon, mpc.con.b[mpc.con.i_b])
328-
end
329-
return nothing
330-
end
331-
332272
"""
333273
extended_vectors!(Ue, Ŷe, mpc::PredictiveController, U0, Ŷ0) -> Ue, Ŷe
334274

src/controller/explicitmpc.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ function Base.show(io::IO, mpc::ExplicitMPC)
182182
print_estim_dim(io, mpc.estim, n)
183183
end
184184

185-
linconstraint!(::ExplicitMPC, ::LinModel) = nothing
185+
linconstraint!(::ExplicitMPC, ::LinModel, ::TranscriptionMethod) = nothing
186186

187187
@doc raw"""
188188
optim_objective!(mpc::ExplicitMPC) -> Z̃

src/controller/nonlinmpc.jl

+1-55
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ function init_optimization!(mpc::NonLinMPC, model::SimModel, optim)
509509
@operator(optim, J, nZ̃, Jfunc, ∇Jfunc!)
510510
@objective(optim, Min, J(Z̃var...))
511511
init_nonlincon!(mpc, model, transcription, gfuncs, ∇gfuncs!, geqfuncs, ∇geqfuncs!)
512-
set_nonlincon!(mpc, model, optim)
512+
set_nonlincon!(mpc, model, transcription, optim)
513513
return nothing
514514
end
515515

@@ -692,60 +692,6 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
692692
return Jfunc, ∇Jfunc!, gfuncs, ∇gfuncs!, geqfuncs, ∇geqfuncs!
693693
end
694694

695-
"""
696-
set_nonlincon!(mpc::NonLinMPC, ::LinModel, optim)
697-
698-
Set the custom nonlinear inequality constraints for `LinModel`.
699-
"""
700-
function set_nonlincon!(
701-
mpc::NonLinMPC, ::LinModel, optim::JuMP.GenericModel{JNT}
702-
) where JNT<:Real
703-
Z̃var = optim[:Z̃var]
704-
con = mpc.con
705-
nonlin_constraints = JuMP.all_constraints(optim, JuMP.NonlinearExpr, MOI.LessThan{JNT})
706-
map(con_ref -> JuMP.delete(optim, con_ref), nonlin_constraints)
707-
for i in 1:con.nc
708-
gfunc_i = optim[Symbol("g_c_$i")]
709-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
710-
end
711-
return nothing
712-
end
713-
714-
"""
715-
set_nonlincon!(mpc::NonLinMPC, ::NonLinModel, optim)
716-
717-
Also set output prediction `Ŷ` and terminal state `x̂end` constraints when not a `LinModel`.
718-
"""
719-
function set_nonlincon!(
720-
mpc::NonLinMPC, ::SimModel, optim::JuMP.GenericModel{JNT}
721-
) where JNT<:Real
722-
Z̃var = optim[:Z̃var]
723-
con = mpc.con
724-
nonlin_constraints = JuMP.all_constraints(optim, JuMP.NonlinearExpr, MOI.LessThan{JNT})
725-
map(con_ref -> JuMP.delete(optim, con_ref), nonlin_constraints)
726-
for i in findall(.!isinf.(con.Y0min))
727-
gfunc_i = optim[Symbol("g_Y0min_$(i)")]
728-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
729-
end
730-
for i in findall(.!isinf.(con.Y0max))
731-
gfunc_i = optim[Symbol("g_Y0max_$(i)")]
732-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
733-
end
734-
for i in findall(.!isinf.(con.x̂0min))
735-
gfunc_i = optim[Symbol("g_x̂0min_$(i)")]
736-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
737-
end
738-
for i in findall(.!isinf.(con.x̂0max))
739-
gfunc_i = optim[Symbol("g_x̂0max_$(i)")]
740-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
741-
end
742-
for i in 1:con.nc
743-
gfunc_i = optim[Symbol("g_c_$i")]
744-
@constraint(optim, gfunc_i(Z̃var...) <= 0)
745-
end
746-
return nothing
747-
end
748-
749695
"""
750696
con_nonlinprog!(g, mpc::NonLinMPC, model::LinModel, _ , _ , gc, ϵ) -> g
751697

0 commit comments

Comments
 (0)