@@ -19,11 +19,10 @@ julia> u = mpc([5]); round.(u, digits=3)
1919"""
2020abstract type PredictiveController end
2121
22- mutable struct OptimData
23- model:: JuMP.Model
24- J :: Float64
22+ mutable struct OptimInfo
2523 ΔŨ:: Vector{Float64}
2624 ϵ :: Union{Nothing, Float64}
25+ J :: Float64
2726 u :: Vector{Float64}
2827 U :: Vector{Float64}
2928 ŷ :: Vector{Float64}
3635struct LinMPC <: PredictiveController
3736 model:: LinModel
3837 estim:: StateEstimator
39- optim:: OptimData
38+ optim:: JuMP.Model
39+ info:: OptimInfo
4040 Hp:: Int
4141 Hc:: Int
4242 M_Hp:: Diagonal{Float64}
@@ -76,7 +76,7 @@ struct LinMPC <: PredictiveController
7676 Ps:: Matrix{Float64}
7777 Yop:: Vector{Float64}
7878 Dop:: Vector{Float64}
79- function LinMPC (estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru)
79+ function LinMPC (estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru, optim )
8080 model = estim. model
8181 nu, ny = model. nu, model. ny
8282 validate_weights (model, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru)
@@ -107,27 +107,27 @@ struct LinMPC <: PredictiveController
107107 P̃ = init_quadprog (Ẽ, S̃_Hp, M_Hp, Ñ_Hc, L_Hp)
108108 Ks, Ps = init_stochpred (estim, Hp)
109109 Yop, Dop = repeat (model. yop, Hp), repeat (model. dop, Hp)
110- u, U = copy (model. uop), repeat (model. uop, Hp)
111- ϵ = isinf (C) ? nothing : 0.0 # C = Inf means hard constraints only
112- ŷ, Ŷ = copy (model. yop), repeat (model. yop, Hp)
113- ŷs, Ŷs = zeros (ny), zeros (ny* Hp)
114110 nvar = size (P̃, 1 )
115- optmodel = Model (OSQP. MathOptInterfaceOSQP. Optimizer, add_bridges= false ) # default to OSQP solver
116- set_silent (optmodel)
117- @variable (optmodel, ΔŨ[1 : nvar])
111+ set_silent (optim)
112+ @variable (optim, ΔŨ[1 : nvar])
118113 # dummy q̃ value (the vector is updated just before optimization):
119- q̃ = zeros (nvar)
120- @objective (optmodel , Min, obj_quadprog (ΔŨ, P̃, q̃))
114+ q̃ = zeros (nvar)
115+ @objective (optim , Min, obj_quadprog (ΔŨ, P̃, q̃))
121116 A = [A_Umin; A_Umax; A_ΔŨmin; A_ΔŨmax; A_Ŷmin; A_Ŷmax]
122117 # dummy b vector to detect Inf values (b is updated just before optimization):
123118 b = [- Umin; + Umax; - ΔŨmin; + ΔŨmax; - Ŷmin; + Ŷmax]
124119 i_nonInf = .! isinf .(b)
125120 A = A[i_nonInf, :]
126121 b = b[i_nonInf]
127- @constraint (optmodel, constraint_lin, A* ΔŨ .≤ b)
128- optim = OptimData (optmodel, 0 , zeros (nvar), ϵ, u, U, ŷ, Ŷ, ŷs, Ŷs)
122+ @constraint (optim, constraint_lin, A* ΔŨ .≤ b)
123+ ΔŨ0 = zeros (nvar)
124+ ϵ = isinf (C) ? nothing : 0.0 # C = Inf means hard constraints only
125+ u, U = copy (model. uop), repeat (model. uop, Hp)
126+ ŷ, Ŷ = copy (model. yop), repeat (model. yop, Hp)
127+ ŷs, Ŷs = zeros (ny), zeros (ny* Hp)
128+ info = OptimInfo (ΔŨ0, ϵ, 0 , u, U, ŷ, Ŷ, ŷs, Ŷs)
129129 return new (
130- model, estim, optim,
130+ model, estim, optim, info,
131131 Hp, Hc,
132132 M_Hp, Ñ_Hc, L_Hp, C, R̂u,
133133 Umin, Umax, ΔŨmin, ΔŨmax, Ŷmin, Ŷmax,
@@ -195,7 +195,9 @@ arguments.
195195- `Nwt=fill(0.1,model.nu)` : main diagonal of ``\m athbf{N}`` weight matrix (vector)
196196- `Lwt=fill(0.0,model.nu)` : main diagonal of ``\m athbf{L}`` weight matrix (vector)
197197- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only
198- - `ru=model.uop`: manipulated input setpoints ``\m athbf{r_u}`` (vector)
198+ - `ru=model.uop` : manipulated input setpoints ``\m athbf{r_u}`` (vector)
199+ - `optim=JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)` : the MPC quadratic optimizer,
200+ provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/reference/models/#JuMP.Model)
199201
200202# Extended Help
201203Manipulated inputs setpoints ``\m athbf{r_u}`` are not common but they can be interesting
@@ -220,7 +222,8 @@ function LinMPC(
220222 Nwt = fill (0.1 , estim. model. nu),
221223 Lwt = fill (0.0 , estim. model. nu),
222224 Cwt = 1e5 ,
223- ru = estim. model. uop
225+ ru = estim. model. uop,
226+ optim:: JuMP.Model = JuMP. Model (OSQP. MathOptInterfaceOSQP. Optimizer)
224227)
225228 isa (estim. model, LinModel) || error (" estim.model type must be LinModel" )
226229 poles = eigvals (estim. model. A)
@@ -232,7 +235,7 @@ function LinMPC(
232235 @warn (" prediction horizon Hp ($Hp ) ≤ number of delays in model " *
233236 " ($nk ), the closed-loop system may be zero-gain (unresponsive) or unstable" )
234237 end
235- return LinMPC (estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru)
238+ return LinMPC (estim, Hp, Hc, Mwt, Nwt, Lwt, Cwt, ru, optim )
236239end
237240
238241@doc raw """
@@ -377,10 +380,10 @@ function setconstraint!(
377380 i_nonInf = .! isinf .(b)
378381 A = A[i_nonInf, :]
379382 b = b[i_nonInf]
380- ΔŨ = mpc. optim. model [:ΔŨ ]
381- delete (mpc. optim. model , mpc. optim. model [:constraint_lin ])
382- unregister (mpc. optim. model , :constraint_lin )
383- @constraint (mpc. optim. model , constraint_lin, A* ΔŨ .≤ b)
383+ ΔŨ = mpc. optim[:ΔŨ ]
384+ delete (mpc. optim, mpc. optim[:constraint_lin ])
385+ unregister (mpc. optim, :constraint_lin )
386+ @constraint (mpc. optim, constraint_lin, A* ΔŨ .≤ b)
384387 return mpc
385388end
386389
@@ -442,13 +445,13 @@ function moveinput!(
442445 D̂ :: Vector{<:Real} = repeat (d, mpc. Hp),
443446 ym:: Union{Vector{<:Real}, Nothing} = nothing
444447)
445- lastu = mpc. optim . u
448+ lastu = mpc. info . u
446449 x̂d, x̂s = split_state (mpc. estim)
447450 ŷs, Ŷs = predict_stoch (mpc, mpc. estim, x̂s, d, ym)
448451 F, q̃, p = init_prediction (mpc, mpc. model, d, D̂, Ŷs, R̂y, x̂d, lastu)
449452 b = init_constraint (mpc, mpc. model, F, lastu)
450453 ΔŨ, J = optim_objective! (mpc, b, q̃, p)
451- write_optimdata ! (mpc, ΔŨ, J, ŷs, Ŷs, lastu, F, ym, d)
454+ write_info ! (mpc, ΔŨ, J, ŷs, Ŷs, lastu, F, ym, d)
452455 Δu = ΔŨ[1 : mpc. model. nu] # receding horizon principle: only Δu(k) is used (first one)
453456 u = lastu + Δu
454457 return u
@@ -458,11 +461,11 @@ end
458461"""
459462 initstate!(mpc::PredictiveController, u, ym, d=Float64[])
460463
461- Init `mpc.optim ` and the states of `mpc.estim` [`StateEstimator`](@ref).
464+ Init `mpc.info ` and the states of `mpc.estim` [`StateEstimator`](@ref).
462465"""
463466function initstate! (mpc:: PredictiveController , u, ym, d= Float64[])
464- mpc. optim . u = u
465- mpc. optim . ΔŨ .= 0
467+ mpc. info . u = u
468+ mpc. info . ΔŨ .= 0
466469 return initstate! (mpc. estim, u, ym, d)
467470end
468471
@@ -563,41 +566,50 @@ end
563566Optimize the `mpc` quadratic objective function for [`LinMPC`](@ref) type.
564567"""
565568function optim_objective! (mpc:: LinMPC , b, q̃, p)
566- optmodel = mpc. optim. model
567- ΔŨ = optmodel [:ΔŨ ]
568- lastΔŨ = mpc. optim . ΔŨ
569- set_objective_function (optmodel , obj_quadprog (ΔŨ, mpc. P̃, q̃))
570- set_normalized_rhs .(optmodel [:constraint_lin ], b)
571- # initial ΔŨ (warm start): [Δu_{k-1}(k); Δu_{k-1}(k+1); ... ; 0_{nu × 1}]
569+ optim = mpc. optim
570+ ΔŨ = optim [:ΔŨ ]
571+ lastΔŨ = mpc. info . ΔŨ
572+ set_objective_function (optim , obj_quadprog (ΔŨ, mpc. P̃, q̃))
573+ set_normalized_rhs .(optim [:constraint_lin ], b)
574+ # initial ΔŨ (warm- start): [Δu_{k-1}(k); Δu_{k-1}(k+1); ... ; 0_{nu × 1}]
572575 ΔŨ0 = [lastΔŨ[(mpc. model. nu+ 1 ): (mpc. Hc* mpc. model. nu)]; zeros (mpc. model. nu)]
573576 # if soft constraints, append the last slack value ϵ_{k-1}:
574577 ! isinf (mpc. C) && (ΔŨ0 = [ΔŨ0; lastΔŨ[end ]])
575- # set_start_value.(ΔŨ, ΔŨ0)
576- optimize! (optmodel)
577- status = termination_status (optmodel)
578+ set_start_value .(ΔŨ, ΔŨ0)
579+ try
580+ optimize! (optim)
581+ catch err
582+ if isa (err, MOI. UnsupportedAttribute{MOI. VariablePrimalStart})
583+ MOIU. reset_optimizer (optim)
584+ optimize! (optim)
585+ else
586+ rethrow (err)
587+ end
588+ end
589+ status = termination_status (optim)
578590 if ! (status == OPTIMAL || status == LOCALLY_SOLVED)
579591 @warn " MPC termination status not OPTIMAL or LOCALLY_SOLVED ($status )"
580- @debug solution_summary (optmodel )
592+ @debug solution_summary (optim )
581593 end
582594 ΔŨ = isfatal (status) ? ΔŨ0 : value .(ΔŨ) # fatal status : use last value
583- J = objective_value (optmodel ) + p # optimal objective value by adding constant p
595+ J = objective_value (optim ) + p # optimal objective value by adding constant p
584596 return ΔŨ, J
585597end
586598
587599"""
588- write_optimdata !(mpc::LinMPC, ΔŨ, ϵ, J, info, ŷs, Ŷs, lastu, F, ym, d)
600+ write_info !(mpc::LinMPC, ΔŨ, ϵ, J, info, ŷs, Ŷs, lastu, F, ym, d)
589601
590- Write `mpc.optim ` with the [`LinMPC`](@ref) optimization results.
602+ Write `mpc.info ` with the [`LinMPC`](@ref) optimization results.
591603"""
592- function write_optimdata ! (mpc:: LinMPC , ΔŨ, J, ŷs, Ŷs, lastu, F, ym, d)
593- mpc. optim . ΔŨ = ΔŨ
594- mpc. optim . ϵ = isinf (mpc. C) ? nothing : ΔŨ[end ]
595- mpc. optim . J = J
596- mpc. optim . U = mpc. S̃_Hp* ΔŨ + mpc. T_Hp* lastu
597- mpc. optim . u = mpc. optim . U[1 : mpc. model. nu]
598- mpc. optim . ŷ = isa (mpc. estim, InternalModel) ? mpc. estim (ym, d) : mpc. estim (d)
599- mpc. optim . Ŷ = mpc. Ẽ* ΔŨ + F
600- mpc. optim . ŷs, mpc. optim . Ŷs = ŷs, Ŷs
604+ function write_info ! (mpc:: LinMPC , ΔŨ, J, ŷs, Ŷs, lastu, F, ym, d)
605+ mpc. info . ΔŨ = ΔŨ
606+ mpc. info . ϵ = isinf (mpc. C) ? nothing : ΔŨ[end ]
607+ mpc. info . J = J
608+ mpc. info . U = mpc. S̃_Hp* ΔŨ + mpc. T_Hp* lastu
609+ mpc. info . u = mpc. info . U[1 : mpc. model. nu]
610+ mpc. info . ŷ = isa (mpc. estim, InternalModel) ? mpc. estim (ym, d) : mpc. estim (d)
611+ mpc. info . Ŷ = mpc. Ẽ* ΔŨ + F
612+ mpc. info . ŷs, mpc. info . Ŷs = ŷs, Ŷs
601613end
602614
603615" Repeat predictive controller constraints over prediction `Hp` and control `Hc` horizons."
0 commit comments