Chapter 3: Representations of Orientation

Lesson 5: Practical Pitfalls (ambiguity, singularities)

This lesson examines subtle but crucial pitfalls of common orientation representations: ambiguity, parameterization singularities, and numerical fragility. We focus on Euler angles, axis–angle coordinates, and unit quaternions, and we derive mathematical conditions for problematic cases and show how to detect and mitigate them in software for robotics kinematics.

1. Orientation Parameterizations and Inevitable Singularities

Recall that the set of all proper 3D rotations is the Lie group \( \mathrm{SO}(3) \), a smooth 3-dimensional manifold. Any minimal parameterization of orientation uses 3 real parameters (e.g., Euler angles). A fundamental topological fact is:

There is no smooth, globally one-to-one, globally nonsingular map from \( \mathbb{R}^3 \) onto \( \mathrm{SO}(3) \).

Intuitively, this means every 3-parameter representation must have at least one singular configuration where the mapping loses rank. We can formalize this using local coordinates (charts).

Let \( \boldsymbol{\theta} \in \mathbb{R}^3 \) denote a parameter vector (for example Euler angles) and define the mapping

\[ f : \mathbb{R}^3 \rightarrow \mathrm{SO}(3), \quad \boldsymbol{\theta} \mapsto R(\boldsymbol{\theta}) \]

The Jacobian of this map is the \( 9 \times 3 \) matrix of partial derivatives:

\[ \mathbf{J}_f(\boldsymbol{\theta}) = \frac{\partial \operatorname{vec}(R(\boldsymbol{\theta}))} {\partial \boldsymbol{\theta}^{\top}} \]

where \( \operatorname{vec}(\cdot) \) stacks the 9 entries of the rotation matrix. At a nonsingular point, \( \operatorname{rank} \mathbf{J}_f = 3 \), so small changes in parameters can produce any small change in orientation. At a singular point, the rank drops to 2 or 1, and the representation loses a degree of freedom locally (gimbal lock).

We can visualize the design decisions around orientation parameterizations as a high-level workflow:

flowchart TD
  A["Start: need orientation representation"] --> B["Need minimal 3 parameters?"]
  B -->|yes| C["Use Euler or other angle set"]
  B -->|"no (can use redundancy)"| D["Use quaternion or rotation matrix"]
  C --> E["Accept local singularities (gimbal lock)"]
  D --> F["Handle redundancy and normalization"]
  F --> G["Implement sign / branch handling"]
  E --> G
  G --> H["Choose internal representation for algorithms"]
  H --> I["Expose safe interface to rest of robot software"]
        

2. Euler Angle Ambiguities (Non-uniqueness)

Consider the standard ZYX (yaw–pitch–roll) Euler angles \( (\phi,\theta,\psi) \), with \( R(\phi,\theta,\psi) = R_z(\phi) R_y(\theta) R_x(\psi) \). This mapping is not one-to-one: multiple triples \( (\phi,\theta,\psi) \) can generate the same rotation.

A common identity for ZYZ Euler angles demonstrates this. For ZYZ, the rotation is

\[ R(\alpha,\beta,\gamma) = R_z(\alpha) R_y(\beta) R_z(\gamma). \]

One can show that

\[ R(\alpha,\beta,\gamma) = R(\alpha + \pi,\; -\beta,\; \gamma + \pi). \]

Sketch of derivation. Use the identities \( R_z(\pi) = -I \) on the xy-plane, and the fact that \( R_z(\pi) R_y(\beta) R_z(\pi) = R_y(-\beta) \). Then

\[ \begin{aligned} R(\alpha + \pi, -\beta, \gamma + \pi) &= R_z(\alpha) R_z(\pi) R_y(-\beta) R_z(\pi) R_z(\gamma) \\ &= R_z(\alpha) \bigl( R_z(\pi) R_y(-\beta) R_z(\pi) \bigr) R_z(\gamma) \\ &= R_z(\alpha) R_y(\beta) R_z(\gamma) \\ &= R(\alpha,\beta,\gamma). \end{aligned} \]

