Rotation Representations: A Cross-Reference Guide

Comprehensive reference for conversion between rotation matrices, quaternions, Euler angles, and screw/axis-angle representations.
Author

Dieter Olson

Published

August 30, 2026

Overview

Every volume of Tangent-Space Methods uses rotations. Different representations are optimal for different purposes:

Representation DoF stored Pros Cons
Rotation matrix \(R \in SO(3)\) 9 numbers Intuitive; composition = multiplication Over-parameterized; need orthogonality constraint
Quaternion \(\mathbf{q} \in S^3\) 4 numbers No singularities; efficient composition Less intuitive; double cover of \(SO(3)\)
Euler angles \((\phi, \theta, \psi)\) 3 numbers Minimal; human-readable Gimbal lock; 12 conventions; non-unique
Axis-angle \((\hat{\mathbf{n}}, \theta)\) 4 numbers (or 3 compact) Geometric intuition Singularity at \(\theta = 0\)
Exponential coordinates \(\omega \in \mathfrak{so}(3)\) 3 numbers Lie algebra; first-order dynamics Non-unique for large angles

This reference follows Park & Lynch (Modern Robotics, 2017) in preferring the screw/exponential approach as the primary formalism, while showing equivalences to all other conventions.

Rotation Matrices

A rotation matrix \(R \in SO(3)\) satisfies \(R^\top R = I\) and \(\det(R) = +1\).

Composition: \(R_{AC} = R_{AB} R_{BC}\) (rotate \(B\)-to-\(C\) first, then \(A\)-to-\(B\)).

Inverse: \(R^{-1} = R^\top\) (orthogonality).

Rotation of a vector: \(\mathbf{v}' = R\mathbf{v}\).

Standard Rotation Matrices

Rotation by angle \(\theta\) about the \(x\), \(y\), \(z\) axes respectively:

\[R_x(\theta) = \begin{bmatrix}1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta \\ 0 & \sin\theta & \cos\theta\end{bmatrix}, \quad R_y(\theta) = \begin{bmatrix}\cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta\end{bmatrix}, \quad R_z(\theta) = \begin{bmatrix}\cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix}.\]

Euler Angles

ZYX (aerospace / yaw-pitch-roll): \(R = R_z(\psi) R_y(\theta) R_x(\phi)\).

ZYZ (physics / precession-nutation-spin): \(R = R_z(\alpha) R_y(\beta) R_z(\gamma)\).

WarningGimbal Lock

Euler angles become singular (gimbal lock) when the middle rotation equals \(\pm 90Β°\). For ZYX: singularity at \(\theta = \pm\pi/2\). Near singularity, small physical rotations require large Euler angle changes β€” numerical differentiation fails.

Rule of thumb: avoid Euler angles for dynamics integration. Use quaternions or exponential coordinates instead.

Converting Euler Angles to Rotation Matrix

ZYX convention: \[R = R_z(\psi) R_y(\theta) R_x(\phi) = \begin{bmatrix} c_\psi c_\theta & c_\psi s_\theta s_\phi - s_\psi c_\phi & c_\psi s_\theta c_\phi + s_\psi s_\phi \\ s_\psi c_\theta & s_\psi s_\theta s_\phi + c_\psi c_\phi & s_\psi s_\theta c_\phi - c_\psi s_\phi \\ -s_\theta & c_\theta s_\phi & c_\theta c_\phi \end{bmatrix},\] where \(c_\alpha = \cos\alpha\), \(s_\alpha = \sin\alpha\).

Axis-Angle Representation

A rotation by angle \(\theta\) about unit axis \(\hat{\mathbf{n}} = [n_x, n_y, n_z]^\top\):

\[R = I + \sin\theta \, [\hat{\mathbf{n}}]_\times + (1-\cos\theta) \, [\hat{\mathbf{n}}]_\times^2,\]

