pub struct BetweenFactor<T>{
pub relative_pose: T,
}Expand description
Generic between factor for Lie group pose constraints.
Represents a relative pose measurement between two poses of any Lie group manifold type. This is a generic implementation that works with SE(2), SE(3), SO(2), SO(3), and Rⁿ using static dispatch for zero runtime overhead.
§Type Parameter
T- The Lie group manifold type (e.g., SE2, SE3, SO2, SO3, Rn)
§Mathematical Formulation
Given two poses T_i and T_j in a Lie group, and a measurement T_ij, the residual is:
r = log(T_ij⁻¹ ⊕ T_i⁻¹ ⊕ T_j)where:
⊕is the Lie group composition operationlogis the logarithm map (converts from manifold to tangent space)- The residual dimensionality depends on the manifold’s degrees of freedom (DOF)
§Residual Dimensions by Manifold Type
- SE(3): 6D residual
[v_x, v_y, v_z, ω_x, ω_y, ω_z]- translation + rotation - SE(2): 3D residual
[dx, dy, dθ]- 2D translation + rotation - SO(3): 3D residual
[ω_x, ω_y, ω_z]- 3D rotation only - SO(2): 1D residual
[dθ]- 2D rotation only - Rⁿ: nD residual - Euclidean space
§Jacobian Computation
The Jacobian is computed analytically using the chain rule and Lie group derivatives:
J = ∂r/∂[T_i, T_j]The Jacobian dimensions are DOF × (2 × DOF) where DOF is the manifold’s degrees of freedom:
- SE(3): 6×12 matrix
- SE(2): 3×6 matrix
- SO(3): 3×6 matrix
- SO(2): 1×2 matrix
§Use Cases
- 3D SLAM: Visual odometry, loop closure constraints (SE3)
- 2D SLAM: Robot navigation, mapping (SE2)
- Pose graph optimization: Relative pose constraints (SE2, SE3)
- Orientation tracking: IMU fusion, attitude estimation (SO2, SO3)
- General manifold optimization: Custom manifolds (Rⁿ)
§Examples
§SE(3) - 3D Pose Graph
use apex_solver::factors::{Factor, BetweenFactor};
use apex_solver::manifold::se3::SE3;
use nalgebra::{Vector3, Quaternion, DVector};
let relative_pose = SE3::from_translation_quaternion(
Vector3::new(1.0, 0.0, 0.0),
Quaternion::new(1.0, 0.0, 0.0, 0.0),
);
let between = BetweenFactor::new(relative_pose);
let pose_i = DVector::from_vec(vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]);
let pose_j = DVector::from_vec(vec![0.95, 0.05, 0.0, 1.0, 0.0, 0.0, 0.0]);
let mut residual = vec![0.0f64; between.residual_dim()];
let (rows, cols) = between.jacobian_shape();
let mut jac_buf = vec![0.0f64; rows * cols];
let jac_mut = faer::mat::MatMut::from_column_major_slice_mut(&mut jac_buf, rows, cols);
between.linearize(&[pose_i.as_slice(), pose_j.as_slice()], &mut residual, Some(jac_mut));§SE(2) - 2D Pose Graph
use apex_solver::factors::{Factor, BetweenFactor};
use apex_solver::manifold::se2::SE2;
use nalgebra::DVector;
let relative_pose = SE2::from_xy_angle(1.0, 0.0, 0.1);
let between = BetweenFactor::new(relative_pose);
let pose_i = DVector::from_vec(vec![0.0, 0.0, 0.0]);
let pose_j = DVector::from_vec(vec![0.95, 0.05, 0.12]);
let mut residual = vec![0.0f64; between.residual_dim()];
between.linearize(&[pose_i.as_slice(), pose_j.as_slice()], &mut residual, None);§Performance
This generic implementation uses static dispatch (monomorphization), meaning:
- Zero runtime overhead compared to type-specific implementations
- Compiler optimizes each instantiation (
BetweenFactor<SE3>,BetweenFactor<SE2>, etc.) - All type checking happens at compile time
- No dynamic dispatch or virtual function calls
Fields§
§relative_pose: TThe measured relative pose transformation between the two connected poses
Implementations§
Source§impl<T> BetweenFactor<T>
impl<T> BetweenFactor<T>
Sourcepub fn new(relative_pose: T) -> Self
pub fn new(relative_pose: T) -> Self
Create a new between factor from a relative pose measurement.
This is a generic constructor that works with any Lie group manifold type.
The type parameter T is typically inferred from the relative_pose argument.
§Arguments
relative_pose- The measured relative transformation between two poses
§Returns
A new BetweenFactor<T> instance
§Examples
§SE(3) Between Factor
use apex_solver::factors::BetweenFactor;
use apex_solver::manifold::se3::SE3;
// Create relative pose: move 2m in x, rotate 90° around z-axis
let relative = SE3::from_translation_euler(
2.0, 0.0, 0.0, // translation (x, y, z)
0.0, 0.0, std::f64::consts::FRAC_PI_2 // rotation (roll, pitch, yaw)
);
// Type is inferred as BetweenFactor<SE3>
let factor = BetweenFactor::new(relative);§SE(2) Between Factor
use apex_solver::factors::BetweenFactor;
use apex_solver::manifold::se2::SE2;
// Create relative 2D pose
let relative = SE2::from_xy_angle(1.0, 0.5, 0.1);
// Type is inferred as BetweenFactor<SE2>
let factor = BetweenFactor::new(relative);Trait Implementations§
Source§impl<T> Clone for BetweenFactor<T>
impl<T> Clone for BetweenFactor<T>
Source§fn clone(&self) -> BetweenFactor<T>
fn clone(&self) -> BetweenFactor<T>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<T> Factor for BetweenFactor<T>
impl<T> Factor for BetweenFactor<T>
Source§fn linearize(
&self,
params: &[&[f64]],
residual: &mut [f64],
jacobian: Option<MatMut<'_, f64>>,
)
fn linearize( &self, params: &[&[f64]], residual: &mut [f64], jacobian: Option<MatMut<'_, f64>>, )
Source§fn residual_dim(&self) -> usize
fn residual_dim(&self) -> usize
residual buffer).Source§fn jacobian_shape(&self) -> (usize, usize)
fn jacobian_shape(&self) -> (usize, usize)
(rows, cols) of the Jacobian — rows == residual_dim(), cols == sum of variable DOFs.Source§impl<T> PartialEq for BetweenFactor<T>
impl<T> PartialEq for BetweenFactor<T>
impl<T> StructuralPartialEq for BetweenFactor<T>
Auto Trait Implementations§
impl<T> Freeze for BetweenFactor<T>where
T: Freeze,
impl<T> RefUnwindSafe for BetweenFactor<T>where
T: RefUnwindSafe,
impl<T> Send for BetweenFactor<T>
impl<T> Sync for BetweenFactor<T>
impl<T> Unpin for BetweenFactor<T>where
T: Unpin,
impl<T> UnsafeUnpin for BetweenFactor<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for BetweenFactor<T>where
T: UnwindSafe,
Blanket Implementations§
impl<T> Allocation for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T, U> Imply<T> for U
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::RequestSource§impl<Src, Dst> LosslessTryInto<Dst> for Srcwhere
Dst: LosslessTryFrom<Src>,
impl<Src, Dst> LosslessTryInto<Dst> for Srcwhere
Dst: LosslessTryFrom<Src>,
Source§fn lossless_try_into(self) -> Option<Dst>
fn lossless_try_into(self) -> Option<Dst>
Source§impl<Src, Dst> LossyInto<Dst> for Srcwhere
Dst: LossyFrom<Src>,
impl<Src, Dst> LossyInto<Dst> for Srcwhere
Dst: LossyFrom<Src>,
Source§fn lossy_into(self) -> Dst
fn lossy_into(self) -> Dst
Source§impl<T> OverflowingAs for T
impl<T> OverflowingAs for T
Source§fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
Source§impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
Source§fn overflowing_cast_from(src: Src) -> (Dst, bool)
fn overflowing_cast_from(src: Src) -> (Dst, bool)
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> SaturatingAs for T
impl<T> SaturatingAs for T
Source§fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
Source§impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
Source§fn saturating_cast_from(src: Src) -> Dst
fn saturating_cast_from(src: Src) -> Dst
Source§impl<T> StrictAs for T
impl<T> StrictAs for T
Source§fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
Source§impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
Source§fn strict_cast_from(src: Src) -> Dst
fn strict_cast_from(src: Src) -> Dst
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.