use kornia_3d::pose::Pose3d;
use kornia_algebra::optim::{Factor, FactorError, FactorResult, LinearizationResult};
use kornia_algebra::{DMatF64, DVecF64, Mat3F64, SO3F64, Vec3F64};
use kornia_sensors::imu::{GRAVITY_MAGNITUDE, ImuBias, PreintegratedImu};
#[derive(Debug, Clone, Copy)]
pub struct KfConst {
pub r_bw: Mat3F64,
pub t_wb: Vec3F64,
}
impl KfConst {
pub fn new(pose_world_to_cam: &Pose3d, imu_t_bc: &Pose3d) -> Self {
let r_bc = imu_t_bc.rotation;
let r_cw = pose_world_to_cam.rotation;
let r_bw = r_bc * r_cw;
let t_bw = r_bc * pose_world_to_cam.translation + imu_t_bc.translation;
let t_wb = -(r_bw.transpose() * t_bw);
Self { r_bw, t_wb }
}
}
pub struct InertialInitFactor {
kf_i: KfConst,
kf_j: KfConst,
pim: PreintegratedImu,
sqrt_info: DMatF64,
is_mono: bool,
fixed_bias_vel: bool,
}
impl InertialInitFactor {
pub fn new(kf_i: KfConst, kf_j: KfConst, pim: PreintegratedImu, is_mono: bool) -> Self {
let sqrt_info = sqrt_information_9x9(&pim.covariance);
Self {
kf_i,
kf_j,
pim,
sqrt_info,
is_mono,
fixed_bias_vel: false,
}
}
pub fn with_fixed_bias_vel(mut self, fixed: bool) -> Self {
self.fixed_bias_vel = fixed;
self
}
}
impl Factor for InertialInitFactor {
fn linearize(
&self,
params: &[&[f32]],
compute_jacobian: bool,
) -> FactorResult<LinearizationResult> {
if params.len() != 6 {
return Err(FactorError::DimensionMismatch {
expected: 6,
actual: params.len(),
});
}
let v_i = vec3_from_f32(params[0])?;
let v_j = vec3_from_f32(params[1])?;
let bg = vec3_from_f32(params[2])?;
let ba = vec3_from_f32(params[3])?;
let gdir_arr = quat_array_from_f32(params[4])?;
if params[5].is_empty() {
return Err(FactorError::DimensionMismatch {
expected: 1,
actual: 0,
});
}
let scale = if self.is_mono {
params[5][0] as f64
} else {
1.0
};
let dt = self.pim.dt;
let r_bw_i = self.kf_i.r_bw;
let r_wb_j = self.kf_j.r_bw.transpose();
let bias = ImuBias {
gyro: bg,
accel: ba,
};
let d_rot = self.pim.delta_rotation_with_bias(&bias);
let d_vel = self.pim.delta_velocity_with_bias(&bias);
let d_pos = self.pim.delta_position_with_bias(&bias);
let rwg = SO3F64::from_array(gdir_arr).matrix();
let g_i = Vec3F64::new(0.0, 0.0, -GRAVITY_MAGNITUDE);
let g = rwg * g_i;
let e_r_mat = d_rot.transpose() * r_bw_i * r_wb_j;
let er = SO3F64::from_matrix(&e_r_mat).log();
let dv_world = scale * (v_j - v_i) - g * dt;
let ev = r_bw_i * dv_world - d_vel;
let dp_world = scale * (self.kf_j.t_wb - self.kf_i.t_wb - v_i * dt) - g * (0.5 * dt * dt);
let ep = r_bw_i * dp_world - d_pos;
let residual =
DVecF64::from_vec(vec![er.x, er.y, er.z, ev.x, ev.y, ev.z, ep.x, ep.y, ep.z]);
let jacobian = if compute_jacobian {
let mut jac = DMatF64::zeros(9, 16);
let jr_inv = SO3F64::right_jacobian(er).inverse();
if !self.fixed_bias_vel {
let d_er_d_bg =
(jr_inv * e_r_mat.transpose() * self.pim.d_rotation_d_bias_gyro) * -1.0;
set_block3(&mut jac, 0, 6, d_er_d_bg);
set_block3(&mut jac, 3, 0, r_bw_i * -scale);
set_block3(&mut jac, 3, 3, r_bw_i * scale);
set_block3(&mut jac, 3, 6, self.pim.d_velocity_d_bias_gyro * -1.0);
set_block3(&mut jac, 3, 9, self.pim.d_velocity_d_bias_accel * -1.0);
set_block3(&mut jac, 6, 0, r_bw_i * (-scale * dt));
set_block3(&mut jac, 6, 6, self.pim.d_position_d_bias_gyro * -1.0);
set_block3(&mut jac, 6, 9, self.pim.d_position_d_bias_accel * -1.0);
}
let d_g_d_theta = (rwg * SO3F64::hat(g_i)) * -1.0;
set_block3(&mut jac, 3, 12, (r_bw_i * d_g_d_theta) * dt * -1.0);
set_block3(
&mut jac,
6,
12,
(r_bw_i * d_g_d_theta) * (0.5 * dt * dt) * -1.0,
);
if self.is_mono {
let d_ev_d_s = r_bw_i * (v_j - v_i);
let d_ep_d_s = r_bw_i * (self.kf_j.t_wb - self.kf_i.t_wb - v_i * dt);
set_col3(&mut jac, 3, 15, d_ev_d_s);
set_col3(&mut jac, 6, 15, d_ep_d_s);
}
Some(jac)
} else {
None
};
let residual_w = &self.sqrt_info * &residual;
let jacobian_w = jacobian.map(|j| &self.sqrt_info * j);
let residual_f32: Vec<f32> = residual_w.iter().map(|&x| x as f32).collect();
let jacobian_f32 = jacobian_w.map(|j| {
let mut out = vec![0.0f32; 9 * 16];
for r in 0..9 {
for c in 0..16 {
out[r * 16 + c] = j[(r, c)] as f32;
}
}
out
});
Ok(LinearizationResult::new(residual_f32, jacobian_f32, 16))
}
fn residual_dim(&self) -> usize {
9
}
fn num_variables(&self) -> usize {
6
}
fn variable_local_dim(&self, idx: usize) -> usize {
[3, 3, 3, 3, 3, 1][idx]
}
}
pub struct WeightedZeroPrior {
pub sqrt_weight: f64,
}
impl Factor for WeightedZeroPrior {
fn linearize(
&self,
params: &[&[f32]],
compute_jacobian: bool,
) -> FactorResult<LinearizationResult> {
if params.len() != 1 || params[0].len() != 3 {
return Err(FactorError::DimensionMismatch {
expected: 3,
actual: params.first().map(|p| p.len()).unwrap_or(0),
});
}
let w = self.sqrt_weight as f32;
let residual = vec![params[0][0] * w, params[0][1] * w, params[0][2] * w];
let jacobian = if compute_jacobian {
Some(vec![w, 0.0, 0.0, 0.0, w, 0.0, 0.0, 0.0, w])
} else {
None
};
Ok(LinearizationResult::new(residual, jacobian, 3))
}
fn residual_dim(&self) -> usize {
3
}
fn num_variables(&self) -> usize {
1
}
fn variable_local_dim(&self, _idx: usize) -> usize {
3
}
}
fn vec3_from_f32(s: &[f32]) -> FactorResult<Vec3F64> {
if s.len() < 3 {
return Err(FactorError::DimensionMismatch {
expected: 3,
actual: s.len(),
});
}
Ok(Vec3F64::new(s[0] as f64, s[1] as f64, s[2] as f64))
}
fn quat_array_from_f32(s: &[f32]) -> FactorResult<[f64; 4]> {
if s.len() < 4 {
return Err(FactorError::DimensionMismatch {
expected: 4,
actual: s.len(),
});
}
Ok([s[0] as f64, s[1] as f64, s[2] as f64, s[3] as f64])
}
fn set_block3(jac: &mut DMatF64, row0: usize, col0: usize, m: Mat3F64) {
let a = m.to_cols_array(); for c in 0..3 {
for r in 0..3 {
jac[(row0 + r, col0 + c)] = a[c * 3 + r];
}
}
}
fn set_col3(jac: &mut DMatF64, row0: usize, col0: usize, v: Vec3F64) {
jac[(row0, col0)] = v.x;
jac[(row0 + 1, col0)] = v.y;
jac[(row0 + 2, col0)] = v.z;
}
fn sqrt_information_9x9(cov_col_major: &[f64; 81]) -> DMatF64 {
let cov = DMatF64::from_column_slice(9, 9, cov_col_major);
let cov_sym = (cov.clone() + cov.transpose()) * 0.5;
let info = cov_sym
.try_inverse()
.unwrap_or_else(|| DMatF64::identity(9, 9));
let info_sym = (info.clone() + info.transpose()) * 0.5;
match info_sym.cholesky() {
Some(c) => c.l(),
None => DMatF64::identity(9, 9),
}
}