gtsam/examples/ABC.h

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