Skip to content

Commit 092cf30

Browse files
authored
Merge pull request #92 from JuliaControl/buffer_struct
added: `SimModelBuffer` and `StateEsimatorBuffer` objects to reduce the allocations
2 parents 3c225a0 + 0ac2a7c commit 092cf30

File tree

12 files changed

+156
-63
lines changed

12 files changed

+156
-63
lines changed

src/controller/execute.jl

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
@doc raw"""
2-
initstate!(mpc::PredictiveController, u, ym, d=mpc.estim.model.dop) -> x̂
2+
initstate!(mpc::PredictiveController, u, ym, d=[]) -> x̂
33
44
Init the states of `mpc.estim` [`StateEstimator`](@ref) and warm start `mpc.ΔŨ` at zero.
55
"""
6-
function initstate!(mpc::PredictiveController, u, ym, d=mpc.estim.model.dop)
6+
function initstate!(mpc::PredictiveController, u, ym, d=mpc.estim.buffer.empty)
77
mpc.ΔŨ .= 0
88
return initstate!(mpc.estim, u, ym, d)
99
end
1010

1111
@doc raw"""
12-
moveinput!(
13-
mpc::PredictiveController, ry=mpc.estim.model.yop, d=mpc.estim.model.dop;
14-
<keyword arguments>
15-
) -> u
12+
moveinput!(mpc::PredictiveController, ry=mpc.estim.model.yop, d=[]; <keyword args>) -> u
1613
1714
Compute the optimal manipulated input value `u` for the current control period.
1815
@@ -32,7 +29,7 @@ See also [`LinMPC`](@ref), [`ExplicitMPC`](@ref), [`NonLinMPC`](@ref).
3229
3330
- `mpc::PredictiveController` : solve optimization problem of `mpc`.
3431
- `ry=mpc.estim.model.yop` : current output setpoints ``\mathbf{r_y}(k)``.
35-
- `d=mpc.estim.model.dop` : current measured disturbances ``\mathbf{d}(k)``.
32+
- `d=[]` : current measured disturbances ``\mathbf{d}(k)``.
3633
- `D̂=repeat(d, mpc.Hp)` or *`Dhat`* : predicted measured disturbances ``\mathbf{D̂}``, constant
3734
in the future by default or ``\mathbf{d̂}(k+j)=\mathbf{d}(k)`` for ``j=1`` to ``H_p``.
3835
- `R̂y=repeat(ry, mpc.Hp)` or *`Rhaty`* : predicted output setpoints ``\mathbf{R̂_y}``, constant
@@ -54,7 +51,7 @@ julia> ry = [5]; u = moveinput!(mpc, ry); round.(u, digits=3)
5451
function moveinput!(
5552
mpc::PredictiveController,
5653
ry::Vector = mpc.estim.model.yop,
57-
d ::Vector = mpc.estim.model.dop;
54+
d ::Vector = mpc.estim.buffer.empty;
5855
Dhat ::Vector = repeat(d, mpc.Hp),
5956
Rhaty::Vector = repeat(ry, mpc.Hp),
6057
Rhatu::Vector = mpc.Uop,
@@ -508,11 +505,11 @@ end
508505
set_objective_linear_coef!(::PredictiveController, _ ) = nothing
509506

510507
"""
511-
updatestate!(mpc::PredictiveController, u, ym, d=mpc.estim.model.dop) -> x̂
508+
updatestate!(mpc::PredictiveController, u, ym, d=[]) -> x̂
512509
513510
Call [`updatestate!`](@ref) on `mpc.estim` [`StateEstimator`](@ref).
514511
"""
515-
function updatestate!(mpc::PredictiveController, u, ym, d=mpc.estim.model.dop)
512+
function updatestate!(mpc::PredictiveController, u, ym, d=mpc.estim.buffer.empty)
516513
return updatestate!(mpc.estim, u, ym, d)
517514
end
518515
updatestate!(::PredictiveController, _ ) = throw(ArgumentError("missing measured outputs ym"))

src/estimator/execute.jl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ Also store current inputs without operating points `u0` in `estim.lastu0`. This
77
used for [`PredictiveController`](@ref) computations.
88
"""
99
function remove_op!(estim::StateEstimator, u, ym, d)
10-
u0 = u - estim.model.uop
11-
ym0 = ym - estim.model.yop[estim.i_ym]
12-
d0 = d - estim.model.dop
13-
estim.lastu0[:] = u0
14-
return u0, ym0, d0
10+
u0, d0 = estim.buffer.u, estim.buffer.d
11+
y0m = estim.buffer.ym
12+
u0 .= u .- estim.model.uop
13+
y0m .= @views ym .- estim.model.yop[estim.i_ym]
14+
d0 .= d .- estim.model.dop
15+
estim.lastu0 .= u0
16+
return u0, y0m, d0
1517
end
1618

