UAV Systems Engineering · Module 5
M5-T3: Attitude representation and rotation matrices
Every representation of orientation is a trade: matrices are redundant but safe, Euler angles are readable but singular, rotation vectors are minimal and good for small errors.
The same attitude can be written as a matrix, three angles, an axis and an angle, or a quaternion (lesson M5-T4). Knowing which to use where, and how to convert without losing information or sign, removes a whole class of flight bugs.
Learning objectives
After this lesson, you should be able to:
- Define rotation matrices, Euler angles (ZYX) and axis-angle and convert between them.
- Demonstrate gimbal lock and explain its consequence for control and estimation.
- Use the exponential and logarithm maps and relate angular velocity to Euler angle rates.
- Choose a representation for display, control, estimation and storage.
Conceptual explanation
A rotation matrix (orthogonal, det = +1) has nine entries and six constraints: robust, composable by multiplication, redundant. Euler angles (roll, pitch, yaw) are three numbers that read easily but have a singularity at pitch = ±. The axis-angle or rotation vector is minimal and is the natural coordinate for small errors, with the exponential map ₓ + ₓ taking it to a matrix. Quaternions (lesson M5-T4) combine robustness and compactness.

Engineering intuition
Gimbal lock: with pitch at the roll and yaw axes align; rotating about either gives the same motion, so one degree of freedom is lost and the Euler rates blow up . A multirotor in normal flight stays far from pitch, which is why Euler angles survive on displays, but aerobatic flight, a pitch-over in a failsafe or a vertical-flying VTOL reaches the singularity: control and estimation code must not rely on Euler angles there.
Mathematical formulation
ZYX Euler rotation (yaw , pitch , roll ) as in lesson M1-T4, the rate relation, and the exponential and logarithm maps:
Extracting Euler angles: θ = −asin(R₃₁), φ = atan2(R₃₂, R₃₃), ψ = atan2(R₂₁, R₁₁); near |R₃₁| = 1 roll and yaw are not individually determined. Composition: applies first (in the fixed frame); the same matrix applied in the body frame composes on the right. Orthonormalise after accumulating rounding error: ← from the SVD of .
- [n]ₓ
- skew-symmetric matrix of the unit axis .
- vee
- inverse of the skew map: extracts the vector.
- SO(3)
- the group of 3×3 rotation matrices.
Architecture and data flow
Typical flow in an autopilot: sensors → attitude estimate (quaternion) → controller (attitude error from matrices or quaternions) → rate setpoint → body rates; Euler angles only for the pilot display and logs. When exchanging attitude with other software, document both the representation and the convention (frames, order, handedness).
Real-world UAV applications
- Debugging attitude logs where roll and yaw jump near vertical.
- Choosing a state parametrisation for an estimator.
- Implementing attitude error for a geometric controller (lesson M5-T5).
- Converting between PX4 (FRD/NED) and ROS (FLU/ENU) attitudes.
Implementation example
This runs as shown: Euler to matrix to Euler round trips away from the singularity, loss of the individual angles at pitch, Exp and Log as inverses, and the Euler-rate blow-up near vertical.
import numpy as np
def Rzyx(roll, pitch, yaw):
cr, sr, cp, sp, cy, sy = np.cos(roll), np.sin(roll), np.cos(pitch), np.sin(pitch), np.cos(yaw), np.sin(yaw)
return np.array([[cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr], [sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr], [-sp, cp*sr, cp*cr]])
def euler(R): return np.arctan2(R[2, 1], R[2, 2]), -np.arcsin(np.clip(R[2, 0], -1, 1)), np.arctan2(R[1, 0], R[0, 0])
hat = lambda w: np.array([[0, -w[2], w[1]], [w[2], 0, -w[0]], [-w[1], w[0], 0]])
def Exp(w):
th = np.linalg.norm(w)
if th < 1e-12: return np.eye(3) + hat(w)
K = hat(w / th); return np.eye(3) + np.sin(th) * K + (1 - np.cos(th)) * K @ K
def Log(R):
th = np.arccos(np.clip((np.trace(R) - 1) / 2, -1, 1))
return np.zeros(3) if th < 1e-12 else th / (2 * np.sin(th)) * np.array([R[2, 1] - R[1, 2], R[0, 2] - R[2, 0], R[1, 0] - R[0, 1]])
for ang in ((0.3, -0.5, 2.0), (-1.2, 1.0, -0.4), (0.0, 0.0, 3.0)):
assert np.allclose(euler(Rzyx(*ang)), ang, atol=1e-12) # round trip away from the singularity
w = np.array([0.4, -0.7, 0.2]); assert np.allclose(Log(Exp(w)), w, atol=1e-12) and np.allclose(Exp(w) @ Exp(w).T, np.eye(3), atol=1e-12)
# gimbal lock: at pitch = 90 deg, roll and yaw are no longer separately determined; many (roll, yaw) pairs give the same matrix
R1, R2 = Rzyx(0.5, np.pi / 2, 0.2), Rzyx(0.9, np.pi / 2, 0.6) # roll - yaw is the same in both (0.3)
assert np.allclose(R1, R2, atol=1e-12)
# Euler rate for a constant body rate blows up approaching vertical
w_body = np.array([0.0, 0.0, 1.0]); rates = []
for pitch_deg in (0, 45, 80, 89, 89.9):
roll, pitch = 0.5, np.radians(pitch_deg); W = np.array([[1, np.sin(roll) * np.tan(pitch), np.cos(roll) * np.tan(pitch)], [0, np.cos(roll), -np.sin(roll)], [0, np.sin(roll) / np.cos(pitch), np.cos(roll) / np.cos(pitch)]])
rates.append(np.linalg.norm(W @ w_body))
print("Euler rate magnitude for a 1 rad/s body yaw rate at pitch 0, 45, 80, 89, 89.9 deg:", np.round(rates, 1))
assert rates[-1] > 40 * rates[0]Common engineering mistakes
Using Euler angles in the controller near vertical
Singularities cause jumps and wrong commands.
Averaging Euler angles
Wrap-around and nonlinearity give wrong means.
Not orthonormalising after repeated multiplication
drifts from SO(3) and rotates vectors with a scale error.
Mixing intrinsic and extrinsic Euler conventions
The same three numbers give different rotations.
Interpolating angles linearly
Use the quaternion slerp or the rotation vector between two attitudes.
Failure modes
| Failure mode | How it shows up | Likely cause |
|---|---|---|
| Roll and yaw jump | Displayed angles change suddenly near vertical | Gimbal lock in conversion |
| Slow rotation matrix drift | Vectors shrink or grow when rotated | No re-orthonormalisation |
| Mirrored attitude | Handedness or order wrong | Convention mismatch |
| Wrap-around error | vs − treated as difference | Angles subtracted without wrapping |
Debugging approach
- Check and in a unit test.
- Convert and convert back at several attitudes including near ± pitch.
- Log the rotation matrix or quaternion, not only Euler angles.
- Wrap all angle differences.
Practical exercises
- Show that for < π and what happens at π.
- Derive the Euler-rate matrix from the angular velocity composition.
- Find two different (roll, yaw) pairs giving the same matrix at pitch.
- Implement SVD orthonormalisation and test it on a perturbed matrix.
Interview questions
Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.
Question 1
What is gimbal lock?
Model answer
At ± pitch the roll and yaw axes align, a degree of freedom is lost, and the Euler-angle rates become singular; the attitude is still well defined but not as three angles.
Question 2
When use Euler angles and when not?
Model answer
For display and logs; not in control, estimation or interpolation, where matrices, quaternions or rotation vectors avoid the singularity.
Question 3
What are the exponential and logarithm maps?
Model answer
Exp maps a rotation vector to a rotation matrix (Rodrigues); Log maps a rotation matrix back to the rotation vector.
References and further reading
Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.
- . . Barfoot, State Estimation for Robotics, Cambridge University Press, 2017, chapter 6.
- J. Solà, “Quaternion kinematics for the error-state Kalman filter”, arXiv:1711.02508
- . Diebel, “Representing attitude: Euler angles, unit quaternions, and rotation vectors”, Stanford technical report, 2006.