Skip to content

Commit 1187b82

Browse files
committed
Merge branch 'main' into move_block
2 parents 19b7c72 + db2827a commit 1187b82

File tree

9 files changed

+177
-135
lines changed

9 files changed

+177
-135
lines changed

Project.toml

Lines changed: 3 additions & 3 deletions
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.6.0"
4+
version = "1.6.1"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
@@ -21,8 +21,8 @@ SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35"
2121

2222
[compat]
2323
ControlSystemsBase = "1.9"
24-
DAQP = "0.6"
25-
DifferentiationInterface = "0.6.45"
24+
DAQP = "0.6, 0.7.1"
25+
DifferentiationInterface = "0.6.45, 0.7"
2626
Documenter = "1"
2727
FiniteDiff = "2"
2828
ForwardDiff = "0.10, 1"

docs/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1111

1212
[compat]
1313
ControlSystemsBase = "1"
14-
DAQP = "0.6"
14+
DAQP = "0.6, 0.7.1"
1515
Documenter = "1"
1616
JuMP = "1"
1717
LinearAlgebra = "1.10"

src/controller/construct.jl

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,64 @@ function PredictiveControllerBuffer(
3333
end
3434

3535
"Include all the objective function weights of [`PredictiveController`](@ref)"
36-
struct ControllerWeights{NT<:Real}
37-
M_Hp::Hermitian{NT, Matrix{NT}}
38-
Ñ_Hc::Hermitian{NT, Matrix{NT}}
39-
L_Hp::Hermitian{NT, Matrix{NT}}
36+
struct ControllerWeights{
37+
NT<:Real,
38+
# parameters to support both dense and Diagonal matrices (with specialization):
39+
MW<:AbstractMatrix{NT},
40+
NW<:AbstractMatrix{NT},
41+
LW<:AbstractMatrix{NT},
42+
}
43+
M_Hp::Hermitian{NT, MW}
44+
Ñ_Hc::Hermitian{NT, NW}
45+
L_Hp::Hermitian{NT, LW}
4046
E ::NT
4147
iszero_M_Hp::Vector{Bool}
4248
iszero_Ñ_Hc::Vector{Bool}
4349
iszero_L_Hp::Vector{Bool}
4450
iszero_E::Bool
51+
isinf_C ::Bool
4552
function ControllerWeights{NT}(
46-
model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt=Inf, Ewt=0
47-
) where NT<:Real
53+
model, Hp, Hc, M_Hp::MW, N_Hc::NW, L_Hp::LW, Cwt=Inf, Ewt=0
54+
) where {
55+
NT<:Real,
56+
MW<:AbstractMatrix{NT},
57+
NW<:AbstractMatrix{NT},
58+
LW<:AbstractMatrix{NT}
59+
}
4860
validate_weights(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, Ewt)
49-
# convert `Diagonal` to normal `Matrix` if required:
50-
M_Hp = Hermitian(convert(Matrix{NT}, M_Hp), :L)
51-
N_Hc = Hermitian(convert(Matrix{NT}, N_Hc), :L)
52-
L_Hp = Hermitian(convert(Matrix{NT}, L_Hp), :L)
5361
nΔU = size(N_Hc, 1)
5462
C = Cwt
55-
if !isinf(Cwt)
63+
isinf_C = isinf(C)
64+
if !isinf_C
5665
# ΔŨ = [ΔU; ϵ] (ϵ is the slack variable)
57-
Ñ_Hc = Hermitian([N_Hc zeros(NT, nΔU, 1); zeros(NT, 1, nΔU) C], :L)
66+
Ñ_Hc = [N_Hc zeros(NT, nΔU, 1); zeros(NT, 1, nΔU) C]
67+
isdiag(N_Hc) && (Ñ_Hc = Diagonal(Ñ_Hc)) # NW(Ñ_Hc) does not work on Julia 1.10
5868
else
5969
# ΔŨ = ΔU (only hard constraints)
6070
Ñ_Hc = N_Hc
61-
end
71+
end
72+
M_Hp = Hermitian(M_Hp, :L)
73+
Ñ_Hc = Hermitian(Ñ_Hc, :L)
74+
L_Hp = Hermitian(L_Hp, :L)
6275
E = Ewt
6376
iszero_M_Hp = [iszero(M_Hp)]
6477
iszero_Ñ_Hc = [iszero(Ñ_Hc)]
6578
iszero_L_Hp = [iszero(L_Hp)]
6679
iszero_E = iszero(E)
67-
return new{NT}(M_Hp, Ñ_Hc, L_Hp, E, iszero_M_Hp, iszero_Ñ_Hc, iszero_L_Hp, iszero_E)
80+
return new{NT, MW, NW, LW}(
81+
M_Hp, Ñ_Hc, L_Hp, E,
82+
iszero_M_Hp, iszero_Ñ_Hc, iszero_L_Hp, iszero_E, isinf_C
83+
)
6884
end
6985
end
7086

87+
"Outer constructor to convert weight matrix number type to `NT` if necessary."
88+
function ControllerWeights{NT}(
89+
model, Hp, Hc, M_Hp::MW, N_Hc::NW, L_Hp::LW, Cwt=Inf, Ewt=0
90+
) where {NT<:Real, MW<:AbstractMatrix, NW<:AbstractMatrix, LW<:AbstractMatrix}
91+
return ControllerWeights{NT}(model, Hp, Hc, NT.(M_Hp), NT.(N_Hc), NT.(L_Hp), Cwt, Ewt)
92+
end
93+
7194
"Include all the data for the constraints of [`PredictiveController`](@ref)"
7295
struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
7396
# matrices for the terminal constraints:
@@ -478,8 +501,10 @@ end
478501

479502
"""
480503
init_defaultcon_mpc(
481-
estim::StateEstimator, transcription::TranscriptionMethod,
482-
Hp, Hc, C,
504+
estim::StateEstimator,
505+
weights::ControllerWeights
506+
transcription::TranscriptionMethod,
507+
Hp, Hc,
483508
PΔu, Pu, E,
484509
ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂,
485510
Eŝ, Fŝ, Gŝ, Jŝ, Kŝ, Vŝ, Bŝ,
@@ -491,16 +516,18 @@ Init `ControllerConstraint` struct with default parameters based on estimator `e
491516
Also return `P̃Δu`, `P̃u`, `Ẽ` and `Ẽŝ` matrices for the the augmented decision vector `Z̃`.
492517
"""
493518
function init_defaultcon_mpc(
494-
estim::StateEstimator{NT}, transcription::TranscriptionMethod,
495-
Hp, Hc, C,
519+
estim::StateEstimator{NT},
520+
weights::ControllerWeights,
521+
transcription::TranscriptionMethod,
522+
Hp, Hc,
496523
PΔu, Pu, E,
497524
ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂,
498525
Eŝ, Fŝ, Gŝ, Jŝ, Kŝ, Vŝ, Bŝ,
499526
gc!::GCfunc = nothing, nc = 0
500527
) where {NT<:Real, GCfunc<:Union{Nothing, Function}}
501528
model = estim.model
502529
nu, ny, nx̂ = model.nu, model.ny, estim.nx̂
503-
= isinf(C) ? 0 : 1
530+
= weights.isinf_C ? 0 : 1
504531
u0min, u0max = fill(convert(NT,-Inf), nu), fill(convert(NT,+Inf), nu)
505532
Δumin, Δumax = fill(convert(NT,-Inf), nu), fill(convert(NT,+Inf), nu)
506533
y0min, y0max = fill(convert(NT,-Inf), ny), fill(convert(NT,+Inf), ny)

