Trivial Kalman Filter
Unidimensional trivial Kalman filter (header only, Arduino compatible) library
TrivialKalmanFilter.h
1 /*
2  * Unidimensional Kalman filter.
3  *
4  * Copyright (C) 2018 Damian Wrobel <dwrobel@ertelnet.rybnik.pl>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21  #pragma once
22  #include <math.h>
23 
24  /**
25  * @mainpage Unidimensional trivial Kalman filter (header only, Arduino compatible) library
26  *
27  * @image html DS18B20Test.svg
28  * - @ref TrivialKalmanFilter - API
29  * - Examples
30  * - @ref Example - How to use it
31  * - @ref DS18B20Test.ino - Arduino example
32  * - @ref DS18B20Test.csv - Sample data
33  * - @ref covariance.m - Octave based covariance calculator
34  * - <a href="https://github.com/dwrobel/TrivialKalmanFilter">Project page</a>
35  */
36 
37 /**
38  * Unidimensional Kalman filter
39  *
40  * Useful for reducing input signal noise (e.g. from the temperature sensor).
41  *
42  * Memory efficient, consumes only 4 x sizeof(float)) [bytes] of RAM memory.
43  * On (Arduino Nano 16MHz) execution of update() method takes about 100[us].
44  *
45  * @class TrivialKalmanFilter
46  * @tparam D type to be used for arithmetic operations (e.g. float or double).
47  * @example DS18B20Test.ino
48  * @see https://en.wikipedia.org/wiki/Kalman_filter#Details
49  */
50 template <typename D = float> class TrivialKalmanFilter {
51 public:
52  /**
53  * Instantiates Kalman filter
54  *
55  * @param Rk Estimation of the noise covariances (process)
56  * @param Qk Estimation of the noise covariances (observation)
57  *
58  * @see covariance.m - Covariance calculator for Rk parameter
59  * @see https://en.wikipedia.org/wiki/Kalman_filter#Estimation_of_the_noise_covariances_Qk_and_Rk
60  *
61  * Usage:
62  * @anchor Example
63  * @code
64  * TrivialKalmanFilter tkf<float>(4.7e-3, 1e-5);
65  *
66  * while (true) {
67  * const auto filteredValue = tkf.update(getRawValueFromSensor());
68  * // further processing of filteredValue
69  * }
70  * @endcode
71  */
72  TrivialKalmanFilter(const D Rk, const D Qk)
73  : Rk(Rk)
74  , Qk(Qk) {
75  reset();
76  }
77 
78  /**
79  * Updates Kalman filter
80  *
81  * @param zk measured value
82  *
83  * @return estimated value
84  *
85  * @see https://en.wikipedia.org/wiki/Kalman_filter#Predict
86  */
87  D update(const D zk) {
88  D xk = (Fk * xk_last) + (Bk * uk); // Predicted (a priori) state estimate
89  D Pk = (Fk * Pk_last * Fk_T) + Qk; // Predicted (a priori) error covariance
90  D yk = zk - (Hk * xk); // Innovation or measurement pre-fit residual
91  const D Sk = Rk + (Hk * Pk * Hk_T); // Innovation (or pre-fit residual) covariance
92  D Kk = (Pk * Hk_T) / Sk; // Optimal Kalman gain
93  xk = xk + (Kk * yk); // Updated (a posteriori) state estimate
94  Pk = (I - (Kk * Hk)) * Pk; // Updated (a posteriori) estimate covariance (a.k.a Joseph form)
95 
96 #if 0 // unused part
97  D yk = zk - (Hk_T * xk); // Measurement post-fit residual
98 #endif
99 
100  xk_last = xk;
101  Pk_last = Pk;
102 
103  return xk;
104  }
105 
106  /**
107  * Returns last estimated value
108  *
109  * @return last estimated value
110  */
111  D get() const {
112  return xk_last;
113  }
114 
115  /**
116  * Resets filter to its initial state
117  */
118  void reset(const D xk = 0, const D Pk = 1) {
119  xk_last = xk;
120  Pk_last = Pk;
121  }
122 
123 private:
124  // Assumes simplified model
125  static constexpr D k = 1;
126  static constexpr D Bk = 0;
127  static constexpr D uk = 0;
128  static constexpr D Fk = 1;
129  static constexpr D T = 1;
130  static constexpr D Fk_T = pow(Fk, T);
131  static constexpr D Hk = 1;
132  static constexpr D Hk_T = pow(Hk, T);
133  static constexpr D I = 1;
134 
135  const D Rk;
136  const D Qk;
137 
138  D xk_last;
139  D Pk_last;
140 };
TrivialKalmanFilter(const D Rk, const D Qk)
void reset(const D xk=0, const D Pk=1)