260 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
| /**
 | |
|  * @file ABC.h
 | |
|  * @brief Core components for Attitude-Bias-Calibration systems
 | |
|  *
 | |
|  * This file contains fundamental components and utilities for the ABC system
 | |
|  * based on the paper "Overcoming Bias: Equivariant Filter Design for Biased
 | |
|  * Attitude Estimation with Online Calibration" by Fornasier et al.
 | |
|  * Authors: Darshan Rajasekaran & Jennifer Oum
 | |
|  */
 | |
| #ifndef ABC_H
 | |
| #define ABC_H
 | |
| #include <gtsam/base/Matrix.h>
 | |
| #include <gtsam/base/Vector.h>
 | |
| #include <gtsam/geometry/Rot3.h>
 | |
| #include <gtsam/geometry/Unit3.h>
 | |
| 
 | |
| namespace gtsam {
 | |
| namespace abc_eqf_lib {
 | |
| using namespace std;
 | |
| using namespace gtsam;
 | |
| //========================================================================
 | |
| // Utility Functions
 | |
| //========================================================================
 | |
| 
 | |
| //========================================================================
 | |
| // Utility Functions
 | |
| //========================================================================
 | |
| 
 | |
| /// Check if a vector is a unit vector
 | |
| 
 | |
| bool checkNorm(const Vector3& x, double tol = 1e-3);
 | |
| 
 | |
| /// Check if vector contains NaN values
 | |
| 
 | |
| bool hasNaN(const Vector3& vec);
 | |
| 
 | |
| /// Create a block diagonal matrix from two matrices
 | |
| 
 | |
| Matrix blockDiag(const Matrix& A, const Matrix& B);
 | |
| 
 | |
| /// Repeat a block matrix n times along the diagonal
 | |
| 
 | |
| Matrix repBlock(const Matrix& A, int n);
 | |
| 
 | |
| // Utility Functions Implementation
 | |
| 
 | |
| /**
 | |
|  * @brief Verifies if a vector has unit norm within tolerance
 | |
|  * @param x 3d vector
 | |
|  * @param tol optional tolerance
 | |
|  * @return Bool indicating that the vector norm is approximately 1
 | |
|  */
 | |
| bool checkNorm(const Vector3& x, double tol) {
 | |
|   return abs(x.norm() - 1) < tol || std::isnan(x.norm());
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Checks if the input vector has any NaNs
 | |
|  * @param vec A 3-D vector
 | |
|  * @return true if present, false otherwise
 | |
|  */
 | |
| bool hasNaN(const Vector3& vec) {
 | |
|   return std::isnan(vec[0]) || std::isnan(vec[1]) || std::isnan(vec[2]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Creates a block diagonal matrix from input matrices
 | |
|  * @param A Matrix A
 | |
|  * @param B Matrix B
 | |
|  * @return A single consolidated matrix with A in the top left and B in the
 | |
|  * bottom right
 | |
|  */
 | |
| Matrix blockDiag(const Matrix& A, const Matrix& B) {
 | |
|   if (A.size() == 0) {
 | |
|     return B;
 | |
|   } else if (B.size() == 0) {
 | |
|     return A;
 | |
|   } else {
 | |
|     Matrix result(A.rows() + B.rows(), A.cols() + B.cols());
 | |
|     result.setZero();
 | |
|     result.block(0, 0, A.rows(), A.cols()) = A;
 | |
|     result.block(A.rows(), A.cols(), B.rows(), B.cols()) = B;
 | |
|     return result;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Creates a block diagonal matrix by repeating a matrix 'n' times
 | |
|  * @param A A matrix
 | |
|  * @param n Number of times to be repeated
 | |
|  * @return Block diag matrix with A repeated 'n' times
 | |
|  */
 | |
| Matrix repBlock(const Matrix& A, int n) {
 | |
|   if (n <= 0) return Matrix();
 | |
| 
 | |
|   Matrix result = A;
 | |
|   for (int i = 1; i < n; i++) {
 | |
|     result = blockDiag(result, A);
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| //========================================================================
 | |
| // Core Data Types
 | |
| //========================================================================
 | |
| 
 | |
| /// Input struct for the Biased Attitude System
 | |
| 
 | |
| struct Input {
 | |
|   Vector3 w;              /// Angular velocity (3-vector)
 | |
|   Matrix Sigma;           /// Noise covariance (6x6 matrix)
 | |
|   static Input random();  /// Random input
 | |
|   Matrix3 W() const {     /// Return w as a skew symmetric matrix
 | |
|     return Rot3::Hat(w);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /// Measurement struct
 | |
| struct Measurement {
 | |
|   Unit3 y;           /// Measurement direction in sensor frame
 | |
|   Unit3 d;           /// Known direction in global frame
 | |
|   Matrix3 Sigma;     /// Covariance matrix of the measurement
 | |
|   int cal_idx = -1;  /// Calibration index (-1 for calibrated sensor)
 | |
| };
 | |
| 
 | |
| /// State class representing the state of the Biased Attitude System
 | |
| template <size_t N>
 | |
| class State {
 | |
|  public:
 | |
|   Rot3 R;                 // Attitude rotation matrix R
 | |
|   Vector3 b;              // Gyroscope bias b
 | |
|   std::array<Rot3, N> S;  // Sensor calibrations S
 | |
| 
 | |
|   /// Constructor
 | |
|   State(const Rot3& R = Rot3::Identity(), const Vector3& b = Vector3::Zero(),
 | |
|         const std::array<Rot3, N>& S = std::array<Rot3, N>{})
 | |
|       : R(R), b(b), S(S) {}
 | |
| 
 | |
|   /// Identity function
 | |
|   static State identity() {
 | |
|     std::array<Rot3, N> S_id{};
 | |
|     S_id.fill(Rot3::Identity());
 | |
|     return State(Rot3::Identity(), Vector3::Zero(), S_id);
 | |
|   }
 | |
|   /**
 | |
|    * Compute Local coordinates in the state relative to another state.
 | |
|    * @param other The other state
 | |
|    * @return Local coordinates in the tangent space
 | |
|    */
 | |
|   Vector localCoordinates(const State<N>& other) const {
 | |
|     Vector eps(6 + 3 * N);
 | |
| 
 | |
|     // First 3 elements - attitude
 | |
|     eps.head<3>() = Rot3::Logmap(R.between(other.R));
 | |
|     // Next 3 elements - bias
 | |
|     // Next 3 elements - bias
 | |
|     eps.segment<3>(3) = other.b - b;
 | |
| 
 | |
|     // Remaining elements - calibrations
 | |
|     for (size_t i = 0; i < N; i++) {
 | |
|       eps.segment<3>(6 + 3 * i) = Rot3::Logmap(S[i].between(other.S[i]));
 | |
|     }
 | |
| 
 | |
|     return eps;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Retract from tangent space back to the manifold
 | |
|    * @param v Vector in the tangent space
 | |
|    * @return New state
 | |
|    */
 | |
|   State retract(const Vector& v) const {
 | |
|     if (v.size() != static_cast<Eigen::Index>(6 + 3 * N)) {
 | |
|       throw std::invalid_argument(
 | |
|           "Vector size does not match state dimensions");
 | |
|     }
 | |
|     Rot3 newR = R * Rot3::Expmap(v.head<3>());
 | |
|     Vector3 newB = b + v.segment<3>(3);
 | |
|     std::array<Rot3, N> newS;
 | |
|     for (size_t i = 0; i < N; i++) {
 | |
|       newS[i] = S[i] * Rot3::Expmap(v.segment<3>(6 + 3 * i));
 | |
|     }
 | |
|     return State(newR, newB, newS);
 | |
|   }
 | |
| };
 | |
| 
 | |
| //========================================================================
 | |
| // Symmetry Group
 | |
| //========================================================================
 | |
| 
 | |
| /**
 | |
|  * Symmetry group (SO(3) |x so(3)) x SO(3) x ... x SO(3)
 | |
|  * Each element of the B list is associated with a calibration state
 | |
|  */
 | |
| template <size_t N>
 | |
| struct G {
 | |
|   Rot3 A;                 /// First SO(3) element
 | |
|   Matrix3 a;              /// so(3) element (skew-symmetric matrix)
 | |
|   std::array<Rot3, N> B;  /// List of SO(3) elements for calibration
 | |
| 
 | |
|   /// Initialize the symmetry group G
 | |
|   G(const Rot3& A = Rot3::Identity(), const Matrix3& a = Matrix3::Zero(),
 | |
|     const std::array<Rot3, N>& B = std::array<Rot3, N>{})
 | |
|       : A(A), a(a), B(B) {}
 | |
| 
 | |
|   /// Group multiplication
 | |
|   G operator*(const G<N>& other) const {
 | |
|     std::array<Rot3, N> newB;
 | |
|     for (size_t i = 0; i < N; i++) {
 | |
|       newB[i] = B[i] * other.B[i];
 | |
|     }
 | |
|     return G(A * other.A, a + Rot3::Hat(A.matrix() * Rot3::Vee(other.a)), newB);
 | |
|   }
 | |
| 
 | |
|   /// Group inverse
 | |
|   G inv() const {
 | |
|     Matrix3 Ainv = A.inverse().matrix();
 | |
|     std::array<Rot3, N> Binv;
 | |
|     for (size_t i = 0; i < N; i++) {
 | |
|       Binv[i] = B[i].inverse();
 | |
|     }
 | |
|     return G(A.inverse(), -Rot3::Hat(Ainv * Rot3::Vee(a)), Binv);
 | |
|   }
 | |
| 
 | |
|   /// Identity element
 | |
|   static G identity(int n) {
 | |
|     std::array<Rot3, N> B;
 | |
|     B.fill(Rot3::Identity());
 | |
|     return G(Rot3::Identity(), Matrix3::Zero(), B);
 | |
|   }
 | |
| 
 | |
|   /// Exponential map of the tangent space elements to the group
 | |
|   static G exp(const Vector& x) {
 | |
|     if (x.size() != static_cast<Eigen::Index>(6 + 3 * N)) {
 | |
|       throw std::invalid_argument("Vector size mismatch for group exponential");
 | |
|     }
 | |
|     Rot3 A = Rot3::Expmap(x.head<3>());
 | |
|     Vector3 a_vee = Rot3::ExpmapDerivative(-x.head<3>()) * x.segment<3>(3);
 | |
|     Matrix3 a = Rot3::Hat(a_vee);
 | |
|     std::array<Rot3, N> B;
 | |
|     for (size_t i = 0; i < N; i++) {
 | |
|       B[i] = Rot3::Expmap(x.segment<3>(6 + 3 * i));
 | |
|     }
 | |
|     return G(A, a, B);
 | |
|   }
 | |
| };
 | |
| }  // namespace abc_eqf_lib
 | |
| 
 | |
| template <size_t N>
 | |
| struct traits<abc_eqf_lib::State<N>>
 | |
|     : internal::LieGroupTraits<abc_eqf_lib::State<N>> {};
 | |
| 
 | |
| template <size_t N>
 | |
| struct traits<abc_eqf_lib::G<N>> : internal::LieGroupTraits<abc_eqf_lib::G<N>> {
 | |
| };
 | |
| 
 | |
| }  // namespace gtsam
 | |
| 
 | |
| #endif  // ABC_H
 |