Skip to content

Commit 2555bfb

Browse files
committed
wip: new KalmanCovariance similar to ControllerWeights
1 parent 61de78d commit 2555bfb

File tree

2 files changed

+101
-51
lines changed

2 files changed

+101
-51
lines changed

src/estimator/construct.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,73 @@ function StateEstimatorBuffer{NT}(
3838
return StateEstimatorBuffer{NT}(u, û, k, x̂, P̂, Q̂, R̂, K̂, ym, ŷ, d, empty)
3939
end
4040

41+
"Include all the covariance matrices for the Kalman filters and moving horizon estimator."
42+
struct KalmanCovariances{
43+
NT<:Real,
44+
# parameters to support both dense and Diagonal matrices (with specialization):
45+
Q̂C<:AbstractMatrix{NT},
46+
R̂C<:AbstractMatrix{NT},
47+
}
48+
P̂_0::Hermitian{NT, Matrix{NT}}
49+
::Hermitian{NT, Matrix{NT}}
50+
::Hermitian{NT, Q̂C}
51+
::Hermitian{NT, R̂C}
52+
invP̄::Hermitian{NT, Matrix{NT}}
53+
invQ̂_He::Hermitian{NT, Q̂C}
54+
invR̂_He::Hermitian{NT, R̂C}
55+
function KalmanCovariances{NT}(
56+
model, i_ym, nint_u, nint_ym, Q̂::Q̂C, R̂::R̂C, P̂_0=nothing, He=1
57+
) where {NT<:Real, Q̂C<:AbstractMatrix{NT}, R̂C<:AbstractMatrix{NT}}
58+
validate_kfcov(model, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0)
59+
if isnothing(P̂_0)
60+
P̂_0 = zeros(NT, 0, 0)
61+
end
62+
P̂_0 = Hermitian(P̂_0, :L)
63+
= copy(P̂_0)
64+
= Hermitian(Q̂, :L)
65+
= Hermitian(R̂, :L)
66+
# the following variables are only for the moving horizon estimator:
67+
invP̄, invQ̂, invR̂ = copy(P̂_0), copy(Q̂), copy(R̂)
68+
inv!(invP̄)
69+
inv!(invQ̂)
70+
inv!(invR̂)
71+
invQ̂_He = repeatdiag(invQ̂, He)
72+
invR̂_He = repeatdiag(invR̂, He)
73+
isdiag(invQ̂_He) && (invQ̂_He = Diagonal(invQ̂_He)) # Q̂C(invQ̂_He) does not work on Julia 1.10
74+
isdiag(invR̂_He) && (invR̂_He = Diagonal(invR̂_He)) # R̂C(invR̂_He) does not work on Julia 1.10
75+
invQ̂_He = Hermitian(invQ̂_He, :L)
76+
invR̂_He = Hermitian(invR̂_He, :L)
77+
return new{NT, Q̂C, R̂C}(P̂_0, P̂, Q̂, R̂, invP̄, invQ̂_He, invR̂_He)
78+
end
79+
end
80+
81+
"Outer constructor to convert covariance matrix number type to `NT` if necessary."
82+
function KalmanCovariances(
83+
model::SimModel{NT}, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0=nothing, He=1
84+
) where {NT<:Real}
85+
return KalmanCovariances{NT}(model, i_ym, nint_u, nint_ym, NT.(Q̂), NT.(R̂), P̂_0, He)
86+
end
87+
88+
"""
89+
validate_kfcov(model, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0=nothing)
90+
91+
Validate sizes and Hermitianity of process `Q̂`` and sensor `R̂` noises covariance matrices.
92+
93+
Also validate initial estimate covariance `P̂_0`, if provided.
94+
"""
95+
function validate_kfcov(model, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0=nothing)
96+
nym = length(i_ym)
97+
nx̂ = model.nx + sum(nint_u) + sum(nint_ym)
98+
size(Q̂) (nx̂, nx̂) && error("Q̂ size $(size(Q̂)) ≠ nx̂, nx̂ $((nx̂, nx̂))")
99+
!ishermitian(Q̂) && error("Q̂ is not Hermitian")
100+
size(R̂) (nym, nym) && error("R̂ size $(size(R̂)) ≠ nym, nym $((nym, nym))")
101+
!ishermitian(R̂) && error("R̂ is not Hermitian")
102+
if ~isnothing(P̂_0)
103+
size(P̂_0) (nx̂, nx̂) && error("P̂_0 size $(size(P̂_0)) ≠ nx̂, nx̂ $((nx̂, nx̂))")
104+
!ishermitian(P̂_0) && error("P̂_0 is not Hermitian")
105+
end
106+
end
107+
41108
@doc raw"""
42109
init_estimstoch(model, i_ym, nint_u, nint_ym) -> As, Cs_u, Cs_y, nxs, nint_u, nint_ym
43110

src/estimator/kalman.jl

Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
1+
struct SteadyKalmanFilter{
2+
NT<:Real,
3+
SM<:LinModel,
4+
KC<:KalmanCovariances
5+
} <: StateEstimator{NT}
26
model::SM
7+
cov ::KC
38
x̂op ::Vector{NT}
49
f̂op ::Vector{NT}
510
x̂0 ::Vector{NT}
@@ -20,23 +25,21 @@ struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
2025
D̂d ::Matrix{NT}
2126
Ĉm ::Matrix{NT}
2227
D̂dm ::Matrix{NT}
23-
::Hermitian{NT, Matrix{NT}}
24-
::Hermitian{NT, Matrix{NT}}
2528
::Matrix{NT}
2629
direct::Bool
2730
corrected::Vector{Bool}
2831
buffer::StateEstimatorBuffer{NT}
2932
function SteadyKalmanFilter{NT}(
30-
model::SM, i_ym, nint_u, nint_ym, Q̂, R̂; direct=true
31-
) where {NT<:Real, SM<:LinModel}
33+
model::SM, i_ym, nint_u, nint_ym, cov::KC; direct=true
34+
) where {NT<:Real, SM<:LinModel, KC<:KalmanCovariances}
3235
nu, ny, nd, nk = model.nu, model.ny, model.nd, model.nk
3336
nym, nyu = validate_ym(model, i_ym)
3437
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
3538
nxs = size(As, 1)
3639
nx̂ = model.nx + nxs
3740
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
3841
Ĉm, D̂dm = Ĉ[i_ym, :], D̂d[i_ym, :]
39-
validate_kfcov(nym, nx̂, Q̂, R̂)
42+
R̂, Q̂ = cov.R̂, cov.
4043
if ny == nym
4144
R̂_y =
4245
else
@@ -56,16 +59,15 @@ struct SteadyKalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
5659
end
5760
end
5861
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
59-
Q̂, R̂ = Hermitian(Q̂, :L), Hermitian(R̂, :L)
6062
corrected = [false]
6163
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd, nk)
62-
return new{NT, SM}(
63-
model,
64+
return new{NT, SM, KC}(
65+
model,
66+
cov,
6467
x̂op, f̂op, x̂0,
6568
i_ym, nx̂, nym, nyu, nxs,
6669
As, Cs_u, Cs_y, nint_u, nint_ym,
6770
Â, B̂u, Ĉ, B̂d, D̂d, Ĉm, D̂dm,
68-
Q̂, R̂,
6971
K̂,
7072
direct, corrected,
7173
buffer
@@ -184,9 +186,9 @@ function SteadyKalmanFilter(
184186
σQint_ym = sigmaQint_ym,
185187
) where {NT<:Real, SM<:LinModel{NT}}
186188
# estimated covariances matrices (variance = σ²) :
187-
= Hermitian(diagm(NT[σQ; σQint_u; σQint_ym ].^2), :L)
188-
= Hermitian(diagm(NT[σR;].^2), :L)
189-
return SteadyKalmanFilter{NT}(model, i_ym, nint_u, nint_ym, Q̂, R̂; direct)
189+
= Diagonal([σQ; σQint_u; σQint_ym].^2)
190+
= Diagonal([σR;].^2)
191+
return SteadyKalmanFilter(model, i_ym, nint_u, nint_ym, Q̂, R̂; direct)
190192
end
191193

192194
@doc raw"""
@@ -200,7 +202,8 @@ function SteadyKalmanFilter(
200202
model::SM, i_ym, nint_u, nint_ym, Q̂, R̂; direct=true
201203
) where {NT<:Real, SM<:LinModel{NT}}
202204
Q̂, R̂ = to_mat(Q̂), to_mat(R̂)
203-
return SteadyKalmanFilter{NT}(model, i_ym, nint_u, nint_ym, Q̂, R̂; direct)
205+
cov = KalmanCovariances(model, i_ym, nint_u, nint_ym, Q̂, R̂)
206+
return SteadyKalmanFilter{NT}(model, i_ym, nint_u, nint_ym, cov; direct)
204207
end
205208

206209
"Throw an error if `setmodel!` is called on a SteadyKalmanFilter w/o the default values."
@@ -281,12 +284,16 @@ function predict_estimate_obsv!(estim::StateEstimator, _ , d0, u0)
281284
return nothing
282285
end
283286

284-
struct KalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
287+
struct KalmanFilter{
288+
NT<:Real,
289+
SM<:LinModel,
290+
KC<:KalmanCovariances
291+
} <: StateEstimator{NT}
285292
model::SM
293+
cov ::KC
286294
x̂op::Vector{NT}
287295
f̂op::Vector{NT}
288296
x̂0 ::Vector{NT}
289-
::Hermitian{NT, Matrix{NT}}
290297
i_ym::Vector{Int}
291298
nx̂ ::Int
292299
nym::Int
@@ -304,38 +311,31 @@ struct KalmanFilter{NT<:Real, SM<:LinModel} <: StateEstimator{NT}
304311
D̂d ::Matrix{NT}
305312
Ĉm ::Matrix{NT}
306313
D̂dm ::Matrix{NT}
307-
P̂_0::Hermitian{NT, Matrix{NT}}
308-
::Hermitian{NT, Matrix{NT}}
309-
::Hermitian{NT, Matrix{NT}}
310314
::Matrix{NT}
311315
direct::Bool
312316
corrected::Vector{Bool}
313317
buffer::StateEstimatorBuffer{NT}
314318
function KalmanFilter{NT}(
315-
model::SM, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct=true
316-
) where {NT<:Real, SM<:LinModel}
319+
model::SM, i_ym, nint_u, nint_ym, cov::KC; direct=true
320+
) where {NT<:Real, SM<:LinModel, KC<:KalmanCovariances}
317321
nu, ny, nd, nk = model.nu, model.ny, model.nd, model.nk
318322
nym, nyu = validate_ym(model, i_ym)
319323
As, Cs_u, Cs_y, nint_u, nint_ym = init_estimstoch(model, i_ym, nint_u, nint_ym)
320324
nxs = size(As, 1)
321325
nx̂ = model.nx + nxs
322326
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = augment_model(model, As, Cs_u, Cs_y)
323327
Ĉm, D̂dm = Ĉ[i_ym, :], D̂d[i_ym, :]
324-
validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0)
325328
x̂0 = [zeros(NT, model.nx); zeros(NT, nxs)]
326-
Q̂, R̂ = Hermitian(Q̂, :L), Hermitian(R̂, :L)
327-
P̂_0 = Hermitian(P̂_0, :L)
328-
= Hermitian(copy(P̂_0.data), :L) # copy on P̂_0.data necessary for Julia Nightly
329329
= zeros(NT, nx̂, nym)
330330
corrected = [false]
331331
buffer = StateEstimatorBuffer{NT}(nu, nx̂, nym, ny, nd, nk)
332-
return new{NT, SM}(
332+
return new{NT, SM, KC}(
333333
model,
334-
x̂op, f̂op, x̂0, P̂,
334+
cov,
335+
x̂op, f̂op, x̂0,
335336
i_ym, nx̂, nym, nyu, nxs,
336337
As, Cs_u, Cs_y, nint_u, nint_ym,
337338
Â, B̂u, Ĉ, B̂d, D̂d, Ĉm, D̂dm,
338-
P̂_0, Q̂, R̂,
339339
K̂,
340340
direct, corrected,
341341
buffer
@@ -419,10 +419,10 @@ function KalmanFilter(
419419
σQint_ym = sigmaQint_ym,
420420
) where {NT<:Real, SM<:LinModel{NT}}
421421
# estimated covariances matrices (variance = σ²) :
422-
P̂_0 = Hermitian(diagm(NT[σP_0; σPint_u_0; σPint_ym_0].^2), :L)
423-
= Hermitian(diagm(NT[σQ; σQint_u; σQint_ym ].^2), :L)
424-
= Hermitian(diagm(NT[σR;].^2), :L)
425-
return KalmanFilter{NT}(model, i_ym, nint_u, nint_ym, P̂_0, Q̂ , R̂; direct)
422+
P̂_0 = Diagonal([σP_0; σPint_u_0; σPint_ym_0].^2)
423+
= Diagonal([σQ; σQint_u; σQint_ym ].^2)
424+
= Diagonal([σR;].^2)
425+
return KalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
426426
end
427427

428428
@doc raw"""
@@ -436,7 +436,8 @@ function KalmanFilter(
436436
model::SM, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct=true
437437
) where {NT<:Real, SM<:LinModel{NT}}
438438
P̂_0, Q̂, R̂ = to_mat(P̂_0), to_mat(Q̂), to_mat(R̂)
439-
return KalmanFilter{NT}(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
439+
cov = KalmanCovariances(model, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0)
440+
return KalmanFilter{NT}(model, i_ym, nint_u, nint_ym, cov; direct)
440441
end
441442

442443
@doc raw"""
@@ -1177,24 +1178,6 @@ function init_estimate_cov!(
11771178
return nothing
11781179
end
11791180

1180-
"""
1181-
validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0=nothing)
1182-
1183-
Validate sizes and Hermitianity of process `Q̂`` and sensor `R̂` noises covariance matrices.
1184-
1185-
Also validate initial estimate covariance `P̂_0`, if provided.
1186-
"""
1187-
function validate_kfcov(nym, nx̂, Q̂, R̂, P̂_0=nothing)
1188-
size(Q̂) (nx̂, nx̂) && error("Q̂ size $(size(Q̂)) ≠ nx̂, nx̂ $((nx̂, nx̂))")
1189-
!ishermitian(Q̂) && error("Q̂ is not Hermitian")
1190-
size(R̂) (nym, nym) && error("R̂ size $(size(R̂)) ≠ nym, nym $((nym, nym))")
1191-
!ishermitian(R̂) && error("R̂ is not Hermitian")
1192-
if ~isnothing(P̂_0)
1193-
size(P̂_0) (nx̂, nx̂) && error("P̂_0 size $(size(P̂_0)) ≠ nx̂, nx̂ $((nx̂, nx̂))")
1194-
!ishermitian(P̂_0) && error("P̂_0 is not Hermitian")
1195-
end
1196-
end
1197-
11981181
"""
11991182
correct_estimate_kf!(estim::Union{KalmanFilter, ExtendedKalmanFilter}, y0m, d0, Ĉm)
12001183

0 commit comments

Comments
 (0)