| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | /* ----------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * GTSAM Copyright 2010, Georgia Tech Research Corporation, | 
					
						
							|  |  |  |  * Atlanta, Georgia 30332-0415 | 
					
						
							|  |  |  |  * All Rights Reserved | 
					
						
							|  |  |  |  * Authors: Frank Dellaert, et al. (see THANKS for the full author list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * See LICENSE for the license information | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2025-04-28 09:11:30 +08:00
										 |  |  |  * @file IEKF_SE2Example.cpp | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  |  * @brief A left invariant Extended Kalman Filter example using a Lie group | 
					
						
							|  |  |  |  * odometry as the prediction stage on SE(2) and | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2025-04-28 09:11:30 +08:00
										 |  |  |  * This example uses the templated InvariantEKF class to estimate the state of | 
					
						
							|  |  |  |  * an object using odometry / GPS measurements The prediction stage of the | 
					
						
							|  |  |  |  * InvariantEKF uses a Lie Group element to propagate the stage in a discrete | 
					
						
							|  |  |  |  * InvariantEKF. For most cases, U = exp(u^ * dt) if u is a control vector that | 
					
						
							|  |  |  |  * is constant over the interval dt. However, if u is not constant over dt, | 
					
						
							|  |  |  |  * other approaches are needed to find the value of U. This approach simply | 
					
						
							|  |  |  |  * takes a Lie group element U, which can be found in various different ways. | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This data was compared to a left invariant EKF on SE(2) using identical | 
					
						
							|  |  |  |  * measurements and noise from the source of the InEKF plugin | 
					
						
							|  |  |  |  * https://inekf.readthedocs.io/en/latest/ Based on the paper "An Introduction
 | 
					
						
							|  |  |  |  * to the Invariant Extended Kalman Filter" by Easton R. Potokar, Randal W. | 
					
						
							|  |  |  |  * Beard, and Joshua G. Mangelson | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @date Apr 25, 2025 | 
					
						
							| 
									
										
										
										
											2025-04-27 00:33:05 +08:00
										 |  |  |  * @authors Scott Baker, Matt Kielo, Frank Dellaert | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-04-25 09:10:39 +08:00
										 |  |  | #include <gtsam/geometry/Pose2.h>
 | 
					
						
							| 
									
										
										
										
											2025-04-28 09:11:30 +08:00
										 |  |  | #include <gtsam/navigation/InvariantEKF.h>
 | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 09:10:39 +08:00
										 |  |  | #include <iostream>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace std; | 
					
						
							|  |  |  | using namespace gtsam; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | // Create a 2D GPS measurement function that returns the predicted measurement h
 | 
					
						
							|  |  |  | // and Jacobian H. The predicted measurement h is the translation of the state
 | 
					
						
							|  |  |  | // X.
 | 
					
						
							|  |  |  | Vector2 h_gps(const Pose2& X, OptionalJacobian<2, 3> H = {}) { | 
					
						
							|  |  |  |   return X.translation(H); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-04-28 09:11:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Define a InvariantEKF class that uses the Pose2 Lie group as the state and
 | 
					
						
							|  |  |  | // the Vector2 measurement type.
 | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | int main() { | 
					
						
							|  |  |  |   // // Initialize the filter's state, covariance, and time interval values.
 | 
					
						
							|  |  |  |   Pose2 X0(0.0, 0.0, 0.0); | 
					
						
							|  |  |  |   Matrix3 P0 = Matrix3::Identity() * 0.1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Create the filter with the initial state, covariance, and measurement
 | 
					
						
							|  |  |  |   // function.
 | 
					
						
							| 
									
										
										
										
											2025-04-28 09:11:30 +08:00
										 |  |  |   InvariantEKF<Pose2> ekf(X0, P0); | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Define the process covariance and measurement covariance matrices Q and R.
 | 
					
						
							|  |  |  |   Matrix3 Q = (Vector3(0.05, 0.05, 0.001)).asDiagonal(); | 
					
						
							| 
									
										
										
										
											2025-04-28 01:35:24 +08:00
										 |  |  |   Matrix2 R = I_2x2 * 0.01; | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Define odometry movements.
 | 
					
						
							|  |  |  |   // U1: Move 1 unit in X, 1 unit in Y, 0.5 radians in theta.
 | 
					
						
							|  |  |  |   // U2: Move 1 unit in X, 1 unit in Y, 0 radians in theta.
 | 
					
						
							|  |  |  |   Pose2 U1(1.0, 1.0, 0.5), U2(1.0, 1.0, 0.0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Create GPS measurements.
 | 
					
						
							|  |  |  |   // z1: Measure robot at (1, 0)
 | 
					
						
							|  |  |  |   // z2: Measure robot at (1, 1)
 | 
					
						
							|  |  |  |   Vector2 z1, z2; | 
					
						
							|  |  |  |   z1 << 1.0, 0.0; | 
					
						
							|  |  |  |   z2 << 1.0, 1.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Define a transformation matrix to convert the covariance into (theta, x, y)
 | 
					
						
							|  |  |  |   // form. The paper and data mentioned above uses a (theta, x, y) notation,
 | 
					
						
							|  |  |  |   // whereas GTSAM uses (x, y, theta). The transformation matrix is used to
 | 
					
						
							|  |  |  |   // directly compare results of the covariance matrix.
 | 
					
						
							|  |  |  |   Matrix3 TransformP; | 
					
						
							|  |  |  |   TransformP << 0, 0, 1, 1, 0, 0, 0, 1, 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Propagating/updating the filter
 | 
					
						
							|  |  |  |   // Initial state and covariance
 | 
					
						
							|  |  |  |   cout << "\nInitialization:\n"; | 
					
						
							|  |  |  |   cout << "X0: " << ekf.state() << endl; | 
					
						
							|  |  |  |   cout << "P0: " << TransformP * ekf.covariance() * TransformP.transpose() | 
					
						
							|  |  |  |        << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // First prediction stage
 | 
					
						
							|  |  |  |   ekf.predict(U1, Q); | 
					
						
							|  |  |  |   cout << "\nFirst Prediction:\n"; | 
					
						
							|  |  |  |   cout << "X: " << ekf.state() << endl; | 
					
						
							|  |  |  |   cout << "P: " << TransformP * ekf.covariance() * TransformP.transpose() | 
					
						
							|  |  |  |        << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // First update stage
 | 
					
						
							| 
									
										
										
										
											2025-04-26 23:47:46 +08:00
										 |  |  |   ekf.update(h_gps, z1, R); | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  |   cout << "\nFirst Update:\n"; | 
					
						
							|  |  |  |   cout << "X: " << ekf.state() << endl; | 
					
						
							|  |  |  |   cout << "P: " << TransformP * ekf.covariance() * TransformP.transpose() | 
					
						
							|  |  |  |        << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Second prediction stage
 | 
					
						
							|  |  |  |   ekf.predict(U2, Q); | 
					
						
							|  |  |  |   cout << "\nSecond Prediction:\n"; | 
					
						
							|  |  |  |   cout << "X: " << ekf.state() << endl; | 
					
						
							|  |  |  |   cout << "P: " << TransformP * ekf.covariance() * TransformP.transpose() | 
					
						
							|  |  |  |        << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Second update stage
 | 
					
						
							| 
									
										
										
										
											2025-04-26 23:47:46 +08:00
										 |  |  |   ekf.update(h_gps, z2, R); | 
					
						
							| 
									
										
										
										
											2025-04-26 03:32:31 +08:00
										 |  |  |   cout << "\nSecond Update:\n"; | 
					
						
							|  |  |  |   cout << "X: " << ekf.state() << endl; | 
					
						
							|  |  |  |   cout << "P: " << TransformP * ekf.covariance() * TransformP.transpose() | 
					
						
							|  |  |  |        << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } |