Skip to content

Commit 56b1cf0

Browse files
authoredApr 4, 2024
Merge pull request #45 from JuliaControl/reduce_alloc_linmpc
Improve performance and reduce allocations of `LinMPC` and `KalmanFilter`
2 parents 0493147 + ccecabe commit 56b1cf0

File tree

5 files changed

+40
-23
lines changed

5 files changed

+40
-23
lines changed
 

‎Project.toml

+2-2
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 = "0.20.0"
4+
version = "0.20.1"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
@@ -19,7 +19,7 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
1919
ControlSystemsBase = "1.9"
2020
ForwardDiff = "0.10"
2121
Ipopt = "1"
22-
JuMP = "1"
22+
JuMP = "1.21"
2323
LinearAlgebra = "1.6"
2424
OSQP = "0.8"
2525
PreallocationTools = "0.4"

‎src/controller/execute.jl

+19-12
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,18 @@ function initpred!(mpc::PredictiveController, model::LinModel, d, ym, D̂, R̂y,
173173
mul!(F, mpc.J, mpc.D̂0, 1, 1)
174174
end
175175
mpc.R̂y .= R̂y
176-
C_y = similar(F)
177-
C_y .= mpc.F .- mpc.R̂y
178-
q̃ .= lmul!(2,(mpc.M_Hp*mpc.Ẽ)'*C_y)
179-
p .= dot(C_y, mpc.M_Hp, C_y)
176+
Cy = F .- mpc.R̂y
177+
M_Hp_Ẽ = mpc.M_Hp*mpc.
178+
mul!(q̃, M_Hp_Ẽ', Cy)
179+
p .= dot(Cy, mpc.M_Hp, Cy)
180180
if ~mpc.noR̂u
181181
mpc.R̂u .= R̂u
182-
C_u = similar(mpc.T_lastu)
183-
C_u .= mpc.T_lastu .- mpc.R̂u
184-
mpc..+= lmul!(2, (mpc.L_Hp*mpc.S̃)'*C_u)
185-
mpc.p .+= dot(C_u, mpc.L_Hp, C_u)
182+
Cu = mpc.T_lastu .- mpc.R̂u
183+
L_Hp_S̃ = mpc.L_Hp*mpc.
184+
mul!(q̃, L_Hp_S̃', Cu, 1, 1)
185+
p .+= dot(Cu, mpc.L_Hp, Cu)
186186
end
187+
lmul!(2, q̃)
187188
return nothing
188189
end
189190

@@ -277,8 +278,11 @@ function linconstraint!(mpc::PredictiveController, model::LinModel)
277278
mpc.con.b[(n+1):(n+nx̂)] .= @. -mpc.con.x̂min + fx̂
278279
n += nx̂
279280
mpc.con.b[(n+1):(n+nx̂)] .= @. +mpc.con.x̂max - fx̂
280-
lincon = mpc.optim[:linconstraint]
281-
set_normalized_rhs.(lincon, mpc.con.b[mpc.con.i_b])
281+
if any(mpc.con.i_b)
282+
lincon = mpc.optim[:linconstraint]
283+
set_normalized_rhs(lincon, mpc.con.b[mpc.con.i_b])
284+
end
285+
return nothing
282286
end
283287

284288
"Set `b` excluding predicted output constraints when `model` is not a [`LinModel`](@ref)."
@@ -292,8 +296,11 @@ function linconstraint!(mpc::PredictiveController, ::SimModel)
292296
mpc.con.b[(n+1):(n+nΔŨ)] .= @. -mpc.con.ΔŨmin
293297
n += nΔŨ
294298
mpc.con.b[(n+1):(n+nΔŨ)] .= @. +mpc.con.ΔŨmax
295-
lincon = mpc.optim[:linconstraint]
296-
set_normalized_rhs.(lincon, mpc.con.b[mpc.con.i_b])
299+
if any(mpc.con.i_b)
300+
lincon = mpc.optim[:linconstraint]
301+
set_normalized_rhs(lincon, mpc.con.b[mpc.con.i_b])
302+
end
303+
return nothing
297304
end
298305

299306
@doc raw"""

‎src/controller/linmpc.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,6 @@ end
254254

255255
"For [`LinMPC`](@ref), set the QP linear coefficient `q̃` just before optimization."
256256
function set_objective_linear_coef!(mpc::LinMPC, ΔŨvar)
257-
set_objective_coefficient.(mpc.optim, ΔŨvar, mpc.q̃)
257+
set_objective_coefficient(mpc.optim, ΔŨvar, mpc.q̃)
258258
return nothing
259259
end

‎src/estimator/kalman.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ function update_estimate_kf!(estim::StateEstimator{NT}, u, ym, d, Â, Ĉm, P̂
818818
Q̂, R̂, M̂, K̂ = estim.Q̂, estim.R̂, estim.M̂, estim.
819819
nx̂, nu, ny = estim.nx̂, estim.model.nu, estim.model.ny
820820
x̂next, û, ŷ = Vector{NT}(undef, nx̂), Vector{NT}(undef, nu), Vector{NT}(undef, ny)
821-
mul!(M̂, P̂, Ĉm')
821+
mul!(M̂, P̂.data, Ĉm') # the ".data" weirdly removes a type instability in mul!
822822
rdiv!(M̂, cholesky!(Hermitian(Ĉm ** Ĉm' .+ R̂)))
823823
mul!(K̂, Â, M̂)
824824
ĥ!(ŷ, estim, estim.model, x̂, d)

‎src/estimator/mhe/execute.jl

+17-7
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,13 @@ function initpred!(estim::MovingHorizonEstimator, model::LinModel)
236236
invQ̂_Nk, invR̂_Nk = @views estim.invQ̂_He[1:nŴ, 1:nŴ], estim.invR̂_He[1:nYm, 1:nYm]
237237
M_Nk = [estim.invP̄ zeros(nx̂, nYm); zeros(nYm, nx̂) invR̂_Nk]
238238
Ñ_Nk = [fill(C, nϵ, nϵ) zeros(nϵ, nx̂+nŴ); zeros(nx̂, nϵ+nx̂+nŴ); zeros(nŴ, nϵ+nx̂) invQ̂_Nk]
239-
estim.q̃[1:nZ̃] .= lmul!(2, (M_Nk*ẼZ̃)'*FZ̃)
240-
estim.p .= dot(FZ̃, M_Nk, FZ̃)
241-
estim..data[1:nZ̃, 1:nZ̃] .= lmul!(2, (ẼZ̃'*M_Nk*ẼZ̃ .+ Ñ_Nk))
239+
M_Nk_ẼZ̃ = M_Nk*ẼZ̃
240+
@views mul!(estim.q̃[1:nZ̃], M_Nk_ẼZ̃', FZ̃)
241+
@views lmul!(2, estim.q̃[1:nZ̃])
242+
estim.p .= dot(FZ̃, M_Nk, FZ̃)
243+
estim..data[1:nZ̃, 1:nZ̃] = Ñ_Nk
244+
@views mul!(estim..data[1:nZ̃, 1:nZ̃], ẼZ̃', M_Nk_ẼZ̃, 1, 1)
245+
@views lmul!(2, estim..data[1:nZ̃, 1:nZ̃])
242246
Z̃var_Nk::Vector{VariableRef} = @views optim[:Z̃var][1:nZ̃]
243247
H̃_Nk = @views estim.H̃[1:nZ̃,1:nZ̃]
244248
q̃_Nk = @views estim.q̃[1:nZ̃]
@@ -283,8 +287,11 @@ function linconstraint!(estim::MovingHorizonEstimator, model::LinModel)
283287
estim.con.b[(n+1):(n+nV̂)] .= @. -V̂min + estim.F
284288
n += nV̂
285289
estim.con.b[(n+1):(n+nV̂)] .= @. +V̂max - estim.F
286-
lincon = estim.optim[:linconstraint]
287-
set_normalized_rhs.(lincon, estim.con.b[estim.con.i_b])
290+
if any(estim.con.i_b)
291+
lincon = estim.optim[:linconstraint]
292+
set_normalized_rhs(lincon, estim.con.b[estim.con.i_b])
293+
end
294+
return nothing
288295
end
289296

290297
"Set `b` excluding state and sensor noise bounds if `model` is not a [`LinModel`](@ref)."
@@ -299,8 +306,11 @@ function linconstraint!(estim::MovingHorizonEstimator, ::SimModel)
299306
estim.con.b[(n+1):(n+nŴ)] .= @. -Ŵmin
300307
n += nŴ
301308
estim.con.b[(n+1):(n+nŴ)] .= @. +Ŵmax
302-
lincon = estim.optim[:linconstraint]
303-
set_normalized_rhs.(lincon, estim.con.b[estim.con.i_b])
309+
if any(estim.con.i_b)
310+
lincon = estim.optim[:linconstraint]
311+
set_normalized_rhs(lincon, estim.con.b[estim.con.i_b])
312+
end
313+
return nothing
304314
end
305315

306316
"Truncate the bounds `Bmin` and `Bmax` to the window size `Nk` if `Nk < He`."

0 commit comments

Comments
 (0)