A flexible framework for building readable, highly optimized Kalman-like filters with C++
APACHE-2.0 License
This is a C++ template-based header library for building Kalman-style filters, using Eigen linear algebra data types and algorithms.
n is the dimension of the state.
m is the dimension of (a given) measurement. Note that there can be more than
one measurement type involved, that's fine (and desirable!) - only one goes into
the FlexibleKalmanFilter::correct()
function at a time, where most of these
are used.
Variable | Rows | Cols | Meaning |
---|---|---|---|
x | n | 1 | State |
P | n | n | State error covariance |
Q(dt) | n | n | Process model noise covariance |
z | m | 1 | Measurement |
H | m | n | Measurement Jacobian |
R | m | m | Measurement error covariance |
K | n | m | Kalman gain |
There are separate types for State, Process Model, and one or more Measurement types.
The types involved are loosely related/coupled: the Process Model has to know about the particular State type (in some way), but not vice-versa, and the Measurement needs to know (in some way) about State (more about that below), but about no other Measurement types, or the Process Model.
They are all tied together in the FlexibleKalmanFilter<>
template object,
which holds both an instance of Process Model and State, and has a function that
takes a (deduced, templated) Measurement argument to its correct()
method.
Process Model computations of predicted error covariance often take a similar
form, so a convenience free function
Matrix<n, n> predictErrorCovariance(State const& state, ProcessModel & model, double dt)
is provided. (If you must know: it performs A P Atranspose + Q(dt)
)It's
optional, but its usage is encouraged if your math matches what it does (as it
likely will), so its requirements are also listed below, in addition to the
requirements of the FlexibleKalmanFilter
class methods proper.
Also, a brief note: Most of these types will probably hold what Eigen refers to
as a "fixed-size vectorizable" member, so you'll almost certainly want this line
in the public
section of your types:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
or you'll get weird segfaults if something ends up not being aligned properly.
All the interface stuff below required should be public.
State types should derive publicly from StateBase<MyStateType>
where the
template parameter is their own type (the "Curiously-Recurring Template
Pattern").
Needs public members:
static constexpr size_t Dimension = 12;
void setStateVector(? state)
FlexibleKalmanFilter::correct()
Vector<n> stateVector() const
const
and willconst &
(but if you have a good reason, by value is OKFlexibleKalmanFilter::correct()
and likely many othervoid setErrorCovariance(? errorCovariance)
FlexibleKalmanFilter::correct()
Matrix<n,n> errorCovariance() const
const
andconst &
(but if you have a good reason, by value isFlexibleKalmanFilter::correct()
void postCorrect()
- called at the end of FlexibleKalmanFilter::correct()
Should not contain the filter state - that separate object is kept separately and passed as a parameter as needed. It may contain some state (member variables) of its own if required - typically configuration parameters, etc.
Process model types should derive publicly from
ProcessModelBase<MyProcessModelType>
where the template parameter is their own
type (the "Curiously-Recurring Template Pattern").
using State = YourStateType
FlexibleKalmanFilter<>
void predictState(State & state, double dt)
FlexibleKalmanFilter::predict(dt)
, should update the state todt
seconds. Often uses thepredictErrorCovariance
. Make this const, if youMatrix<n, n> getStateTransitionMatrix(State const&, double dt) const
predictState()
(if manual computation ispredictErrorCovariance
is used in predictState()
Matrix<n, n> getSampledProcessNoiseCovariance(double dt)
predictErrorCovariance
is used in predictState()
Note that there may (and often are) several types of measurements used in a
particular filter - the FlexibleKalmanFilter::correct()
method is a function
template so it can adjust to any number of measurement types. If you're having
difficulty, check your const
and try a named instance of your measurement as
opposed to passing a temporary.
Only FlexibleKalmanFilter::correct()
and functions related to it (other
correction functions like correctUnscented
, etc.) interacts with Measurement
types.
Measurement types should derive publicly from
MeasurementBase<MyMeasurementType>
where the template parameter is their own
type (the "Curiously-Recurring Template Pattern").
static constexpr size_t Dimension = 4;
Vector<m> getResidual(State const& state) const
Matrix<m,m> getCovariance(State & state) const
Matrix<m,n> getJacobian(State & s) const
Vector<m> predictMeasurement(State const& state) const
getResidual()
.Vector<m> getResidual(Vector<m> const &prediction, State const& state) const
getResidual()
, this version does not (usually)predictMeasurement()
.getResidual()
- it becomesreturn getResidual(predictMeasurement(s), s);
(often)In-code references are often to "Welch 1996" - this is to Greg Welch's PhD dissertation. Specifically, the citation follows, and the link is publicly accessible for the full text.
Portions of that dissertation were published as "Welch and Bishop 1997" - if you can get a copy of that paper, it is more condensed, but as far as I can tell everything we use is inside the dissertation. (The link is the canonical DOI link, you'll need some library subscription to get the paper from that place.)
Welch gives two citations for the "externalized-quaternion, internal linear incremental orientation" concept, only one of which I could get a copy of, and which is cited in the code. (As above, the link is the canonical one and behind a paywall.)
Library structure/design was inspired by
This project: Most files are licensed under the Apache License, Version 2.0. Some are licensed "BSL-1.0 OR Apache-2.0", and this is the preference for new additions.
This code originated as a part of tracking code from OSVR-Core. It was then extracted into a project providing the tracking capabilities standalone, UVBI-and-KalmanFramework-Standalone. Since this is by far the most reusable code from that, though, I then extracted it once more into this repository.
/cmake
- Git subtree from https://github.com/rpavlik/cmake-modules used at/vendor/eigen
- Unpacked release from http://eigen.tuxfamily.org/ -EIGEN_MPL2_ONLY
to exclude modules with other/vendor/catch-single-header-2.13.7
- Single-file version of the