gtsam
Loading...
Searching...
No Matches
ManifoldEKF.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
24
25#pragma once
26
27#include <gtsam/base/Manifold.h> // Include for traits and IsManifold
28#include <gtsam/base/Matrix.h>
29#include <gtsam/base/Vector.h>
30
31#include <stdexcept>
32#include <string>
33#include <type_traits>
34
35namespace gtsam {
36
49template <typename M>
51 public:
53 static constexpr int Dim = traits<M>::dimension;
54
58 using Covariance = Eigen::Matrix<double, Dim, Dim>;
60 using Jacobian = Eigen::Matrix<double, Dim, Dim>;
61
67 ManifoldEKF(const M& X0, const Covariance& P0) : X_(X0) {
68 static_assert(IsManifold<M>::value,
69 "Template parameter M must be a GTSAM Manifold.");
70
72 if constexpr (Dim == Eigen::Dynamic) {
73 // Validate dimensions of initial covariance P0.
74 if (!isMatrixOfSize(P0, n_, n_)) {
75 throw std::invalid_argument(
76 "ManifoldEKF: Initial covariance P0 dimensions (" +
77 std::to_string(P0.rows()) + "x" + std::to_string(P0.cols()) +
78 ") do not match state's tangent space dimension (" +
79 std::to_string(n_) + ").");
80 }
81 I_ = Jacobian::Identity(n_, n_);
82 } else {
83 I_ = Jacobian::Identity();
84 }
85
86 P_ = P0;
87 }
88
89 virtual ~ManifoldEKF() = default;
90
92 const M& state() const { return X_; }
93
95 const Covariance& covariance() const { return P_; }
96
98 size_t dimension() const { return n_; }
99
114 void predict(const M& X_next, const Jacobian& F, const Covariance& Q) {
115 if constexpr (Dim == Eigen::Dynamic) {
116 if (!isMatrixOfSize(F, n_, n_) || !isMatrixOfSize(Q, n_, n_)) {
117 throw std::invalid_argument(
118 "ManifoldEKF::predict: Dynamic F/Q dimensions must match state "
119 "dimension " +
120 std::to_string(n_) + ".");
121 }
122 }
123
124 X_ = X_next;
125 P_ = F * P_ * F.transpose() + Q;
126 }
127
129 template <typename HMatrix, typename RMatrix>
130 auto KalmanGain(const HMatrix& H, const RMatrix& R) const {
131 auto S = H * P_ * H.transpose() + R; // Innovation covariance
132 return P_ * H.transpose() * S.inverse();
133 }
134
137 template <typename GainMatrix, typename HMatrix, typename RMatrix>
138 void JosephUpdate(const GainMatrix& K, const HMatrix& H, const RMatrix& R) {
139 Jacobian I_KH = I_ - K * H;
140 P_ = I_KH * P_ * I_KH.transpose() + K * R * K.transpose();
141 }
142
155 template <typename Measurement>
156 void update(
157 const Measurement& prediction,
158 const Eigen::Matrix<double, traits<Measurement>::dimension, Dim>& H,
159 const Measurement& z,
160 const Eigen::Matrix<double, traits<Measurement>::dimension,
162 bool performReset = true) {
163 static constexpr int MeasDim = traits<Measurement>::dimension;
164
165 // Innovation: y = h(x_pred) - z. In tangent space: local(z, h(x_pred))
166 // NOTE: we use the `z_hat - z` sign convention, NOT `z - z_hat`.
167 typename traits<Measurement>::TangentVector innovation =
168 traits<Measurement>::Local(z, prediction);
169
170 // Kalman Gain: K = P H^T S^-1
171 // K will be Eigen::Matrix<double, Dim, MeasDim>
172 Eigen::Matrix<double, Dim, MeasDim> K = KalmanGain(H, R);
173
174 // Correction vector in tangent space of M: delta_xi = K * innovation
175 const TangentVector delta_xi =
176 -K * innovation; // delta_xi is Dim x 1 (or n_ x 1 if dynamic)
177
178 // --- Update covariance in the tangent space at the current state
179 this->JosephUpdate(K, H, R);
180
181 // Update state using retract/ transport or just retract
182 if (performReset)
183 reset(delta_xi);
184 else
185 X_ = traits<M>::Retract(X_, delta_xi);
186 }
187
199 template <typename Measurement, typename MeasurementFunction>
200 void update(MeasurementFunction&& h, const Measurement& z,
201 const Eigen::Matrix<double, traits<Measurement>::dimension,
203 bool performReset = true) {
204 static_assert(IsManifold<Measurement>::value,
205 "Template parameter Measurement must be a GTSAM Manifold.");
206
207 // Predict measurement and get Jacobian H = dh/dlocal(X)
209 Measurement prediction = h(X_, H);
210
211 // Call the other update function
212 update<Measurement>(prediction, H, z, R, performReset);
213 }
214
226 void updateWithVector(const gtsam::Vector& prediction, const Matrix& H,
227 const gtsam::Vector& z, const Matrix& R,
228 bool performReset = true) {
229 validateInputs(prediction, H, z, R);
230 update<Vector>(prediction, H, z, R, performReset);
231 }
232
241 void reset(const TangentVector& eta) {
242 if constexpr (HasRetractJacobian<M>::value) {
243 Jacobian B;
244 if constexpr (Dim == Eigen::Dynamic) B.resize(n_, n_);
245 X_ = traits<M>::Retract(X_, eta, &B);
246 P_ = B * P_ * B.transpose();
247 } else {
248 X_ = traits<M>::Retract(X_, eta);
249 // Covariance unchanged when Jacobian is not available.
250 }
251 }
252
253 protected:
255 void validateInputs(const gtsam::Vector& prediction, const Matrix& H,
256 const gtsam::Vector& z, const Matrix& R) {
257 const size_t m = static_cast<size_t>(prediction.size());
258 if (static_cast<size_t>(z.size()) != m) {
259 throw std::invalid_argument(
260 "ManifoldEKF::updateWithVector: prediction and z must have same "
261 "length.");
262 }
263 if (!isMatrixOfSize(H, m, n_)) {
264 throw std::invalid_argument(
265 "ManifoldEKF::updateWithVector: H must be m x n where m = "
266 "measurement size and n = state dimension.");
267 }
268 if (!isMatrixOfSize(R, m, m)) {
269 throw std::invalid_argument(
270 "ManifoldEKF::updateWithVector: R must be m x m where m = "
271 "measurement size.");
272 }
273 }
274
276 template <typename MatrixType>
277 static bool isMatrixOfSize(const MatrixType& matrix, size_t rows,
278 size_t cols) {
279 return static_cast<size_t>(matrix.rows()) == rows &&
280 static_cast<size_t>(matrix.cols()) == cols;
281 }
282
283 protected:
284 M X_;
287 size_t n_;
288
289 private:
290 // Detection helper: check if traits<T>::Retract(x, v, Jacobian*) is valid.
291 template <typename T, typename = void>
292 struct HasRetractJacobian : std::false_type {};
293 template <typename T>
294 struct HasRetractJacobian<
295 T, std::void_t<decltype(traits<T>::Retract(
296 std::declval<const T&>(),
297 std::declval<const typename traits<T>::TangentVector&>(),
298 (Jacobian*)nullptr))>> : std::true_type {};
299};
300
301} // namespace gtsam
typedef and functions to augment Eigen's MatrixXd
Base class and basic functions for Manifold types.
typedef and functions to augment Eigen's VectorXd
STL namespace.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
Extended Kalman Filter on a generic manifold M.
Definition ManifoldEKF.h:50
void update(const Measurement &prediction, const Eigen::Matrix< double, traits< Measurement >::dimension, Dim > &H, const Measurement &z, const Eigen::Matrix< double, traits< Measurement >::dimension, traits< Measurement >::dimension > &R, bool performReset=true)
Measurement update: Corrects the state and covariance using a pre-calculated predicted measurement an...
Definition ManifoldEKF.h:156
auto KalmanGain(const HMatrix &H, const RMatrix &R) const
Kalman gain K = P H^T S^-1.
Definition ManifoldEKF.h:130
Eigen::Matrix< double, Dim, Dim > Covariance
Definition ManifoldEKF.h:58
void validateInputs(const gtsam::Vector &prediction, const Matrix &H, const gtsam::Vector &z, const Matrix &R)
Validate inputs to update.
Definition ManifoldEKF.h:255
const M & state() const
Definition ManifoldEKF.h:92
Jacobian I_
Definition ManifoldEKF.h:286
void reset(const TangentVector &eta)
Reset step: retract the state by a tangent perturbation and, if available, transport the covariance f...
Definition ManifoldEKF.h:241
void update(MeasurementFunction &&h, const Measurement &z, const Eigen::Matrix< double, traits< Measurement >::dimension, traits< Measurement >::dimension > &R, bool performReset=true)
Measurement update: Corrects the state and covariance using a measurement model function.
Definition ManifoldEKF.h:200
static constexpr int Dim
Definition ManifoldEKF.h:53
G X_
Definition ManifoldEKF.h:284
Eigen::Matrix< double, Dim, Dim > Jacobian
Definition ManifoldEKF.h:60
size_t dimension() const
Definition ManifoldEKF.h:98
ManifoldEKF(const M &X0, const Covariance &P0)
Constructor: initialize with state and covariance.
Definition ManifoldEKF.h:67
static bool isMatrixOfSize(const MatrixType &matrix, size_t rows, size_t cols)
Check whether a matrix has the expected runtime dimensions.
Definition ManifoldEKF.h:277
void JosephUpdate(const GainMatrix &K, const HMatrix &H, const RMatrix &R)
Joseph-form covariance update in the current tangent space using a precomputed gain.
Definition ManifoldEKF.h:138
Covariance P_
Definition ManifoldEKF.h:285
const Covariance & covariance() const
Definition ManifoldEKF.h:95
typename traits< G >::TangentVector TangentVector
Definition ManifoldEKF.h:56
size_t n_
Definition ManifoldEKF.h:287
void predict(const M &X_next, const Jacobian &F, const Covariance &Q)
Basic predict step: Updates state and covariance given the predicted next state and the state transit...
Definition ManifoldEKF.h:114
void updateWithVector(const gtsam::Vector &prediction, const Matrix &H, const gtsam::Vector &z, const Matrix &R, bool performReset=true)
Convenience bridge for wrappers: vector measurement update calling update<Vector>.
Definition ManifoldEKF.h:226