Skip to content

Commit bbe3acd

Browse files
[sample] add liquid temperature example
1 parent 1a7c131 commit bbe3acd

File tree

4 files changed

+136
-64
lines changed

4 files changed

+136
-64
lines changed

include/fcarouge/kalman.hpp

+21-4
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,27 @@ class kalman
9494
return observation{ Identity<observation>()() };
9595
};
9696

97-
observation_noise_uncertainty (*noise_observation_r)();
98-
state_transition (*transition_state_f)(const PredictionArguments &...);
99-
process_noise_uncertainty (*noise_process_q)(const PredictionArguments &...);
100-
control (*transition_control_g)(const PredictionArguments &...);
97+
observation_noise_uncertainty (*noise_observation_r)() = []() {
98+
return observation_noise_uncertainty{};
99+
};
100+
101+
state_transition (*transition_state_f)(const PredictionArguments &...) =
102+
[](const PredictionArguments &...arguments) {
103+
static_cast<void>((arguments, ...));
104+
return state_transition{ Identity<state_transition>()() };
105+
};
106+
107+
process_noise_uncertainty (*noise_process_q)(const PredictionArguments &...) =
108+
[](const PredictionArguments &...arguments) {
109+
static_cast<void>((arguments, ...));
110+
return process_noise_uncertainty{};
111+
};
112+
113+
control (*transition_control_g)(const PredictionArguments &...) =
114+
[](const PredictionArguments &...arguments) {
115+
static_cast<void>((arguments, ...));
116+
return control{};
117+
};
101118

