EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ > Class Template Reference

A Kalman filter: a Gaussian state and the KalmanRecursion that predicts it through a process model and conditions it on measurements. More...

#include <EmrysFilter/Filters/KalmanFilter.h>

Inheritance diagram for Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >:
Emrys::InfoFilter< Space_, Model_, Method_, Scalar_, Upkeep_ > Emrys::SqrtInfoFilter< Space_, Model_, Method_, Scalar_, Upkeep_ > Emrys::SqrtKalmanFilter< Space_, Model_, Method_, Scalar_, Upkeep_ >

Public Types

using Scalar = typename Params<Component_>::Scalar
 the scalar type
using SpaceType = typename Params<Component_>::SpaceType
 the state space
using ModelType = Model_
 the process model
using MethodType = Method_
 the prediction method
using StateType = Params<Component_>
 the state the filter owns
using RecursionType = Recursion_
 the steps it runs (see Recursion)

Public Member Functions

 KalmanFilter (Model_ model, Params< Component_ > state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={})
 A filter with process model model, initial state state, prediction method method, and upkeep upkeep, which maintain() runs; a fixed-step model carries its own method, so there is none to give.
 KalmanFilter (Model_ model, Params< Component_ > state, std::type_identity_t< Upkeep_ > upkeep)
 A filter with process model model, initial state state, the default prediction method, and upkeep upkeep, which maintain() runs.
 KalmanFilter (Recursion_ recursion, StateType state, std::type_identity_t< Upkeep_ > upkeep={})
 A filter that runs recursion, the library's or one of the user's own (see Recursion), on the initial state state, whose space the recursion's model must partition, with upkeep upkeep, which maintain() runs.
StateType const & state () const
StateType & state ()
void setState (StateType state)
 Replaces the state estimate, as when reinitializing, with one whose space the process model's writes partition, as the constructor's must.
