Skip to content

Commit

Permalink
Merge 20178da into 76e9a4e
Browse files Browse the repository at this point in the history
  • Loading branch information
FrancoisCarouge committed Feb 17, 2024
2 parents 76e9a4e + 20178da commit b430f6e
Show file tree
Hide file tree
Showing 40 changed files with 667 additions and 445 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
# Kalman Filter

The Kalman filter is a Bayesian filter that uses multivariate Gaussians, a recursive state estimator, a linear quadratic estimator (LQE), and an Infinite Impulse Response (IIR) filter. It is a control theory tool applicable to signal estimation, sensor fusion, or data assimilation problems. The filter is applicable for unimodal and uncorrelated uncertainties. The filter assumes white noise, propagation and measurement functions are differentiable, and that the uncertainty stays centered on the state estimate. The filter is the optimal linear filter under assumptions. The filter updates estimates by multiplying Gaussians rather than integrating differential equations. The filter predicts estimates by adding Gaussians. The filter maintains an estimate of the state and its uncertainty over the sequential estimation process. The filter is named after Rudolf E. Kálmán, who was one of the primary developers of its theory in 1960.

Designing a filter is as much art as science, with the following recipe. Model the real world in state-space notation. Then, compute and select the fundamental matrices, select the states *X*, *P*, the processes *F*, *Q*, the measurements *Z*, *R*, the measurement function *H*, and if the system has control inputs *U*, *G*. Evaluate the performance and iterate.

This library supports various simple and extended filters. The implementation is independent from linear algebra backends. Arbitrary parameters can be added to the prediction and update stages to participate in gain-scheduling or linear parameter varying (LPV) systems. The default filter type is a generalized, customizable, and extended filter. The default type parameters implement a one-state, one-output, and double-precision floating-point type filter. The default update equation uses the Joseph form. Examples illustrate various usages and implementation tradeoffs. A standard formatter specialization is included for representation of the filter states. Filters with `state x output x input` dimensions as 1x1x1 and 1x1x0 (no input) are supported through vanilla C++. Higher dimension filters require a linear algebra backend. Customization points and type injections allow for implementation tradeoffs.

