@@ -193,14 +193,46 @@ end
193193 correct_estimate!(estim::SteadyKalmanFilter, y0m, d0)
194194
195195Correct `estim.x̂0` with measured outputs `y0m` and disturbances `d0` for current time step.
196+
197+ It computes the corrected state estimate ``\m athbf{x̂}_{k}(k)``. See the docstring of
198+ [`update_estimate!(::SteadyKalmanFilter, ::Any, ::Any)`](@ref) for the equations.
196199"""
197200function correct_estimate! (estim:: SteadyKalmanFilter , y0m, d0)
198- return correct_estimate_obsv! (estim, y0m, d0)
201+ return correct_estimate_obsv! (estim, y0m, d0, estim. K̂)
202+ end
203+
204+ @doc raw """
205+ update_estimate!(estim::SteadyKalmanFilter, y0m, d0, u0)
206+
207+ Update `estim.x̂0` estimate with current inputs `u0`, measured outputs `y0m` and dist. `d0`.
208+
209+ If `estim.direct == false`, the [`SteadyKalmanFilter`](@ref) first corrects the state
210+ estimate with the precomputed Kalman gain ``\m athbf{K̂}``. Afterward, it predicts the next
211+ state with the augmented process model. The correction step is skipped if `direct == true`
212+ since it is already done by the user through the [`preparestate!`](@ref) function (that
213+ calls [`correct_estimate!`](@ref)). The correction and prediction step equations are
214+ provided below.
215+
216+ # Correction Step
217+ ```math
218+ \m athbf{x̂}_k(k) = \m athbf{x̂}_{k-1}(k) + \m athbf{K̂}[\m athbf{y^m}(k) - \m athbf{Ĉ^m x̂}_{k-1}(k)
219+ - \m athbf{D̂_d^m d}(k) ]
220+ ```
221+
222+ # Prediction Step
223+ ```math
224+ \m athbf{x̂}_{k}(k+1) = \m athbf{Â x̂}_{k}(k) + \m athbf{B̂_u u}(k) + \m athbf{B̂_d d}(k)
225+ ```
226+ """
227+ function update_estimate! (estim:: SteadyKalmanFilter , y0m, d0, u0)
228+ if ! estim. direct
229+ correct_estimate_obsv! (estim, y0m, d0, estim. K̂)
230+ end
231+ return predict_estimate_obsv! (estim:: StateEstimator , y0m, d0, u0)
199232end
200233
201234" Allow code reuse for `SteadyKalmanFilter` and `Luenberger` (observers with constant gain)."
202- function correct_estimate_obsv! (estim:: StateEstimator , y0m, d0)
203- K̂ = estim. K̂
235+ function correct_estimate_obsv! (estim:: StateEstimator , y0m, d0, K̂)
204236 Ĉm, D̂dm = @views estim. Ĉ[estim. i_ym, :], estim. D̂d[estim. i_ym, :]
205237 ŷ0m = @views estim. buffer. ŷ[estim. i_ym]
206238 # in-place operations to reduce allocations:
@@ -213,26 +245,8 @@ function correct_estimate_obsv!(estim::StateEstimator, y0m, d0)
213245 return nothing
214246end
215247
216- @doc raw """
217- update_estimate!(estim::SteadyKalmanFilter, y0m, d0, u0)
218-
219- Update `estim.x̂0` estimate with current inputs `u0`, measured outputs `y0m` and dist. `d0`.
220-
221- The [`SteadyKalmanFilter`](@ref) updates it with the precomputed Kalman gain ``\m athbf{K̂}``:
222- ```math
223- \m athbf{x̂}_{k}(k+1) = \m athbf{Â x̂}_{k-1}(k) + \m athbf{B̂_u u}(k) + \m athbf{B̂_d d}(k)
224- + \m athbf{K̂}[\m athbf{y^m}(k) - \m athbf{Ĉ^m x̂}_{k-1}(k) - \m athbf{D̂_d^m d}(k)]
225- ```
226- """
227- function update_estimate! (estim:: SteadyKalmanFilter , y0m, d0, u0)
228- return update_estimate_obsv! (estim:: StateEstimator , y0m, d0, u0)
229- end
230-
231248" Allow code reuse for `SteadyKalmanFilter` and `Luenberger` (observers with constant gain)."
232- function update_estimate_obsv! (estim:: StateEstimator , y0m, d0, u0)
233- if ! estim. direct
234- correct_estimate_obsv! (estim, y0m, d0)
235- end
249+ function predict_estimate_obsv! (estim:: StateEstimator , y0m, d0, u0)
236250 x̂0corr = estim. x̂0
237251 Â, B̂u, B̂d = estim. Â, estim. B̂u, estim. B̂d
238252 x̂0next = estim. buffer. x̂
@@ -385,10 +399,13 @@ function KalmanFilter(
385399 return KalmanFilter {NT, SM} (model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
386400end
387401
388- """
402+ @doc raw """
389403 correct_estimate!(estim::KalmanFilter, y0m, d0)
390404
391- Do the same but for the time varying [`KalmanFilter`](@ref).
405+ Correct `estim.x̂0` and `estim.P̂` using the time-varying [`KalmanFilter`](@ref).
406+
407+ It computes the corrected state estimate ``\m athbf{x̂}_{k}(k)`` estimation covariance
408+ ``\m athbf{P̂}_{k}(k)``.
392409"""
393410function correct_estimate! (estim:: KalmanFilter , y0m, d0)
394411 Ĉm = @views estim. Ĉ[estim. i_ym, :]
@@ -401,32 +418,40 @@ end
401418
402419Update [`KalmanFilter`](@ref) state `estim.x̂0` and estimation error covariance `estim.P̂`.
403420
404- It implements the time-varying Kalman Filter in its predictor (observer) form :
421+ It implements the classical time-varying Kalman Filter based on the process model described
422+ in [`SteadyKalmanFilter`](@ref). If `estim.direct == false`, it first corrects the estimate
423+ before predicting the next state. The correction step is skipped if `estim.direct == true`
424+ since it's already done by the user. The correction and prediction step equations are
425+ provided below, see [^2] for details.
426+
427+ # Correction Step
405428```math
406429\b egin{aligned}
407- \m athbf{M̂}(k) &= \m athbf{P̂}_{k-1}(k)\m athbf{Ĉ^m}'
408- [\m athbf{Ĉ^m P̂}_{k-1}(k)\m athbf{Ĉ^m}' + \m athbf{R̂}]^{-1} \\
409- \m athbf{K̂}(k) &= \m athbf{Â M̂}(k) \\
410- \m athbf{ŷ^m}(k) &= \m athbf{Ĉ^m x̂}_{k-1}(k) + \m athbf{D̂_d^m d}(k) \\
411- \m athbf{x̂}_{k}(k+1) &= \m athbf{Â x̂}_{k-1}(k) + \m athbf{B̂_u u}(k) + \m athbf{B̂_d d}(k)
412- + \m athbf{K̂}(k)[\m athbf{y^m}(k) - \m athbf{ŷ^m}(k)] \\
413-
414-
430+ \m athbf{Ŝ}(k) &= \m athbf{Ĉ^m P̂}_{k-1}(k)\m athbf{Ĉ^m}' + \m athbf{R̂} \\
431+ \m athbf{K̂}(k) &= \m athbf{P̂}_{k-1}(k)\m athbf{Ĉ^m}'\m athbf{Ŝ^{-1}}(k) \\
432+ \m athbf{ŷ^m}(k) &= \m athbf{Ĉ^m x̂}_{k-1}(k) + \m athbf{D̂_d^m d}(k) \\
433+ \m athbf{x̂}_{k}(k) &= \m athbf{x̂}_{k-1}(k) + \m athbf{K̂}(k)[\m athbf{y^m}(k) - \m athbf{ŷ^m}(k)] \\
434+ \m athbf{P̂}_{k}(k) &= [\m athbf{I - K̂}(k)\m athbf{Ĉ^m}]\m athbf{P̂}_{k-1}(k)
435+ \e nd{aligned}
436+ ```
415437
416- \m athbf{P̂}_{k}(k+1) &= \m athbf{Â}[\m athbf{P̂}_{k-1}(k)
417- - \m athbf{M̂}(k)\m athbf{Ĉ^m P̂}_{k-1}(k)]\m athbf{Â}' + \m athbf{Q̂}
438+ # Prediction Step
439+ ```math
440+ \b egin{aligned}
441+ \m athbf{x̂}_{k}(k+1) &= \m athbf{Â x̂}_{k}(k) + \m athbf{B̂_u u}(k) + \m athbf{B̂_d d}(k) \\
442+ \m athbf{P̂}_{k}(k+1) &= \m athbf{Â P̂}_{k}(k)\m athbf{Â}' + \m athbf{Q̂}
418443\e nd{aligned}
419444```
420- based on the process model described in [`SteadyKalmanFilter`](@ref). The notation
421- ``\m athbf{x̂}_{k-1}(k)`` refers to the state for the current time ``k`` estimated at the last
422- control period ``k-1``. See [^2] for details.
423445
424- [^2]: Boyd S., "Lecture 8 : The Kalman Filter" (Winter 2008-09) [course slides], *EE363:
425- Linear Dynamical Systems*, <https://web.stanford.edu/class/ee363/lectures/kf.pdf> .
446+ [^2]: " Kalman Filter", *Wikipedia: The Free Encyclopedia*,
447+ <https://en.wikipedia.org/wiki/Kalman_filter>, Accessed 2024-08-08 .
426448"""
427449function update_estimate! (estim:: KalmanFilter , y0m, d0, u0)
428450 Ĉm = @views estim. Ĉ[estim. i_ym, :]
429- return update_estimate_kf! (estim, y0m, d0, u0, Ĉm, estim. Â)
451+ if ! estim. direct
452+ correct_estimate_kf! (estim, y0m, d0, Ĉm)
453+ end
454+ return predict_estimate_kf! (estim, y0m, d0, u0, Ĉm, estim. Â)
430455end
431456
432457
@@ -916,48 +941,60 @@ end
916941Update [`ExtendedKalmanFilter`](@ref) state `estim.x̂0` and covariance `estim.P̂`.
917942
918943The equations are similar to [`update_estimate!(::KalmanFilter)`](@ref) but with the
919- substitutions ``\m athbf{Â = F̂}(k)`` and ``\m athbf{Ĉ^m = Ĥ^m}(k)``:
944+ substitutions ``\m athbf{Â = F̂}(k)`` and ``\m athbf{Ĉ^m = Ĥ^m}(k)``, the Jacobians of the
945+ augmented process model:
920946```math
921947\b egin{aligned}
922- \m athbf{M̂}(k) &= \m athbf{P̂}_{k-1}(k)\m athbf{Ĥ^m}'(k)
923- [\m athbf{Ĥ^m}(k)\m athbf{P̂}_{k-1}(k)\m athbf{Ĥ^m}'(k) + \m athbf{R̂}]^{-1} \\
924- \m athbf{K̂}(k) &= \m athbf{F̂}(k) \m athbf{M̂}(k) \\
925- \m athbf{ŷ^m}(k) &= \m athbf{ĥ^m}\B ig( \m athbf{x̂}_{k-1}(k), \m athbf{d}(k) \B ig) \\
926- \m athbf{x̂}_{k}(k+1) &= \m athbf{f̂}\B ig( \m athbf{x̂}_{k-1}(k), \m athbf{u}(k), \m athbf{d}(k) \B ig)
927- + \m athbf{K̂}(k)[\m athbf{y^m}(k) - \m athbf{ŷ^m}(k)] \\
928- \m athbf{P̂}_{k}(k+1) &= \m athbf{F̂}(k)[\m athbf{P̂}_{k-1}(k)
929- - \m athbf{M̂}(k)\m athbf{Ĥ^m}(k)\m athbf{P̂}_{k-1}(k)]\m athbf{F̂}'(k)
930- + \m athbf{Q̂}
948+ \m athbf{Ĥ}(k) &= \l eft. \f rac{∂\m athbf{ĥ}(\m athbf{x̂}, \m athbf{d})}{∂\m athbf{x̂}} \r ight|_{\m athbf{x̂ = x̂}_{k-1}(k),\, \m athbf{d = d}(k)} \\
949+ \m athbf{F̂}(k) &= \l eft. \f rac{∂\m athbf{f̂}(\m athbf{x̂}, \m athbf{u}, \m athbf{d})}{∂\m athbf{x̂}} \r ight|_{\m athbf{x̂ = x̂}_{k}(k), \, \m athbf{u = u}(k),\, \m athbf{d = d}(k)}
931950\e nd{aligned}
932951```
933- [`ForwardDiff.jacobian`](https://juliadiff.org/ForwardDiff.jl/stable/user/api/#ForwardDiff.jacobian)
934- automatically computes the Jacobians:
952+ The matrix ``\m athbf{Ĥ^m}`` is the rows of ``\m athbf{Ĥ}`` that are measured outputs. The
953+ function [`ForwardDiff.jacobian`](https://juliadiff.org/ForwardDiff.jl/stable/user/api/#ForwardDiff.jacobian)
954+ automatically computes them. The correction and prediction step equations are provided below.
955+
956+ # Correction Step
935957```math
936958\b egin{aligned}
937- \m athbf{F̂}(k) &= \l eft. \f rac{∂\m athbf{f̂}(\m athbf{x̂}, \m athbf{u}, \m athbf{d})}{∂\m athbf{x̂}} \r ight|_{\m athbf{x̂ = x̂}_{k-1}(k),\, \m athbf{u = u}(k),\, \m athbf{d = d}(k)} \\
938- \m athbf{Ĥ}(k) &= \l eft. \f rac{∂\m athbf{ĥ}(\m athbf{x̂}, \m athbf{d})}{∂\m athbf{x̂}} \r ight|_{\m athbf{x̂ = x̂}_{k-1}(k),\, \m athbf{d = d}(k)}
959+ \m athbf{Ŝ}(k) &= \m athbf{Ĥ^m}(k)\m athbf{P̂}_{k-1}(k)\m athbf{Ĥ^m}'(k) + \m athbf{R̂} \\
960+ \m athbf{K̂}(k) &= \m athbf{P̂}_{k-1}(k)\m athbf{Ĥ^m}'(k)\m athbf{Ŝ^{-1}}(k) \\
961+ \m athbf{ŷ^m}(k) &= \m athbf{ĥ^m}\B ig( \m athbf{x̂}_{k-1}(k), \m athbf{d}(k) \B ig) \\
962+ \m athbf{x̂}_{k}(k) &= \m athbf{x̂}_{k-1}(k) + \m athbf{K̂}(k)[\m athbf{y^m}(k) - \m athbf{ŷ^m}(k)] \\
963+ \m athbf{P̂}_{k}(k) &= [\m athbf{I - K̂}(k)\m athbf{Ĥ^m}(k)]\m athbf{P̂}_{k-1}(k)
964+ \e nd{aligned}
965+ ```
966+
967+ # Prediction Step
968+ ```math
969+ \b egin{aligned}
970+ \m athbf{x̂}_{k}(k+1) &= \m athbf{f̂}\B ig( \m athbf{x̂}_{k}(k), \m athbf{u}(k), \m athbf{d}(k) \B ig) \\
971+ \m athbf{P̂}_{k}(k+1) &= \m athbf{F̂}(k)\m athbf{P̂}_{k}(k)\m athbf{F̂}'(k) + \m athbf{Q̂}
939972\e nd{aligned}
940973```
941- The matrix ``\m athbf{Ĥ^m}`` is the rows of ``\m athbf{Ĥ}`` that are measured outputs.
942974"""
943975function update_estimate! (estim:: ExtendedKalmanFilter{NT} , y0m, d0, u0) where NT<: Real
944976 model, x̂0 = estim. model, estim. x̂0
945977 nx̂, nu = estim. nx̂, model. nu
946- # concatenate x̂0next and û0 vectors to allows û0 vector with dual numbers for AD:
947- # TODO : remove this allocation using estim.buffer
948- x̂0nextû = Vector {NT} (undef, nx̂ + nu)
949- f̂AD! = (x̂0nextû, x̂0) -> @views f̂! (
950- x̂0nextû[1 : nx̂], x̂0nextû[nx̂+ 1 : end ], estim, model, x̂0, u0, d0
951- )
952- ForwardDiff. jacobian! (estim. F̂_û, f̂AD!, x̂0nextû, x̂0)
978+ Ĥ = estim. Ĥ
953979 if ! estim. direct
954980 ŷ0 = estim. buffer. ŷ
955981 ĥAD! = (ŷ0, x̂0) -> ĥ! (ŷ0, estim, model, x̂0, d0)
956- ForwardDiff. jacobian! (estim. Ĥ, ĥAD!, ŷ0, x̂0)
982+ ForwardDiff. jacobian! (Ĥ, ĥAD!, ŷ0, x̂0)
983+ Ĥm = @views Ĥ[estim. i_ym, :]
984+ correct_estimate_kf! (estim, y0m, d0, Ĥm)
985+ else
986+ Ĥm = @views Ĥ[estim. i_ym, :]
957987 end
988+ x̂0corr = estim. x̂0
989+ # concatenate x̂0next and û0 vectors to allows û0 vector with dual numbers for AD:
990+ # TODO : remove this allocation using estim.buffer
991+ x̂0nextû = Vector {NT} (undef, nx̂ + nu)
992+ f̂AD! = (x̂0nextû, x̂0corr) -> @views f̂! (
993+ x̂0nextû[1 : nx̂], x̂0nextû[nx̂+ 1 : end ], estim, model, x̂0corr, u0, d0
994+ )
995+ ForwardDiff. jacobian! (estim. F̂_û, f̂AD!, x̂0nextû, x̂0corr)
958996 F̂ = @views estim. F̂_û[1 : estim. nx̂, :]
959- Ĥm = @views estim. Ĥ[estim. i_ym, :]
960- return update_estimate_kf! (estim, y0m, d0, u0, Ĥm, F̂)
997+ return predict_estimate_kf! (estim, y0m, d0, u0, Ĥm, F̂)
961998end
962999
9631000" Set `estim.P̂` to `estim.P̂_0` for the time-varying Kalman Filters."
@@ -1024,20 +1061,15 @@ function correct_estimate_kf!(estim::Union{KalmanFilter, ExtendedKalmanFilter},
10241061end
10251062
10261063"""
1027- update_estimate_kf !(estim::Union{KalmanFilter, ExtendedKalmanFilter}, y0m, d0, u0, Ĉm, Â)
1064+ predict_estimate_kf !(estim::Union{KalmanFilter, ExtendedKalmanFilter}, y0m, d0, u0, Ĉm, Â)
10281065
1029- Update time-varying/extended Kalman Filter estimates with augmented `Ĉm` and `Â` matrices.
1066+ Predict time-varying/extended Kalman Filter estimates with augmented `Ĉm` and `Â` matrices.
10301067
10311068Allows code reuse for [`KalmanFilter`](@ref), [`ExtendedKalmanFilterKalmanFilter`](@ref).
1032- They update the state `x̂` and covariance `P̂` with the same equations. The extended filter
1033- substitutes the augmented model matrices with its Jacobians (`Â = F̂` and `Ĉm = Ĥm`).
1034- The implementation uses in-place operations and explicit factorization to reduce
1035- allocations. See e.g. [`KalmanFilter`](@ref) docstring for the equations.
1069+ They predict the state `x̂` and covariance `P̂` with the same equations. See
1070+ [`update_estimate`](@ref) methods for the equations.
10361071"""
1037- function update_estimate_kf! (estim:: Union{KalmanFilter, ExtendedKalmanFilter} , y0m, d0, u0, Ĉm, Â)
1038- if ! estim. direct
1039- correct_estimate_kf! (estim, y0m, d0, Ĉm)
1040- end
1072+ function predict_estimate_kf! (estim:: Union{KalmanFilter, ExtendedKalmanFilter} , y0m, d0, u0, Ĉm, Â)
10411073 x̂0corr, P̂corr = estim. x̂0, estim. P̂
10421074 Q̂ = estim. Q̂
10431075 x̂0next, û0 = estim. buffer. x̂, estim. buffer. û
0 commit comments