gtsam/examples/ABC_EQF/main.cpp

195 lines
6.5 KiB
C++

//
// Created by darshan on 3/11/25.
//
#include "EqF.h"
#include "State.h"
#include "Input.h"
#include "Direction.h"
#include "Measurements.h"
#include "Data.h"
#include "utilities.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <chrono>
#include <cmath>
using namespace std;
using namespace gtsam;
// Simplified data loading function - in a real application, implement proper CSV parsing
std::vector<Data> loadSimulatedData() {
std::vector<Data> data_list;
double t = 0.0;
double dt = 0.01;
// Number of data points
int num_points = 100;
// Set up one calibration state
int n_cal = 1;
for (int i = 0; i < num_points; i++) {
t += dt;
// Create a simple sinusoidal trajectory
double angle = 0.1 * sin(t);
Rot3 R = Rot3::Rz(angle);
// Create a bias
Vector3 b(0.01, 0.02, 0.03);
// Create a calibration
std::vector<Rot3> S;
S.push_back(Rot3::Ry(0.05));
// State
State xi(R, b, S);
// Input (angular velocity)
Vector3 w(0.1 * cos(t), 0.05 * sin(t), 0.02);
Matrix Sigma_u = Matrix::Identity(6, 6) * 0.01;
Input u(w, Sigma_u);
// Measurements
std::vector<Measurement> measurements;
// Measurement 1 - from uncalibrated sensor
Vector3 d1_vec = Vector3(1, 0, 0).normalized(); // Known direction in global frame
Vector3 y1_vec = S[0].inverse().matrix() * R.inverse().matrix() * d1_vec; // Direction in sensor frame
Matrix3 Sigma1 = Matrix3::Identity() * 0.01;
measurements.push_back(Measurement(y1_vec, d1_vec, Sigma1, 0)); // cal_idx = 0
// Measurement 2 - from calibrated sensor
Vector3 d2_vec = Vector3(0, 1, 0).normalized(); // Known direction in global frame
Vector3 y2_vec = R.inverse().matrix() * d2_vec; // Direction in sensor frame
Matrix3 Sigma2 = Matrix3::Identity() * 0.01;
measurements.push_back(Measurement(y2_vec, d2_vec, Sigma2, -1)); // cal_idx = -1
// Add to data list
data_list.push_back(Data(xi, n_cal, u, measurements, 2, t, dt));
}
return data_list;
}
void runSimulation(EqF& filter, const std::vector<Data>& data) {
std::cout << "Starting simulation with " << data.size() << " data points..." << std::endl;
// Track time for performance measurement
auto start = std::chrono::high_resolution_clock::now();
// Store results for analysis
std::vector<double> times;
std::vector<Vector3> attitude_errors;
std::vector<Vector3> bias_errors;
std::vector<Vector3> calibration_errors;
for (const auto& d : data) {
// Propagation
try {
filter.propagation(d.u, d.dt);
} catch (const std::exception& e) {
std::cerr << "Propagation error at t=" << d.t << ": " << e.what() << std::endl;
continue;
}
// Update with measurements
for (const auto& y : d.y) {
try {
if (!std::isnan(y.y.d.unitVector().norm()) && !std::isnan(y.d.d.unitVector().norm())) {
filter.update(y);
}
} catch (const std::exception& e) {
std::cerr << "Update error at t=" << d.t << ": " << e.what() << std::endl;
}
}
// Get state estimate
State estimate = filter.stateEstimate();
// Compute errors
Vector3 att_error = Rot3::Logmap(d.xi.R.between(estimate.R));
Vector3 bias_error = estimate.b - d.xi.b;
Vector3 cal_error = Vector3::Zero();
if (!d.xi.S.empty() && !estimate.S.empty()) {
cal_error = Rot3::Logmap(d.xi.S[0].between(estimate.S[0]));
}
// Store results
times.push_back(d.t);
attitude_errors.push_back(att_error);
bias_errors.push_back(bias_error);
calibration_errors.push_back(cal_error);
// Print some info
if (d.t < 0.1 || fmod(d.t, 1.0) < d.dt) {
std::cout << "Time: " << d.t
<< ", Attitude error (deg): " << (att_error.norm() * 180.0/M_PI)
<< ", Bias error: " << bias_error.norm()
<< ", Calibration error (deg): " << (cal_error.norm() * 180.0/M_PI)
<< std::endl;
}
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
std::cout << "Simulation completed in " << elapsed.count() << " seconds" << std::endl;
// Print summary statistics
double avg_att_error = 0.0;
double avg_bias_error = 0.0;
double avg_cal_error = 0.0;
for (size_t i = 0; i < times.size(); i++) {
avg_att_error += attitude_errors[i].norm();
avg_bias_error += bias_errors[i].norm();
avg_cal_error += calibration_errors[i].norm();
}
avg_att_error /= times.size();
avg_bias_error /= times.size();
avg_cal_error /= times.size();
std::cout << "Average attitude error (deg): " << (avg_att_error * 180.0/M_PI) << std::endl;
std::cout << "Average bias error: " << avg_bias_error << std::endl;
std::cout << "Average calibration error (deg): " << (avg_cal_error * 180.0/M_PI) << std::endl;
}
int main(int argc, char** argv) {
std::cout << "ABC-EqF: Attitude-Bias-Calibration Equivariant Filter" << std::endl;
std::cout << "========================================================" << std::endl;
// Initialize filter
int n_cal = 1; // Number of calibration states
int n_sensors = 2; // Number of sensors
// Initial covariance - larger values for higher uncertainty
Matrix initialSigma = Matrix::Identity(6 + 3*n_cal, 6 + 3*n_cal);
initialSigma.diagonal().head<3>() = Vector3::Constant(0.5); // Attitude uncertainty
initialSigma.diagonal().segment<3>(3) = Vector3::Constant(0.1); // Bias uncertainty
initialSigma.diagonal().tail<3>() = Vector3::Constant(0.5); // Calibration uncertainty
std::cout << "Creating filter with " << n_cal << " calibration states..." << std::endl;
try {
// Create filter
EqF filter(initialSigma, n_cal, n_sensors);
// Generate simulated data
std::cout << "Generating simulated data..." << std::endl;
std::vector<Data> data = loadSimulatedData();
// Run simulation
runSimulation(filter, data);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
std::cout << "Done." << std::endl;
return 0;
}