Thus Euler angles are inherently ambiguous: the same physical orientation may correspond to infinitely many angle triples. This is not just a cosmetic issue; it affects interpolation, filtering, and feedback control when using Euler angles as internal state.

A practical consequence is that any routine rotm2eul (rotation matrix to Euler) must choose a branch, i.e., a consistent convention for mapping a rotation back to a canonical triple. This is typically done by constraining angles to a specific interval, such as \( \phi,\psi \in (-\pi,\pi] \) and \( \theta \in [-\pi/2,\pi/2] \).

3. Gimbal Lock as a Parameterization Singularity

In the ZYX convention, gimbal lock occurs when the second angle \( \theta \) equals \( \pm \pi/2 \). The rotation matrix entries are

\[ R(\phi,\theta,\psi) = \begin{bmatrix} c_{\phi} c_{\theta} & c_{\phi} s_{\theta} s_{\psi} - s_{\phi} c_{\psi} & c_{\phi} s_{\theta} c_{\psi} + s_{\phi} s_{\psi} \\ s_{\phi} c_{\theta} & s_{\phi} s_{\theta} s_{\psi} + c_{\phi} c_{\psi} & s_{\phi} s_{\theta} c_{\psi} - c_{\phi} s_{\psi} \\ -s_{\theta} & c_{\theta} s_{\psi} & c_{\theta} c_{\psi} \end{bmatrix} \]

where \( c_{\phi} = \cos\phi \) and \( s_{\phi} = \sin\phi \), and similarly for \( \theta,\psi \).

At \( \theta = \pi/2 \), we have \( c_{\theta} = 0 \) and \( s_{\theta} = 1 \), so

\[ R(\phi,\pi/2,\psi) = \begin{bmatrix} 0 & c_{\phi} s_{\psi} - s_{\phi} c_{\psi} & c_{\phi} c_{\psi} + s_{\phi} s_{\psi} \\ 0 & s_{\phi} s_{\psi} + c_{\phi} c_{\psi} & s_{\phi} c_{\psi} - c_{\phi} s_{\psi} \\ -1 & 0 & 0 \end{bmatrix}. \]

Note that only the combinations \( \phi + \psi \) and \( \phi - \psi \) appear through the trigonometric sums, so we lose the ability to distinguish independent changes in yaw and roll; effectively one rotational degree of freedom disappears.

Formally, at \( \theta = \pm \pi/2 \), the Jacobian \( \mathbf{J}_f(\phi,\theta,\psi) \) loses rank: differentials \( d\phi \) and \( d\psi \) span only a 2-dimensional subspace of the tangent space to \( \mathrm{SO}(3) \). This is the parameterization singularity known as gimbal lock.

Important consequences for robotics:

  • Integrating angular velocity directly in Euler angles near gimbal lock is numerically unstable.
  • Control laws written in terms of Euler angles can exhibit large effective gains or even discontinuous behavior near singularities.
  • Sensor fusion (e.g., IMU filters) should avoid Euler angles as internal state; quaternions or rotation matrices are preferred.

4. Quaternion Double-Cover and Normalization Issues

A unit quaternion is \( q = (q_0,\mathbf{q}_v) \in \mathbb{R}^4 \) with \( \lVert q \rVert = 1 \), where \( q_0 \) is the scalar part and \( \mathbf{q}_v \in \mathbb{R}^3 \) the vector part. The corresponding rotation matrix is

\[ R(q) = \begin{bmatrix} 1 - 2(q_2^2 + q_3^2) & 2(q_1 q_2 - q_0 q_3) & 2(q_1 q_3 + q_0 q_2) \\ 2(q_1 q_2 + q_0 q_3) & 1 - 2(q_1^2 + q_3^2) & 2(q_2 q_3 - q_0 q_1) \\ 2(q_1 q_3 - q_0 q_2) & 2(q_2 q_3 + q_0 q_1) & 1 - 2(q_1^2 + q_2^2) \end{bmatrix}. \]

Double-cover property. Quaternions provide a double cover of \( \mathrm{SO}(3) \): the two unit quaternions \( q \) and \( -q \) represent the same physical rotation.

