Skip to content

Commit 7b40131

Browse files
committed
reduce allocation in NonLinMPC
1 parent 4f2ae5b commit 7b40131

File tree

4 files changed

+64
-47
lines changed

4 files changed

+64
-47
lines changed

example/juMPC.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ setconstraint!(mpc, umin=[5, 9.9], umax=[Inf,Inf])
9595
setconstraint!(mpc, ŷmin=[-Inf,-Inf], ŷmax=[55, 35])
9696
setconstraint!(mpc, Δumin=[-Inf,-Inf],Δumax=[+Inf,+Inf])
9797

98-
nmpc = NonLinMPC(kf, Hp=15, Hc=1, Mwt=[1, 1] , Nwt=[0.1, 0.1], Cwt=1e5)
98+
nmpc = NonLinMPC(kf, Hp=15, Hc=1, Mwt=[1, 1] , Nwt=[0.1, 0.1], Cwt=1e5, Ewt=0.0)
9999

100100
setconstraint!(nmpc, c_umin=[0,0], c_umax=[0,0])
101101
setconstraint!(nmpc, c_ŷmin=[1,1], c_ŷmax=[1,1])
@@ -112,7 +112,7 @@ function test_mpc(model, mpc)
112112
d_data = zeros(1,N)
113113
u = model.uop
114114
d = model.dop
115-
r = [50,31]
115+
r = [50,32]
116116
setstate!(model, zeros(model.nx))
117117
setstate!(mpc, zeros(mpc.estim.nx̂))
118118
initstate!(mpc,u,model(d),d)

src/controller/linmpc.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ struct LinMPC{S<:StateEstimator} <: PredictiveController
2828
::Vector{Float64}
2929
Ks::Matrix{Float64}
3030
Ps::Matrix{Float64}
31-
d0::Vector{Float64}
32-
D̂0::Vector{Float64}
31+
d::Vector{Float64}
32+
::Vector{Float64}
3333
Yop::Vector{Float64}
3434
Dop::Vector{Float64}
3535
function LinMPC{S}(estim::S, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru, optim) where {S<:StateEstimator}
@@ -49,7 +49,7 @@ struct LinMPC{S<:StateEstimator} <: PredictiveController
4949
con, S̃_Hp, Ñ_Hc, Ẽ = init_defaultcon(model, Hp, Hc, C, S_Hp, S_Hc, N_Hc, E)
5050
P̃, q̃ = init_quadprog(model, Ẽ, S̃_Hp, M_Hp, Ñ_Hc, L_Hp)
5151
Ks, Ps = init_stochpred(estim, Hp)
52-
d0, D̂0 = zeros(nd), zeros(nd*Hp)
52+
d, D̂ = zeros(nd), zeros(nd*Hp)
5353
Yop, Dop = repeat(model.yop, Hp), repeat(model.dop, Hp)
5454
nvar = size(Ẽ, 2)
5555
ΔŨ = zeros(nvar)
@@ -61,7 +61,7 @@ struct LinMPC{S<:StateEstimator} <: PredictiveController
6161
S̃_Hp, T_Hp, T_Hc,
6262
Ẽ, F, G, J, Kd, Q, P̃, q̃,
6363
Ks, Ps,
64-
d0, D̂0,
64+
d, D̂,
6565
Yop, Dop,
6666
)
6767
init_optimization!(mpc)

src/controller/nonlinmpc.jl

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
3030
::Vector{Float64}
3131
Ks::Matrix{Float64}
3232
Ps::Matrix{Float64}
33-
d0::Vector{Float64}
34-
D̂0::Vector{Float64}
33+
d::Vector{Float64}
34+
::Vector{Float64}
3535
Yop::Vector{Float64}
3636
Dop::Vector{Float64}
3737
function NonLinMPC{S, JEFunc}(
@@ -53,7 +53,7 @@ struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
5353
con, S̃_Hp, Ñ_Hc, Ẽ = init_defaultcon(model, Hp, Hc, C, S_Hp, S_Hc, N_Hc, E)
5454
P̃, q̃ = init_quadprog(model, Ẽ, S̃_Hp, M_Hp, Ñ_Hc, L_Hp)
5555
Ks, Ps = init_stochpred(estim, Hp)
56-
d0, D̂0 = zeros(nd), zeros(nd*Hp)
56+
d, D̂ = zeros(nd), zeros(nd*Hp)
5757
Yop, Dop = repeat(model.yop, Hp), repeat(model.dop, Hp)
5858
nvar = size(Ẽ, 2)
5959
ΔŨ = zeros(nvar)
@@ -65,7 +65,7 @@ struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
6565
S̃_Hp, T_Hp, T_Hc,
6666
Ẽ, F, G, J, Kd, Q, P̃, q̃,
6767
Ks, Ps,
68-
d0, D̂0,
68+
d, D̂,
6969
Yop, Dop,
7070
)
7171
init_optimization!(mpc)
@@ -117,7 +117,7 @@ default arguments.
117117
- `Nwt=fill(0.1,model.nu)` : main diagonal of ``\mathbf{N}`` weight matrix (vector)
118118
- `Lwt=fill(0.0,model.nu)` : main diagonal of ``\mathbf{L}`` weight matrix (vector)
119119
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only
120-
- `Ewt=1.0` : economic costs weight ``E`` (scalar).
120+
- `Ewt=0.0` : economic costs weight ``E`` (scalar).
121121
- `JE=(_,_,_)->0.0` : economic function ``J_E(\mathbf{U}_E, \mathbf{D̂}_E, \mathbf{Ŷ}_E)``.
122122
- `ru=model.uop` : manipulated input setpoints ``\mathbf{r_u}`` (vector)
123123
- `optim=JuMP.Model(Ipopt.Optimizer)` : nonlinear optimizer used in the predictive
@@ -179,7 +179,7 @@ function NonLinMPC(
179179
Nwt = fill(0.1, estim.model.nu),
180180
Lwt = fill(0.0, estim.model.nu),
181181
Cwt = 1e5,
182-
Ewt = 1.0,
182+
Ewt = 0.0,
183183
JE::JEFunc = (_,_,_) -> 0.0,
184184
ru = estim.model.uop,
185185
optim::JuMP.Model = JuMP.Model(optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes"))
@@ -206,18 +206,21 @@ function init_optimization!(mpc::NonLinMPC)
206206
# --- nonlinear optimization init ---
207207
model = mpc.estim.model
208208
ncon = length(mpc.con.Ŷmin) + length(mpc.con.Ŷmax)
209-
npred = mpc.Hp*mpc.estim.model.ny
210-
Jfunc, Cfunc = let mpc=mpc, model=model, nvar=nvar, ncon=ncon, npred=npred
211-
last_ΔŨ::Vector{Float64} = zeros(nvar)
212-
::Vector{Float64} = zeros(npred)
209+
nŶ = mpc.Hp*mpc.estim.model.ny
210+
Jfunc, Cfunc = let mpc=mpc, model=model, nvar=nvar, ncon=ncon, nŶ=nŶ
211+
# inspired from https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/tips_and_tricks/#User-defined-functions-with-vector-outputs
212+
last_ΔŨtup = nothing
213+
::Vector{Float64} = zeros(nŶ)
213214
C::Vector{Float64} = zeros(ncon)
214-
last_dΔŨtup, dC, dŶ = nothing, nothing, nothing
215+
last_dΔŨtup = nothing
216+
dŶ = nothing
217+
dC = nothing
215218
function Jfunc(ΔŨtup::Float64...)
216219
ΔŨ = collect(ΔŨtup)
217-
if ΔŨ last_ΔŨ
220+
if ΔŨtup !== last_ΔŨtup
218221
= predict(mpc, model, ΔŨ)
219222
C = con_nonlinprog(mpc, model, Ŷ, ΔŨ)
220-
last_ΔŨ = ΔŨ
223+
last_ΔŨtup = ΔŨtup
221224
end
222225
return obj_nonlinprog(mpc, model, Ŷ, ΔŨ)
223226
end
@@ -231,11 +234,11 @@ function init_optimization!(mpc::NonLinMPC)
231234
return obj_nonlinprog(mpc, model, dŶ, dΔŨ)
232235
end
233236
function con_nonlinprog_i(i, ΔŨtup::NTuple{N, Float64}) where {N}
234-
ΔŨ = collect(ΔŨtup)
235-
if ΔŨ last_ΔŨ
237+
if ΔŨtup last_ΔŨtup
238+
ΔŨ = collect(ΔŨtup)
236239
= predict(mpc, model, ΔŨ)
237240
C = con_nonlinprog(mpc, model, Ŷ, ΔŨ)
238-
last_ΔŨ = ΔŨ
241+
last_ΔŨtup = ΔŨtup
239242
end
240243
return C[i]
241244
end
@@ -293,12 +296,15 @@ end
293296
Objective function for [`NonLinMPC`] when `model` is a [`LinModel`](@ref).
294297
"""
295298
function obj_nonlinprog(mpc::NonLinMPC, model::LinModel, Ŷ, ΔŨ::Vector{T}) where {T<:Real}
296-
Jqp = obj_quadprog(ΔŨ, mpc.P̃, mpc.q̃)
297-
U = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0 + model.uop)
298-
UE = [U; U[(end - model.nu + 1):end]]
299-
ŶE = [mpc.ŷ; Ŷ]
300-
D̂E = [mpc.d0 + model.dop; mpc.D̂0 + mpc.Dop]
301-
return Jqp + mpc.E*mpc.JE(UE, ŶE, D̂E)
299+
J = obj_quadprog(ΔŨ, mpc.P̃, mpc.q̃)
300+
if !iszero(mpc.E)
301+
U = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0 + model.uop)
302+
UE = [U; U[(end - model.nu + 1):end]]
303+
ŶE = [mpc.ŷ; Ŷ]
304+
D̂E = [mpc.d; mpc.D̂]
305+
J += mpc.E*mpc.JE(UE, ŶE, D̂E)
306+
end
307+
return J
302308
end
303309

304310
"""
@@ -312,21 +318,29 @@ function obj_nonlinprog(mpc::NonLinMPC, model::SimModel, Ŷ, ΔŨ::Vector{T})
312318
JR̂y = êy'*mpc.M_Hp*êy
313319
# --- move suppression term ---
314320
JΔŨ = ΔŨ'*mpc.Ñ_Hc*ΔŨ
321+
# --- input over prediction horizon ---
322+
if !isempty(mpc.R̂u) || !iszero(mpc.E)
323+
U = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0 + model.uop)
324+
end
315325
# --- input setpoint tracking term ---
316-
U = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0 + model.uop)
317326
if !isempty(mpc.R̂u)
318-
êu = mpc.R̂u - U
327+
êu = mpc.R̂u - U
319328
JR̂u = êu'*mpc.L_Hp*
320329
else
321330
JR̂u = 0.0
322331
end
323332
# --- slack variable term ---
324333
= !isinf(mpc.C) ? mpc.C*ΔŨ[end] : 0.0
325334
# --- economic term ---
326-
UE = [U; U[(end - model.nu + 1):end]]
327-
ŶE = [mpc.ŷ; Ŷ]
328-
D̂E = [mpc.d0 + model.dop; mpc.D̂0 + mpc.Dop]
329-
return JR̂y + JΔŨ + JR̂u ++ mpc.E*mpc.JE(UE, ŶE, D̂E)
335+
if !iszero(mpc.E)
336+
UE = [U; U[(end - model.nu + 1):end]]
337+
ŶE = [mpc.ŷ; Ŷ]
338+
D̂E = [mpc.d; mpc.D̂]
339+
E_JE = mpc.E*mpc.JE(UE, ŶE, D̂E)
340+
else
341+
E_JE = 0.0
342+
end
343+
return JR̂y + JΔŨ + JR̂u ++ E_JE
330344
end
331345