where \([\hat{\mathbf{n}}]_\times\) is the skew-symmetric matrix:

\[[\hat{\mathbf{n}}]_\times = \begin{bmatrix}0 & -n_z & n_y \\ n_z & 0 & -n_x \\ -n_y & n_x & 0\end{bmatrix}.\]

This is the Rodrigues formula (also called the matrix exponential): \(R = \exp(\theta [\hat{\mathbf{n}}]_\times)\).

Exponential Coordinates (Lie Algebra \(\mathfrak{so}(3)\))

The compact exponential coordinates store the rotation as a single 3-vector \(\boldsymbol{\omega} = \theta \hat{\mathbf{n}} \in \mathbb{R}^3\):

\[R = \exp([\boldsymbol{\omega}]_\times).\]

Key properties: - \(\boldsymbol{\omega} = \mathbf{0}\) gives \(R = I\) (identity) - The inverse map: \(\boldsymbol{\omega} = \log(R)\) (matrix logarithm of \(SO(3)\)) - Angular velocity in body frame: \(\dot{R} = R [\boldsymbol{\omega}_b]_\times\) - Angular velocity in world frame: \(\dot{R} = [\boldsymbol{\omega}_w]_\times R\)

This is the formalism used throughout Tangent-Space Methods and in the screw theory reference.

Quaternions

A unit quaternion \(\mathbf{q} = (w, x, y, z)\) with \(w^2 + x^2 + y^2 + z^2 = 1\) represents a rotation by angle \(\theta = 2\arccos(w)\) about axis \(\hat{\mathbf{n}} = (x,y,z)/\sin(\theta/2)\).

Quaternion to Rotation Matrix

\[R = \begin{bmatrix} 1-2(y^2+z^2) & 2(xy-wz) & 2(xz+wy) \\ 2(xy+wz) & 1-2(x^2+z^2) & 2(yz-wx) \\ 2(xz-wy) & 2(yz+wx) & 1-2(x^2+y^2) \end{bmatrix}.\]

Quaternion Composition

\[\mathbf{q}_1 \otimes \mathbf{q}_2 = \begin{bmatrix} w_1 w_2 - x_1 x_2 - y_1 y_2 - z_1 z_2 \\ w_1 x_2 + x_1 w_2 + y_1 z_2 - z_1 y_2 \\ w_1 y_2 - x_1 z_2 + y_1 w_2 + z_1 x_2 \\ w_1 z_2 + x_1 y_2 - y_1 x_2 + z_1 w_2 \end{bmatrix}.\]

Quaternion Derivative

In the Hamilton convention, left multiplication corresponds to angular velocity in the space (world) frame \(\boldsymbol{\omega}_s\), while right multiplication corresponds to the body frame \(\boldsymbol{\omega}_b\) (consistent with \(\dot{R} = R[\boldsymbol{\omega}_b]_\times\) above):

\[\dot{\mathbf{q}} = \frac{1}{2} \begin{bmatrix}0 \\ \boldsymbol{\omega}_s\end{bmatrix} \otimes \mathbf{q} \qquad\text{(space frame)}, \qquad \dot{\mathbf{q}} = \frac{1}{2}\, \mathbf{q} \otimes \begin{bmatrix}0 \\ \boldsymbol{\omega}_b\end{bmatrix} \qquad\text{(body frame)}.\]

NoteDouble Cover

Both \(\mathbf{q}\) and \(-\mathbf{q}\) represent the same rotation. When interpolating or integrating quaternions, always check that consecutive quaternions have positive dot product \(\mathbf{q}_1 \cdot \mathbf{q}_2 > 0\); if not, negate one before interpolating to take the short path.

Conversion Summary Table

From β†’ To Formula
\(R\) β†’ axis-angle \(\theta = \arccos\frac{\text{tr}(R)-1}{2}\), \(\hat{\mathbf{n}} = \frac{1}{2\sin\theta}\begin{bmatrix}R_{32}-R_{23}\\R_{13}-R_{31}\\R_{21}-R_{12}\end{bmatrix}\)
\(R\) β†’ quaternion \(w=\frac{1}{2}\sqrt{1+\text{tr}(R)}\), \((x,y,z)=\frac{1}{4w}(R_{32}-R_{23}, R_{13}-R_{31}, R_{21}-R_{12})\)
\(R\) β†’ ZYX Euler \(\psi=\text{atan2}(R_{21},R_{11})\), \(\theta=-\arcsin(R_{31})\), \(\phi=\text{atan2}(R_{32},R_{33})\)
quaternion β†’ \(R\) Quaternion-to-rotation-matrix formula (see Quaternion to Rotation Matrix above)
quaternion β†’ axis-angle \(\theta=2\arccos(w)\), \(\hat{\mathbf{n}}=(x,y,z)/\sin(\theta/2)\)
axis-angle β†’ \(R\) Rodrigues: \(R=I+\sin\theta[\hat{\mathbf{n}}]_\times+(1-\cos\theta)[\hat{\mathbf{n}}]_\times^2\)
ZYX Euler β†’ \(R\) \(R=R_z(\psi)R_y(\theta)R_x(\phi)\)

Python Implementation

import numpy as np


def skew(v):
    """Skew-symmetric matrix from 3-vector."""
    return np.array([[0, -v[2], v[1]],
                     [v[2], 0, -v[0]],
                     [-v[1], v[0], 0]])


def axis_angle_to_R(n_hat, theta):
    """Rodrigues formula: rotation matrix from axis n_hat and angle theta."""
    K = skew(n_hat)
    return np.eye(3) + np.sin(theta) * K + (1 - np.cos(theta)) * K @ K


def R_to_axis_angle(R):
    """Extract axis and angle from rotation matrix."""
    theta = np.arccos(np.clip((np.trace(R) - 1) / 2, -1, 1))
    if abs(theta) < 1e-10:
        return np.array([0, 0, 1.0]), 0.0
    n_hat = np.array([R[2, 1] - R[1, 2],
                      R[0, 2] - R[2, 0],
                      R[1, 0] - R[0, 1]]) / (2 * np.sin(theta))
    return n_hat, theta


def R_to_quaternion(R):
    """Convert rotation matrix to unit quaternion (w, x, y, z)."""
    tr = np.trace(R)
    if tr > 0:
        scale = 2 * np.sqrt(1 + tr)
        w = 0.25 * scale
        x = (R[2, 1] - R[1, 2]) / scale
        y = (R[0, 2] - R[2, 0]) / scale
        z = (R[1, 0] - R[0, 1]) / scale
    elif R[0, 0] > R[1, 1] and R[0, 0] > R[2, 2]:
        scale = 2 * np.sqrt(1 + R[0, 0] - R[1, 1] - R[2, 2])
        w = (R[2, 1] - R[1, 2]) / scale
        x = 0.25 * scale
        y = (R[0, 1] + R[1, 0]) / scale
        z = (R[0, 2] + R[2, 0]) / scale
    elif R[1, 1] > R[2, 2]:
        scale = 2 * np.sqrt(1 + R[1, 1] - R[0, 0] - R[2, 2])
        w = (R[0, 2] - R[2, 0]) / scale
        x = (R[0, 1] + R[1, 0]) / scale
        y = 0.25 * scale
        z = (R[1, 2] + R[2, 1]) / scale
    else:
        scale = 2 * np.sqrt(1 + R[2, 2] - R[0, 0] - R[1, 1])
        w = (R[1, 0] - R[0, 1]) / scale
        x = (R[0, 2] + R[2, 0]) / scale
        y = (R[1, 2] + R[2, 1]) / scale
        z = 0.25 * scale
    return np.array([w, x, y, z])