102119
inline constexpr void update(const output &output_z)
103120
{

sample/building_height.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,16 @@ namespace
3939
// Prediction
4040
// Now, we shall predict the next state based on the initialization values.
4141
assert(60 == k.state_x &&
42-
"Since our system's Dynamic Model is constant, i.e. the building "
42+
"Since our system's dynamic model is constant, i.e. the building "
4343
"doesn't change its height: 60 meters.");
4444
assert(225 == k.estimate_uncertainty_p &&
4545
"The extrapolated estimate uncertainty (variance) also doesn't "
4646
"change: 225");
4747

4848
// Measure and Update
49-
// The first measurement is: z1 = 48.54m.
50-
// Since the standard deviation (σ) of the altimeter measurement error is 5,
51-
// the variance (σ^2) would be 25, thus the measurement uncertainty is: r1
52-
// = 25.
49+
// The first measurement is: z1 = 48.54m. Since the standard deviation σ of
50+
// the altimeter measurement error is 5, the variance σ^2 would be 25, thus
51+
// the measurement uncertainty is: r1 = 25.
5352
k.noise_observation_r = []() {
5453
return kalman::observation_noise_uncertainty{ 5 * 5 };
5554
};
@@ -68,7 +67,7 @@ namespace
6867

6968
// After 10 measurements the filter estimates the height of the building
7069
// at 49.57m.
71-
assert(49.57 - 0.01 < k.state_x && k.state_x < 49.57 + 0.01 &&
70+
assert(49.57 - 0.001 < k.state_x && k.state_x < 49.57 + 0.001 &&
7271
"After 10 measurement and update iterations, the building estimated "
7372
"height is: 49.57m.");
7473

sample/liquid_temperature.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "fcarouge/kalman.hpp"
2+
3+
#include <cassert>
4+
5+
namespace fcarouge::sample
6+
{
7+
namespace
8+
{
9+
//! @test Estimating the Temperature of the Liquid in a Tank
10+
//!
11+
//! @copyright This example is transcribed from KalmanFilter.NET copyright Alex
12+
//! Becker.
13+
//!
14+
//! @see https://www.kalmanfilter.net/kalman1d.html#ex6
15+
//!
16+
//! @details We would like to estimate the temperature of the liquid in a tank.
17+
//! We assume that at steady state the liquid temperature is constant. However,
18+
//! some fluctuations in the true liquid temperature are possible. We can
19+
//! describe the system dynamics by the following equation: xn = T + wn where: T
20+
//! is the constant temperature wn is a random process noise with variance q.
21+
//! Let us assume a true temperature of 50 degrees Celsius. The measurements are
22+
//! taken every 5 seconds. The true liquid temperature at the measurement points
23+
//! is: 49.979°C, 50.025°C, 50°C, 50.003°C, 49.994°C, 50.002°C, 49.999°C,
24+
//! 50.006°C, 49.998°C, and 49.991°C. The set of measurements is: 49.95°C,
25+
//! 49.967°C, 50.1°C, 50.106°C, 49.992°C, 49.819°C, 49.933°C, 50.007°C,
26+
//! 50.023°C, and 49.99°C.
27+
//!
28+
//! @example liquid_temperature.cpp
29+
[[maybe_unused]] constexpr auto liquid_temperature{ []() {
30+
// One-dimensional filter, constant system dynamic model.
31+
using kalman = kalman<float>;
32+
kalman k;
33+
34+
// Initialization
35+
// Before the first iteration, we must initialize the Kalman filter and
36+
// predict the next state (which is the first state). We don't know what the
37+
// temperature of the liquid is, and our guess is 10°C.
38+
k.state_x = kalman::state{ 10 };
39+
40+
// Our guess is very imprecise, so we set our initialization estimate error σ
41+
// to 100. The estimate uncertainty of the initialization is the error
42+
// variance σ^2: p0,0 = 100^2 = 10,000. This variance is very high. If we
43+
// initialize with a more meaningful value, we will get faster Kalman filter
44+
// convergence.
45+
k.estimate_uncertainty_p = kalman::estimate_uncertainty{ 100 * 100 };
46+
47+
// Prediction
48+
// Now, we shall predict the next state based on the initialization values. We
49+
// think that we have an accurate model, thus we set the process noise
50+
// variance q to 0.0001.
51+
k.noise_process_q = []() {
52+
return kalman::process_noise_uncertainty{ 0.0001 };
53+
};
54+
55+
k.predict();
56+
57+
assert(10 == k.state_x &&
58+
"Since our model has constant dynamics, the predicted estimate is "
59+
"equal to the current estimate: x^1,0 = 10°C.");
60+
assert(10000.0001 - 0.001 < k.estimate_uncertainty_p &&
61+
k.estimate_uncertainty_p < 10000.0001 + 0.001 &&
62+
"The extrapolated estimate uncertainty (variance): p1,0 = p0,0 + q = "
63+
"10000 + 0.0001 = 10000.0001.");
64+
65+
// Measure and Update
66+
// The measurement value: z1 = 49.95°C. Since the measurement error is σ =
67+
// 0.1, the variance σ^2 would be 0.01, thus the measurement uncertainty is:
68+
// r1 = 0.01. The measurement error (standard deviation) is 0.1 degrees
69+
// Celsius.
70+
k.noise_observation_r = []() {
71+
return kalman::observation_noise_uncertainty{ 0.1 * 0.1 };
72+
};
73+
74+
k.update(49.95);
75+
76+
// And so on.
77+
k.predict();
78+
k.update(49.967);
79+
k.predict();
80+
k.update(50.1);
81+
k.predict();
82+
k.update(50.106);
83+
k.predict();
84+
k.update(49.992);
85+
k.predict();
86+
k.update(49.819);
87+
k.predict();
88+
k.update(49.933);
89+
k.predict();
90+
k.update(50.007);
91+
k.predict();
92+
k.update(50.023);
93+
k.predict();
94+
k.update(49.99);
95+
k.predict();
96+
97+
// The estimate uncertainty quickly goes down, after 10 measurements:
98+
assert(49.988 - 0.0001 < k.state_x && k.state_x < 49.988 + 0.0001 &&
99+
"The filter estimates the liquid temperature at 49.988°C.");
100+
assert(0.0013 - 0.0001 < k.estimate_uncertainty_p &&
101+
k.estimate_uncertainty_p < 0.0013 + 0.0001 &&
102+
"The estimate uncertainty is 0.0013, i.e. the estimate error standard "
103+
"deviation is: 0.036°C.");
104+
// So we can say that the liquid temperature estimate is: 49.988 ± 0.036°C.
105+
106+
return 0;
107+
}() };
108+
109+
} // namespace
110+
} // namespace fcarouge::sample

sample/sample.cpp

-54
This file was deleted.

0 commit comments

Comments
 (0)