Proof. Substitute \( -q \) into the formula for \( R(q) \). Every term is either quadratic in components (e.g. \( q_1^2 \)) or bilinear (e.g. \( q_1 q_2 \), \( q_0 q_3 \)). Replacing each component by its negative multiplies quadratic terms by one (since \( (-q_i)^2 = q_i^2 \)) and bilinear terms by one as well (each involves a product of two components, both of which change sign). Therefore the entire matrix remains unchanged:

\[ R(-q) = R(q). \]

This ambiguity is benign mathematically but dangerous numerically:

  • Directly interpolating between \( q_a \) and \( q_b \) without sign correction can take the long way around the 4D sphere.
  • Estimating orientation from sensors over time may produce sudden sign flips, causing discontinuities in logged data or controllers that treat quaternions as 4-vectors.

Normalization drift. When integrating quaternion kinematics numerically, finite-precision arithmetic causes the norm to drift away from 1. If \( q \) solves the ODE

\[ \dot{q} = \tfrac{1}{2}\, \Omega(\boldsymbol{\omega}) q, \]

where \( \boldsymbol{\omega} \) is angular velocity, any explicit time-stepping method must be followed by explicit renormalization to keep \( \lVert q \rVert \approx 1 \).

5. Axis–Angle and Log Map Near 180 Degrees

In axis–angle form, a rotation is parameterized by a unit axis \( \mathbf{u} \in \mathbb{R}^3 \), \( \lVert \mathbf{u} \rVert = 1 \), and an angle \( \theta \in [0,\pi] \), with \( R = \exp(\widehat{\mathbf{u}}\,\theta) \), where \( \widehat{\mathbf{u}} \) is the skew-symmetric matrix associated with \( \mathbf{u} \).

The matrix logarithm \( \log : \mathrm{SO}(3) \rightarrow \mathfrak{so}(3) \) for rotations with \( \theta \neq 0,\pi \) is

\[ \log(R) = \frac{\theta}{2\sin\theta}\,(R - R^{\top}), \quad \theta = \arccos\Bigl(\frac{\operatorname{trace}(R) - 1}{2}\Bigr). \]

As \( \theta \) approaches \( \pi \), the factor \( \theta/(2\sin\theta) \) becomes ill-conditioned because \( \sin\theta \) is close to zero while the numerator remains of order \( \pi \). Small perturbations in \( R \) (for instance from noise) lead to large changes in \( \log(R) \), i.e. axis–angle estimates become highly sensitive near 180-degree rotations.

In practice:

  • Avoid using the log map directly for rotations whose trace is very close to \(-1\).
  • For robust algorithms, detect the region near \( \theta \approx \pi \) and use alternative formulas or regularization.
  • In optimization, limit step sizes in axis–angle space so that iteration stays away from \( \theta \approx \pi \) when possible.

6. Practical Detection and Handling of Pitfalls

Many software bugs in robot kinematics come from silently crossing singularities or branch cuts. A robust orientation module should:

  • Expose a canonical representation for logging and I/O.
  • Use a numerically safe representation internally (often quaternions or rotation matrices).
  • Detect Euler singularities and 180-degree rotations and switch formulas accordingly.
  • Enforce quaternion normalization and sign continuity.

The following flow illustrates a robust update step:

flowchart TD
  A["Given: previous quaternion q_prev, new rotation matrix R_new"] --> B["Convert R_new to quaternion q_raw"]
  B --> C["If dot(q_prev, q_raw) < 0 then q_raw := -q_raw"]
  C --> D["Normalize q = q_raw / ||q_raw||"]
  D --> E["Optionally convert q to Euler for UI"]
  E --> F["If |cos(theta)| < eps then warn: near gimbal lock"]
        

The sign-correction step keeps the quaternion trajectory continuous, and the gimbal-lock warning can be used to adjust visualization or fall back to a different parameterization.

7. Python Implementation Lab

We demonstrate quaternion sign handling and near-gimbal detection using numpy. If available, one can also use scipy.spatial.transform.Rotation for robust conversions, but here we show core logic explicitly.


