Working with an Attitude

Attitude — the orientation of one frame relative to another — is deceptively subtle. A single wrong transpose or a swapped multiplication order silently corrupts a whole navigation or bundle-adjustment pipeline. This page builds engineering intuition for the representations (Euler, axis-angle, Rodrigues, quaternion), the group SO(3) with its exp / log maps, the kinematics, and how you carry a covariance for something that doesn't live in a vector space.

Symbols used on this page are defined in the Nomenclature → attitude & rotations.

Derived from the internal lecture "Working with an Attitude!!" (N. V. Adurthi, a commercial space operator) and its MATLAB visualizations, re-implemented here as interactive JS.

Rotations vs coordinate changes (active vs passive)

Two things get called "rotation" and confusing them is the #1 source of bugs:

  • Active rotation $R$ — physically rotates a vector within one fixed frame: $\mathbf{v}' = R\,\mathbf{v}$. Natural for physical motion (a camera boresight swinging in inertial space).
  • Passive rotation / coordinate change (DCM) $C$ — the vector stays put; you re-express its coordinates in another frame: $\mathbf{v}_B = C\,\mathbf{v}_A$. Needed to write Newton's laws in the body frame.

They are inverses of each other: $C = R^{-1} = R^{\top}$ (rotations are orthogonal). Pick one convention for all your math and code — most confusion is bookkeeping, not physics.

Intrinsic vs extrinsic composition

When you chain two rotations there are two interpretations, and they give different results:

  • Extrinsic (global): every rotation is about the original fixed axes. Sequential rotations left-multiply: $R = R_2\,R_1$.
  • Intrinsic (local): each subsequent rotation is about the already-rotated (body) axis. Sequential rotations right-multiply: $R = R_1\,R_2$.

So "rotate about z then about x" is $R_x R_z$ extrinsically but $R_z R_x$ intrinsically — completely different final frames. Computer-graphics tools (Blender, Omniverse) use extrinsic; robot joints often enforce intrinsic. Drag the angles and flip the toggle:

The same idea, on real hardware — a rocket rotating intrinsically vs extrinsically:

Intrinsic (rotations about body axes)

Extrinsic (rotations about global axes)

The rotation group SO(3)

Rotation matrices form the special orthogonal group

$$ SO(3) = \{\, R\in\mathbb{R}^{3\times3} : R^\top R = I,\ \det R = +1 \,\}. $$

This is a 3-dimensional curved manifold, not a vector space — you cannot simply add two rotation matrices and get a rotation. That single fact drives everything about attitude estimation.

The Lie algebra & the hat map

The tangent space at the identity is $\mathfrak{so}(3)$, the space of $3\times3$ skew-symmetric matrices. A rotation vector $\boldsymbol{\phi}=\phi\,\hat{\mathbf e}\in\mathbb{R}^3$ maps to a matrix by the hat (cross-product) operator:

$$ [\boldsymbol{\phi}]_\times = \begin{bmatrix} 0 & -\phi_3 & \phi_2\\ \phi_3 & 0 & -\phi_1\\ -\phi_2 & \phi_1 & 0\end{bmatrix}, \qquad [\boldsymbol{\phi}]_\times\,\mathbf{v} = \boldsymbol{\phi}\times\mathbf{v}. $$