- [Kalman Filter](#kalman-filter)
- [Examples](#examples)
- [1x1 Constant System Dynamic Model Filter](#1x1-constant-system-dynamic-model-filter)
- [6x2 Constant Acceleration Dynamic Model Filter](#6x2-constant-acceleration-dynamic-model-filter)
Expand Down
4 changes: 1 addition & 3 deletions benchmark/predict_1x1x0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ namespace fcarouge::benchmark {
namespace {
//! @benchmark Measure predict, empty benchmark performance.
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<float, float>;

kalman filter;
kalman filter{state{0.F}, output<float>};

for (auto _ : benchmark_state) {
::benchmark::ClobberMemory();
Expand Down
6 changes: 3 additions & 3 deletions benchmark/predict_1x1x1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ namespace fcarouge::benchmark {
namespace {
//! @benchmark Measure predict, empty benchmark performance.
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<float, float, float>;

kalman filter;
kalman filter{state{0.F}, output<float>, input<float>};
std::random_device random_device;
std::mt19937 random_generator{random_device()};
std::uniform_real_distribution<float> uniformly_distributed;

using kalman = decltype(filter);

for (auto _ : benchmark_state) {
const typename kalman::output u{uniformly_distributed(random_generator)};

Expand Down
7 changes: 4 additions & 3 deletions benchmark/predict_linalg_x1x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ template <auto Size> using vector = column_vector<float, Size>;
//! states and inputs with the Eigen linear algebra backend.
template <auto StateSize, auto InputSize>
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<vector<StateSize>, float, vector<InputSize>>;

kalman filter;
kalman filter{state{vector<StateSize>{}}, output<float>,
input<vector<InputSize>>};
std::random_device random_device;
std::mt19937 random_generator{random_device()};
std::uniform_real_distribution<float> uniformly_distributed;

using kalman = decltype(filter);

for (auto _ : benchmark_state) {
float uv[InputSize];

Expand Down
6 changes: 3 additions & 3 deletions benchmark/update_1x1x0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ namespace fcarouge::benchmark {
namespace {
//! @benchmark Measure update, empty benchmark performance.
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<float, float>;

kalman filter;
kalman filter{state{0.F}, output<float>};
std::random_device random_device;
std::mt19937 random_generator{random_device()};
std::uniform_real_distribution<float> uniformly_distributed;

using kalman = decltype(filter);

for (auto _ : benchmark_state) {
const typename kalman::output z{uniformly_distributed(random_generator)};

Expand Down
6 changes: 3 additions & 3 deletions benchmark/update_1x1x1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ namespace fcarouge::benchmark {
namespace {
//! @benchmark Measure update, empty benchmark performance.
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<float, float, float>;

kalman filter;
kalman filter{state{0.F}, output<float>, input<float>};
std::random_device random_device;
std::mt19937 random_generator{random_device()};
std::uniform_real_distribution<float> uniformly_distributed;

using kalman = decltype(filter);

for (auto _ : benchmark_state) {
const typename kalman::output z{uniformly_distributed(random_generator)};

Expand Down
6 changes: 3 additions & 3 deletions benchmark/update_linalg_xx0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ template <auto Size> using vector = column_vector<float, Size>;
//! states and outputs with the Eigen linear algebra backend.
template <auto StateSize, auto OutputSize>
void bench(::benchmark::State &benchmark_state) {
using kalman = kalman<vector<StateSize>, vector<OutputSize>, void>;

kalman filter;
kalman filter{state{vector<StateSize>{}}, output<vector<OutputSize>>};
std::random_device random_device;
std::mt19937 random_generator{random_device()};
std::uniform_real_distribution<float> uniformly_distributed;

using kalman = decltype(filter);

for (auto _ : benchmark_state) {
float zv[OutputSize];

Expand Down
24 changes: 17 additions & 7 deletions documentation/Doxyfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Doxyfile 1.9.6
# Doxyfile 1.11.0

#---------------------------------------------------------------------------
# Project related configuration options
Expand Down Expand Up @@ -52,6 +52,7 @@ OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
MARKDOWN_ID_STYLE = GITHUB
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
Expand All @@ -65,6 +66,7 @@ INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 0
TIMESTAMP = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
Expand Down Expand Up @@ -117,7 +119,8 @@ WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
WARN_AS_ERROR = NO
WARN_IF_UNDOC_ENUM_VAL = NO
WARN_AS_ERROR = FAIL_ON_WARNINGS
WARN_FORMAT = "$file:$line: $text"
WARN_LINE_FORMAT = "at line $line of file $file"
WARN_LOGFILE =
Expand Down Expand Up @@ -222,9 +225,9 @@ HTML_COLORSTYLE = TOGGLE
HTML_COLORSTYLE_HUE = 120
HTML_COLORSTYLE_SAT = 90
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = YES
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = YES
HTML_CODE_FOLDING = YES
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
Expand All @@ -239,6 +242,7 @@ GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
SITEMAP_URL = https://francoiscarouge.github.io/Kalman/
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
Expand Down Expand Up @@ -292,7 +296,6 @@ USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
Expand Down Expand Up @@ -328,6 +331,12 @@ DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
GENERATE_SQLITE3 = NO
SQLITE3_OUTPUT = sqlite3
SQLITE3_RECREATE_DB = YES
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
Expand Down Expand Up @@ -355,9 +364,8 @@ ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
# Configuration options related to diagram generator tools
#---------------------------------------------------------------------------
DIA_PATH =
HIDE_UNDOC_RELATIONS = NO
HAVE_DOT = YES
DOT_NUM_THREADS = 0
Expand All @@ -384,7 +392,7 @@ DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = YES
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIA_PATH =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
Expand All @@ -394,3 +402,5 @@ MAX_DOT_GRAPH_DEPTH = 0
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
MSCGEN_TOOL =
MSCFILE_DIRS =
30 changes: 13 additions & 17 deletions include/fcarouge/internal/format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,49 +45,44 @@ For more information, please refer to <https://unlicense.org> */
#include <type_traits>

namespace fcarouge {
template <typename, typename, typename, typename, typename> class kalman;
template <typename> class kalman;
} // namespace fcarouge

template <typename State, typename Output, typename Input, typename UpdateTypes,
typename PredictionTypes, typename Char>
template <typename Filter, typename Char>
// It is allowed to add template specializations for any standard library class
// template to the namespace std only if the declaration depends on at least one
// program-defined type and the specialization satisfies all requirements for
// the original template, except where such specializations are prohibited.
// NOLINTNEXTLINE(cert-dcl58-cpp)
struct std::formatter<
fcarouge::kalman<State, Output, Input, UpdateTypes, PredictionTypes>,
Char> {
using kalman =
fcarouge::kalman<State, Output, Input, UpdateTypes, PredictionTypes>;

struct std::formatter<fcarouge::kalman<Filter>, Char> {
constexpr auto parse(std::basic_format_parse_context<Char> &parse_context) {
return parse_context.begin();
}

//! @todo P2585 may be useful in simplifying and standardizing the support.
template <typename OutputIt>
auto format(const kalman &filter,
auto format(const fcarouge::kalman<Filter> &filter,
std::basic_format_context<OutputIt, Char> &format_context) const
-> OutputIt {
format_context.advance_to(
format_to(format_context.out(), R"({{"f": {}, )", filter.f()));

if constexpr (fcarouge::internal::has_input_control_method<kalman>) {
if constexpr (fcarouge::has_input_control<Filter>) {
format_context.advance_to(
format_to(format_context.out(), R"("g": {}, )", filter.g()));
}

if constexpr (fcarouge::internal::has_output_model_method<kalman>) {
if constexpr (fcarouge::has_output_model<Filter>) {
format_context.advance_to(
format_to(format_context.out(), R"("h": {}, )", filter.h()));
}

format_context.advance_to(format_to(
format_context.out(), R"("k": {}, "p": {}, )", filter.k(), filter.p()));

{
constexpr auto end{fcarouge::internal::repack_s<PredictionTypes>};
if constexpr (fcarouge::internal::has_prediction_types<Filter>) {
constexpr auto end{
fcarouge::internal::repack_s<typename Filter::prediction_types>};
constexpr decltype(end) begin{0};
constexpr decltype(end) next{1};
fcarouge::internal::for_constexpr<begin, end, next>(
Expand All @@ -104,13 +99,14 @@ struct std::formatter<

//! @todo Generalize out internal method concept when MSVC has better
//! if-constexpr-requires support.
if constexpr (fcarouge::internal::has_input_method<kalman>) {
if constexpr (fcarouge::has_input<Filter>) {
format_context.advance_to(
format_to(format_context.out(), R"("u": {}, )", filter.u()));
}

{
constexpr auto end{fcarouge::internal::repack_s<UpdateTypes>};
if constexpr (fcarouge::internal::has_update_types<Filter>) {
constexpr auto end{
fcarouge::internal::repack_s<typename Filter::update_types>};
constexpr decltype(end) begin{0};
constexpr decltype(end) next{1};
fcarouge::internal::for_constexpr<begin, end, next>(
Expand Down
Loading

0 comments on commit b430f6e

Please sign in to comment.