import numpy as np

def rotm_to_quat(R):
    """Convert rotation matrix to quaternion [w, x, y, z]."""
    # Assumes R is a proper rotation
    t = np.trace(R)
    if t > 0.0:
        s = np.sqrt(t + 1.0) * 2.0  # 4 * w
        w = 0.25 * s
        x = (R[2, 1] - R[1, 2]) / s
        y = (R[0, 2] - R[2, 0]) / s
        z = (R[1, 0] - R[0, 1]) / s
    else:
        # Compute largest diagonal element for stability
        if (R[0, 0] > R[1, 1]) and (R[0, 0] > R[2, 2]):
            s = np.sqrt(1.0 + R[0, 0] - R[1, 1] - R[2, 2]) * 2.0
            w = (R[2, 1] - R[1, 2]) / s
            x = 0.25 * s
            y = (R[0, 1] + R[1, 0]) / s
            z = (R[0, 2] + R[2, 0]) / s
        elif R[1, 1] > R[2, 2]:
            s = np.sqrt(1.0 + R[1, 1] - R[0, 0] - R[2, 2]) * 2.0
            w = (R[0, 2] - R[2, 0]) / s
            x = (R[0, 1] + R[1, 0]) / s
            y = 0.25 * s
            z = (R[1, 2] + R[2, 1]) / s
        else:
            s = np.sqrt(1.0 + R[2, 2] - R[0, 0] - R[1, 1]) * 2.0
            w = (R[1, 0] - R[0, 1]) / s
            x = (R[0, 2] + R[2, 0]) / s
            y = (R[1, 2] + R[2, 1]) / s
            z = 0.25 * s
    q = np.array([w, x, y, z])
    return q / np.linalg.norm(q)

def continuous_quat(q_prev, q_new):
    """Flip sign of q_new if needed to keep continuity with q_prev."""
    if np.dot(q_prev, q_new) < 0.0:
        q_new = -q_new
    return q_new / np.linalg.norm(q_new)

def rotm_to_euler_zyx(R, eps=1e-6):
    """Rotation matrix to ZYX Euler, with near-gimbal detection."""
    # theta is pitch
    theta = -np.arcsin(R[2, 0])
    cos_theta = np.cos(theta)
    near_gimbal = abs(cos_theta) < eps

    if not near_gimbal:
        phi = np.arctan2(R[1, 0], R[0, 0])    # yaw
        psi = np.arctan2(R[2, 1], R[2, 2])    # roll
    else:
        # In gimbal lock, we choose a convention (e.g. set phi = 0)
        phi = 0.0
        if theta > 0:
            psi = np.arctan2(R[0, 1], R[1, 1])
        else:
            psi = -np.arctan2(R[0, 1], R[1, 1])

    return phi, theta, psi, near_gimbal

# Example usage
R = np.eye(3)
q_prev = np.array([1.0, 0.0, 0.0, 0.0])
q_raw = rotm_to_quat(R)
q_cont = continuous_quat(q_prev, q_raw)
phi, theta, psi, near_gimbal = rotm_to_euler_zyx(R)
print("q_cont:", q_cont)
print("Euler (phi, theta, psi):", phi, theta, psi, "near_gimbal:", near_gimbal)
      

The logic for gimbal detection and quaternion sign correction can be reused in other languages and frameworks.

8. C++ Implementation with Eigen

In C++, the Eigen library is a de facto standard for robotics kinematics. The snippet below shows sign-continuous quaternions and ZYX Euler extraction with near-gimbal detection.


#include <iostream>
#include <Eigen/Dense>

using Quaterniond = Eigen::Quaterniond;
using Matrix3d    = Eigen::Matrix3d;
using Vector3d    = Eigen::Vector3d;

Quaterniond continuousQuat(const Quaterniond& q_prev,
                           const Quaterniond& q_raw)
{
    Quaterniond q_new = q_raw;
    if (q_prev.coeffs().dot(q_new.coeffs()) < 0.0)
    {
        q_new.coeffs() *= -1.0;
    }
    q_new.normalize();
    return q_new;
}

