Skip to content

Commit 65f409b

Browse files
committed
added: NonLinMPC with LinModel works with MultipleShooting
but not `NonLinModel` for now
1 parent a79a42b commit 65f409b

File tree

6 files changed

+253
-161
lines changed

6 files changed

+253
-161
lines changed

src/controller/construct.jl

Lines changed: 58 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function PredictiveControllerBuffer(
2020
estim::StateEstimator{NT}, transcription::TranscriptionMethod, Hp::Int, Hc::Int, nϵ::Int
2121
) where NT <: Real
2222
nu, ny, nd, nx̂ = estim.model.nu, estim.model.ny, estim.model.nd, estim.nx̂
23-
nZ̃ = get_nZ̃(estim, transcription, Hp, Hc, nϵ)
23+
nZ̃ = get_nZ(estim, transcription, Hp, Hc) +
2424
u = Vector{NT}(undef, nu)
2525
= Vector{NT}(undef, nZ̃)
2626
= Vector{NT}(undef, nd*Hp)
@@ -238,7 +238,8 @@ function setconstraint!(
238238
ΔUmin = DeltaUmin, ΔUmax = DeltaUmax,
239239
C_Δumin = C_Deltaumin, C_Δumax = C_Deltaumax,
240240
)
241-
model, con, optim = mpc.estim.model, mpc.con, mpc.optim
241+
model, con = mpc.estim.model, mpc.con
242+
transcription, optim = mpc.transcription, mpc.optim
242243
nu, ny, nx̂, Hp, Hc = model.nu, model.ny, mpc.estim.nx̂, mpc.Hp, mpc.Hc
243244
nϵ, nc = mpc.nϵ, con.nc
244245
notSolvedYet = (JuMP.termination_status(optim) == JuMP.OPTIMIZE_NOT_CALLED)
@@ -370,7 +371,7 @@ function setconstraint!(
370371
i_x̂min, i_x̂max = .!isinf.(con.x̂0min), .!isinf.(con.x̂0max)
371372
if notSolvedYet
372373
con.i_b[:], con.i_g[:], con.A[:] = init_matconstraint_mpc(
373-
model, nc,
374+
model, transcription, nc,
374375
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
375376
i_Ymin, i_Ymax, i_x̂min, i_x̂max,
376377
con.A_Umin, con.A_Umax, con.A_ΔŨmin, con.A_ΔŨmax,
@@ -386,7 +387,7 @@ function setconstraint!(
386387
set_nonlincon!(mpc, model, optim)
387388
else
388389
i_b, i_g = init_matconstraint_mpc(
389-
model, nc,
390+
model, transcription, nc,
390391
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
391392
i_Ymin, i_Ymax, i_x̂min, i_x̂max
392393
)
@@ -400,7 +401,7 @@ end
400401

401402
@doc raw"""
402403
init_matconstraint_mpc(
403-
model::LinModel, nc::Int,
404+
model::LinModel, transcription::TranscriptionMethod, nc::Int,
404405
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
405406
args...
406407
) -> i_b, i_g, A, Aeq
@@ -424,40 +425,59 @@ also returns the ``\mathbf{A, A_{eq}}`` matrices if `args` is provided. In such
424425
`A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ`.
425426
"""
426427
function init_matconstraint_mpc(
427-
::LinModel{NT}, nc::Int,
428+
::LinModel{NT}, ::TranscriptionMethod, nc::Int,
428429
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
429430
args...
430431
) where {NT<:Real}
432+
if isempty(args)
433+
A, Aeq = nothing, nothing
434+
else
435+
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
436+
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
437+
Aeq = A_ŝ
438+
end
431439
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax; i_Ymin; i_Ymax; i_x̂min; i_x̂max]
432440
i_g = trues(nc)
441+
return i_b, i_g, A, Aeq
442+
end
443+
444+
"Init `i_b` without output constraints if [`NonLinModel`](@ref) & [`MultipleShooting`](@ref)."
445+
function init_matconstraint_mpc(
446+
::NonLinModel{NT}, ::MultipleShooting, nc::Int,
447+
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
448+
args...
449+
) where {NT<:Real}
433450
if isempty(args)
434451
A, Aeq = nothing, nothing
435452
else
436453
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
437454
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
438455
Aeq = A_ŝ
439456
end
457+
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax; i_x̂min; i_x̂max]
458+
i_g = [i_Ymin; i_Ymax; trues(nc)]
440459
return i_b, i_g, A, Aeq
441460
end
442461

443-
"Init `i_b, A` without outputs and terminal constraints if `model` is not a [`LinModel`](@ref)."
462+
"Init `i_b` without output & terminal constraints if [`NonLinModel`](@ref) & [`SingleShooting`](@ref)."
444463
function init_matconstraint_mpc(
445-
::SimModel{NT}, nc::Int,
464+
::NonLinModel{NT}, ::SingleShooting, nc::Int,
446465
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
447466
args...
448467
) where {NT<:Real}
449-
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax]
450-
i_g = [i_Ymin; i_Ymax; i_x̂min; i_x̂max; trues(nc)]
451468
if isempty(args)
452469
A, Aeq = nothing, nothing
453470
else
454471
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂min, A_x̂max, A_ŝ = args
455472
A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ymin; A_Ymax; A_x̂min; A_x̂max]
456473
Aeq = A_ŝ
457474
end
475+
i_b = [i_Umin; i_Umax; i_ΔŨmin; i_ΔŨmax]
476+
i_g = [i_Ymin; i_Ymax; i_x̂min; i_x̂max; trues(nc)]
458477
return i_b, i_g, A, Aeq
459478
end
460479

