Skip to content

Commit fb85728

Browse files
committed
compute predictions only once for NonLinMPC with nonlin output constraints
1 parent ef97703 commit fb85728

File tree

2 files changed

+60
-36
lines changed

2 files changed

+60
-36
lines changed

example/juMPC.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ setconstraint!(nmpc2, ŷmin=[-Inf,-Inf], ŷmax=[55, 35])
8484
setconstraint!(nmpc2, Δumin=[-Inf,-Inf],Δumax=[+Inf,+Inf])
8585

8686

87-
88-
8987
nx = linModel4.nx
9088
kf = KalmanFilter(linModel4, σP0=10*ones(nx), σQ=0.01*ones(nx), σR=[0.1, 0.1], σQ_int=0.05*ones(2), σP0_int=10*ones(2))
9189

@@ -116,6 +114,7 @@ function test_mpc(model, mpc)
116114
d = model.dop
117115
r = [50,31]
118116
setstate!(model, zeros(model.nx))
117+
setstate!(mpc, zeros(mpc.estim.nx̂))
119118
initstate!(mpc,u,model(d),d)
120119
for k = 0:N-1
121120
if k == 40

src/controller/nonlinmpc.jl

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,38 +72,54 @@ struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
7272
A = con.A[con.i_b, :]
7373
b = con.b[con.i_b]
7474
@constraint(optim, linconstraint, A*ΔŨ .≤ b)
75-
J = let mpc=mpc, model=model # capture mpc and model variables
76-
(ΔŨ...) -> obj_nonlinprog(mpc, model, ΔŨ)
75+
76+
last_ΔŨ, last_C, last_Ŷ = nothing, nothing, nothing
77+
function Jfunc(ΔŨ::Float64...)
78+
if ΔŨ !== last_ΔŨ
79+
last_Ŷ = evalŶ(mpc, model, ΔŨ)
80+
last_C = con_nonlinprog(mpc, model, last_Ŷ, ΔŨ)
81+
last_ΔŨ = ΔŨ
82+
end
83+
return obj_nonlinprog(mpc, model, last_Ŷ, ΔŨ)
84+
end
85+
last_dΔŨ, last_dC, last_dŶ = nothing, nothing, nothing
86+
function Jfunc(dΔŨ::T...) where {T<:Real}
87+
if dΔŨ !== last_dΔŨ
88+
last_dŶ = evalŶ(mpc, model, dΔŨ)
89+
last_dC = con_nonlinprog(mpc, model, last_dŶ, dΔŨ)
90+
last_dΔŨ = dΔŨ
91+
end
92+
return obj_nonlinprog(mpc, model, last_dŶ, dΔŨ)
7793
end
78-
register(optim, :J, nvar, J, autodiff=true)
79-
@NLobjective(optim, Min, J(ΔŨ...))
94+
register(optim, :Jfunc, nvar, Jfunc, autodiff=true)
95+
@NLobjective(optim, Min, Jfunc(ΔŨ...))
8096
ncon = length(mpc.con.Ŷmin) + length(mpc.con.Ŷmax)
81-
C = let mpc=mpc, model=model, ncon=ncon # capture the 3 variables
82-
last_ΔŨ, last_C = nothing, nothing
83-
function con_nonlinprog_i(i, ΔŨ::NTuple{N, Float64}) where {N}
84-
if ΔŨ !== last_ΔŨ
85-
last_C, last_ΔŨ = con_nonlinprog(mpc, model, ΔŨ), ΔŨ
86-
end
87-
return last_C[i]
97+
function con_nonlinprog_i(i, ΔŨ::NTuple{N, Float64}) where {N}
98+
if ΔŨ !== last_ΔŨ
99+
last_Ŷ = evalŶ(mpc, model, ΔŨ)
100+
last_C = con_nonlinprog(mpc, model, last_Ŷ, ΔŨ)
101+
last_ΔŨ = ΔŨ
88102
end
89-
last_dΔŨ, last_dCdΔŨ = nothing, nothing
90-
function con_nonlinprog_i(i, dΔŨ::NTuple{N, T}) where {N, T<:Real}
91-
if dΔŨ !== last_dΔŨ
92-
last_dCdΔŨ, last_dΔŨ = con_nonlinprog(mpc, model, dΔŨ), dΔŨ
93-
end
94-
return last_dCdΔŨ[i]
103+
return last_C[i]
104+
end
105+
function con_nonlinprog_i(i, dΔŨ::NTuple{N, T}) where {N, T<:Real}
106+
if dΔŨ !== last_dΔŨ
107+
last_dŶ = evalŶ(mpc, model, dΔŨ)
108+
last_dC = con_nonlinprog(mpc, model, last_dŶ, dΔŨ)
109+
last_dΔŨ = dΔŨ
95110
end
96-
[(ΔŨ...) -> con_nonlinprog_i(i, ΔŨ) for i in 1:ncon]
111+
return last_dC[i]
97112
end
113+
Cfunc = [(ΔŨ...) -> con_nonlinprog_i(i, ΔŨ) for i in 1:ncon]
98114
n = 0
99115
for i in eachindex(con.Ŷmin)
100116
sym = Symbol("C_Ŷmin_$i")
101-
register(optim, sym, nvar, C[n + i], autodiff=true)
117+
register(optim, sym, nvar, Cfunc[n + i], autodiff=true)
102118
end
103119
n = lastindex(con.Ŷmin)
104120
for i in eachindex(con.Ŷmax)
105121
sym = Symbol("C_Ŷmax_$i")
106-
register(optim, sym, nvar, C[n + i], autodiff=true)
122+
register(optim, sym, nvar, Cfunc[n + i], autodiff=true)
107123
end
108124
set_silent(optim)
109125
return mpc
@@ -253,12 +269,12 @@ init_objective!(mpc::NonLinMPC, _ ) = nothing
253269
254270
Objective function for [`NonLinMPC`] when `model` is a [`LinModel`](@ref).
255271
"""
256-
function obj_nonlinprog(mpc::NonLinMPC, model::LinModel, ΔŨ::NTuple{N, T}) where {N, T}
272+
function obj_nonlinprog(mpc::NonLinMPC, model::LinModel, Ŷ, ΔŨ::NTuple{N, T}) where {N, T}
257273
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
258274
Jqp = obj_quadprog(ΔŨ, mpc.P̃, mpc.q̃)
259275
U = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0 + model.uop)
260276
UE = [U; U[(end - model.nu + 1):end]]
261-
ŶE = [mpc.ŷ; mpc.*ΔŨ + mpc.F]
277+
ŶE = [mpc.ŷ; ]
262278
D̂E = [mpc.d0 + model.dop; mpc.D̂0 + mpc.Dop]
263279
return Jqp + mpc.E*mpc.JE(UE, ŶE, D̂E)
264280
end
@@ -268,11 +284,10 @@ end
268284
269285
Objective function for [`NonLinMPC`] when `model` is not a [`LinModel`](@ref).
270286
"""
271-
function obj_nonlinprog(mpc::NonLinMPC, model::SimModel, ΔŨ::NTuple{N, T}) where {N, T}
287+
function obj_nonlinprog(mpc::NonLinMPC, model::SimModel, Ŷ, ΔŨ::NTuple{N, T}) where {N, T}
272288
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
273289
U0 = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0)
274290
# --- output setpoint tracking term ---
275-
= evalŶ(mpc, model, mpc.x̂d, mpc.d0, mpc.D̂0, U0)
276291
êy = mpc.R̂y -
277292
JR̂y = êy'*mpc.M_Hp*êy
278293
# --- move suppression term ---
@@ -300,18 +315,15 @@ end
300315
301316
Nonlinear constraints for [`NonLinMPC`](@ref) when `model` is a [`LinModel`](@ref).
302317
"""
303-
function con_nonlinprog(mpc::NonLinMPC, ::LinModel, ΔŨ::NTuple{N, T}) where {N, T}
304-
return zeros(T, 2*mpc.ny*mpc.Hp)
318+
function con_nonlinprog(mpc::NonLinMPC, model::LinModel, _, ΔŨ::NTuple{N, T}) where {N, T}
319+
return zeros(T, 2*model.ny*mpc.Hp)
305320
end
306321
"""
307322
con_nonlinprog(mpc::NonLinMPC, model::NonLinModel, ΔŨ::NTuple{N, T}) where {N, T}
308323
309324
Nonlinear constrains for [`NonLinMPC`](@ref) when `model` is not a [`LinModel`](ref).
310325
"""
311-
function con_nonlinprog(mpc::NonLinMPC, model::SimModel, ΔŨ::NTuple{N, T}) where {N, T}
312-
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
313-
U0 = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0)
314-
= evalŶ(mpc, model, mpc.x̂d, mpc.d0, mpc.D̂0, U0)
326+
function con_nonlinprog(mpc::NonLinMPC, ::SimModel, Ŷ, ΔŨ::NTuple{N, T}) where {N, T}
315327
if !isinf(mpc.C) # constraint softening activated :
316328
ϵ = ΔŨ[end]
317329
C_Ŷmin = (mpc.con.Ŷmin - Ŷ) - ϵ*mpc.con.c_Ŷmin
@@ -328,18 +340,31 @@ function con_nonlinprog(mpc::NonLinMPC, model::SimModel, ΔŨ::NTuple{N, T}) wh
328340
end
329341

330342

343+
"""
344+
evalŶ(mpc::NonLinMPC, model::LinModel, x̂d, d0, D̂0, U0::Vector{T}) where {T}
345+
346+
Evaluate the outputs predictions ``\\mathbf{Ŷ}`` when `model` is a [`LinModel`](@ref).
347+
"""
348+
function evalŶ(mpc::NonLinMPC, ::LinModel, ΔŨ::NTuple{N, T}) where {N, T}
349+
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
350+
return mpc.*ΔŨ + mpc.F
351+
end
352+
331353
"""
332354
evalŶ(mpc::NonLinMPC, model::SimModel, x̂d, d0, D̂0, U0::Vector{T}) where {T}
333355
334-
Evaluate the outputs predictions ``\\mathbf{Ŷ}`` when `model` is not a [`LinModel`](@ref).
356+
Evaluate ``\\mathbf{Ŷ}`` when `model` is not a [`LinModel`](@ref).
335357
"""
336-
function evalŶ(mpc::NonLinMPC, model::SimModel, x̂d, d0, D̂0, U0::Vector{T}) where {T}
358+
function evalŶ(mpc::NonLinMPC, model::SimModel, ΔŨ::NTuple{N, T}) where {N, T}
359+
ΔŨ = collect(ΔŨ) # convert NTuple to Vector
360+
U0 = mpc.S̃_Hp*ΔŨ + mpc.T_Hp*(mpc.estim.lastu0)
337361
Ŷd0 = Vector{T}(undef, model.ny*mpc.Hp)
338-
x̂d::Vector{T} = copy(x̂d)
362+
x̂d::Vector{T} = copy(mpc.x̂d)
363+
d0 = mpc.d0
339364
for j=1:mpc.Hp
340365
u0 = U0[(1 + model.nu*(j-1)):(model.nu*j)]
341366
x̂d[:] = f(model, x̂d, u0, d0)
342-
d0 = D̂0[(1 + model.nd*(j-1)):(model.nd*j)]
367+
d0 = mpc.D̂0[(1 + model.nd*(j-1)):(model.nd*j)]
343368
Ŷd0[(1 + model.ny*(j-1)):(model.ny*j)] = h(model, x̂d, d0)
344369
end
345370
return Ŷd0 + mpc.F # F = Yop + Ŷs

0 commit comments

Comments
 (0)