def quaternion_to_R(q):
    """Convert unit quaternion (w, x, y, z) to rotation matrix."""
    w, x, y, z = q / np.linalg.norm(q)
    return np.array([
        [1 - 2*(y**2 + z**2),     2*(x*y - w*z),     2*(x*z + w*y)],
        [    2*(x*y + w*z), 1 - 2*(x**2 + z**2),     2*(y*z - w*x)],
        [    2*(x*z - w*y),     2*(y*z + w*x), 1 - 2*(x**2 + y**2)]
    ])


def R_to_euler_zyx(R):
    """Extract ZYX Euler angles (psi, theta, phi) from rotation matrix."""
    psi = np.arctan2(R[1, 0], R[0, 0])
    theta = -np.arcsin(np.clip(R[2, 0], -1, 1))
    phi = np.arctan2(R[2, 1], R[2, 2])
    return psi, theta, phi


def euler_zyx_to_R(psi, theta, phi):
    """ZYX Euler angles to rotation matrix: R = Rz(psi) Ry(theta) Rx(phi)."""
    Rz = np.array([[np.cos(psi), -np.sin(psi), 0],
                   [np.sin(psi),  np.cos(psi), 0],
                   [0,            0,            1]])
    Ry = np.array([[ np.cos(theta), 0, np.sin(theta)],
                   [0,              1, 0             ],
                   [-np.sin(theta), 0, np.cos(theta)]])
    Rx = np.array([[1, 0,           0          ],
                   [0, np.cos(phi), -np.sin(phi)],
                   [0, np.sin(phi),  np.cos(phi)]])
    return Rz @ Ry @ Rx


# --- Verification: round-trip conversion ---
n_hat = np.array([1, 1, 1]) / np.sqrt(3)
theta = np.radians(47.3)
R_orig = axis_angle_to_R(n_hat, theta)

n_hat_back, theta_back = R_to_axis_angle(R_orig)
q = R_to_quaternion(R_orig)
R_from_q = quaternion_to_R(q)
psi, th, phi = R_to_euler_zyx(R_orig)
R_from_euler = euler_zyx_to_R(psi, th, phi)

axis_pi = np.array([-1.0, 2.0, -3.0])
axis_pi = axis_pi / np.linalg.norm(axis_pi)
q_pi = R_to_quaternion(axis_angle_to_R(axis_pi, np.pi))
expected_q_pi = np.concatenate(([0.0], axis_pi))
assert np.allclose(q_pi, expected_q_pi) or np.allclose(q_pi, -expected_q_pi)

print(f"Axis-angle round-trip error:  {abs(theta - theta_back):.2e}")
print(f"Quaternion round-trip error:  {np.max(np.abs(R_orig - R_from_q)):.2e}")
print(f"Euler ZYX round-trip error:   {np.max(np.abs(R_orig - R_from_euler)):.2e}")

Cross-Reference With Book Chapters

NoteTangent-Space Methods β€” Forthcoming Textbook Series

The β€œVol I”, β€œVol II”, etc. references in this table point to chapters in Tangent-Space Methods for Nonlinear Control and Biomechanics β€” a book-length treatment of control-affine biomechanics currently in development. These volumes are not yet publicly available. The table is included to show the structural connections between the AffineDrift articles and the forthcoming textbook.

Chapter Rotation representation used Cross-reference
Vol 0, Ch 3–4 \(SO(3)\), \(\mathfrak{so}(3)\), \(SE(3)\) Primary formalism
Vol 0, Ch 5 Exponential coordinates \(e^{[\omega]}\) Product of Exponentials
Vol I, Ch 1 FrΓ©chet derivative of \(R\) Tangent space of \(SO(3)\)
Vol I, Ch 7 Counterfactual: what if no rotation? Drift-control decomposition
Vol II, Ch 3 All four representations side-by-side Multi-representation derivations
Screw Theory Ref. Twists as \(\mathfrak{se}(3)\) elements Joint screw axes