Skip to content

Commit e8daf2c

Browse files
committed
NonLinMPC with economic function as a concrete type
1 parent 02f4bca commit e8daf2c

File tree

2 files changed

+49
-27
lines changed

2 files changed

+49
-27
lines changed

example/juMPC.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function myfunc()
8282
optimize!(model)
8383
end
8484

85-
#myfunc()
85+
myfunc()
8686

8787
nx = linModel4.nx
8888
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))
@@ -130,8 +130,8 @@ function test_mpc(model, mpc)
130130
return u_data, y_data, r_data, d_data
131131
end
132132

133-
@time u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
134-
@profview u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
133+
u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
134+
#@profview u_data, y_data, r_data, d_data = test_mpc(linModel4, mpc)
135135
#=
136136
using PlotThemes, Plots
137137
#theme(:default)

src/predictive_control.jl

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ struct LinMPC{S<:StateEstimator} <: PredictiveController
115115
i_nonInf = .!isinf.(b)
116116
A = A[i_nonInf, :]
117117
b = b[i_nonInf]
118-
@constraint(optim, constraint_lin, A*ΔŨ .≤ b)
118+
@constraint(optim, linconstraint, A*ΔŨ .≤ b)
119119
ΔŨ0 = zeros(nvar)
120120
ϵ = isinf(C) ? NaN : 0.0 # C = Inf means hard constraints only
121121
u, U = copy(model.uop), repeat(model.uop, Hp)
@@ -263,7 +263,7 @@ end
263263

264264

265265

