UKF: an unscented Kalman filter library written for embedded targets
Unscented Kalman Filter library for state and parameter estimation
At a glance
- What is it?
- The sfwa/ukf library is a header style C++ implementation of the unscented Kalman filter, with standard, square-root and parameter estimation variants, built on Eigen and written to avoid dynamic allocation. It suits an embedded engineer who needs a filter that fits in a small processor and does not want a general purpose numerical framework.
- Who is it for?
- UKF suits an embedded developer who needs a predictable, allocation free filter for state or parameter estimation and is prepared to specialise three class templates, and it does not suit a data analyst who wants a filter in a notebook, since there is no scripting interface here.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three filter variants, one templated interface
The library provides several different UKF implementations, and the readme lists them as three.
The standard Unscented Kalman Filter for state estimation, as originally described in the first reference, with extensions for quaternions as described in the second. The square-root Unscented Kalman Filter for state estimation, implemented as described in the third reference. And an optimised form of the square-root filter for parameter estimation, also as described in the third reference.
That last one matters for anyone doing system identification rather than tracking. The square-root formulation works with the covariance square root rather than the covariance itself, which is numerically better behaved when the covariance matrix is ill conditioned, and the parameter estimation variant applies that to the harder problem of estimating unknown parameters alongside states.
The design goal is stated plainly: the library aims to provide efficient UKF implementations for use on embedded systems, with a strong focus on minimal dependencies and avoiding non deterministic operations. That is a different set of priorities from a research library that maximises clarity or generality.
Two implementation choices support that goal. It makes heavy use of C++11 and C++14 features in an attempt to avoid any dynamic memory allocations and to maximise opportunities for compile time optimisations, and it depends on Eigen for the linear algebra, matrix and vector operations. Eigen is header only, which fits the minimal dependency goal, and it is where the matrix algebra actually comes from.
The filter can also be compiled in either single or double precision, chosen with a preprocessor definition, either UKF_SINGLE_PRECISION or UKF_DOUBLE_PRECISION. On an embedded target that is not a trivial choice, and having it as a compile time switch rather than a runtime option keeps the code size down.
The state vector is a list of typed fields, not a raw array
The usage model is the part of this library that differs most from a textbook implementation, and it is what makes the quaternion support work.
There are three main classes to specialise: the state vector, the measurement vector and the core. The readme points out that all the examples are derived from the unit tests, which is a good sign for anyone reading them, since the tests double as worked examples.
The state vector is made up of fields, each containing a key and a type. The stated reason is to allow the filter to handle quaternions in the state vector transparently.
enum MyFields {
LatLon,
Altitude,
Velocity,
Attitude
};
using MyStateVector = UKF::StateVector<
UKF::Field<LatLon, UKF::Vector<2>>,
UKF::Field<Altitude, real_t>,
UKF::Field<Velocity, UKF::Vector<3>>,
UKF::Field<Attitude, UKF::Quaternion>
>;So the state is a heterogeneous list, and the Attitude field is a quaternion alongside plain scalars and vectors. Underneath, the readme says these fields are all stored together as one contiguous Eigen column vector and all key lookups are done at compile time.
That last point is the one to hold on to. A filter with a fixed size state is a candidate for static allocation, and static allocation is what makes an embedded implementation possible. The ergonomic cost is that you cannot add a field to the state after the fact, which is a real constraint when you are iterating on a model, and a real benefit when the model has stopped changing.
The UKF scaling parameters are also compile time values, specialised per state vector type.
template <> constexpr real_t UKF::Parameters::AlphaSquared<MyStateVector> = 1e-6;The readme points to the second reference or to the comments in include/UKF/StateVector.h for what the parameters do.
Fixed or dynamic measurement vectors, and what that costs
The measurement vector is specialised the same way, with one choice that affects how your sensors have to behave.
The fixed version requires that all measurements are provided every filter iteration.
using MyMeasurementVector = UKF::FixedMeasurementVector<
UKF::Field<Accelerometer, UKF::Vector<3>>,
UKF::Field<Gyroscope, UKF::Vector<3>>,
UKF::Field<StaticPressure, real_t>,
UKF::Field<DynamicPressure, real_t>
>;The dynamic version allows for a filter where some measurements are not available at every iteration, and so should only be fused when they are available.
using MyMeasurementVector = UKF::DynamicMeasurementVector<
UKF::Field<Accelerometer, UKF::Vector<3>>,
UKF::Field<Gyroscope, UKF::Vector<3>>,
UKF::Field<StaticPressure, real_t>,
UKF::Field<DynamicPressure, real_t>
>;The readme is candid about the price: there is a small performance overhead for the dynamic measurement vector, but it does not do any dynamic memory allocation. That is a good trade for a sensor fusion system where, say, the barometer updates at 20 Hz and the gyroscope at 200 Hz, and a poor trade if every sensor is always present, because then the fixed version is strictly better.
The measurement model is specified per field rather than for the vector as a whole, and the readme explains why: it is to allow the expected measurement vector to be constructed for the dynamic measurement vector where not all measurements may be available each iteration. Each measurement model function takes a state vector as input, and the state vector type is provided to the measurement model specialisation as a template parameter, which allows a measurement vector class to be shared across multiple state vectors with a different process model defined for each.
That is a genuinely useful piece of design. In practice it means the physics of your sensors is written once and reused across several state formulations, which is exactly what you want when you are trying two different state vectors to see which one converges better.
The core, the process model and the integrator
The Core class holds the filter state and the step function, and it is specialised with the state vector, the measurement vector and an integrator.
using MyCore = UKF::Core<
MyStateVector,
MyMeasurementVector,
UKF::IntegratorRK4
>;The square-root variant swaps in a different core class and nothing else.
using MyCore = UKF::SquareRootCore<
MyStateVector,
MyMeasurementVector,
UKF::IntegratorRK4
>;The integrator is a template parameter because the process model is expressed as an ordinary differential equation and the library solves it with the integrator you name. The readme points at include/UKF/Integrator.h for the list of available integrators. Choosing RK4 is the obvious default for a continuous time system, and being able to swap it is what lets the same filter serve a discrete plant with a different scheme.
The process model is a user function that computes the derivative.
template <> template <>
ProcessModelTestStateVector ProcessModelTestStateVector::derivative<>() const {
ProcessModelTestStateVector temp;
/* Position derivative. */
temp.set_field<Position>(get_field<Velocity>());
/* Velocity derivative. */
temp.set_field<Velocity>(UKF::Vector<3>(0, 0, 0));
return temp;
}And the derivative function can also take arbitrary user specified inputs, which is how a known acceleration is fed into the model.
template <> template <>
ProcessModelTestStateVector ProcessModelTestStateVector::derivative<UKF::Vector<3>>(
const UKF::Vector<3>& acceleration) const {
ProcessModelTestStateVector temp;
/* Position derivative. */
temp.set_field<Position>(get_field<Velocity>());
/* Velocity derivative. */
temp.set_field<Velocity>(acceleration);
return temp;
}The set_field and get_field calls are the same field keyed access as the state vector, so the model code reads in terms of Position and Velocity rather than column indices. The specialisation syntax of template <> template <> reflects that the derivative is a member template of a class template, and it is the price of the zero allocation design.
The repository carries an examples/ directory with an ahrs/ example, which is an attitude heading reference system and therefore a quaternion and sensor fusion case, and an sfwa_ukf/ example. There is also a benchmark/ directory, a test/ directory and a patch/ directory.
Where the UKF library is the wrong tool
There are three cases where this is the wrong choice, and one where it is the only reasonable one.
The first wrong case is data analysis rather than embedded control. If your problem is smoothing a time series offline, fitting a model to recorded data or exploring a filter's behaviour interactively, this library is the wrong shape. Everything here is a compile time template, there is no scripting interface, and the unit tests are the documentation. A filter designed for exploration, with a runtime configurable state and a quick way to plot the covariance, is what that work needs.
The second is a linear system. The extended Kalman filter and the plain Kalman filter are simpler, and for a system that is genuinely linear the difference in accuracy is nil while the difference in code is large. The unscented filter earns its extra cost when the nonlinearity is real, and for some state estimates, quaternions in particular, the plain linear error propagation is badly wrong in a way the EKF also handles badly.
The third is a team that wants the filter to be a black box with a familiar API. The integration work is real. You specialise the state vector, the measurement vector, the core, the process model and each measurement model, and the readme says specialisations of the process model and measurement model must also be provided. That is several files of template code before the filter runs at all.
The case where it is right is embedded state estimation on a small processor, especially with quaternions in the state and a mix of sensor update rates. A library that avoids dynamic allocation, avoids non deterministic operations, depends only on a header only linear algebra library, lets you choose single or double precision at compile time, and handles a quaternion as a first class field is doing something that a general purpose matrix library wrapped in a filter class does not.
On the alternative, the difference in approach against a general purpose filter library is the direction of the abstraction. Most libraries take a vector of doubles and a covariance matrix and hand you back an updated pair, so the caller owns the layout, the sizes and the allocation, and a quaternion has to be shoehorned into a fixed size vector with a manual normalisation step after every update. This library inverts that: the state is a typed list of fields, the layout is decided at compile time, and the library knows that one of those fields is a quaternion and can handle it transparently. The cost is that you must specialise types rather than pass arrays, and the benefit is that the special cases the filter actually cares about are expressed once, at compile time, instead of at every call site.
The maintenance picture is worth stating. The last push was on 2026-04-21 and the repository has no GitHub releases, so there is no version to pin and no changelog of tagged releases to read. The licence is MIT, which is permissive and asks only for attribution, and the package is header style with the includes under include/UKF/.
Editorial conclusion
UKF suits an embedded developer who needs a predictable, allocation free filter for state or parameter estimation and is prepared to specialise three class templates, and it does not suit a data analyst who wants a filter in a notebook, since there is no scripting interface here. Before you adopt it, check that the measurement model specialisation pattern in the readme matches how your sensors deliver data, since that is where most of the integration work sits, and note that the last push was on 2026-04-21 with no tagged releases, so pin a commit rather than a version.
Frequently asked questions
What is the sfwa/ukf library?
It is a C++ library for the unscented Kalman filter, providing a standard implementation for state estimation, a square-root implementation, and an optimised square-root form for parameter estimation. It depends on Eigen and is written to avoid dynamic memory allocation.
How do I use the UKF library?
You specialise three class templates: the state vector, the measurement vector and the core. You also provide specialisations of the process model and the measurement model, and choose an integrator for the process model in the core specialisation.
What is the difference between the fixed and dynamic measurement vector in UKF?
The fixed measurement vector requires every measurement to be provided on every filter iteration, while the dynamic one allows some measurements to be unavailable at a given iteration and to be fused only when present. The dynamic version carries a small performance overhead but does not allocate dynamically.
How does the UKF library handle quaternions?
The state vector is a list of typed fields, and a field can be a UKF::Quaternion alongside scalars and vectors, so the filter handles quaternions in the state vector transparently. The fields are stored together as one contiguous Eigen column vector with compile time key lookups.
Can the UKF library run in single precision?
Yes. The filter can be compiled in either single or double precision by defining UKF_SINGLE_PRECISION or UKF_DOUBLE_PRECISION, which matters on an embedded target where code size and floating point throughput are limited.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sfwa-ukf)Was this useful?