332346

@@ -336,7 +350,7 @@ end
336350
Nonlinear constraints for [`NonLinMPC`](@ref) when `model` is a [`LinModel`](@ref).
337351
"""
338352
function con_nonlinprog(mpc::NonLinMPC, model::LinModel, _, ΔŨ::Vector{T}) where {T<:Real}
339-
return zeros(T, 2*model.ny*mpc.Hp)
353+
return zeros(T, 0)
340354
end
341355
"""
342356
con_nonlinprog(mpc::NonLinMPC, model::NonLinModel, ΔŨ::Vector{Real})

src/predictive_control.jl

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ end
340340

341341

342342
@doc raw"""
343-
initpred!(mpc, model::LinModel, d, D̂, Ŷs, R̂y)
343+
initpred!(mpc, model::LinModel, d, D̂, R̂y)
344344
345345
Init linear model prediction matrices `F`, `q̃` and `p`.
346346
@@ -349,8 +349,8 @@ See [`init_deterpred`](@ref) and [`init_quadprog`](@ref) for the definition of t
349349
function initpred!(mpc::PredictiveController, model::LinModel, d, D̂, R̂y)
350350
mpc.F[:] = mpc.Kd*mpc.x̂d + mpc.Q*mpc.estim.lastu0 + mpc.Yop + mpc.Ŷs
351351
if model.nd 0
352-
mpc.d0[:], mpc.D̂0[:] = d - model.dop, D̂ - mpc.Dop
353-
mpc.F[:] = mpc.F + mpc.G*mpc.d0 + mpc.J*mpc.D̂0
352+
mpc.d[:], mpc.[:] = d, D̂
353+
mpc.F[:] = mpc.F + mpc.G*(mpc.d - model.dop) + mpc.J*(mpc.- mpc.Dop)
354354
end
355355
mpc.R̂y[:] = R̂y
356356
= mpc.F - R̂y
@@ -366,16 +366,16 @@ function initpred!(mpc::PredictiveController, model::LinModel, d, D̂, R̂y)
366366
end
367367

368368
@doc raw"""
369-
initpred!(mpc::PredictiveController, model::SimModel, d, D̂, Ŷs, R̂y )
369+
initpred!(mpc::PredictiveController, model::SimModel, d, D̂, R̂y)
370370
371371
Init `d0` and `D̂0` matrices when model is not a [`LinModel`](@ref).
372372
373-
`d0` and `D̂0` are the measured disturbances and its predictions without the operating points
373+
`d0` and `D̂0` are the measured disturbances and its predictions without the operating points
374374
``\mathbf{d_{op}}``.
375375
"""
376376
function initpred!(mpc::PredictiveController, model::SimModel, d, D̂, R̂y)
377377
if model.nd 0
378-
mpc.d0[:], mpc.D̂0[:] = d - model.dop, D̂ - mpc.Dop
378+
mpc.d[:], mpc.[:] = d, D̂
379379
end
380380
mpc.R̂y[:] = R̂y
381381
p = 0.0 # only used for LinModel objects
@@ -397,15 +397,18 @@ end
397397
Evaluate ``\\mathbf{Ŷ}`` when `model` is not a [`LinModel`](@ref).
398398
"""
399399
function predict(mpc::PredictiveController, model::SimModel, ΔŨ::Vector{T}) where {T<:Real}
400+
nu, ny, nd, Hp = model.nu, model.ny, model.nd, mpc.Hp
401+
yop, dop = model.yop, model.dop
400402
U0 = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0)
401-
Ŷd = Vector{T}(undef, model.ny*mpc.Hp)
403+
Ŷd::Vector{T} = Vector{T}(undef, ny*Hp)
404+
u0::Vector{T} = Vector{T}(undef, nu)
402405
x̂d::Vector{T} = copy(mpc.x̂d)
403-
d0 = mpc.d0
404-
for j=1:mpc.Hp
405-
u0 = U0[(1 + model.nu*(j-1)):(model.nu*j)]
406+
d0 = mpc.d - dop
407+
for j=1:Hp
408+
u0[:] = U0[(1 + nu*(j-1)):(nu*j)]
406409
x̂d[:] = f(model, x̂d, u0, d0)
407-
d0 = mpc.D̂0[(1 + model.nd*(j-1)):(model.nd*j)]
408-
Ŷd[(1 + model.ny*(j-1)):(model.ny*j)] = h(model, x̂d, d0) + model.yop
410+
d0[:] = mpc.[(1 + nd*(j-1)):(nd*j)] - dop
411+
Ŷd[(1 + ny*(j-1)):(ny*j)] = h(model, x̂d, d0) + yop
409412
end
410413
return Ŷd + mpc.Ŷs
411414
end

0 commit comments

Comments
 (0)