RecursionType const & recursion () const
Model_ const & model () const
Model_ & model ()
Method_ const & method () const
template<typename... Inputs>
requires (!isFixedStep<Model_>)
auto predict (TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
 Moves the state forward by dt, any TimeStep (see Time steps), through the process model, with inputs passed to its model as in Emrys::predict(): an uncertain input carries its uncertainty through the dynamics, and anything else is known.
template<typename... Inputs>
requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
auto predict (Inputs const &... inputs)
 Moves the state forward by the fixed-step model's step, with inputs passed as in Emrys::predict(), returning as predict(dt, inputs...) does.
template<typename... Inputs>
requires isFixedStep<Model_>
auto predict (Steps steps, Inputs const &... inputs)
 Moves the state forward by steps of the fixed-step model, as that many calls of predict(inputs...) do, with inputs at every step.
template<typename... Inputs>
requires (!isFixedStep<Model_> && !IsForm)
PredictedJoint< SpaceType, Scalar > predictJoint (TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
 Moves the state forward as predict(dt, inputs...) does.
template<typename... Inputs>
requires (isFixedStep<Model_> && !IsForm && !internal::leadsWithSteps<Inputs...>)
PredictedJoint< SpaceType, Scalar > predictJoint (Inputs const &... inputs)
 Moves the state forward as predict(inputs...) does with a fixed-step model.
template<Measurement M, typename Policy = reductions::Collapse>
requires (!internal::isLinearizedAbout<Policy>)
auto update (M const &measurement, Policy const &policy={})
 Conditions the state on measurement, by the measurement's own method, and reduces the branches to the new state with policy, a ReductionPolicy to the component (Collapse, Select, or a Gate).
template<Measurement M, typename Reference, typename Policy = reductions::Collapse>
auto update (M const &measurement, LinearizedAbout< Reference > const &about, Policy const &policy={})
 Conditions the state on measurement as update(measurement, policy) does, with the measurement linearized about about, a point or a distribution (see linearizedAbout()), rather than at the state (decisions.md [linearization-reference]): a shadow filter's update about another filter's state, which with predict(dt, about) runs the shadow's whole step about it.
template<Measurement M, typename Policy, typename Result>
requires (!internal::isLinearizedAbout<Policy>)
std::expected< void, GaussianError > update (M const &measurement, Policy const &policy, Result &result)
 As update(measurement, policy), but into result, the update inside the std::expected that update() returns, whose storage is reused: with the same result at every step, an update under mixture noise, whose branches are a run-time count, allocates nothing once the result has held as many (see Allocation).
template<Measurement M, typename Reference, typename Policy, typename Result>
std::expected< void, GaussianError > update (M const &measurement, LinearizedAbout< Reference > const &about, Policy const &policy, Result &result)
 As update(measurement, about, policy), but into result, as update(measurement, policy, result) fills it.

Detailed Description

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
requires std::same_as<typename Recursion_::ModelType, Model_> && std::same_as<typename Recursion_::MethodType, Method_>
class Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >

A Kalman filter: a Gaussian state and the KalmanRecursion that predicts it through a process model and conditions it on measurements.

Prediction uses Method (Linearized or a sigma-point method; Linearized is exact for the built-in models). Each update uses its measurement's own method, and its branches are reduced to one Gaussian by a policy, Collapse by default. With Gaussian measurement noise there is one branch and the policy has nothing to do; with mixture noise, such as an inlier and an outlier component, the filter is outlier-robust.

The filter of Kalman (1960); with the Linearized method it is the extended Kalman filter and with Unscented the unscented Kalman filter of Julier and Uhlmann (2004) (Särkkä (2013), chapters 4 to 6). Mixture noise with Collapse is the probabilistic data association filter of Bar-Shalom and Tse (1975).

With a FixedStepModel the filter predicts over the model's step, by the model's method, and predict() takes no time step. Given fixedStep(model, dt), it binds the step to its state's space:

auto filter = KalmanFilter{fixedStep(ConstantVelocity<Position, Velocity>{0.1}, 0.01), initial};
filter.predict();
KalmanFilter(Model_ model, Params< Component_ > state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={})
A filter with process model model, initial state state, prediction method method, and upkeep upkeep,...
Definition KalmanFilter.h:580
UnboundFixedStep< Model, Method, Time > fixedStep(Model model, Time dt, Method method={})
Definition ProcessModel.h:3736
Kinematic< Scalar, Position, Velocity > ConstantVelocity
Constant velocity (white-noise acceleration): position moves by velocity times .
Definition ProcessModel.h:1152

The filter adds nothing to the free update() of its state but storage, so what it doesn't do is done on state() and stored with setState(). The free update() returns the MeasurementUpdate, posteriors included, which is how a caller looks before it commits, as nearest-neighbor data association does, keeping the most likely of several detections:

auto best = std::optional<std::remove_cvref_t<decltype(*update(filter.state(), observe(method, gps, zs[0])))>>{};
for (auto const & z : zs)
{
auto candidate = update(filter.state(), observe(method, gps, z));
if (candidate && (!best || candidate->logLikelihood() > best->logLikelihood())) { best = std::move(*candidate); }
}
if (best) { filter.setState(*reductions::collapse()(*std::move(best))); }
Method_ const & method() const
Definition KalmanFilter.h:663
auto update(M const &measurement, Policy const &policy={})
Conditions the state on measurement, by the measurement's own method, and reduces the branches to the...
Definition KalmanFilter.h:772
constexpr Collapse collapse()
Definition Reduction.h:291
auto observe(Method method, M &&model, Z &&observation, Args &&... args)
Definition MeasurementModel.h:1399
std::expected< CategoricalUpdate< Space, Scalar >, WeightError > update(CategoricalParams< Space, Scalar > const &prior, E const &emission)
Definition Emission.h:97

Upkeep (see Upkeep) applies to state() between steps, as to any filter's: a covariance that rounding has left not positive definite, as the standard update can after a measurement far more precise than the prior, is repaired by

auto const repair = upkeep::repairCovariance(); // roots::ridge() unless given another repair
auto const repaired = repair(filter.state()); // true if it was not positive definite
constexpr RepairCovariance< R > repairCovariance(R repair={})
Definition Upkeep.h:1340

or by the filter's own upkeep, given last to the constructor and run by maintain() where the caller calls it, as every filter holds one:

auto filter = KalmanFilter{model, initial, upkeep::repairCovariance()};
filter.predict(dt);
(void)filter.update(fix);
auto const repaired = filter.maintain();
Model_ const & model() const
Definition KalmanFilter.h:649

Over a square-root or information form of the Gaussian (decisions.md [gaussian-forms]) it is the square-root, information, or square-root information filter, which SqrtKalmanFilter, InfoFilter, and SqrtInfoFilter spell space first; see KalmanRecursion for what changes in the steps.

Template Parameters
Component_the state's type (see Component), a Gaussian over the state space (see Spaces), or a form of the Gaussian (see GaussianForm)
Model_the process model, a ProcessModel or a FixedStepModel, which partition the space
Method_the prediction method; a FixedStepModel's own
Recursion_the recursion that runs the steps (see Recursion): the KalmanRecursion over the component unless given one, as KalmanFilter{recursion, state} deduces
Upkeep_the upkeep maintain() runs on the state (see Upkeep), upkeep::None unless given one
See also
predict(), update(), KalmanRecursion, Recursion, Filters

Member Function Documentation

◆ method()

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
Method_ const & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::method ( ) const
inlinenodiscard
Returns
the prediction method

◆ model() [1/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
Model_ & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::model ( )
inlinenodiscard
Returns
the process model, to change its parameters

◆ model() [2/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
Model_ const & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::model ( ) const
inlinenodiscard
Returns
the process model

◆ predict() [1/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires isFixedStep<Model_>
auto Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::predict ( Steps steps,
Inputs const &... inputs )
inlinenodiscard

Moves the state forward by steps of the fixed-step model, as that many calls of predict(inputs...) do, with inputs at every step.

Returns
as predict(inputs...) does; for a form, the first step the form could not take, which leaves the state as that step left it

◆ predict() [2/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (!isFixedStep<Model_>)
auto Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::predict ( TimeStep< Scalar > auto const & dt,
Inputs const &... inputs )
inlinenodiscard

Moves the state forward by dt, any TimeStep (see Time steps), through the process model, with inputs passed to its model as in Emrys::predict(): an uncertain input carries its uncertainty through the dynamics, and anything else is known.

A linearizedAbout() reference first among inputs linearizes the step about it rather than at the state (decisions.md [linearization-reference]), as a shadow filter's step is linearized about another filter's state.

Returns
nothing for a Gaussian; for a form of the Gaussian, a std::expected that is empty when the form could not take the step (see KalmanRecursion::predict())

◆ predictJoint() [1/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (isFixedStep<Model_> && !IsForm && !internal::leadsWithSteps<Inputs...>)
PredictedJoint< SpaceType, Scalar > Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::predictJoint ( Inputs const &... inputs)
inlinenodiscard

Moves the state forward as predict(inputs...) does with a fixed-step model.

Returns
the step as a PredictedJoint, as predictJoint(dt, inputs...) does

◆ predictJoint() [2/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (!isFixedStep<Model_> && !IsForm)
PredictedJoint< SpaceType, Scalar > Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::predictJoint ( TimeStep< Scalar > auto const & dt,
Inputs const &... inputs )
inlinenodiscard

Moves the state forward as predict(dt, inputs...) does.

Returns
the step as a PredictedJoint: the state before it, the state after it, and their cross-covariance, for a GaussianHistory to record

◆ recursion()

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
RecursionType const & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::recursion ( ) const
inlinenodiscard
Returns
the recursion: the model and method the steps run with

◆ state() [1/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
StateType & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::state ( )
inlinenodiscard
Returns
the current state estimate, to change where it is between steps, as when inflating its covariance after a gap in the measurements. It must keep its space; setState() replaces it with another, and checks it.

◆ state() [2/2]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
StateType const & Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::state ( ) const
inlinenodiscard
Returns
the current state estimate

◆ update() [1/4]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Reference, typename Policy, typename Result>
std::expected< void, GaussianError > Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::update ( M const & measurement,
LinearizedAbout< Reference > const & about,
Policy const & policy,
Result & result )
inlinenodiscard

As update(measurement, about, policy), but into result, as update(measurement, policy, result) fills it.

Returns
nothing, or the error update(measurement, about, policy) would return

◆ update() [2/4]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Reference, typename Policy = reductions::Collapse>
auto Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::update ( M const & measurement,
LinearizedAbout< Reference > const & about,
Policy const & policy = {} )
inlinenodiscard

Conditions the state on measurement as update(measurement, policy) does, with the measurement linearized about about, a point or a distribution (see linearizedAbout()), rather than at the state (decisions.md [linearization-reference]): a shadow filter's update about another filter's state, which with predict(dt, about) runs the shadow's whole step about it.

Returns
as update(measurement, policy) does

◆ update() [3/4]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Policy, typename Result>
requires (!internal::isLinearizedAbout<Policy>)
std::expected< void, GaussianError > Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::update ( M const & measurement,
Policy const & policy,
Result & result )
inlinenodiscard

As update(measurement, policy), but into result, the update inside the std::expected that update() returns, whose storage is reused: with the same result at every step, an update under mixture noise, whose branches are a run-time count, allocates nothing once the result has held as many (see Allocation).

Returns
nothing, or the error update(measurement, policy) would return

◆ update() [4/4]

template<KalmanState Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, RecursionOver< Component_ > Recursion_ = KalmanRecursion<Component_, Model_, Method_>, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Policy = reductions::Collapse>
requires (!internal::isLinearizedAbout<Policy>)
auto Emrys::KalmanFilter< Component_, Model_, Method_, Recursion_, Upkeep_ >::update ( M const & measurement,
Policy const & policy = {} )
inlinenodiscard

Conditions the state on measurement, by the measurement's own method, and reduces the branches to the new state with policy, a ReductionPolicy to the component (Collapse, Select, or a Gate).

If the policy rejects the measurement, the state is unchanged.

Returns
the update before reduction, without its posteriors, and whether it was accepted (see FilterUpdate): each noise component's posterior probability, such as the probability that the measurement is an outlier, and its innovation, from which the observation's log-likelihood and the NIS follow when asked for (decisions.md [innovation-statistics]), in a std::expected that is empty, with the state unchanged, when the update could not be taken (see KalmanRecursion::update())

The documentation for this class was generated from the following file: