Skip to content

Commit fa445c7

Browse files
committed
doc: equations for defects equation and matrices
1 parent 70de7b6 commit fa445c7

File tree

5 files changed

+88
-15
lines changed

5 files changed

+88
-15
lines changed

docs/src/internals/predictive_control.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The prediction methodology of this module is mainly based on Maciejowski textboo
1414
```@docs
1515
ModelPredictiveControl.init_ΔUtoU
1616
ModelPredictiveControl.init_predmat
17+
ModelPredictiveControl.init_defectmat
1718
ModelPredictiveControl.relaxU
1819
ModelPredictiveControl.relaxΔU
1920
ModelPredictiveControl.relaxŶ

src/controller/construct.jl

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,9 @@ end
508508

509509

510510
@doc raw"""
511-
init_predmat(estim, model::LinModel, Hp, Hc) -> E, G, J, K, V, ex̂, gx̂, jx̂, kx̂, vx̂
511+
init_predmat(
512+
estim, model::LinModel, Hp, Hc, transcription
513+
) -> E, G, J, K, V, ex̂, gx̂, jx̂, kx̂, vx̂
512514
513515
Construct the prediction matrices for [`LinModel`](@ref) `model`.
514516
@@ -603,7 +605,9 @@ each control period ``k``, see [`initpred!`](@ref) and [`linconstraint!`](@ref).
603605
\end{aligned}
604606
```
605607
"""
606-
function init_predmat(estim::StateEstimator{NT}, model::LinModel, Hp, Hc) where {NT<:Real}
608+
function init_predmat(
609+
estim::StateEstimator{NT}, model::LinModel, Hp, Hc, transcription
610+
) where {NT<:Real}
607611
Â, B̂u, Ĉ, B̂d, D̂d = estim.Â, estim.B̂u, estim.Ĉ, estim.B̂d, estim.D̂d
608612
nu, nx̂, ny, nd = model.nu, estim.nx̂, model.ny, model.nd
609613
# --- pre-compute matrix powers ---
@@ -670,7 +674,9 @@ function init_predmat(estim::StateEstimator{NT}, model::LinModel, Hp, Hc) where
670674
end
671675

672676
"Return empty matrices if `model` is not a [`LinModel`](@ref)"
673-
function init_predmat(estim::StateEstimator{NT}, model::SimModel, Hp, Hc) where {NT<:Real}
677+
function init_predmat(
678+
estim::StateEstimator{NT}, model::SimModel, Hp, Hc, transcription
679+
) where {NT<:Real}
674680
nu, nx̂, nd = model.nu, estim.nx̂, model.nd
675681
E = zeros(NT, 0, nu*Hc)
676682
G = zeros(NT, 0, nd)
@@ -682,6 +688,59 @@ function init_predmat(estim::StateEstimator{NT}, model::SimModel, Hp, Hc) where
682688
return E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂
683689
end
684690

691+
692+
@doc raw"""
693+
init_defectmat(estim::StateEstimator, model::LinModel, Hp, Hc, transcription)
694+
695+
Init the matrices for computing the defects over the predicted states.
696+
697+
The defects are calculated with an equation similar to the prediction matrices (see
698+
[`init_predmat`](@ref)):
699+
```math
700+
\begin{aligned*}
701+
\mathbf{Ŝ} &= \mathbf{E_ŝ Z} + \mathbf{G_ŝ d_0}(k) + \mathbf{J_ŝ D̂_0}
702+
+ \mathbf{K_ŝ x̂_0}(k) + \mathbf{V_ŝ u_0}(k-1)
703+
+ \mathbf{B_ŝ}
704+
&= \mathbf{E_ŝ Z} + \mathbf{F_ŝ}
705+
706+
\end{aligned*}
707+
```
708+
They are forced to be ``\mathbf{Ŝ = 0}`` using the equality constraints of the optimizer.
709+
The matrices ``\mathbf{E_ŝ, G_ŝ, J_ŝ, K_ŝ, V_ŝ, B_ŝ}`` are defined in the Extended Help
710+
section.
711+
712+
# Extended Help
713+
!!! details "Extended Help"
714+
The matrices are computed by:
715+
```math
716+
\begin{aligned}
717+
\mathbf{E_ŝ} &= \begin{bmatrix}
718+
\mathbf{B̂_u} & \mathbf{0} & \cdots & \mathbf{0} & -\mathbf{I} & \mathbf{0} & \cdots & \mathbf{0} \\
719+
\mathbf{0} & \mathbf{B̂_u} & \cdots & \mathbf{0} & \mathbf{Â} & -\mathbf{I} & \cdots & \mathbf{0} \\
720+
\vdots & \vdots & \ddots & \vdots & \vdots & \vdots & \ddots & \vdots \\
721+
\mathbf{0} & \mathbf{0} & \cdots & \mathbf{B̂_u} \mathbf{0} & \mathbf{0} & \cdots & -\mathbf{I} \end{bmatrix} \\
722+
\mathbf{G_ŝ} &= \begin{bmatrix}
723+
\mathbf{B̂_d} \\ \mathbf{0} \\ \vdots \\ \mathbf{0} \end{bmatrix} \\
724+
\mathbf{J_ŝ} &= \begin{bmatrix}
725+
\mathbf{0} & \mathbf{0} & \cdots & \mathbf{0} & \mathbf{0} \\
726+
\mathbf{B̂_d} & \mathbf{0} & \cdots & \mathbf{0} & \mathbf{0} \\
727+
\vdots & \vdots & \ddots & \vdots & \vdots \\
728+
\mathbf{0} & \mathbf{0} & \cdots & \mathbf{B̂_d} & \mathbf{0} \end{bmatrix} \\
729+
\mathbf{K_ŝ} &= \begin{bmatrix}
730+
\mathbf{Â} \\ \mathbf{0} \\ \vdots \\ \mathbf{0} \end{bmatrix} \\
731+
\mathbf{V_ŝ} &= \begin{bmatrix}
732+
\mathbf{B̂_u} \\ \mathbf{B̂_u} \\ \mathbf{B̂_u} \\ \mathbf{B̂_u} \end{bmatrix} \\
733+
\mathbf{B_ŝ} &= \begin{bmatrix}
734+
\mathbf{f̂_{op} - x̂_{op}} \\ \mathbf{f̂_{op} - x̂_{op}} \\ \vdots \\ \mathbf{f̂_{op} - x̂_{op}} \end{bmatrix}
735+
\end{aligned}
736+
```
737+
"""
738+
function init_defectmat(
739+
estim::StateEstimator{NT}, model::LinModel, Hp, Hc, transcription
740+
) where {NT<:Real}
741+
742+
end
743+
685744
@doc raw"""
686745
init_quadprog(model::LinModel, weights::ControllerWeights, Ẽ, S) -> H̃
687746

