1+ struct ExplicitMPC{S<: StateEstimator } <: PredictiveController
2+ estim:: S
3+ ΔŨ:: Vector{Float64}
4+ x̂d:: Vector{Float64}
5+ x̂s:: Vector{Float64}
6+ ŷ :: Vector{Float64}
7+ Ŷs:: Vector{Float64}
8+ Hp:: Int
9+ Hc:: Int
10+ M_Hp:: Diagonal{Float64, Vector{Float64}}
11+ Ñ_Hc:: Diagonal{Float64, Vector{Float64}}
12+ L_Hp:: Diagonal{Float64, Vector{Float64}}
13+ R̂u:: Vector{Float64}
14+ R̂y:: Vector{Float64}
15+ S̃_Hp:: Matrix{Bool}
16+ T_Hp:: Matrix{Bool}
17+ T_Hc:: Matrix{Bool}
18+ Ẽ :: Matrix{Float64}
19+ F :: Vector{Float64}
20+ G :: Matrix{Float64}
21+ J :: Matrix{Float64}
22+ Kd:: Matrix{Float64}
23+ Q :: Matrix{Float64}
24+ P̃ :: Hermitian{Float64, Matrix{Float64}}
25+ q̃ :: Vector{Float64}
26+ p :: Vector{Float64}
27+ Ks:: Matrix{Float64}
28+ Ps:: Matrix{Float64}
29+ d:: Vector{Float64}
30+ D̂:: Vector{Float64}
31+ Yop:: Vector{Float64}
32+ Dop:: Vector{Float64}
33+ function ExplicitMPC {S} (estim:: S , Hp, Hc, Mwt, Nwt, Lwt, ru) where {S<: StateEstimator }
34+ model = estim. model
35+ nu, nxd, nxs, ny, nd = model. nu, model. nx, estim. nxs, model. ny, model. nd
36+ x̂d, x̂s, ŷ, Ŷs = zeros (nxd), zeros (nxs), zeros (ny), zeros (ny* Hp)
37+ validate_weights (model, Hp, Hc, Mwt, Nwt, Lwt, Inf , ru)
38+ M_Hp = Diagonal {Float64} (repeat (Mwt, Hp))
39+ N_Hc = Diagonal {Float64} (repeat (Nwt, Hc))
40+ L_Hp = Diagonal {Float64} (repeat (Lwt, Hp))
41+ # manipulated input setpoint predictions are constant over Hp :
42+ R̂u = ~ iszero (Lwt) ? repeat (ru, Hp) : R̂u = Float64[]
43+ R̂y = zeros (ny* Hp) # dummy R̂y (updated just before optimization)
44+ S_Hp, T_Hp, S_Hc, T_Hc = init_ΔUtoU (nu, Hp, Hc)
45+ E, F, G, J, Kd, Q = init_deterpred (model, Hp, Hc)
46+ _ , S̃_Hp, Ñ_Hc, Ẽ = init_defaultcon (model, Hp, Hc, Inf , S_Hp, S_Hc, N_Hc, E)
47+ P̃, q̃, p = init_quadprog (model, Ẽ, S̃_Hp, M_Hp, Ñ_Hc, L_Hp)
48+ Ks, Ps = init_stochpred (estim, Hp)
49+ d, D̂ = zeros (nd), zeros (nd* Hp)
50+ Yop, Dop = repeat (model. yop, Hp), repeat (model. dop, Hp)
51+ nvar = size (Ẽ, 2 )
52+ ΔŨ = zeros (nvar)
53+ mpc = new (
54+ estim,
55+ ΔŨ, x̂d, x̂s, ŷ, Ŷs,
56+ Hp, Hc,
57+ M_Hp, Ñ_Hc, L_Hp, R̂u, R̂y,
58+ S̃_Hp, T_Hp, T_Hc,
59+ Ẽ, F, G, J, Kd, Q, P̃, q̃, p,
60+ Ks, Ps,
61+ d, D̂,
62+ Yop, Dop,
63+ )
64+ return mpc
65+ end
66+ end
67+
68+ @doc raw """
69+ ExplicitMPC(model::LinModel; <keyword arguments>)
70+
71+ Construct an explicit linear predictive controller based on [`LinModel`](@ref) `model`.
72+
73+ The controller minimizes the following objective function at each discrete time ``k``:
74+ ```math
75+ \m in_{\m athbf{ΔU}} \m athbf{(R̂_y - Ŷ)}' \m athbf{M}_{H_p} \m athbf{(R̂_y - Ŷ)}
76+ + \m athbf{(ΔU)}' \m athbf{N}_{H_c} \m athbf{(ΔU)}
77+ + \m athbf{(R̂_u - U)}' \m athbf{L}_{H_p} \m athbf{(R̂_u - U)}
78+ ```
79+
80+ This method uses the default state estimator, a [`SteadyKalmanFilter`](@ref) with default
81+ arguments.
82+
83+ # Arguments
84+ - `model::LinModel` : model used for controller predictions and state estimations.
85+ - `Hp=10+nk`: prediction horizon ``H_p``, `nk` is the number of delays in `model`.
86+ - `Hc=2` : control horizon ``H_c``.
87+ - `Mwt=fill(1.0,model.ny)` : main diagonal of ``\m athbf{M}`` weight matrix (vector).
88+ - `Nwt=fill(0.1,model.nu)` : main diagonal of ``\m athbf{N}`` weight matrix (vector).
89+ - `Lwt=fill(0.0,model.nu)` : main diagonal of ``\m athbf{L}`` weight matrix (vector).
90+ - `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
91+ - `ru=model.uop` : manipulated input setpoints ``\m athbf{r_u}`` (vector).
92+
93+ # Examples
94+ ```jldoctest
95+ julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4);
96+
97+ julia> mpc = ExplicitMPC(model, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
98+ ExplicitMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SteadyKalmanFilter estimator and:
99+ 30 prediction steps Hp
100+ 1 control steps Hc
101+ 1 manipulated inputs u
102+ 4 states x̂
103+ 2 measured outputs ym
104+ 0 unmeasured outputs yu
105+ 0 measured disturbances d
106+ ```
107+
108+ """
109+ ExplicitMPC (model:: LinModel ; kwargs... ) = ExplicitMPC (SteadyKalmanFilter (model); kwargs... )
110+
111+ """
112+ ExplicitMPC(estim::StateEstimator; <keyword arguments>)
113+
114+ Use custom state estimator `estim` to construct `ExplicitMPC`.
115+
116+ `estim.model` must be a [`LinModel`](@ref). Else, a [`NonLinMPC`](@ref) is required.
117+
118+ # Examples
119+ ```jldoctest
120+ julia> estim = KalmanFilter(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4), i_ym=[2]);
121+
122+ julia> mpc = ExplicitMPC(estim, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
123+ ExplicitMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, KalmanFilter estimator and:
124+ 30 prediction steps Hp
125+ 1 control steps Hc
126+ 1 manipulated inputs u
127+ 3 states x̂
128+ 1 measured outputs ym
129+ 1 unmeasured outputs yu
130+ 0 measured disturbances d
131+ ```
132+ """
133+ function ExplicitMPC (
134+ estim:: S ;
135+ Hp:: Union{Int, Nothing} = nothing ,
136+ Hc:: Int = 2 ,
137+ Mwt = fill (1.0 , estim. model. ny),
138+ Nwt = fill (0.1 , estim. model. nu),
139+ Lwt = fill (0.0 , estim. model. nu),
140+ ru = estim. model. uop,
141+ ) where {S<: StateEstimator }
142+ isa (estim. model, LinModel) || error (" estim.model type must be LinModel" )
143+ poles = eigvals (estim. model. A)
144+ nk = sum (poles .≈ 0 )
145+ if isnothing (Hp)
146+ Hp = 10 + nk
147+ end
148+ if Hp ≤ nk
149+ @warn (" prediction horizon Hp ($Hp ) ≤ number of delays in model " *
150+ " ($nk ), the closed-loop system may be zero-gain (unresponsive) or unstable" )
151+ end
152+ return ExplicitMPC {S} (estim, Hp, Hc, Mwt, Nwt, Lwt, ru)
153+ end
154+
155+ function Base. show (io:: IO , mpc:: ExplicitMPC )
156+ Hp, Hc = mpc. Hp, mpc. Hc
157+ nu, nd = mpc. estim. model. nu, mpc. estim. model. nd
158+ nx̂, nym, nyu = mpc. estim. nx̂, mpc. estim. nym, mpc. estim. nyu
159+ n = maximum (ndigits .((Hp, Hc, nu, nx̂, nym, nyu, nd))) + 1
160+ println (io, " $(typeof (mpc). name. name) controller with a sample time Ts = " *
161+ " $(mpc. estim. model. Ts) s, " *
162+ " $(typeof (mpc. estim). name. name) estimator and:" )
163+ println (io, " $(lpad (Hp, n)) prediction steps Hp" )
164+ println (io, " $(lpad (Hc, n)) control steps Hc" )
165+ print_estim_dim (io, mpc. estim, n)
166+ end
167+
168+ linconstraint! (:: ExplicitMPC , :: LinModel ) = nothing
169+
170+ """
171+ Analytically solve the optimization problem for [`ExplicitMPC`](@ref).
172+ """
173+ function optim_objective! (mpc:: ExplicitMPC )
174+ mpc. ΔŨ[:] = - mpc. P̃\ mpc. q̃
175+ return mpc. ΔŨ
176+ end
0 commit comments