Vector3d rotmToEulerZYX(const Matrix3d& R, bool& nearGimbal,
                        double eps = 1e-6)
{
    double theta = -std::asin(R(2, 0));
    double cosTheta = std::cos(theta);
    nearGimbal = std::abs(cosTheta) < eps;

    double phi, psi;
    if (!nearGimbal)
    {
        phi = std::atan2(R(1, 0), R(0, 0)); // yaw
        psi = std::atan2(R(2, 1), R(2, 2)); // roll
    }
    else
    {
        phi = 0.0;
        if (theta > 0.0)
            psi = std::atan2(R(0, 1), R(1, 1));
        else
            psi = -std::atan2(R(0, 1), R(1, 1));
    }
    return Vector3d(phi, theta, psi);
}

int main()
{
    Matrix3d R = Matrix3d::Identity();
    Quaterniond q_prev(1.0, 0.0, 0.0, 0.0);
    Quaterniond q_raw(R);
    Quaterniond q_cont = continuousQuat(q_prev, q_raw);

    bool nearGimbal = false;
    Vector3d eul = rotmToEulerZYX(R, nearGimbal);
    std::cout << "q_cont = " << q_cont.coeffs().transpose() << std::endl;
    std::cout << "Euler (phi, theta, psi) = " << eul.transpose()
              << ", nearGimbal = " << nearGimbal << std::endl;
    return 0;
}
      

This pattern integrates directly into a kinematics or state-estimation module written in C++.

9. Java, MATLAB/Simulink, and Mathematica Implementations

9.1 Java (with a simple Quaternion class)


public class Quaternion {
    public double w, x, y, z;

    public Quaternion(double w, double x, double y, double z) {
        this.w = w; this.x = x; this.y = y; this.z = z;
    }

    public double dot(Quaternion other) {
        return w * other.w + x * other.x + y * other.y + z * other.z;
    }

    public void normalize() {
        double n = Math.sqrt(w*w + x*x + y*y + z*z);
        w /= n; x /= n; y /= n; z /= n;
    }

    public static Quaternion continuous(Quaternion qPrev, Quaternion qRaw) {
        Quaternion qNew = new Quaternion(qRaw.w, qRaw.x, qRaw.y, qRaw.z);
        if (qPrev.dot(qNew) < 0.0) {
            qNew.w = -qNew.w;
            qNew.x = -qNew.x;
            qNew.y = -qNew.y;
            qNew.z = -qNew.z;
        }
        qNew.normalize();
        return qNew;
    }
}
      

9.2 MATLAB/Simulink

MATLAB has built-in helpers such as rotm2eul, eul2quat, and quatnormalize (in the Aerospace Toolbox or Robotics System Toolbox).


function [q_cont, eulZYX, nearGimbal] = robustOrientationUpdate(R, q_prev)

q_raw = rotm2quat(R);          % [w x y z]
if dot(q_prev, q_raw) < 0
    q_raw = -q_raw;
end
q_cont = quatnormalize(q_raw);

% ZYX Euler: [yaw pitch roll]
eulZYX = rotm2eul(R, "ZYX");
theta = eulZYX(2);
nearGimbal = abs(cos(theta)) < 1e-6;

end
      

In Simulink, a typical implementation uses:

  • A block that integrates angular velocity into a quaternion state.
  • A Quaternion Normalization block (or a custom MATLAB Function block implementing quatnormalize).
  • Optional blocks converting quaternion to Euler angles for logging, with a warning signal emitted when abs(cos(theta)) drops below a threshold (gimbal region).

9.3 Wolfram Mathematica

Mathematica provides symbolic and numeric tools for rotations.


(* Construct a rotation matrix and extract Euler angles *)
R = RotationMatrix[{Pi/4, {0, 0, 1}}].RotationMatrix[{Pi/6, {0, 1, 0}}];

eulZYX[rot_] := Module[{angles},
  angles = EulerAngles[rot, {3, 2, 1}]; (* Z, Y, X sequence *)
  angles
];

(* Quaternion from rotation *)
qFromR[rot_] := Quaternion @ RotationMatrix[rot];