The exponential map (Rodrigues' formula)

The exponential map takes a rotation vector to a rotation matrix. Because $[\hat{\mathbf e}]_\times^3 = -[\hat{\mathbf e}]_\times$, the matrix series collapses to the closed-form Rodrigues formula:

$$ R = \exp\big([\boldsymbol{\phi}]_\times\big) = I + \sin\phi\,[\hat{\mathbf e}]_\times + (1-\cos\phi)\,[\hat{\mathbf e}]_\times^2, \qquad \phi=\lVert\boldsymbol{\phi}\rVert. $$

The logarithm map

Its inverse recovers the axis-angle from a rotation matrix:

$$ \phi = \cos^{-1}\!\left(\frac{\operatorname{tr}(R)-1}{2}\right),\qquad [\hat{\mathbf e}]_\times = \frac{1}{2\sin\phi}\big(R - R^\top\big). $$

Why this matters: exp/log let you step off the manifold into the flat tangent space $\mathbb{R}^3$, do ordinary vector calculus (add errors, form a Gaussian, take a Jacobian), then map back. This is the mathematical engine behind the Multiplicative EKF below and behind pose-graph optimization and bundle adjustment in vision-based navigation.

Turn the algebra into a picture: set an axis and an angle, and watch exp produce the rotation matrix that carries the body frame there. Push the angle past 180° to see why log is only unique on $(-\pi,\pi]$ — the same rotation has many rotation-vector preimages, which is exactly why filters keep the error small and reset every step:

Attitude representations

One physical rotation, many coordinates. Each has trade-offs:

Euler angles

Three sequential rotations (e.g. yaw–pitch–roll, $Z$–$Y$–$X$). Minimal (3 numbers) and intuitive for actuators, but they suffer gimbal lock — the kinematics become singular when two axes align (pitch $=\pm90^\circ$). Interpreting a covariance in Euler angles is awkward because the mapping is nonlinear and order-dependent.

$$ R_{ZYX}(\psi,\theta,\varphi) = R_z(\psi)\,R_y(\theta)\,R_x(\varphi). $$

Principal rotation (axis-angle)

Euler's theorem: any orientation is a single rotation by angle $\Phi$ about a unit axis $\hat{\mathbf e}$ — the principal rotation vector $\boldsymbol{\phi}=\Phi\hat{\mathbf e}$. This is exactly the log of $R$. Singular kinematics at $\Phi=0$ (axis undefined).

Quaternions

The unit quaternion packs axis-angle without singularities:

$$ \mathbf{q} = \begin{bmatrix} q_0\\ \mathbf{q}_v\end{bmatrix} = \begin{bmatrix} \cos(\Phi/2)\\ \hat{\mathbf e}\,\sin(\Phi/2)\end{bmatrix},\qquad \lVert\mathbf{q}\rVert=1. $$

They live on the unit 3-sphere $S^3$ in 4-D, double-cover SO(3) ($\mathbf{q}$ and $-\mathbf{q}$ are the same rotation), and compose by Hamilton multiplication. Rotation of a vector and coordinate change:

$$ \mathbf{v}' = \mathbf{q}\otimes\mathbf{v}\otimes\mathbf{q}^{-1}\ (\text{active}),\qquad \mathbf{v}_B = \mathbf{q}^{-1}\otimes\mathbf{v}_A\otimes\mathbf{q}\ (\text{coordinate change}). $$

Singularity-free — the standard choice for the mean attitude state.

Classical & Modified Rodrigues Parameters

Two minimal (3-vector) parametrizations obtained by stereographic-type projection of the quaternion:

$$ \underbrace{\mathbf{g} = \hat{\mathbf e}\,\tan\!\frac{\Phi}{2} = \frac{\mathbf{q}_v}{q_0}}_{\text{CRP (Gibbs vector)}}, \qquad \underbrace{\boldsymbol{\sigma} = \hat{\mathbf e}\,\tan\!\frac{\Phi}{4} = \frac{\mathbf{q}_v}{1+q_0}}_{\text{MRP}}. $$
  • CRP is singular at $\Phi=180^\circ$ (denominator $q_0\to0$).
  • MRP pushes the singularity out to $\Phi=360^\circ$; a "shadow set" switch keeps it bounded.
Every 3-element parametrization of attitude has a singularity somewhere (you can't smoothly cover a curved 3-manifold with one 3-D chart). So use quaternions/DCM for the global mean, and use a minimal 3-vector (rotation vector / MRP) only for the local error in a filter.

See all five for the same rotation — and watch CRP diverge as $\Phi\to180^\circ$:

Attitude kinematics

An IMU reads body angular rate $\boldsymbol{\omega}$. The orientation evolves as:

Rotation matrix / DCM $$\dot R = R\,[\boldsymbol{\omega}]_\times$$

Singularity-free; 9 numbers with 6 constraints.

Quaternion $$\dot{\mathbf q} = \tfrac12\,\mathbf q \otimes \begin{bmatrix}0\\ \boldsymbol{\omega}\end{bmatrix}$$

Singularity-free; 4 numbers with 1 constraint.

Euler-angle kinematics involve a matrix that blows up at the gimbal-lock pitch — another reason to integrate quaternions/DCM and read out Euler angles only for display.

Covariance of an attitude — the Multiplicative EKF

A standard Kalman filter assumes the state lives in a vector space where mean and covariance share one flat plane. Attitude does not. The Multiplicative EKF (MEKF) (Markley, 2003) resolves this by splitting the state into a global mean (a quaternion/DCM, kept on the manifold) and a small local error (a rotation vector in the tangent plane, where the covariance lives):

$$ R = \delta R(\boldsymbol{\delta\theta})\;\bar R,\qquad \boldsymbol{\delta\theta}\sim\mathcal N(\mathbf 0, P),\qquad \delta R \approx I + [\boldsymbol{\delta\theta}]_\times. $$

The mean $\bar R$ carries the orientation on SO(3); the $3\times3$ covariance $P$ lives in the tangent plane at $\bar R$. After a measurement update the error mean is nonzero, so a reset step folds it back into $\bar R$ and re-centers the tangent plane — the covariance then lives in a slightly different plane.

Covariance propagation & the "constant in inertial frame" insight

With no noise or bias, propagating only initial-condition uncertainty under a constant body rate $\boldsymbol{\omega}$ gives the coupled flow (exactly the UQ MATLAB app's covP.m):

$$ \dot P = -[\boldsymbol{\omega}]_\times\,P - P\,[\boldsymbol{\omega}]_\times^{\!\top},\qquad \dot{\bar R} = \bar R\,[\boldsymbol{\omega}]_\times. $$

Watching $P$ in the body frame, its ellipsoid appears to tumble and change shape — which looks alarming, since with no process noise the uncertainty should be conserved. The resolution: the eigenvalues of $P$ are invariant, and $S = \bar R\,P\,\bar R^\top$ is constant — the covariance is stationary in the inertial frame; only its body-frame coordinates rotate. The same is true for an Euler-angle covariance.

This is the MATLAB visualization, re-implemented live — the 3σ ellipsoid rides the unit sphere at the current Euler axis while its eigenvalues stay put:

The original MATLAB-rendered animation of the same idea:

Representing attitude errors — which frame?

To express a small misalignment $\delta R$ you again choose global or local, and they give different frames (and different Jacobians):

$$ R' = \delta R\;R\quad(\text{global / left}),\qquad R' = R\;\delta R\quad(\text{local / right}). $$

This is the applying_misalignment MATLAB app — global vs local application side by side:

Jacobians for optimization

In nonlinear least squares over attitude (calibration, pose-graph, bundle adjustment) you perturb the estimate multiplicatively and differentiate w.r.t. the tangent-space error. The Jacobian of a rotated point $R\,\mathbf p$ w.r.t. a right (local) perturbation is

$$ \frac{\partial (R\,\exp([\boldsymbol{\delta\theta}]_\times)\,\mathbf p)}{\partial \boldsymbol{\delta\theta}} \bigg|_{0} = -R\,[\mathbf p]_\times, $$

and it is different for a left perturbation. Getting the side wrong is a classic, hard-to-debug optimization error — the whole reason to fix one convention across math and code.

Summary. Use a quaternion or DCM for the global mean (no singularities); carry the covariance in the tangent plane via a minimal rotation-vector error (MEKF); move between the manifold and the tangent space with exp / log; and pick one convention for active/passive and intrinsic/extrinsic everywhere.

Related: Extended Kalman Filter · Star Tracker (attitude measurement) · IMU (angular rate) · the same $SE(3)$/$SO(3)$ transforms appear in Perception.

Reference: F. L. Markley, "Attitude Error Representations for Kalman Filtering," J. Guidance, Control & Dynamics, 26(2), 2003.