1719
@doc raw"""
@@ -79,7 +81,7 @@ end
7981

8082

8183
@doc raw"""
82-
initstate!(estim::StateEstimator, u, ym, d=estim.model.dop) -> x̂
84+
initstate!(estim::StateEstimator, u, ym, d=[]) -> x̂
8385
8486
Init `estim.x̂0` states from current inputs `u`, measured outputs `ym` and disturbances `d`.
8587
@@ -111,7 +113,7 @@ julia> evaloutput(estim) ≈ y
111113
true
112114
```
113115
"""
114-
function initstate!(estim::StateEstimator, u, ym, d=estim.model.dop)
116+
function initstate!(estim::StateEstimator, u, ym, d=estim.buffer.empty)
115117
# --- validate arguments ---
116118
validate_args(estim, u, ym, d)
117119
# --- init state estimate ----
@@ -150,6 +152,7 @@ measured outputs ``\mathbf{y^m}``.
150152
function init_estimate!(estim::StateEstimator, ::LinModel, u0, ym0, d0)
151153
Â, B̂u, Ĉ, B̂d, D̂d = estim.Â, estim.B̂u, estim.Ĉ, estim.B̂d, estim.D̂d
152154
Ĉm, D̂dm = @views Ĉ[estim.i_ym, :], D̂d[estim.i_ym, :] # measured outputs ym only
155+
# TODO: use estim.buffer.x̂ to reduce allocations
153156
estim.x̂0 .= [I - Â; Ĉm]\[B̂u*u0 + B̂d*d0 + estim.f̂op - estim.x̂op; ym0 - D̂dm*d0]
154157
return nothing
155158
end
@@ -161,7 +164,7 @@ Left `estim.x̂0` estimate unchanged if `model` is not a [`LinModel`](@ref).
161164
init_estimate!(::StateEstimator, ::SimModel, _ , _ , _ ) = nothing
162165

163166
@doc raw"""
164-
evaloutput(estim::StateEstimator, d=estim.model.dop) -> ŷ
167+
evaloutput(estim::StateEstimator, d=[]) -> ŷ
165168
166169
Evaluate `StateEstimator` outputs `ŷ` from `estim.x̂0` states and disturbances `d`.
167170
@@ -176,21 +179,21 @@ julia> ŷ = evaloutput(kf)
176179
20.0
177180
```
178181
"""
179-
function evaloutput(estim::StateEstimator{NT}, d=estim.model.dop) where NT <: Real
182+
function evaloutput(estim::StateEstimator{NT}, d=estim.buffer.empty) where NT <: Real
180183
validate_args(estim.model, d)
181-
ŷ0 = Vector{NT}(undef, estim.model.ny)
182-
d0 = d - estim.model.dop
184+
ŷ0, d0 = estim.buffer., estim.buffer.d
185+
d0 .= d .- estim.model.dop
183186
ĥ!(ŷ0, estim, estim.model, estim.x̂0, d0)
184187
= ŷ0
185188
.+= estim.model.yop
186189
return
187190
end
188191

189192
"Functor allowing callable `StateEstimator` object as an alias for `evaloutput`."
190-
(estim::StateEstimator)(d=estim.model.dop) = evaloutput(estim, d)
193+
(estim::StateEstimator)(d=estim.buffer.empty) = evaloutput(estim, d)
191194

192195
@doc raw"""
193-
updatestate!(estim::StateEstimator, u, ym, d=estim.model.dop) -> x̂
196+
updatestate!(estim::StateEstimator, u, ym, d=[]) -> x̂
194197
195198
Update `estim.x̂0` estimate with current inputs `u`, measured outputs `ym` and dist. `d`.
196199
@@ -207,10 +210,10 @@ julia> x̂ = updatestate!(kf, [1], [0]) # x̂[2] is the integrator state (nint_y
207210
0.0
208211
```
209212
"""
210-
function updatestate!(estim::StateEstimator, u, ym, d=estim.model.dop)
213+
function updatestate!(estim::StateEstimator, u, ym, d=estim.buffer.empty)
211214
validate_args(estim, u, ym, d)
212215
u0, ym0, d0 = remove_op!(estim, u, ym, d)
213-
x̂0next = update_estimate!(estim, u0, ym0, d0)
216+
x̂0next = update_estimate!(estim, u0, ym0, d0)
214217
x̂next = x̂0next
215218
x̂next .+= estim.x̂op
216219
return x̂next

src/estimator/internal_model.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,31 @@ struct InternalModel{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
2222
D̂d::Matrix{NT}
2323
Âs::Matrix{NT}
2424
B̂s::Matrix{NT}
25+
buffer::StateEstimatorBuffer{NT}
2526
function InternalModel{NT, SM}(
2627
model::SM, i_ym, Asm, Bsm, Csm, Dsm
2728
) where {NT<:Real, SM<:SimModel}
29+
nu, ny, nd = model.nu, model.ny, model.nd
2830
nym, nyu = validate_ym(model, i_ym)
2931
validate_internalmodel(model, nym, Csm, Dsm)
3032
As, Bs, Cs, Ds = stoch_ym2y(model, i_ym, Asm, Bsm, Csm, Dsm)
3133
nxs = size(As,1)
3234
nx̂ = model.nx
3335
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = matrices_internalmodel(model)
3436
Âs, B̂s = init_internalmodel(As, Bs, Cs, Ds)
35-
lastu0 = zeros(NT, model.nu)
37+
lastu0 = zeros(NT, nu)
3638
# x̂0 and x̂d are same object (updating x̂d will update x̂0):
3739
x̂d = x̂0 = zeros(NT, model.nx)
3840
x̂s = zeros(NT, nxs)
41+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
3942
return new{NT, SM}(
4043
model,
4144
lastu0, x̂op, f̂op, x̂0, x̂d, x̂s,
4245
i_ym, nx̂, nym, nyu, nxs,
4346
As, Bs, Cs, Ds,
4447
Â, B̂u, Ĉ, B̂d, D̂d,
45-
Âs, B̂s
48+
Âs, B̂s,
49+
buffer
4650
)
4751
end
4852
end

src/estimator/kalman.jl

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
2222
::Hermitian{NT, Matrix{NT}}
2323
::Hermitian{NT, Matrix{NT}}
2424
::Matrix{NT}
25+
buffer::StateEstimatorBuffer{NT}
2526
function SteadyKalmanFilter{NT, SM}(
2627
model::SM, i_ym, nint_u, nint_ym, Q̂, R̂
2728
) where {NT<:Real, SM<:LinModel}
29+
nu, ny, nd = model.nu, model.ny, model.nd
2830
nym, nyu = validate_ym(model, i_ym)
2931
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
3032
nxs = size(As, 1)
@@ -33,7 +35,7 @@ struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
3335
validate_kfcov(nym, nx̂, Q̂, R̂)
3436
= try
3537
Q̂_kalman = Matrix(Q̂) # Matrix() required for Julia 1.6
36-
R̂_kalman = zeros(NT, model.ny, model.ny)
38+
R̂_kalman = zeros(NT, ny, ny)
3739
R̂_kalman[i_ym, i_ym] =
3840
ControlSystemsBase.kalman(Discrete, Â, Ĉ, Q̂_kalman, R̂_kalman)[:, i_ym]
3941
catch my_error
@@ -45,17 +47,19 @@ struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
4547
rethrow()
4648
end
4749
end
48-
lastu0 = zeros(NT, model.nu)
50+
lastu0 = zeros(NT, nu)
4951
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
5052
Q̂, R̂ = Hermitian(Q̂, :L), Hermitian(R̂, :L)
53+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
5154
return new{NT, SM}(
5255
model,
5356
lastu0, x̂op, f̂op, x̂0,
5457
i_ym, nx̂, nym, nyu, nxs,
5558
As, Cs_u, Cs_y, nint_u, nint_ym,
5659
Â, B̂u, Ĉ, B̂d, D̂d,
5760
Q̂, R̂,
58-
61+
K̂,
62+
buffer
5963
)
6064
end
6165
end
@@ -234,29 +238,33 @@ struct KalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
234238
::Hermitian{NT, Matrix{NT}}
235239
::Matrix{NT}
236240
::Matrix{NT}
241+
buffer::StateEstimatorBuffer{NT}
237242
function KalmanFilter{NT, SM}(
238243
model::SM, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂
239244
) where {NT<:Real, SM<:LinModel}
245+
nu, ny, nd = model.nu, model.ny, model.nd
240246
nym, nyu = validate_ym(model, i_ym)
241247
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
242248
nxs = size(As, 1)
243249
nx̂ = model.nx + nxs
244250
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
245251
validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0)
246-
lastu0 = zeros(NT, model.nu)
252+
lastu0 = zeros(NT, nu)
247253
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
248254
Q̂, R̂ = Hermitian(Q̂, :L), Hermitian(R̂, :L)
249255
P̂_0 = Hermitian(P̂_0, :L)
250256
= copy(P̂_0)
251257
K̂, M̂ = zeros(NT, nx̂, nym), zeros(NT, nx̂, nym)
258+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
252259
return new{NT, SM}(
253260
model,
254261
lastu0, x̂op, f̂op, x̂0, P̂,
255262
i_ym, nx̂, nym, nyu, nxs,
256263
As, Cs_u, Cs_y, nint_u, nint_ym,
257264
Â, B̂u, Ĉ, B̂d, D̂d,
258265
P̂_0, Q̂, R̂,
259-
K̂, M̂
266+
K̂, M̂,
267+
buffer
260268
)
261269
end
262270
end
@@ -402,17 +410,19 @@ struct UnscentedKalmanFilter{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
402410
γ::NT
403411
::Vector{NT}
404412
::Diagonal{NT, Vector{NT}}
413+
buffer::StateEstimatorBuffer{NT}
405414
function UnscentedKalmanFilter{NT, SM}(
406415
model::SM, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂, α, β, κ
407416
) where {NT<:Real, SM<:SimModel{NT}}
417+
nu, ny, nd = model.nu, model.ny, model.nd
408418
nym, nyu = validate_ym(model, i_ym)
409419
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
410420
nxs = size(As, 1)
411421
nx̂ = model.nx + nxs
412422
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
413423
validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0)
414424
nσ, γ, m̂, Ŝ = init_ukf(model, nx̂, α, β, κ)
415-
lastu0 = zeros(NT, model.nu)
425+
lastu0 = zeros(NT, nu)
416426
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
417427
Q̂, R̂ = Hermitian(Q̂, :L), Hermitian(R̂, :L)
418428
P̂_0 = Hermitian(P̂_0, :L)
@@ -421,6 +431,7 @@ struct UnscentedKalmanFilter{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
421431
= Hermitian(zeros(NT, nym, nym), :L)
422432
X̂0, Ŷ0m = zeros(NT, nx̂, nσ), zeros(NT, nym, nσ)
423433
sqrtP̂ = LowerTriangular(zeros(NT, nx̂, nx̂))
434+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
424435
return new{NT, SM}(
425436
model,
426437
lastu0, x̂op, f̂op, x̂0, P̂,
@@ -429,7 +440,8 @@ struct UnscentedKalmanFilter{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
429440
Â, B̂u, Ĉ, B̂d, D̂d,
430441
P̂_0, Q̂, R̂,
431442
K̂, M̂, X̂0, Ŷ0m, sqrtP̂,
432-
nσ, γ, m̂, Ŝ
443+
nσ, γ, m̂, Ŝ,
444+
buffer
433445
)
434446
end
435447
end
@@ -698,23 +710,26 @@ struct ExtendedKalmanFilter{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
698710
::Matrix{NT}
699711
F̂_û::Matrix{NT}
700712
::Matrix{NT}
713+
buffer::StateEstimatorBuffer{NT}
701714
function ExtendedKalmanFilter{NT, SM}(
702715
model::SM, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂
703716
) where {NT<:Real, SM<:SimModel}
717+
nu, ny, nd = model.nu, model.ny, model.nd
704718
nym, nyu = validate_ym(model, i_ym)
705719
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
706720
nxs = size(As, 1)
707721
nx̂ = model.nx + nxs
708722
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
709723
validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0)
710-
lastu0 = zeros(NT, model.nu)
724+
lastu0 = zeros(NT, nu)
711725
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
712726
P̂_0 = Hermitian(P̂_0, :L)
713727
= Hermitian(Q̂, :L)
714728
= Hermitian(R̂, :L)
715729
= copy(P̂_0)
716730
K̂, M̂ = zeros(NT, nx̂, nym), zeros(NT, nx̂, nym)
717-
F̂_û, Ĥ = zeros(NT, nx̂+model.nu, nx̂), zeros(NT, model.ny, nx̂)
731+
F̂_û, Ĥ = zeros(NT, nx̂+nu, nx̂), zeros(NT, ny, nx̂)
732+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
718733
return new{NT, SM}(
719734
model,
720735
lastu0, x̂op, f̂op, x̂0, P̂,
@@ -723,7 +738,8 @@ struct ExtendedKalmanFilter{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
723738
Â, B̂u, Ĉ, B̂d, D̂d,
724739
P̂_0, Q̂, R̂,
725740
K̂, M̂,
726-
F̂_û, Ĥ
741+
F̂_û, Ĥ,
742+
buffer
727743
)
728744
end
729745
end

src/estimator/luenberger.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ struct Luenberger{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
2020
B̂d ::Matrix{NT}
2121
D̂d ::Matrix{NT}
2222
::Matrix{NT}
23+
buffer::StateEstimatorBuffer{NT}
2324
function Luenberger{NT, SM}(
2425
model, i_ym, nint_u, nint_ym, poles
2526
) where {NT<:Real, SM<:LinModel}
27+
nu, ny, nd = model.nu, model.ny, model.nd
2628
nym, nyu = validate_ym(model, i_ym)
2729
validate_luenberger(model, nint_u, nint_ym, poles)
2830
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
@@ -34,15 +36,17 @@ struct Luenberger{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
3436
catch
3537
error("Cannot compute the Luenberger gain K̂ with specified poles.")
3638
end
37-
lastu0 = zeros(NT, model.nu)
39+
lastu0 = zeros(NT, nu)
3840
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
41+
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd)
3942
return new{NT, SM}(
4043
model,
4144
lastu0, x̂op, f̂op, x̂0,
4245
i_ym, nx̂, nym, nyu, nxs,
4346
As, Cs_u, Cs_y, nint_u, nint_ym,
4447
Â, B̂u, Ĉ, B̂d, D̂d,
45-
48+
K̂,
49+
buffer
4650
)
4751
end
4852
end

0 commit comments

Comments
 (0)