src/controller/explicitmpc.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
4545
L_Hp = Hermitian(convert(Matrix{NT}, L_Hp), :L)
4646
# dummy vals (updated just before optimization):
4747
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
48-
S, T = init_ΔUtoU(model, Hp, Hc)
49-
E, G, J, K, V, B = init_predmat(estim, model, Hp, Hc)
48+
transcription = :singleshooting
49+
S, T = init_ZtoU(estim, Hp, Hc, transcription)
50+
E, G, J, K, V, B = init_predmat(estim, model, Hp, Hc, transcription)
5051
# dummy val (updated just before optimization):
5152
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
5253
S̃, Ñ_Hc, Ẽ = S, N_Hc, E # no slack variable ϵ for ExplicitMPC

src/controller/linmpc.jl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const DEFAULT_LINMPC_OPTIMIZER = OSQP.MathOptInterfaceOSQP.Optimizer
2+
const DEFAULT_LINMPC_TRANSCRIPTION = :singleshooting
23

34
struct LinMPC{
45
NT<:Real,
@@ -8,6 +9,7 @@ struct LinMPC{
89
estim::SE
910
# note: `NT` and the number type `JNT` in `JuMP.GenericModel{JNT}` can be
1011
# different since solvers that support non-Float64 are scarce.
12+
transcription::Symbol
1113
optim::JM
1214
con::ControllerConstraint{NT, Nothing}
1315
ΔŨ::Vector{NT}
@@ -41,16 +43,19 @@ struct LinMPC{
4143
Dop::Vector{NT}
4244
buffer::PredictiveControllerBuffer{NT}
4345
function LinMPC{NT}(
44-
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim::JM
46+
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt,
47+
transcription, optim::JM
4548
) where {NT<:Real, SE<:StateEstimator, JM<:JuMP.GenericModel}
4649
model = estim.model
4750
nu, ny, nd, nx̂ = model.nu, model.ny, model.nd, estim.nx̂
4851
= copy(model.yop) # dummy vals (updated just before optimization)
4952
weights = ControllerWeights{NT}(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt)
5053
# dummy vals (updated just before optimization):
5154
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
52-
S, T = init_ΔUtoU(model, Hp, Hc)
53-
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(estim, model, Hp, Hc)
55+
S, T = init_ZtoU(estim, Hp, Hc, transcription)
56+
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(
57+
estim, model, Hp, Hc, transcription
58+
)
5459
# dummy vals (updated just before optimization):
5560
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
5661
con, nϵ, S̃, Ẽ = init_defaultcon_mpc(
@@ -67,7 +72,7 @@ struct LinMPC{
6772
ΔŨ = zeros(NT, nΔŨ)
6873
buffer = PredictiveControllerBuffer{NT}(nu, ny, nd, Hp, Hc, nϵ)
6974
mpc = new{NT, SE, JM}(
70-
estim, optim, con,
75+
estim, transcription, optim, con,
7176
ΔŨ, ŷ,
7277
Hp, Hc, nϵ,
7378
weights,
@@ -130,6 +135,8 @@ arguments. This controller allocates memory at each time step for the optimizati
130135
- `N_Hc=diagm(repeat(Nwt,Hc))` : positive semidefinite symmetric matrix ``\mathbf{N}_{H_c}``.
131136
- `L_Hp=diagm(repeat(Lwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{L}_{H_p}``.
132137
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
138+
- `transcription=:singleshooting` : transcription method for the nonlinear optimization
139+
problem, can be `:singleshooting` or `:multipleshoting`.
133140
- `optim=JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)` : quadratic optimizer used in
134141
the predictive controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
135142
(default to [`OSQP`](https://osqp.org/docs/parsers/jump.html) optimizer).
@@ -187,11 +194,12 @@ function LinMPC(
187194
N_Hc = diagm(repeat(Nwt, Hc)),
188195
L_Hp = diagm(repeat(Lwt, Hp)),
189196
Cwt = DEFAULT_CWT,
197+
transcription = DEFAULT_LINMPC_TRANSCRIPTION,
190198
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
191199
kwargs...
192200
)
193201
estim = SteadyKalmanFilter(model; kwargs...)
194-
return LinMPC(estim; Hp, Hc, Mwt, Nwt, Lwt, Cwt, M_Hp, N_Hc, L_Hp, optim)
202+
return LinMPC(estim; Hp, Hc, Mwt, Nwt, Lwt, Cwt, M_Hp, N_Hc, L_Hp, transcription, optim)
195203
end
196204

197205

@@ -229,6 +237,7 @@ function LinMPC(
229237
N_Hc = diagm(repeat(Nwt, Hc)),
230238
L_Hp = diagm(repeat(Lwt, Hp)),
231239
Cwt = DEFAULT_CWT,
240+
transcription = DEFAULT_LINMPC_TRANSCRIPTION,
232241
optim::JM = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
233242
) where {NT<:Real, SE<:StateEstimator{NT}, JM<:JuMP.GenericModel}
234243
isa(estim.model, LinModel) || error("estim.model type must be a LinModel")
@@ -237,7 +246,7 @@ function LinMPC(
237246
@warn("prediction horizon Hp ($Hp) ≤ estimated number of delays in model "*
238247
"($nk), the closed-loop system may be unstable or zero-gain (unresponsive)")
239248
end
240-
return LinMPC{NT}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, optim)
249+
return LinMPC{NT}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, transcription, optim)
241250
end
242251

243252
"""

src/controller/nonlinmpc.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const DEFAULT_NONLINMPC_OPTIMIZER = optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes")
2+
const DEFAULT_NONLINMPC_TRANSCRIPTION = :singleshooting
23

34
struct NonLinMPC{
45
NT<:Real,
@@ -65,7 +66,9 @@ struct NonLinMPC{
6566
# dummy vals (updated just before optimization):
6667
R̂y, R̂u, T_lastu = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
6768
S, T = init_ZtoU(estim, Hp, Hc, transcription)
68-
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(estim, model, Hp, Hc)
69+
E, G, J, K, V, B, ex̂, gx̂, jx̂, kx̂, vx̂, bx̂ = init_predmat(
70+
estim, model, Hp, Hc, transcription
71+
)
6972
# dummy vals (updated just before optimization):
7073
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
7174
con, nϵ, S̃, Ẽ = init_defaultcon_mpc(
@@ -255,7 +258,7 @@ function NonLinMPC(
255258
gc ::Function = gc!,
256259
nc::Int = 0,
257260
p = model.p,
258-
transcription = :singleshooting,
261+
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
259262
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
260263
kwargs...
261264
)
@@ -284,7 +287,7 @@ function NonLinMPC(
284287
gc ::Function = gc!,
285288
nc::Int = 0,
286289
p = model.p,
287-
transcription = :singleshooting,
290+
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
288291
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
289292
kwargs...
290293
)
@@ -337,7 +340,7 @@ function NonLinMPC(
337340
gc ::Function = gc!,
338341
nc = 0,
339342
p::P = estim.model.p,
340-
transcription = :singleshooting,
343+
transcription = DEFAULT_NONLINMPC_TRANSCRIPTION,
341344
optim::JM = JuMP.Model(DEFAULT_NONLINMPC_OPTIMIZER, add_bridges=false),
342345
) where {
343346
NT<:Real,

0 commit comments

Comments
 (0)