src/controller/execute.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ function initpred!(mpc::PredictiveController, model::LinModel, d, D̂, R̂y, R̂
212212
mul!(F, mpc.G, mpc.d0, 1, 1) # F = F + G*d0
213213
mul!(F, mpc.J, mpc.D̂0, 1, 1) # F = F + J*D̂0
214214
end
215-
Cy, Cu, M_Hp_Ẽ, L_Hp_P̃U = mpc.buffer.Ŷ, mpc.buffer.U, mpc.buffer.Ẽ, mpc.buffer.P̃u
215+
Cy, Cu, M_Hp_Ẽ, L_Hp_P̃u = mpc.buffer.Ŷ, mpc.buffer.U, mpc.buffer.Ẽ, mpc.buffer.P̃u
216216
q̃, r = mpc.q̃, mpc.r
217217
q̃ .= 0
218218
r .= 0
@@ -226,8 +226,8 @@ function initpred!(mpc::PredictiveController, model::LinModel, d, D̂, R̂y, R̂
226226
# --- input setpoint tracking term ---
227227
if !mpc.weights.iszero_L_Hp[]
228228
Cu .= mpc.Tu_lastu0 .+ mpc.Uop .- R̂u
229-
mul!(L_Hp_P̃U, mpc.weights.L_Hp, mpc.P̃u)
230-
mul!(q̃, L_Hp_P̃U', Cu, 1, 1) # q̃ = q̃ + L_Hp*P̃u'*Cu
229+
mul!(L_Hp_P̃u, mpc.weights.L_Hp, mpc.P̃u)
230+
mul!(q̃, L_Hp_P̃u', Cu, 1, 1) # q̃ = q̃ + L_Hp*P̃u'*Cu
231231
r .+= dot(Cu, mpc.weights.L_Hp, Cu) # r = r + Cu'*L_Hp*Cu
232232
end
233233
# --- finalize ---

src/controller/explicitmpc.jl

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
1+
struct ExplicitMPC{
2+
NT<:Real,
3+
SE<:StateEstimator,
4+
CW<:ControllerWeights
5+
} <: PredictiveController{NT}
26
estim::SE
37
transcription::SingleShooting
48
::Vector{NT}
@@ -7,7 +11,7 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
711
Hc::Int
812
::Int
913
nb::Vector{Int}
10-
weights::ControllerWeights{NT}
14+
weights::CW
1115
R̂u::Vector{NT}
1216
R̂y::Vector{NT}
1317
P̃Δu::Matrix{NT}
@@ -35,17 +39,12 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
3539
Dop::Vector{NT}
3640
buffer::PredictiveControllerBuffer{NT}
3741
function ExplicitMPC{NT}(
38-
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp
39-
) where {NT<:Real, SE<:StateEstimator}
42+
estim::SE, Hp, Hc, weights::CW
43+
) where {NT<:Real, SE<:StateEstimator, CW<:ControllerWeights}
4044
model = estim.model
4145
nu, ny, nd, nx̂ = model.nu, model.ny, model.nd, estim.nx̂
4246
= copy(model.yop) # dummy vals (updated just before optimization)
4347
= 0 # no slack variable ϵ for ExplicitMPC
44-
weights = ControllerWeights{NT}(model, Hp, Hc, M_Hp, N_Hc, L_Hp)
45-
# convert `Diagonal` to normal `Matrix` if required:
46-
M_Hp = Hermitian(convert(Matrix{NT}, M_Hp), :L)
47-
N_Hc = Hermitian(convert(Matrix{NT}, N_Hc), :L)
48-
L_Hp = Hermitian(convert(Matrix{NT}, L_Hp), :L)
4948
# dummy vals (updated just before optimization):
5049
R̂y, R̂u, Tu_lastu0 = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
5150
transcription = SingleShooting() # explicit MPC only supports SingleShooting
@@ -55,7 +54,7 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
5554
E, G, J, K, V, B = init_predmat(model, estim, transcription, Hp, Hc)
5655
# dummy val (updated just before optimization):
5756
F, fx̂ = zeros(NT, ny*Hp), zeros(NT, nx̂)
58-
P̃Δu, P̃u, Ñ_Hc, = PΔu, Pu, N_Hc, E # no slack variable ϵ for ExplicitMPC
57+
P̃Δu, P̃u, Ẽ = PΔu, Pu, E # no slack variable ϵ for ExplicitMPC
5958
= init_quadprog(model, weights, Ẽ, P̃Δu, P̃u)
6059
# dummy vals (updated just before optimization):
6160
q̃, r = zeros(NT, size(H̃, 1)), zeros(NT, 1)
@@ -67,7 +66,7 @@ struct ExplicitMPC{NT<:Real, SE<:StateEstimator} <: PredictiveController{NT}
6766
nZ̃ = get_nZ(estim, transcription, Hp, Hc) +
6867
= zeros(NT, nZ̃)
6968
buffer = PredictiveControllerBuffer(estim, transcription, Hp, Hc, nϵ)
70-
mpc = new{NT, SE}(
69+
mpc = new{NT, SE, CW}(
7170
estim,
7271
transcription,
7372
Z̃, ŷ,
@@ -133,9 +132,9 @@ function ExplicitMPC(
133132
Mwt = fill(DEFAULT_MWT, model.ny),
134133
Nwt = fill(DEFAULT_NWT, model.nu),
135134
Lwt = fill(DEFAULT_LWT, model.nu),
136-
M_Hp = diagm(repeat(Mwt, Hp)),
137-
N_Hc = diagm(repeat(Nwt, Hc)),
138-
L_Hp = diagm(repeat(Lwt, Hp)),
135+
M_Hp = Diagonal(repeat(Mwt, Hp)),
136+
N_Hc = Diagonal(repeat(Nwt, Hc)),
137+
L_Hp = Diagonal(repeat(Lwt, Hp)),
139138
kwargs...
140139
)
141140
estim = SteadyKalmanFilter(model; kwargs...)
@@ -156,17 +155,18 @@ function ExplicitMPC(
156155
Mwt = fill(DEFAULT_MWT, estim.model.ny),
157156
Nwt = fill(DEFAULT_NWT, estim.model.nu),
158157
Lwt = fill(DEFAULT_LWT, estim.model.nu),
159-
M_Hp = diagm(repeat(Mwt, Hp)),
160-
N_Hc = diagm(repeat(Nwt, Hc)),
161-
L_Hp = diagm(repeat(Lwt, Hp)),
158+
M_Hp = Diagonal(repeat(Mwt, Hp)),
159+
N_Hc = Diagonal(repeat(Nwt, Hc)),
160+
L_Hp = Diagonal(repeat(Lwt, Hp)),
162161
) where {NT<:Real, SE<:StateEstimator{NT}}
163162
isa(estim.model, LinModel) || error("estim.model type must be a LinModel")
164163
nk = estimate_delays(estim.model)
165164
if Hp nk
166165
@warn("prediction horizon Hp ($Hp) ≤ estimated number of delays in model "*
167166
"($nk), the closed-loop system may be unstable or zero-gain (unresponsive)")
168167
end
169-
return ExplicitMPC{NT}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp)
168+
weights = ControllerWeights{NT}(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp)
169+
return ExplicitMPC{NT}(estim, Hp, Hc, weights)
170170
end
171171

172172
setconstraint!(::ExplicitMPC; kwargs...) = error("ExplicitMPC does not support constraints.")

src/controller/linmpc.jl

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const DEFAULT_LINMPC_TRANSCRIPTION = SingleShooting()
44
struct LinMPC{
55
NT<:Real,
66
SE<:StateEstimator,
7+
CW<:ControllerWeights,
78
TM<:TranscriptionMethod,
89
JM<:JuMP.GenericModel
910
} <: PredictiveController{NT}
@@ -19,7 +20,7 @@ struct LinMPC{
1920
Hc::Int
2021
::Int
2122
nb::Vector{Int}
22-
weights::ControllerWeights{NT}
23+
weights::CW
2324
R̂u::Vector{NT}
2425
R̂y::Vector{NT}
2526
P̃Δu::Matrix{NT}
@@ -46,13 +47,18 @@ struct LinMPC{
4647
Dop::Vector{NT}
4748
buffer::PredictiveControllerBuffer{NT}
4849
function LinMPC{NT}(
49-
estim::SE, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt,
50+
estim::SE, Hp, Hc, weights::CW,
5051
transcription::TM, optim::JM
51-
) where {NT<:Real, SE<:StateEstimator, TM<:TranscriptionMethod, JM<:JuMP.GenericModel}
52+
) where {
53+
NT<:Real,
54+
SE<:StateEstimator,
55+
CW<:ControllerWeights,
56+
TM<:TranscriptionMethod,
57+
JM<:JuMP.GenericModel
58+
}
5259
model = estim.model
5360
nu, ny, nd, nx̂ = model.nu, model.ny, model.nd, estim.nx̂
5461
= copy(model.yop) # dummy vals (updated just before optimization)
55-
weights = ControllerWeights{NT}(model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt)
5662
# dummy vals (updated just before optimization):
5763
R̂y, R̂u, Tu_lastu0 = zeros(NT, ny*Hp), zeros(NT, nu*Hp), zeros(NT, nu*Hp)
5864
nb = ones(Hc)
@@ -65,8 +71,9 @@ struct LinMPC{
6571
# dummy vals (updated just before optimization):
6672
F, fx̂, Fŝ = zeros(NT, ny*Hp), zeros(NT, nx̂), zeros(NT, nx̂*Hp)
6773
con, nϵ, P̃Δu, P̃u, Ẽ, Ẽŝ = init_defaultcon_mpc(
68-
estim, transcription,
69-
Hp, Hc, Cwt, PΔu, Pu, E,
74+
estim, weights, transcription,
75+
Hp, Hc,
76+
PΔu, Pu, E,
7077
ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂,
7178
Eŝ, Fŝ, Gŝ, Jŝ, Kŝ, Vŝ, Bŝ
7279
)
@@ -80,7 +87,7 @@ struct LinMPC{
8087
nZ̃ = get_nZ(estim, transcription, Hp, Hc) +
8188
= zeros(NT, nZ̃)
8289
buffer = PredictiveControllerBuffer(estim, transcription, Hp, Hc, nϵ)
83-
mpc = new{NT, SE, TM, JM}(
90+
mpc = new{NT, SE, CW, TM, JM}(
8491
estim, transcription, optim, con,
8592
Z̃, ŷ,
8693
Hp, Hc, nϵ, nb,
@@ -142,9 +149,9 @@ arguments. This controller allocates memory at each time step for the optimizati
142149
- `Mwt=fill(1.0,model.ny)` : main diagonal of ``\mathbf{M}`` weight matrix (vector).
143150
- `Nwt=fill(0.1,model.nu)` : main diagonal of ``\mathbf{N}`` weight matrix (vector).
144151
- `Lwt=fill(0.0,model.nu)` : main diagonal of ``\mathbf{L}`` weight matrix (vector).
145-
- `M_Hp=diagm(repeat(Mwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{M}_{H_p}``.
146-
- `N_Hc=diagm(repeat(Nwt,Hc))` : positive semidefinite symmetric matrix ``\mathbf{N}_{H_c}``.
147-
- `L_Hp=diagm(repeat(Lwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{L}_{H_p}``.
152+
- `M_Hp=Diagonal(repeat(Mwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{M}_{H_p}``.
153+
- `N_Hc=Diagonal(repeat(Nwt,Hc))` : positive semidefinite symmetric matrix ``\mathbf{N}_{H_c}``.
154+
- `L_Hp=Diagonal(repeat(Lwt,Hp))` : positive semidefinite symmetric matrix ``\mathbf{L}_{H_p}``.
148155
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
149156
- `transcription=SingleShooting()` : a [`TranscriptionMethod`](@ref) for the optimization.
150157
- `optim=JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)` : quadratic optimizer used in
@@ -201,9 +208,9 @@ function LinMPC(
201208
Mwt = fill(DEFAULT_MWT, model.ny),
202209
Nwt = fill(DEFAULT_NWT, model.nu),
203210
Lwt = fill(DEFAULT_LWT, model.nu),
204-
M_Hp = diagm(repeat(Mwt, Hp)),
205-
N_Hc = diagm(repeat(Nwt, Hc)),
206-
L_Hp = diagm(repeat(Lwt, Hp)),
211+
M_Hp = Diagonal(repeat(Mwt, Hp)),
212+
N_Hc = Diagonal(repeat(Nwt, Hc)),
213+
L_Hp = Diagonal(repeat(Lwt, Hp)),
207214
Cwt = DEFAULT_CWT,
208215
transcription::TranscriptionMethod = DEFAULT_LINMPC_TRANSCRIPTION,
209216
optim::JuMP.GenericModel = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
@@ -244,9 +251,9 @@ function LinMPC(
244251
Mwt = fill(DEFAULT_MWT, estim.model.ny),
245252
Nwt = fill(DEFAULT_NWT, estim.model.nu),
246253
Lwt = fill(DEFAULT_LWT, estim.model.nu),
247-
M_Hp = diagm(repeat(Mwt, Hp)),
248-
N_Hc = diagm(repeat(Nwt, Hc)),
249-
L_Hp = diagm(repeat(Lwt, Hp)),
254+
M_Hp = Diagonal(repeat(Mwt, Hp)),
255+
N_Hc = Diagonal(repeat(Nwt, Hc)),
256+
L_Hp = Diagonal(repeat(Lwt, Hp)),
250257
Cwt = DEFAULT_CWT,
251258
transcription::TranscriptionMethod = DEFAULT_LINMPC_TRANSCRIPTION,
252259
optim::JM = JuMP.Model(DEFAULT_LINMPC_OPTIMIZER, add_bridges=false),
@@ -257,7 +264,8 @@ function LinMPC(
257264
@warn("prediction horizon Hp ($Hp) ≤ estimated number of delays in model "*
258265
"($nk), the closed-loop system may be unstable or zero-gain (unresponsive)")
259266
end
260-
return LinMPC{NT}(estim, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt, transcription, optim)
267+
weights = ControllerWeights{NT}(estim.model, Hp, Hc, M_Hp, N_Hc, L_Hp, Cwt)
268+
return LinMPC{NT}(estim, Hp, Hc, weights, transcription, optim)
261269
end
262270

263271
"""

0 commit comments

Comments
 (0)