(* Continuous quaternion update *)
continuousQuat[qPrev_, qRaw_] := Module[{qNew = qRaw},
  If[Dot[QuaternionVector[qPrev], QuaternionVector[qRaw]] < 0,
    qNew = -qNew;
  ];
  qNew/Norm[QuaternionVector[qNew]]
];

phiThetaPsi = eulZYX[R];
qRaw = qFromR[R];
qPrev = Quaternion[1, 0, 0, 0];
qCont = continuousQuat[qPrev, qRaw];
      

Mathematica is especially useful for deriving Jacobians of orientation mappings symbolically and analyzing singularities.

10. Problems and Solutions

Problem 1 (Gimbal lock condition for ZYX): Starting from the ZYX Euler parameterization \( R(\phi,\theta,\psi) = R_z(\phi) R_y(\theta) R_x(\psi) \), derive explicitly the condition on \( \theta \) for which the mapping from \( (\phi,\theta,\psi) \) to \( R \) loses one degree of freedom (gimbal lock).

Solution:

Using the explicit rotation matrix from Section 3, we observe that the only place where the pitch \( \theta \) appears in combination with yaw \( \phi \) and roll \( \psi \) is via the products \( c_{\theta} \cos\phi \), \( c_{\theta} \sin\phi \), and \( c_{\theta} \), plus the entries proportional to \( s_{\theta} \). The mapping loses rank when differentials in \( \phi \) and \( \psi \) become linearly dependent in the tangent space of \( \mathrm{SO}(3) \).

If we compute the Jacobian of a minimal vector parameter extracted from \( R \), one can show that its determinant includes a factor \( \cos\theta \). When \( \cos\theta = 0 \), i.e. \( \theta = \pm \pi/2 \), that determinant vanishes and the Jacobian loses rank. Intuitively, the intermediate y-axis gimbal aligns with the outer z-axis, so yaw and roll become indistinguishable.

Therefore, the gimbal-lock condition for ZYX is exactly \( \theta = \pm \pi/2 \).

Problem 2 (Quaternion double cover): Show directly that for any unit quaternion \( q = (q_0,q_1,q_2,q_3) \), the quaternions \( q \) and \( -q \) produce the same rotation matrix.

Solution:

The entries of \( R(q) \) are all either quadratic (e.g. \( q_1^2 \), \( q_2^2 \)) or bilinear (e.g. \( q_1 q_2 \), \( q_0 q_3 \)) in the components of \( q \). If we form \( -q = (-q_0,-q_1,-q_2,-q_3) \), then \( (-q_i)^2 = q_i^2 \) and \( (-q_i)(-q_j) = q_i q_j \). Hence every entry of \( R(-q) \) equals the corresponding entry of \( R(q) \), so \( R(-q) = R(q) \). This proves the double-cover property.

Problem 3 (Axis–angle sensitivity near 180 degrees): Consider the log map \( \log : \mathrm{SO}(3) \rightarrow \mathfrak{so}(3) \) given by \( \log(R) = \frac{\theta}{2\sin\theta}(R - R^{\top}) \) with \( \theta = \arccos((\operatorname{trace}R - 1)/2) \). Explain why this map becomes ill-conditioned near \( \theta \approx \pi \).

Solution:

As \( \theta \) approaches \( \pi \) from below, we have \( \sin\theta \rightarrow 0 \) while the numerator \( \theta \) approaches the nonzero value \( \pi \). Thus the scalar factor \( \theta/(2\sin\theta) \) tends to infinity in magnitude, so any perturbation in \( R \) (and therefore in \( R - R^{\top} \)) gets multiplied by a large number. A first-order perturbation analysis shows that the derivative of \( \log(R) \) with respect to \( R \) scales like this factor, so the condition number blows up. Therefore, numerical estimation of axis–angle coordinates from matrices becomes extremely sensitive near 180-degree rotations.

Problem 4 (Continuous quaternion interpolation): Suppose we have a sequence of unit quaternions \( q_k \in \mathbb{S}^3 \) over discrete time steps, and we want a sequence \( \tilde{q}_k \) representing the same rotations but varying continuously in \( \mathbb{R}^4 \). Propose and justify a rule to construct \( \tilde{q}_k \) from \( q_k \).