266-
struct NonLinMPC{S<:StateEstimator} <: PredictiveController
266+
struct NonLinMPC{S<:StateEstimator, JEFunc<:Function} <: PredictiveController
267267
estim::S
268268
optim::JuMP.Model
269269
info::OptimInfo
@@ -274,6 +274,7 @@ struct NonLinMPC{S<:StateEstimator} <: PredictiveController
274274
L_Hp::Diagonal{Float64, Vector{Float64}}
275275
C::Float64
276276
E::Float64
277+
JE::JEFunc
277278
R̂u::Vector{Float64}
278279
Umin ::Vector{Float64}
279280
Umax ::Vector{Float64}
@@ -306,9 +307,9 @@ struct NonLinMPC{S<:StateEstimator} <: PredictiveController
306307
Ps::Matrix{Float64}
307308
Yop::Vector{Float64}
308309
Dop::Vector{Float64}
309-
function NonLinMPC{S}(
310-
estim::S, Hp, Hc, Mwt, Nwt, Lwt, Cwt, Ewt, J_E, ru, optim
311-
) where {S<:StateEstimator}
310+
function NonLinMPC{S, JEFunc}(
311+
estim::S, Hp, Hc, Mwt, Nwt, Lwt, Cwt, Ewt, JE::JEFunc, ru, optim
312+
) where {S<:StateEstimator, JEFunc<:Function}
312313
model = estim.model
313314
nu, ny = model.nu, model.ny
314315
validate_weights(model, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru, Ewt)
@@ -345,7 +346,7 @@ struct NonLinMPC{S<:StateEstimator} <: PredictiveController
345346
#i_nonInf = .!isinf.(b)
346347
#A = A[i_nonInf, :]
347348
#b = b[i_nonInf]
348-
#@constraint(optim, constraint_lin, A*ΔŨ .≤ b)
349+
#@constraint(optim, linconstraint, A*ΔŨ .≤ b)
349350
ΔŨ0 = zeros(nvar)
350351
ϵ = isinf(C) ? NaN : 0.0 # C = Inf means hard constraints only
351352
u, U = copy(model.uop), repeat(model.uop, Hp)
@@ -355,7 +356,7 @@ struct NonLinMPC{S<:StateEstimator} <: PredictiveController
355356
return new(
356357
estim, optim, info,
357358
Hp, Hc,
358-
M_Hp, Ñ_Hc, L_Hp, Cwt, Ewt, R̂u,
359+
M_Hp, Ñ_Hc, L_Hp, Cwt, Ewt, JE, R̂u,
359360
Umin, Umax, ΔŨmin, ΔŨmax, Ŷmin, Ŷmax,
360361
c_Umin, c_Umax, c_ΔUmin, c_ΔUmax, c_Ŷmin, c_Ŷmax,
361362
S̃_Hp, T_Hp, S̃_Hc, T_Hc,
@@ -390,8 +391,10 @@ the manipulated inputs, the predicted outputs and measured disturbances from ``k
390391
\mathbf{Ŷ}_E = \begin{bmatrix} \mathbf{ŷ}(k) \\ \mathbf{Ŷ} \end{bmatrix} \text{,} \qquad
391392
\mathbf{D̂}_E = \begin{bmatrix} \mathbf{d}(k) \\ \mathbf{D̂} \end{bmatrix}
392393
```
394+
since ``H_c ≤ H_p`` implies that ``\mathbf{u}(k+H_p) = \mathbf{u}(k+H_p-1)``.
395+
393396
!!! tip
394-
Replace any of the 3 arguments with `_` if not needed (see `J_E` default value below).
397+
Replace any of the 3 arguments with `_` if not needed (see `JE` default value below).
395398
396399
This method uses the default state estimator, an [`UnscentedKalmanFilter`](@ref) with
397400
default arguments.
@@ -405,7 +408,7 @@ default arguments.
405408
- `Lwt=fill(0.0,model.nu)` : main diagonal of ``\mathbf{L}`` weight matrix (vector)
406409
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only
407410
- `Ewt=1.0` : economic costs weight ``E`` (scalar).
408-
- `J_E=(_,_,_)->0.0` : economic function ``J_E(\mathbf{U}_E, \mathbf{D̂}_E, \mathbf{Ŷ}_E)``.
411+
- `JE=(_,_,_)->0.0` : economic function ``J_E(\mathbf{U}_E, \mathbf{D̂}_E, \mathbf{Ŷ}_E)``.
409412
- `ru=model.uop` : manipulated input setpoints ``\mathbf{r_u}`` (vector)
410413
- `optim=JuMP.Model(Ipopt.Optimizer)` : nonlinear optimizer used in the predictive
411414
controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/reference/models/#JuMP.Model)
@@ -461,11 +464,11 @@ function NonLinMPC(
461464
Lwt = fill(0.0, estim.model.nu),
462465
Cwt = 1e5,
463466
Ewt = 1.0,
464-
J_E = (_,_,_) -> 0.0,
467+
JE::JEFunc = (_,_,_) -> 0.0,
465468
ru = estim.model.uop,
466469
optim::JuMP.Model = JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)
467-
) where {S<:StateEstimator}
468-
return NonLinMPC{S}(estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, Ewt, J_E, ru, optim)
470+
) where {S<:StateEstimator, JEFunc<:Function}
471+
return NonLinMPC{S, JEFunc}(estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, Ewt, JE, ru, optim)
469472
end
470473

471474
@doc raw"""
@@ -611,9 +614,9 @@ function setconstraint!(
611614
A = A[i_nonInf, :]
612615
b = b[i_nonInf]
613616
ΔŨ = mpc.optim[:ΔŨ]
614-
delete(mpc.optim, mpc.optim[:constraint_lin])
615-
unregister(mpc.optim, :constraint_lin)
616-
@constraint(mpc.optim, constraint_lin, A*ΔŨ .≤ b)
617+
delete(mpc.optim, mpc.optim[:linconstraint])
618+
unregister(mpc.optim, :linconstraint)
619+
@constraint(mpc.optim, linconstraint, A*ΔŨ .≤ b)
617620
return mpc
618621
end
619622

@@ -674,12 +677,12 @@ function moveinput!(
674677
R̂y::Vector{<:Real} = repeat(ry, mpc.Hp),
675678
::Vector{<:Real} = repeat(d, mpc.Hp),
676679
ym::Union{Vector{<:Real}, Nothing} = nothing
677-
) where{C<:PredictiveController}
680+
) where {C<:PredictiveController}
678681
lastu = mpc.info.u
679682
x̂d, x̂s = split_state(mpc.estim)
680683
ŷs, Ŷs = predict_stoch(mpc, mpc.estim, x̂s, d, ym)
681684
F, q̃, p = init_prediction(mpc, mpc.estim.model, d, D̂, Ŷs, R̂y, x̂d, lastu)
682-
b = init_constraint(mpc, mpc.estim.model, F, lastu)
685+
b, _ = init_constraint(mpc, mpc.estim.model, lastu, F)
683686
ΔŨ, J = optim_objective!(mpc, b, q̃, p)
684687
write_info!(mpc, ΔŨ, J, ŷs, Ŷs, lastu, F, ym, d)
685688
Δu = ΔŨ[1:mpc.estim.model.nu] # receding horizon principle: only Δu(k) is used (1st one)
@@ -760,7 +763,9 @@ Init linear model prediction matrices `F`, `q̃` and `p`.
760763
761764
See [`init_deterpred`](@ref) and [`init_quadprog`](@ref) for the definition of the matrices.
762765
"""
763-
function init_prediction(mpc, model::LinModel, d, D̂, Ŷs, R̂y, x̂d, lastu)
766+
function init_prediction(
767+
mpc::C, model::LinModel, d, D̂, Ŷs, R̂y, x̂d, lastu
768+
) where {C<:PredictiveController}
764769
F = mpc.Kd*x̂d + mpc.Q*(lastu - model.uop) + Ŷs + mpc.Yop
765770
if model.nd 0
766771
F += mpc.G*(d - model.dop) + mpc.J*(D̂ - mpc.Dop)
@@ -778,11 +783,11 @@ end
778783

779784

780785
@doc raw"""
781-
init_constraint(mpc, ::LinModel, F)
786+
init_constraint(mpc::PredictiveController, ::LinModel, lastu, F)
782787
783788
Init `b` vector for the linear model inequality constraints (``\mathbf{A ΔŨ ≤ b}``).
784789
"""
785-
function init_constraint(mpc::C, ::LinModel, F, lastu) where {C<:PredictiveController}
790+
function init_constraint(mpc::C, ::LinModel, lastu, F) where {C<:PredictiveController}
786791
b = [
787792
-mpc.Umin + mpc.T_Hc*lastu
788793
+mpc.Umax - mpc.T_Hc*lastu
@@ -793,7 +798,24 @@ function init_constraint(mpc::C, ::LinModel, F, lastu) where {C<:PredictiveContr
793798
]
794799
i_nonInf = .!isinf.(b)
795800
b = b[i_nonInf]
796-
return b
801+
return b, i_nonInf
802+
end
803+
804+
@doc raw"""
805+
init_constraint(mpc::PredictiveController, ::NonLinModel, lastu)
806+
807+
Init `b` without predicted output ``\mathbf{Ŷ}`` constraints for [`NonLinModel`](@ref).
808+
"""
809+
function init_constraint(mpc::C, ::NonLinModel, lastu, _ ) where {C<:PredictiveController}
810+
b = [
811+
-mpc.Umin + mpc.T_Hc*lastu
812+
+mpc.Umax - mpc.T_Hc*lastu
813+
-mpc.ΔŨmin
814+
+mpc.ΔŨmax
815+
]
816+
i_nonInf = .!isinf.(b)
817+
b = b[i_nonInf]
818+
return b, i_nonInf
797819
end
798820

799821
"""
@@ -807,7 +829,7 @@ function optim_objective!(mpc::LinMPC, b, q̃, p)
807829
ΔŨ = optim[:ΔŨ]
808830
lastΔŨ = mpc.info.ΔŨ
809831
set_objective_function(optim, obj_quadprog(ΔŨ, mpc.P̃, q̃))
810-
set_normalized_rhs.(optim[:constraint_lin], b)
832+
set_normalized_rhs.(optim[:linconstraint], b)
811833
# initial ΔŨ (warm-start): [Δu_{k-1}(k); Δu_{k-1}(k+1); ... ; 0_{nu × 1}]
812834
ΔŨ0 = [lastΔŨ[(model.nu+1):(mpc.Hc*model.nu)]; zeros(model.nu)]
813835
# if soft constraints, append the last slack value ϵ_{k-1}:
@@ -1120,8 +1142,8 @@ function relaxŶ(::LinModel, C, c_Ŷmin, c_Ŷmax, E)
11201142
return A_Ŷmin, A_Ŷmax, Ẽ
11211143
end
11221144

1123-
"Return empty matrices for models different than [`LinModel`](@ref)"
1124-
function relaxŶ(::SimModel, C, c_Ŷmin, c_Ŷmax, E)
1145+
"Return empty matrices if model is a [`NonLinModel`](@ref)"
1146+
function relaxŶ(::NonLinModel, C, c_Ŷmin, c_Ŷmax, E)
11251147
= !isinf(C) ? [E zeros(0, 1)] : E
11261148
A_Ŷmin, A_Ŷmax = Ẽ, Ẽ
11271149
return A_Ŷmin, A_Ŷmax, Ẽ

0 commit comments

Comments
 (0)