Lie groups

Camera poses live in SE(3)\mathrm{SE}(3) and rotations in SO(3)\mathrm{SO}(3) — these are manifolds, not vector spaces. You cannot add two rotation matrices and get a rotation, so standard “update xx+Δxx \leftarrow x + \Delta x” optimization does not directly apply. Lie theory bridges this gap: each Lie group (the curved space of rotations/poses) has an associated Lie algebra (a flat vector space tangent to the group at identity), connected by the exponential and logarithm maps. Optimizers work in the flat algebra and map updates back onto the group.

so(3) and SO(3). The Lie algebra so(3)\mathfrak{so}(3) consists of 3×33 \times 3 skew-symmetric matrices [ϕ]×[\boldsymbol{\phi}]_\times, parameterized by a vector ϕR3\boldsymbol{\phi} \in \mathbb{R}^3 (axis times angle). The exponential map is Rodrigues’ rotation formula:

R=exp([ϕ]×)=I+sinϕϕ[ϕ]×+1cosϕϕ2[ϕ]×2R = \exp([\boldsymbol{\phi}]_\times) = I + \frac{\sin\|\boldsymbol{\phi}\|}{\|\boldsymbol{\phi}\|}[\boldsymbol{\phi}]_\times + \frac{1-\cos\|\boldsymbol{\phi}\|}{\|\boldsymbol{\phi}\|^2}[\boldsymbol{\phi}]_\times^2

The logarithm map log:SO(3)so(3)\log: \mathrm{SO}(3) \to \mathfrak{so}(3) is its inverse — it recovers the rotation vector from a rotation matrix.

se(3) and SE(3). For rigid-body poses, elements of se(3)\mathfrak{se}(3) are parameterized by ξ=[ρT,ϕT]TR6\boldsymbol{\xi} = [\boldsymbol{\rho}^T, \boldsymbol{\phi}^T]^T \in \mathbb{R}^6 (translation part ρ\boldsymbol{\rho}, rotation part ϕ\boldsymbol{\phi}):

ξ^=[[ϕ]×ρ0T0],T=exp(ξ^)=[exp([ϕ]×)Jρ0T1]\hat{\boldsymbol{\xi}} = \begin{bmatrix} [\boldsymbol{\phi}]_\times & \boldsymbol{\rho} \\ \mathbf{0}^T & 0 \end{bmatrix}, \qquad T = \exp(\hat{\boldsymbol{\xi}}) = \begin{bmatrix} \exp([\boldsymbol{\phi}]_\times) & J\boldsymbol{\rho} \\ \mathbf{0}^T & 1 \end{bmatrix}

where JJ is the left Jacobian of SO(3)\mathrm{SO}(3):

J=I+1cosϕϕ2[ϕ]×+ϕsinϕϕ3[ϕ]×2J = I + \frac{1 - \cos\|\boldsymbol{\phi}\|}{\|\boldsymbol{\phi}\|^2}[\boldsymbol{\phi}]_\times + \frac{\|\boldsymbol{\phi}\| - \sin\|\boldsymbol{\phi}\|}{\|\boldsymbol{\phi}\|^3}[\boldsymbol{\phi}]_\times^2

Why this parameterization wins. A pose has exactly 6 degrees of freedom, and ξR6\boldsymbol{\xi} \in \mathbb{R}^6 is a minimal, singularity-managed parameterization for local updates: no constraints to maintain (unlike a 3×43\times4 matrix or a unit quaternion), no gimbal lock (unlike Euler angles at their singularity). In SLAM optimization, each iteration solves for a small update ξ\boldsymbol{\xi} in the algebra and applies it as a perturbation on the group:

TTexp(ξ^)(right perturbation)orTexp(ξ^)T(left perturbation)T \leftarrow T \cdot \exp(\hat{\boldsymbol{\xi}}) \quad \text{(right perturbation)} \qquad \text{or} \qquad T \leftarrow \exp(\hat{\boldsymbol{\xi}}) \cdot T \quad \text{(left perturbation)}

Jacobians of residuals (e.g., reprojection error) are derived with respect to ξ\boldsymbol{\xi}, evaluated at ξ=0\boldsymbol{\xi} = 0.

A worked perturbation Jacobian

The whole calculus reduces to one move: expand exp\exp to first order, exp([δϕ]×)I+[δϕ]×\exp([\delta\boldsymbol{\phi}]_\times) \approx I + [\delta\boldsymbol{\phi}]_\times, and use [a]×b=[b]×a[\mathbf{a}]_\times \mathbf{b} = -[\mathbf{b}]_\times \mathbf{a}. For a rotated point RpR\mathbf{p} under a left perturbation:

exp([δϕ]×)Rp    (I+[δϕ]×)Rp=Rp+[δϕ]×Rp=Rp[Rp]×δϕ\exp([\delta\boldsymbol{\phi}]_\times)\, R\,\mathbf{p} \;\approx\; (I + [\delta\boldsymbol{\phi}]_\times) R \mathbf{p} = R\mathbf{p} + [\delta\boldsymbol{\phi}]_\times R\mathbf{p} = R\mathbf{p} - [R\mathbf{p}]_\times\, \delta\boldsymbol{\phi}

so (Rp)/δϕ=[Rp]×\partial(R\mathbf{p})/\partial\,\delta\boldsymbol{\phi} = -[R\mathbf{p}]_\times. The right-perturbation version follows the same two lines and gives R[p]×-R[\mathbf{p}]_\times. Chain this with the projection Jacobian π/p\partial\pi/\partial\mathbf{p} and you have derived the reprojection-error Jacobian used by every bundle-adjustment implementation — no matrix calculus tables required.

In code

Sophus is the standalone C++ implementation of this machinery (Eigen-based), and its API mirrors the math one-to-one:

#include <sophus/se3.hpp>

Eigen::Matrix<double, 6, 1> xi = ...;      // twist in se(3)
Sophus::SE3d T = Sophus::SE3d::exp(xi);    // exp map: algebra -> group
Eigen::Matrix<double, 6, 1> back = T.log();// log map: group -> algebra

T = T * Sophus::SE3d::exp(delta);          // right-perturbation update step

The same pattern is baked into every SLAM library: g2o’s SE3 vertices, Ceres’ manifolds (local parameterizations), and GTSAM’s Pose3. The machinery extends to Sim(3)\mathrm{Sim}(3) (pose + scale), which monocular SLAM uses for loop closure because scale drifts along the trajectory.

Common pitfalls

Why it matters for SLAM

Every optimization-based SLAM component — bundle adjustment, pose graph optimization, IMU preintegration, direct image alignment — differentiates residuals with respect to poses, and Lie group machinery is how that is done correctly. Residuals between poses are themselves expressed via log\log (as in pose graph costs log(Tij1Ti1Tj)2\|\log(T_{ij}^{-1} T_i^{-1} T_j)\|^2). If you cannot read exp\exp/log\log/perturbation notation fluently, back-end papers are unreadable; once you can, they all look pleasantly alike.

Hands-on