#![allow(dead_code)]
pub mod nclt;
use apex_solver::apex_manifolds::LieGroup;
use apex_solver::apex_manifolds::se3::SE3;
use apex_solver::core::VarKey;
use apex_solver::core::noise::NoiseModel;
use apex_solver::core::problem::Problem;
use apex_solver::factors::imu::{
ImuMeasurement, ImuParameters, ImuPreintegration, ImuSensorReadings, SpeedAndBias,
};
use apex_solver::factors::pose::PriorFactor;
use apex_solver::optimizer::levenberg_marquardt::{LevenbergMarquardt, LevenbergMarquardtConfig};
use nalgebra::{DVector, UnitQuaternion, Vector3};
pub const G: f64 = 9.81;
pub const DT_IMU: f64 = 0.005;
pub struct Trajectory;
impl Trajectory {
pub fn position(t: f64) -> Vector3<f64> {
Vector3::new(4.0 * (0.5 * t).cos(), 4.0 * (0.5 * t).sin(), 0.5 * t)
}
pub fn velocity(t: f64) -> Vector3<f64> {
Vector3::new(-2.0 * (0.5 * t).sin(), 2.0 * (0.5 * t).cos(), 0.5)
}
pub fn acceleration(t: f64) -> Vector3<f64> {
Vector3::new(-(0.5 * t).cos(), -(0.5 * t).sin(), 0.0)
}
pub fn rotation(t: f64) -> UnitQuaternion<f64> {
UnitQuaternion::from_euler_angles(0.05 * (0.7 * t).sin(), 0.04 * (0.9 * t).cos(), 0.5 * t)
}
pub fn pose(t: f64) -> SE3 {
SE3::new(Self::position(t), Self::rotation(t))
}
}
pub fn imu_params() -> ImuParameters {
ImuParameters {
sigma_g_c: 1.6968e-04,
sigma_a_c: 2.0000e-03,
sigma_gw_c: 1.9393e-05,
sigma_aw_c: 3.0000e-03,
g: G,
..ImuParameters::default()
}
}
pub const BG_TRUE: [f64; 3] = [0.01, -0.005, 0.002];
pub const BA_TRUE: [f64; 3] = [0.05, -0.03, 0.04];
pub fn gyro_truth(t: f64) -> Vector3<f64> {
Vector3::new(
0.5 * 0.05 * (0.7 * t).cos(),
-0.5 * 0.04 * (0.9 * t).sin(),
0.5,
)
}
pub fn build_imu_dataset(times: &[f64]) -> (Vec<(SE3, SpeedAndBias)>, Vec<Vec<ImuMeasurement>>) {
let params = imu_params();
let mut state = (
SE3::new(
Trajectory::position(times[0]),
Trajectory::rotation(times[0]),
),
{
let mut sb = SpeedAndBias::zeros();
let v = Trajectory::velocity(times[0]);
sb[0] = v.x;
sb[1] = v.y;
sb[2] = v.z;
sb
},
);
let mut keyframes = vec![state.clone()];
let mut segments = Vec::new();
for w in times.windows(2) {
let n = ((w[1] - w[0]) / DT_IMU).round() as usize;
let mut samples: Vec<ImuMeasurement> = Vec::with_capacity(n + 1);
for i in 0..=n {
let t = w[0] + i as f64 * DT_IMU;
let r = state.0.rotation_so3().rotation_matrix();
let a_world = Trajectory::acceleration(t);
let a_body = r * (a_world + Vector3::new(0.0, 0.0, G));
samples.push(ImuMeasurement::new(
t,
ImuSensorReadings {
gyroscopes: gyro_truth(t) + Vector3::new(BG_TRUE[0], BG_TRUE[1], BG_TRUE[2]),
accelerometers: a_body + Vector3::new(BA_TRUE[0], BA_TRUE[1], BA_TRUE[2]),
},
));
if samples.len() >= 2 {
let start = samples.len() - 2;
ImuPreintegration::propagation(
&samples[start..],
¶ms,
&mut state.0,
&mut state.1,
t - DT_IMU,
t,
);
}
}
segments.push(samples);
keyframes.push(state.clone());
}
(keyframes, segments)
}
pub fn pose_to_dvector(pose: &SE3) -> DVector<f64> {
DVector::from_column_slice(pose.as_param_slice())
}
pub fn lm_solver(max_iterations: usize) -> LevenbergMarquardt {
let config = LevenbergMarquardtConfig::new()
.with_max_iterations(max_iterations)
.with_cost_tolerance(1e-12)
.with_parameter_tolerance(1e-10);
LevenbergMarquardt::with_config(config)
}
pub fn velocity_dvector(v: &Vector3<f64>) -> DVector<f64> {
DVector::from_vec(vec![v.x, v.y, v.z])
}
pub fn anchor_rn(problem: &mut Problem, key: VarKey, params: &[f64]) {
problem.add_residual_block_with_noise(
&[key],
Box::new(apex_solver::factors::pose::EuclideanPriorFactor::new(
DVector::from_vec(params.to_vec()),
)),
None,
NoiseModel::from_sigmas(&vec![1e-6; params.len()]).unwrap_or_else(|e| panic!("{e}")),
);
}
pub fn anchor_se3(problem: &mut Problem, key: VarKey, pose: &SE3) {
problem.add_residual_block_with_noise(
&[key],
Box::new(PriorFactor::new(pose.clone())),
None,
NoiseModel::from_sigmas(&[1e-6; 6]).unwrap_or_else(|e| panic!("{e}")),
);
}