Estimating the rocket altitude.
Estimating the rocket altitude.
In this example, we will estimate the altitude of a rocket. The rocket is equipped with an onboard altimeter that provides altitude measurements. In addition to an altimeter, the rocket is equipped with an accelerometer that measures the rocket acceleration. The accelerometer serves as a control input to the Kalman Filter. We assume constant acceleration dynamics. Accelerometers don't sense gravity. An accelerometer at rest on a table would measure 1g upwards, while accelerometers in free fall will measure zero. Thus, we need to subtract the gravitational acceleration constant g from each accelerometer measurement. The accelerometer measurement at time step n is: an = a − g + ϵ where:
#include "fcarouge/linalg.hpp"
#include <cassert>
#include <chrono>
#include <cmath>
namespace {
template <auto Size> using vector = column_vector<double, Size>;
template <auto Row, auto Column> using matrix = matrix<double, Row, Column>;
using altitude = double;
using acceleration = double;
using milliseconds = std::chrono::milliseconds;
using state = fcarouge::state<vector<2>>;
[[maybe_unused]] auto sample{[] {
state{0., 0.},
output<altitude>,
input<acceleration>,
estimate_uncertainty{{500., 0.}, {0., 500.}},
process_uncertainty{[]([[maybe_unused]] const vector<2> &x,
const milliseconds &delta_time) {
const auto dt{std::chrono::duration<double>(delta_time).count()};
return matrix<2, 2>{
{0.1 * 0.1 * dt * dt * dt * dt / 4, 0.1 * 0.1 * dt * dt * dt / 2},
{0.1 * 0.1 * dt * dt * dt / 2, 0.1 * 0.1 * dt * dt}};
}},
output_uncertainty{400.},
state_transition{[]([[maybe_unused]] const vector<2> &x,
[[maybe_unused]] const acceleration &u,
const milliseconds &delta_time) {
const auto dt{std::chrono::duration<double>(delta_time).count()};
return matrix<2, 2>{{1., dt}, {0., 1.}};
}},
input_control{[](const milliseconds &delta_time) {
const auto dt{std::chrono::duration<double>(delta_time).count()};
return vector<2>{0.0313, dt};
}},
prediction_types<milliseconds>};
const double gravity{-9.8};
const milliseconds delta_time{250};
filter.predict(delta_time, -gravity);
assert(std::abs(1 - filter.x()[0] / 0.3) < 0.03 &&
std::abs(1 - filter.x()[1] / 2.45) < 0.03 &&
"The state estimates expected at 3% accuracy.");
assert(std::abs(1 - filter.p()(0, 0) / 531.25) < 0.001 &&
std::abs(1 - filter.p()(0, 1) / 125) < 0.001 &&
std::abs(1 - filter.p()(1, 0) / 125) < 0.001 &&
std::abs(1 - filter.p()(1, 1) / 500) < 0.001 &&
"The estimate uncertainty expected at 0.1% accuracy.");
filter.update(-32.4);
assert(std::abs(1 - filter.x()[0] / -18.35) < 0.001 &&
std::abs(1 - filter.x()[1] / -1.94) < 0.001 &&
"The state estimates expected at 0.1% accuracy.");
assert(std::abs(1 - filter.p()(0, 0) / 228.2) < 0.001 &&
std::abs(1 - filter.p()(0, 1) / 53.7) < 0.001 &&
std::abs(1 - filter.p()(1, 0) / 53.7) < 0.001 &&
std::abs(1 - filter.p()(1, 1) / 483.2) < 0.001 &&
"The estimate uncertainty expected at 0.1% accuracy.");
filter.predict(delta_time, 39.72 + gravity);
assert(std::abs(1 - filter.x()[0] / -17.9) < 0.001 &&
std::abs(1 - filter.x()[1] / 5.54) < 0.001 &&
"The state estimates expected at 0.1% accuracy.");
assert(std::abs(1 - filter.p()(0, 0) / 285.2) < 0.001 &&
std::abs(1 - filter.p()(0, 1) / 174.5) < 0.001 &&
std::abs(1 - filter.p()(1, 0) / 174.5) < 0.001 &&
std::abs(1 - filter.p()(1, 1) / 483.2) < 0.001 &&
"The estimate uncertainty expected at 0.1% accuracy.");
const auto step{[&filter, &delta_time](altitude measured_altitude,
acceleration measured_acceleration) {
filter.update(measured_altitude);
filter.predict(delta_time, measured_acceleration);
}};
step(-11.1, 40.02 + gravity);
assert(std::abs(1 - filter.x()[0] / -12.3) < 0.002 &&
std::abs(1 - filter.x()[1] / 14.8) < 0.002 &&
"The state estimates expected at 0.2% accuracy.");
assert(std::abs(1 - filter.p()(0, 0) / 244.9) < 0.001 &&
std::abs(1 - filter.p()(0, 1) / 211.6) < 0.001 &&
std::abs(1 - filter.p()(1, 0) / 211.6) < 0.001 &&
std::abs(1 - filter.p()(1, 1) / 438.8) < 0.001 &&
"The estimate uncertainty expected at 0.1% accuracy.");
step(18., 39.97 + gravity);
step(22.9, 39.81 + gravity);
step(19.5, 39.75 + gravity);
step(28.5, 39.6 + gravity);
step(46.5, 39.77 + gravity);
step(68.9, 39.83 + gravity);
step(48.2, 39.73 + gravity);
step(56.1, 39.87 + gravity);
step(90.5, 39.81 + gravity);
step(104.9, 39.92 + gravity);
step(140.9, 39.78 + gravity);
step(148., 39.98 + gravity);
step(187.6, 39.76 + gravity);
step(209.2, 39.86 + gravity);
step(244.6, 39.61 + gravity);
step(276.4, 39.86 + gravity);
step(323.5, 39.74 + gravity);
step(357.3, 39.87 + gravity);
step(357.4, 39.63 + gravity);
step(398.3, 39.67 + gravity);
step(446.7, 39.96 + gravity);
step(465.1, 39.8 + gravity);
step(529.4, 39.89 + gravity);
step(570.4, 39.85 + gravity);
step(636.8, 39.9 + gravity);
step(693.3, 39.81 + gravity);
step(707.3, 39.81 + gravity);
filter.update(748.5);
assert(std::abs(1 - filter.p()(0, 0) / 49.3) < 0.001 &&
"At this point, the altitude uncertainty px = 49.3, which means that "
"the standard deviation of the prediction is square root of 49.3: "
"7.02m (remember that the standard deviation of the measurement is "
"20m).");
filter.predict(delta_time, 39.68 + gravity);
assert(std::abs(1 - filter.x()[0] / 831.5) < 0.001 &&
std::abs(1 - filter.x()[1] / 222.94) < 0.001 &&
"The state estimates expected at 0.1% accuracy.");
assert(std::abs(1 - filter.p()(0, 0) / 54.3) < 0.01 &&
std::abs(1 - filter.p()(0, 1) / 10.4) < 0.01 &&
std::abs(1 - filter.p()(1, 0) / 10.4) < 0.01 &&
std::abs(1 - filter.p()(1, 1) / 2.6) < 0.01 &&
"The estimate uncertainty expected at 1% accuracy.");
return 0;
}()};
}
}
The Kalman filter class and library top-level header.
Examples, tutorials, demonstrators of the library.
kalman(Arguments... arguments) -> kalman< kalman_internal::deduce_filter< Arguments... > >
Deduces the filter type from its declared configuration.