@@ -148,8 +148,8 @@ function SteadyKalmanFilter(
148148 σQint_ym:: Vector = fill (1 , max (sum (nint_ym), 0 ))
149149) where {NT<: Real , SM<: LinModel{NT} }
150150 # estimated covariances matrices (variance = σ²) :
151- Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 );
152- R̂ = Diagonal {NT} (σR.^ 2 );
151+ Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 )
152+ R̂ = Diagonal {NT} (σR.^ 2 )
153153 return SteadyKalmanFilter {NT, SM} (model, i_ym, nint_u, nint_ym, Q̂ , R̂)
154154end
155155
@@ -181,7 +181,7 @@ function update_estimate!(estim::SteadyKalmanFilter, u, ym, d=empty(estim.x̂))
181181 Â, B̂u, B̂d, Ĉm, D̂dm = estim. Â, estim. B̂u, estim. B̂d, estim. Ĉm, estim. D̂dm
182182 x̂, K̂ = estim. x̂, estim. K̂
183183 x̂[:] = Â* x̂ + B̂u* u + B̂d* d + K̂* (ym - Ĉm* x̂ - D̂dm* d)
184- return x̂
184+ return nothing
185185end
186186
187187struct KalmanFilter{NT<: Real , SM<: LinModel } <: StateEstimator{NT}
@@ -253,13 +253,13 @@ its initial value with ``\mathbf{P̂}_{-1}(0) =
253253
254254# Arguments
255255- `model::LinModel` : (deterministic) model for the estimations.
256- - `σP0=fill(1/model.nx,model.nx)` : main diagonal of the initial estimate covariance
257- ``\m athbf{P}(0)``, specified as a standard deviation vector.
258- - `σP0int_u=fill(1,sum(nint_u))` : same than `σP0` but for the unmeasured disturbances at
259- manipulated inputs ``\m athbf{P_{int_u}}(0)`` (composed of integrators).
260- - `σP0int_ym=fill(1,sum(nint_ym))` : same than `σP0` but for the unmeasured disturbances at
261- measured outputs ``\m athbf{P_{int_{ym}}}(0)`` (composed of integrators).
262256- `<keyword arguments>` of [`SteadyKalmanFilter`](@ref) constructor.
257+ - `σP0=σQ` : main diagonal of the initial estimate covariance ``\m athbf{P}(0)``, specified
258+ as a standard deviation vector.
259+ - `σP0int_u=σQint_u` : same than `σP0` but for the unmeasured disturbances at manipulated
260+ inputs ``\m athbf{P_{int_u}}(0)`` (composed of integrators).
261+ - `σP0int_ym=σQint_ym` : same than `σP0` but for the unmeasured disturbances at measured
262+ outputs ``\m athbf{P_{int_{ym}}}(0)`` (composed of integrators).
263263
264264# Examples
265265```jldoctest
@@ -277,20 +277,20 @@ KalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
277277function KalmanFilter (
278278 model:: SM ;
279279 i_ym:: IntRangeOrVector = 1 : model. ny,
280- σP0 :: Vector = fill (1 / model. nx, model. nx),
281- σQ :: Vector = fill (1 / model . nx, model . nx ),
282- σR :: Vector = fill ( 1 , length (i_ym)) ,
280+ σQ :: Vector = fill (1 / model. nx, model. nx),
281+ σR :: Vector = fill (1 , length (i_ym) ),
282+ σP0 :: Vector = σQ ,
283283 nint_u :: IntVectorOrInt = 0 ,
284284 σQint_u :: Vector = fill (1 , max (sum (nint_u), 0 )),
285- σP0int_u :: Vector = fill ( 1 , max ( sum (nint_u), 0 )) ,
285+ σP0int_u :: Vector = σQint_u ,
286286 nint_ym :: IntVectorOrInt = default_nint (model, i_ym, nint_u),
287287 σQint_ym :: Vector = fill (1 , max (sum (nint_ym), 0 )),
288- σP0int_ym:: Vector = fill ( 1 , max ( sum (nint_ym), 0 ))
288+ σP0int_ym:: Vector = σQint_ym,
289289) where {NT<: Real , SM<: LinModel{NT} }
290290 # estimated covariances matrices (variance = σ²) :
291- P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 );
292- Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 );
293- R̂ = Diagonal {NT} (σR.^ 2 );
291+ P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 )
292+ Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 )
293+ R̂ = Diagonal {NT} (σR.^ 2 )
294294 return KalmanFilter {NT, SM} (model, i_ym, nint_u, nint_ym, P̂0, Q̂ , R̂)
295295end
296296
@@ -443,23 +443,23 @@ responsibility to ensure that the augmented model is still observable.
443443function UnscentedKalmanFilter (
444444 model:: SM ;
445445 i_ym:: IntRangeOrVector = 1 : model. ny,
446- σP0 :: Vector = fill (1 / model. nx, model. nx),
447- σQ :: Vector = fill (1 / model . nx, model . nx ),
448- σR :: Vector = fill ( 1 , length (i_ym)) ,
446+ σQ :: Vector = fill (1 / model. nx, model. nx),
447+ σR :: Vector = fill (1 , length (i_ym) ),
448+ σP0 :: Vector = σQ ,
449449 nint_u :: IntVectorOrInt = 0 ,
450450 σQint_u :: Vector = fill (1 , max (sum (nint_u), 0 )),
451- σP0int_u :: Vector = fill ( 1 , max ( sum (nint_u), 0 )) ,
451+ σP0int_u :: Vector = σQint_u ,
452452 nint_ym :: IntVectorOrInt = default_nint (model, i_ym, nint_u),
453453 σQint_ym :: Vector = fill (1 , max (sum (nint_ym), 0 )),
454- σP0int_ym:: Vector = fill ( 1 , max ( sum (nint_ym), 0 )) ,
454+ σP0int_ym:: Vector = σQint_ym ,
455455 α:: Real = 1e-3 ,
456456 β:: Real = 2 ,
457457 κ:: Real = 0
458458) where {NT<: Real , SM<: SimModel{NT} }
459459 # estimated covariances matrices (variance = σ²) :
460- P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 );
461- Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 );
462- R̂ = Diagonal {NT} (σR.^ 2 );
460+ P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 )
461+ Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 )
462+ R̂ = Diagonal {NT} (σR.^ 2 )
463463 return UnscentedKalmanFilter {NT, SM} (model, i_ym, nint_u, nint_ym, P̂0, Q̂, R̂, α, β, κ)
464464end
465465
@@ -578,7 +578,7 @@ function update_estimate!(estim::UnscentedKalmanFilter{NT}, u, ym, d) where NT<:
578578 x̂[:] = X̂_next * m̂
579579 X̄_next = X̂_next .- x̂
580580 P̂. data[:] = X̄_next * Ŝ * X̄_next' + Q̂ # .data is necessary for Hermitians
581- return x̂, P̂
581+ return nothing
582582end
583583
584584struct ExtendedKalmanFilter{NT<: Real , SM<: SimModel } <: StateEstimator{NT}
@@ -669,20 +669,20 @@ ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s, NonLinModel and:
669669function ExtendedKalmanFilter (
670670 model:: SM ;
671671 i_ym:: IntRangeOrVector = 1 : model. ny,
672- σP0 :: Vector = fill (1 / model. nx, model. nx),
673- σQ :: Vector = fill (1 / model . nx, model . nx ),
674- σR :: Vector = fill ( 1 , length (i_ym)) ,
672+ σQ :: Vector = fill (1 / model. nx, model. nx),
673+ σR :: Vector = fill (1 , length (i_ym) ),
674+ σP0 :: Vector = σQ ,
675675 nint_u :: IntVectorOrInt = 0 ,
676676 σQint_u :: Vector = fill (1 , max (sum (nint_u), 0 )),
677- σP0int_u :: Vector = fill ( 1 , max ( sum (nint_u), 0 )) ,
677+ σP0int_u :: Vector = σQint_u ,
678678 nint_ym :: IntVectorOrInt = default_nint (model, i_ym, nint_u),
679679 σQint_ym :: Vector = fill (1 , max (sum (nint_ym), 0 )),
680- σP0int_ym:: Vector = fill ( 1 , max ( sum (nint_ym), 0 ))
680+ σP0int_ym:: Vector = σQint_ym,
681681) where {NT<: Real , SM<: SimModel{NT} }
682682 # estimated covariances matrices (variance = σ²) :
683- P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 );
684- Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 );
685- R̂ = Diagonal {NT} (σR.^ 2 );
683+ P̂0 = Diagonal {NT} ([σP0; σP0int_u; σP0int_ym]. ^ 2 )
684+ Q̂ = Diagonal {NT} ([σQ; σQint_u; σQint_ym]. ^ 2 )
685+ R̂ = Diagonal {NT} (σR.^ 2 )
686686 return ExtendedKalmanFilter {NT, SM} (model, i_ym, nint_u, nint_ym, P̂0, Q̂ , R̂)
687687end
688688
@@ -746,36 +746,42 @@ end
746746"""
747747 validate_kfcov(nym, nx̂, Q̂, R̂, P̂0=nothing)
748748
749- Validate sizes of process `Q̂`` and sensor `R̂` noises covariance matrices.
749+ Validate sizes and Hermitianity of process `Q̂`` and sensor `R̂` noises covariance matrices.
750750
751- Also validate initial estimate covariance size , if provided.
751+ Also validate initial estimate covariance `P̂0` , if provided.
752752"""
753753function validate_kfcov (nym, nx̂, Q̂, R̂, P̂0= nothing )
754754 size (Q̂) ≠ (nx̂, nx̂) && error (" Q̂ size $(size (Q̂)) ≠ nx̂, nx̂ $((nx̂, nx̂)) " )
755+ ! ishermitian (Q̂) && error (" Q̂ is not Hermitian" )
755756 size (R̂) ≠ (nym, nym) && error (" R̂ size $(size (R̂)) ≠ nym, nym $((nym, nym)) " )
757+ ! ishermitian (R̂) && error (" R̂ is not Hermitian" )
756758 if ~ isnothing (P̂0)
757759 size (P̂0) ≠ (nx̂, nx̂) && error (" P̂0 size $(size (P̂0)) ≠ nx̂, nx̂ $((nx̂, nx̂)) " )
760+ ! ishermitian (P̂0) && error (" P̂0 is not Hermitian" )
758761 end
759762end
760763
761764"""
762- update_estimate_kf!(estim, Â, Ĉm, u, ym, d) -> x̂, P̂
765+ update_estimate_kf!(estim, Â, Ĉm, u, ym, d; updatestate=true)
763766
764767Update time-varying/extended Kalman Filter estimates with augmented `Â` and `Ĉm` matrices.
765768
766769Allows code reuse for [`KalmanFilter`](@ref) and [`ExtendedKalmanFilterKalmanFilter`](@ref).
767770They update the state `x̂` and covariance `P̂` with the same equations. The extended filter
768771substitutes the augmented model matrices with its Jacobians (`Â = F̂` and `Ĉm = Ĥm`).
769772The implementation uses in-place operations and explicit factorization to reduce
770- allocations. See e.g. [`KalmanFilter`](@ref) docstring for the equations.
773+ allocations. See e.g. [`KalmanFilter`](@ref) docstring for the equations. If `updatestate`
774+ is `false`, only the covariance `P̂` is updated.
771775"""
772- function update_estimate_kf! (estim, Â, Ĉm, u, ym, d)
776+ function update_estimate_kf! (estim, Â, Ĉm, u, ym, d; updatestate = true )
773777 x̂, P̂, Q̂, R̂, K̂, M̂ = estim. x̂, estim. P̂, estim. Q̂, estim. R̂, estim. K̂, estim. M̂
774778 mul! (M̂, P̂, Ĉm' )
775779 rdiv! (M̂, cholesky! (Hermitian (Ĉm * P̂ * Ĉm' + R̂)))
776- mul! (K̂, Â, M̂)
777- ŷm = @views ĥ (estim, estim. model, x̂, d)[estim. i_ym]
778- x̂[:] = f̂ (estim, estim. model, x̂, u, d) + K̂ * (ym - ŷm)
780+ if updatestate
781+ mul! (K̂, Â, M̂)
782+ ŷm = @views ĥ (estim, estim. model, x̂, d)[estim. i_ym]
783+ x̂[:] = f̂ (estim, estim. model, x̂, u, d) + K̂ * (ym - ŷm)
784+ end
779785 P̂. data[:] = Â * (P̂ - M̂ * Ĉm * P̂) * Â' + Q̂ # .data is necessary for Hermitians
780- return x̂, P̂
786+ return nothing
781787end
0 commit comments