480+
461481
"By default, there is no nonlinear constraint, thus do nothing."
462482
set_nonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
463483

@@ -512,11 +532,12 @@ Init the quadratic programming Hessian `H̃` for MPC.
512532
513533
The matrix appear in the quadratic general form:
514534
```math
515-
J = \min_{\mathbf{ΔŨ}} \frac{1}{2}\mathbf{(ΔŨ)'H̃(ΔŨ)} + \mathbf{q̃'(ΔŨ)} + r
535+
J = \min_{\mathbf{}} \frac{1}{2}\mathbf{Z̃' H̃ Z̃} + \mathbf{q̃'} + r
516536
```
517537
The Hessian matrix is constant if the model and weights are linear and time invariant (LTI):
518538
```math
519-
\mathbf{H̃} = 2 ( \mathbf{Ẽ}'\mathbf{M}_{H_p}\mathbf{Ẽ} + \mathbf{Ñ}_{H_c}
539+
\mathbf{H̃} = 2 ( \mathbf{Ẽ}'\mathbf{M}_{H_p}\mathbf{Ẽ}
540+
+ \mathbf{P̃}'\mathbf{Ñ}_{H_c}\mathbf{P̃}
520541
+ \mathbf{S̃}'\mathbf{L}_{H_p}\mathbf{S̃} )
521542
```
522543
The vector ``\mathbf{q̃}`` and scalar ``r`` need recalculation each control period ``k``, see
@@ -571,19 +592,17 @@ function init_defaultcon_mpc(
571592
repeat_constraints(Hp, Hc, u0min, u0max, Δumin, Δumax, y0min, y0max)
572593
C_umin, C_umax, C_Δumin, C_Δumax, C_ymin, C_ymax =
573594
repeat_constraints(Hp, Hc, c_umin, c_umax, c_Δumin, c_Δumax, c_ymin, c_ymax)
574-
A_Umin, A_Umax, S̃ = relaxU(model, nϵ, C_umin, C_umax, S)
575-
A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃ = relaxΔU(
576-
model, transcription, nϵ, C_Δumin, C_Δumax, ΔUmin, ΔUmax, P
577-
)
578-
A_Ymin, A_Ymax, Ẽ = relaxŶ(model, nϵ, C_ymin, C_ymax, E)
579-
A_x̂min, A_x̂max, ẽx̂ = relaxterminal(model, nϵ, c_x̂min, c_x̂max, ex̂)
580-
A_ŝ, Ẽŝ = augmentdefect(model, nϵ, Eŝ)
595+
A_Umin, A_Umax, S̃ = relaxU(S, C_umin, C_umax, nϵ)
596+
A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃ = relaxΔU(P, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ)
597+
A_Ymin, A_Ymax, Ẽ = relaxŶ(E, C_ymin, C_ymax, nϵ)
598+
A_x̂min, A_x̂max, ẽx̂ = relaxterminal(ex̂, c_x̂min, c_x̂max, nϵ)
599+
A_ŝ, Ẽŝ = augmentdefect(Eŝ, nϵ)
581600
i_Umin, i_Umax = .!isinf.(U0min), .!isinf.(U0max)
582601
i_ΔŨmin, i_ΔŨmax = .!isinf.(ΔŨmin), .!isinf.(ΔŨmax)
583602
i_Ymin, i_Ymax = .!isinf.(Y0min), .!isinf.(Y0max)
584603
i_x̂min, i_x̂max = .!isinf.(x̂0min), .!isinf.(x̂0max)
585604
i_b, i_g, A, Aeq = init_matconstraint_mpc(
586-
model, nc,
605+
model, transcription, nc,
587606
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_x̂min, i_x̂max,
588607
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_x̂max, A_x̂min,
589608
A_ŝ
@@ -615,10 +634,8 @@ function repeat_constraints(Hp, Hc, umin, umax, Δumin, Δumax, ymin, ymax)
615634
return Umin, Umax, ΔUmin, ΔUmax, Ymin, Ymax
616635
end
617636

618-
#TODO: change all the next docstringgs with Z̃ instead of ΔŨ !!!!!
619-
620637
@doc raw"""
621-
relaxU(model, nϵ, C_umin, C_umax, S) -> A_Umin, A_Umax, S̃
638+
relaxU(S, C_umin, C_umax, ) -> A_Umin, A_Umax, S̃
622639
623640
Augment manipulated inputs constraints with slack variable ϵ for softening.
624641
@@ -640,7 +657,7 @@ constraints:
640657
in which ``\mathbf{U_{min}}`` and ``\mathbf{U_{max}}`` vectors respectively contains
641658
``\mathbf{u_{min}}`` and ``\mathbf{u_{max}}`` repeated ``H_p`` times.
642659
"""
643-
function relaxU(::SimModel{NT}, nϵ, C_umin, C_umax, S) where NT<:Real
660+
function relaxU(S::Matrix{NT}, C_umin, C_umax, ) where NT<:Real
644661
if== 1 # Z̃ = [Z; ϵ]
645662
# ϵ impacts Z → U conversion for constraint calculations:
646663
A_Umin, A_Umax = -[S C_umin], [S -C_umax]
@@ -654,9 +671,7 @@ function relaxU(::SimModel{NT}, nϵ, C_umin, C_umax, S) where NT<:Real
654671
end
655672

656673
@doc raw"""
657-
relaxΔU(
658-
model, transcription, nϵ, C_Δumin, C_Δumax, ΔUmin, ΔUmax, P
659-
) -> A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃
674+
relaxΔU(P, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) -> A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃
660675
661676
Augment input increments constraints with slack variable ϵ for softening.
662677
@@ -684,10 +699,7 @@ bound, which is more specific than a linear inequality constraint. However, it i
684699
convenient to treat it as a linear inequality constraint since the optimizer `OSQP.jl` does
685700
not support pure bounds on the decision variables.
686701
"""
687-
function relaxΔU(
688-
::SimModel{NT}, transcription::TranscriptionMethod,
689-
nϵ, C_Δumin, C_Δumax, ΔUmin, ΔUmax, P
690-
) where NT<:Real
702+
function relaxΔU(P::Matrix{NT}, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) where NT<:Real
691703
nZ = size(P, 2)
692704
if== 1 # Z̃ = [Z; ϵ]
693705
ΔŨmin, ΔŨmax = [ΔUmin; NT[0.0]], [ΔUmax; NT[Inf]] # 0 ≤ ϵ ≤ ∞
@@ -703,7 +715,7 @@ function relaxΔU(
703715
end
704716

705717
@doc raw"""
706-
relaxŶ(::LinModel, nϵ, C_ymin, C_ymax, E) -> A_Ymin, A_Ymax, Ẽ
718+
relaxŶ(E, C_ymin, C_ymax, ) -> A_Ymin, A_Ymax, Ẽ
707719
708720
Augment linear output prediction constraints with slack variable ϵ for softening.
709721
@@ -724,8 +736,12 @@ Denoting the decision variables augmented with the slack variable
724736
in which ``\mathbf{Y_{min}, Y_{max}}`` and ``\mathbf{Y_{op}}`` vectors respectively contains
725737
``\mathbf{y_{min}, y_{max}}`` and ``\mathbf{y_{op}}`` repeated ``H_p`` times.
726738
"""
727-
function relaxŶ(::LinModel{NT}, nϵ, C_ymin, C_ymax, E) where NT<:Real
739+
function relaxŶ(E::Matrix{NT}, C_ymin, C_ymax, ) where NT<:Real
728740
if== 1 # Z̃ = [Z; ϵ]
741+
if iszero(size(E, 1))
742+
# model is not a LinModel, thus Ŷ constraints are not linear:
743+
C_ymin = C_ymax = zeros(NT, 0, 1)
744+
end
729745
# ϵ impacts predicted output constraint calculations:
730746
A_Ymin, A_Ymax = -[E C_ymin], [E -C_ymax]
731747
# ϵ has no impact on output predictions:
@@ -737,15 +753,8 @@ function relaxŶ(::LinModel{NT}, nϵ, C_ymin, C_ymax, E) where NT<:Real
737753
return A_Ymin, A_Ymax, Ẽ
738754
end
739755

740-
"Return empty matrices if model is not a [`LinModel`](@ref)"
741-
function relaxŶ(::SimModel{NT}, nϵ, C_ymin, C_ymax, E) where NT<:Real
742-
= [E zeros(NT, 0, nϵ)]
743-
A_Ymin, A_Ymax = -Ẽ, Ẽ
744-
return A_Ymin, A_Ymax, Ẽ
745-
end
746-
747756
@doc raw"""
748-
relaxterminal(::LinModel, nϵ, c_x̂min, c_x̂max, ex̂) -> A_x̂min, A_x̂max, ẽx̂
757+
relaxterminal(ex̂, c_x̂min, c_x̂max, ) -> A_x̂min, A_x̂max, ẽx̂
749758
750759
Augment terminal state constraints with slack variable ϵ for softening.
751760
@@ -765,8 +774,13 @@ the inequality constraints:
765774
\end{bmatrix}
766775
```
767776
"""
768-
function relaxterminal(::LinModel{NT}, nϵ, c_x̂min, c_x̂max, ex̂) where {NT<:Real}
777+
function relaxterminal(ex̂::Matrix{NT}, c_x̂min, c_x̂max, ) where {NT<:Real}
769778
if== 1 # Z̃ = [Z; ϵ]
779+
if iszero(size(ex̂, 1))
780+
# model is not a LinModel and transcription is a SingleShooting, thus terminal
781+
# state constraints are not linear:
782+
c_x̂min = c_x̂max = zeros(NT, 0, 1)
783+
end
770784
# ϵ impacts terminal state constraint calculations:
771785
A_x̂min, A_x̂max = -[ex̂ c_x̂min], [ex̂ -c_x̂max]
772786
# ϵ has no impact on terminal state predictions:
@@ -778,15 +792,8 @@ function relaxterminal(::LinModel{NT}, nϵ, c_x̂min, c_x̂max, ex̂) where {NT<
778792
return A_x̂min, A_x̂max, ẽx̂
779793
end
780794

781-
"Return empty matrices if model is not a [`LinModel`](@ref)"
782-
function relaxterminal(::SimModel{NT}, nϵ, c_x̂min, c_x̂max, ex̂) where {NT<:Real}
783-
ẽx̂ = [ex̂ zeros(NT, 0, nϵ)]
784-
A_x̂min, A_x̂max = -ẽx̂, ẽx̂
785-
return A_x̂min, A_x̂max, ẽx̂
786-
end
787-
788795
@doc raw"""
789-
augmentdefect(::LinModel{NT}, nϵ, Eŝ) where NT<:Real
796+
augmentdefect(Eŝ, nϵ) -> A_ŝ, Ẽŝ
790797
791798
Augment defect equality constraints with slack variable ϵ if `nϵ == 1`.
792799
@@ -796,7 +803,7 @@ It returns the ``\mathbf{Ẽŝ}`` matrix that appears in the defect equation
796803
\mathbf{A_ŝ Z̃} = - \mathbf{F_ŝ}
797804
```
798805
"""
799-
function augmentdefect(::LinModel{NT}, nϵ, Eŝ) where NT<:Real
806+
function augmentdefect(Eŝ::Matrix{NT}, nϵ) where NT<:Real
800807
if== 1 # Z̃ = [Z; ϵ]
801808
Ẽŝ = [Eŝ zeros(NT, size(Eŝ, 1), 1)]
802809
else # Z̃ = Z (only hard constraints)
@@ -806,14 +813,6 @@ function augmentdefect(::LinModel{NT}, nϵ, Eŝ) where NT<:Real
806813
return A_ŝ, Ẽŝ
807814
end
808815

809-
"Return empty matrices if model is not a [`LinModel`](@ref)"
810-
function augmentdefect(::SimModel{NT}, nϵ, Eŝ) where NT<:Real
811-
Ẽŝ = [Eŝ zeros(NT, 0, nϵ)]
812-
A_ŝ = Ẽŝ
813-
return A_ŝ, Ẽŝ
814-
end
815-
816-
817816
@doc raw"""
818817
init_stochpred(estim::InternalModel, Hp) -> Ks, Ps
819818

0 commit comments

Comments
 (0)