Solution:

A simple rule is:

  • Set \( \tilde{q}_0 = q_0 \).
  • For each \( k > 0 \), define \( \tilde{q}_k = \begin{cases} q_k & \text{if } \tilde{q}_{k-1}^{\top} q_k \geq 0, \\ -q_k & \text{otherwise.} \end{cases} \)

Since \( q_k \) and \( -q_k \) represent the same rotation, this rule preserves physical orientation while ensuring that consecutive quaternions have a nonnegative dot product, meaning that the angle between them in \( \mathbb{R}^4 \) is at most \( \pi/2 \). This enforces a continuous (in fact, locally shortest) path on the 4D unit sphere and prevents sign flips in the representation.

Problem 5 (Branch cut for inverse trigonometric functions): Many formulas for Euler angles use atan2 and asin. Explain how their branch cuts interact with angle conventions and why it is dangerous to mix conventions without care.

Solution:

Functions such as atan2(y,x) return angles in a fixed interval, typically \( (-\pi,\pi] \). Likewise, asin returns values in \( [-\pi/2,\pi/2] \). Euler-angle extraction formulas are derived assuming specific ranges, e.g. yaw and roll in \( (-\pi,\pi] \) and pitch in \( [-\pi/2,\pi/2] \). If we map angles out of these ranges (for example by manually adding multiples of \( 2\pi \)) without preserving equivalent representations, we can introduce discontinuities when crossing the branch boundaries at \( \pm \pi \) or \( \pm \pi/2 \). Control or filtering algorithms that assume small angle differences can then see apparent jumps of nearly \( 2\pi \), causing instability. Therefore one must choose a consistent range convention and maintain it throughout the software stack.

11. Summary

In this lesson we studied practical pitfalls of orientation representations. We saw that any 3-parameter representation of \( \mathrm{SO}(3) \) must contain singularities, as illustrated by gimbal lock in Euler angles, and that Euler angles are also inherently ambiguous due to multiple equivalent triplets. We analyzed quaternion double-cover redundancy and the need for normalization and sign continuity, and we examined the numerical instability of the log map near 180-degree rotations in axis–angle form. Finally, we implemented robust detection and handling of singular cases in Python, C++, Java, MATLAB/Simulink, and Mathematica, preparing the ground for reliable kinematic and dynamic computations in subsequent chapters.

12. References

  1. Stuelpnagel, J. (1964). On the parameterization of the three-dimensional rotation group. SIAM Review, 6(4), 422–430.
  2. Shuster, M.D. (1993). A survey of attitude representations. Journal of the Astronautical Sciences, 41(4), 439–517.
  3. Kuipers, J.B. (1999). Quaternions and Rotation Sequences: A Primer with Applications to Orbits, Aerospace, and Virtual Reality. Princeton University Press.
  4. Diebel, J. (2006). Representing attitude: Euler angles, unit quaternions, and rotation vectors. Technical Report, Stanford University.
  5. Hertzberg, C., Wagner, R., Frese, U., & Schröder, L. (2013). Integrating generic sensor fusion algorithms with sound state representations through encapsulation of manifolds. Information Fusion, 14(1), 57–77.
  6. Chirikjian, G.S. (2012). Stochastic Models, Information Theory, and Lie Groups, Volume 2: Analytic Methods and Modern Applications. Birkhäuser.
  7. Titterton, D., & Weston, J. (2004). Strapdown Inertial Navigation Technology (2nd ed.). IET.
  8. Bar-Itzhack, I.Y. (2000). New method for extracting the quaternion from a rotation matrix. Journal of Guidance, Control, and Dynamics, 23(6), 1085–1087.
  9. Bullo, F., & Lewis, A.D. (2004). Geometric Control of Mechanical Systems. Springer.
  10. Huynh, D.Q. (2009). Metrics for 3D rotations: Comparison and analysis. Journal of Mathematical Imaging and Vision, 35(2), 155–164.