smooth_feedback
Control and estimation on Lie groups
Loading...
Searching...
No Matches
ekf.hpp
1// Copyright (C) 2022 Petter Nilsson. MIT License.
2
3#pragma once
4
5#include <Eigen/Cholesky>
6#include <boost/numeric/odeint.hpp>
7#include <smooth/compat/odeint.hpp>
8#include <smooth/concepts/lie_group.hpp>
9#include <smooth/diff.hpp>
10
11namespace smooth::feedback {
12
27template<
28 LieGroup G,
29 diff::Type DiffType = diff::Type::Default,
30 template<typename...> typename Stpr = boost::numeric::odeint::euler>
31 requires(Dof<G> > 0)
32class EKF
33{
34public:
37 using CovT = Eigen::Matrix<Scalar<G>, Dof<G>, Dof<G>>;
38
45 void reset(const G & g, const CovT & P)
46 {
47 g_hat_ = g;
48 P_ = P;
49 }
50
54 G estimate() const { return g_hat_; }
55
59 CovT covariance() const { return P_; }
60
79 template<typename F, typename QDer>
80 void predict(F && f, const Eigen::MatrixBase<QDer> & Q, Scalar<G> tau, std::optional<Scalar<G>> dt = {})
81 {
82 const auto state_ode = [&f](const G & g, Tangent<G> & dg, Scalar<G> t) { dg = f(t, g); };
83
84 const auto cov_ode = [this, &f, &Q](const CovT & cov, CovT & dcov, Scalar<G> t) {
85 const auto f_x = [&f, &t]<typename _T>(const CastT<_T, G> & x) -> Tangent<CastT<_T, G>> { return f(t, x); };
86 const auto [fv, dr] = diff::dr<1, DiffType>(f_x, wrt(g_hat_));
87 const CovT A = -ad<G>(fv) + dr;
88 dcov = (A * cov + cov * A.transpose() + Q).template selfadjointView<Eigen::Upper>();
89 };
90
91 Scalar<G> t = 0;
92 const Scalar<G> dt_v = dt.value_or(2 * tau);
93 while (t + dt_v < tau) {
94 // step covariance first since it depends on g_hat_
95 cst_.do_step(cov_ode, P_, t, dt_v);
96 sst_.do_step(state_ode, g_hat_, t, dt_v);
97 t += dt_v;
98 }
99
100 // last step up to time t
101 cst_.do_step(cov_ode, P_, t, tau - t);
102 sst_.do_step(state_ode, g_hat_, t, tau - t);
103 }
104
116 template<typename F, typename RDev, Manifold Y = std::invoke_result_t<F, G>>
117 void update(F && h, const Y & y, const Eigen::MatrixBase<RDev> & R)
118 {
119 const auto [hval, H] = diff::dr<1, DiffType>(h, wrt(g_hat_));
120
121 using Result = std::decay_t<decltype(hval)>;
122
123 static_assert(Manifold<Result>, "h(x) is not a Manifold");
124
125 static constexpr Eigen::Index Ny = Dof<Result>;
126
127 static_assert(Ny > 0, "h(x) must be statically sized");
128
129 const Eigen::Matrix<Scalar<G>, Ny, Ny> S =
130 (H * P_.template selfadjointView<Eigen::Upper>() * H.transpose() + R).template triangularView<Eigen::Upper>();
131
132 // solve for Kalman gain
133 const Eigen::Matrix<Scalar<G>, Dof<G>, Ny> K =
134 S.template selfadjointView<Eigen::Upper>().ldlt().solve(H * P_).transpose();
135
136 // update estimate and covariance
137 g_hat_ += K * (y - hval);
138 P_ = ((CovT::Identity() - K * H) * P_).template selfadjointView<Eigen::Upper>();
139 }
140
141private:
142 // filter estimate and covariance
143 G g_hat_ = Default<G>();
144 CovT P_ = CovT::Identity();
145
146 // steppers for numerical ODE solutions
147 Stpr<G, Scalar<G>, Tangent<G>, Scalar<G>, boost::numeric::odeint::vector_space_algebra> sst_{};
148 Stpr<CovT, Scalar<G>, CovT, Scalar<G>, boost::numeric::odeint::vector_space_algebra> cst_{};
149};
150
151} // namespace smooth::feedback
Extended Kalman filter on Lie groups.
Definition: ekf.hpp:33
Eigen::Matrix< Scalar< G >, Dof< G >, Dof< G > > CovT
Definition: ekf.hpp:37
void predict(F &&f, const Eigen::MatrixBase< QDer > &Q, Scalar< G > tau, std::optional< Scalar< G > > dt={})
Propagate EKF through dynamics with covariance over a time interval .
Definition: ekf.hpp:80
void reset(const G &g, const CovT &P)
Reset the state of the EKF.
Definition: ekf.hpp:45
void update(F &&h, const Y &y, const Eigen::MatrixBase< RDev > &R)
Update EKF with a measurement where .
Definition: ekf.hpp:117
G estimate() const
Access filter state estimate.
Definition: ekf.hpp:54
CovT covariance() const
Access filter covariance.
Definition: ekf.hpp:59