explainer
Euler angles and gimbal lock: when different angles mean the same orientation
Explore roll, pitch, and yaw with full rotation matrices. Compare equivalent orientations at ±90° pitch and separate Euler angle rates from angular velocity.
What you will learn
- State the axis order, handedness, and vector convention behind a roll-pitch-yaw tuple.
- Compare full rotation matrices to recognize equivalent Euler angle tuples.
- Explain the singularity at plus or minus 90 degrees of pitch.
- Distinguish Euler angle rates from a body's angular velocity.
Before you start
- Right-handed rotation matrices and column-vector multiplication
- Body and world coordinate frames
- Rates as change per unit time
The angle triples (20°, 90°, 50°) and (50°, 90°, 80°) describe the same orientation under the convention in this lesson. Their numbers differ, but every column of their rotation matrices agrees.
This is an Euler angle singularity, often called gimbal lock in software. Understanding it starts with choosing exactly what the three angles mean.
Choose a convention before reading the angles
Euler angles describe orientation using three successive rotations. There are several axis sequences. Tait-Bryan angles use three different axes, such as x, y, z; proper Euler angles repeat the first axis as the third, such as z, x, z.
“Euler angles” often covers both families. Bernardes and Viollet's paper treats the six proper Euler and six Tait-Bryan sequences separately.
Here the tuple order is (roll φ, pitch θ, yaw ψ). We use active, right-handed rotations of column vectors:
R = Rz(ψ) Ry(θ) Rx(φ)
v′ = Rv
Positive angles follow the right-hand rule. The lab displays degrees, while trigonometric calculations use radians. We restrict pitch to [−90°, 90°] and roll and yaw to [−180°, 180°].
Read the fixed-axis and body-axis sequences
The rightmost matrix acts first. Using fixed world axes, apply:
- Roll φ about the original world x-axis.
- Pitch θ about the original world y-axis.
- Yaw ψ about the original world z-axis.
This is an extrinsic XYZ sequence. The equivalent intrinsic ZYX sequence starts with aligned frames, then applies yaw about body z, pitch about the body's new y-axis, and roll about its newest x-axis. Both descriptions produce the same final R when they use the corresponding angles.
Drake documents this fixed XYZ and body ZYX equivalence. “Roll, pitch, yaw” alone does not state which sequence a library expects.
For example, SciPy uses lowercase axis letters for fixed-axis rotations and uppercase for body-axis rotations. Under its API, from_euler('xyz', [roll, pitch, yaw], degrees=True) matches from_euler('ZYX', [yaw, pitch, roll], degrees=True). Its documentation defines the case-sensitive convention.
Calculate one complete orientation
Take roll = 20°, pitch = 90°, yaw = 50°. At this pitch, the product simplifies in terms of δ = yaw − roll = 30°:
R =
[0, −sin δ, cos δ;
0, cos δ, sin δ;
−1, 0, 0]
Using sin 30° = 1/2 and cos 30° = √3/2 gives:
| World component | Body x column | Body y column | Body z column |
|---|---|---|---|
| x | 0 | −1/2 | √3/2 |
| y | 0 | √3/2 | 1/2 |
| z | −1 | 0 | 0 |
The first column sends the reference x-axis to (0, 0, −1). The other columns locate the remaining body axes. Together, the three columns specify the full orientation.
Now add 30° to both roll and yaw. The new tuple (50°, 90°, 80°) keeps δ = 30°, so the full matrix remains unchanged. This proves equality for every input vector.
Change the tuple and compare the matrices
The lab compares the original tuple with a coupled change to roll and yaw. All three body axes appear in the same fixed world view. The tables supply their full coordinates, including the depth hidden by the drawings.
Choose Near lock (+85°). The same 30° changes now produce a maximum matrix-entry difference of about 0.040888. With Away from lock (0°), that difference rises to about 0.492404.
The comparison checks all nine entries before rounding. Matching one arrow or one projected picture would be insufficient: a rotation can leave one vector fixed while changing the others.
Try the negative-lock preset too. It changes the coupling rule so that roll increases while yaw decreases. Switching back to the same-sign rule makes the orientations differ, even though the original pitch remains singular.
Find what changes at the two singular pitches
At pitch = +90°, the orientation depends on yaw − roll. Adding the same amount to both leaves this difference unchanged.
At pitch = −90°, the orientation depends on yaw + roll. Adding to roll and subtracting from yaw preserves the sum. For example, (20°, −90°, 50°) and (50°, −90°, 20°) both have a sum of 70°.
Infinitely many angle tuples therefore describe each such orientation. A matrix-to-Euler conversion cannot recover unique roll and yaw values there. A program must choose one valid pair according to a convention.
The name comes from an analogy with aligned axes in a gimbal mechanism. Mechanical gimbal constraints require their own physical model. This lab studies the coordinate representation of orientation.
Separate angle rates from angular velocity
Roll rate, pitch rate, and yaw rate measure how the three coordinates change over time. A body's angular velocity is a geometric vector. Its body-frame components generally differ from those three rates.
For our convention, with all rates in radians per second:
ω_B = E(φ, θ)[φ̇, θ̇, ψ̇]ᵀ
E =
[1, 0, −sin θ;
0, cos φ, sin φ cos θ;
0, −sin φ, cos φ cos θ]
Here ω_B is the body's angular velocity relative to the fixed world, expressed along body axes. Drake gives this body-frame rate relationship.
The determinant of E is cos θ. At ±90°, its rank drops from 3 to 2. At +90°, equal roll and yaw rates cancel when pitch rate is zero: E(1, 0, 1)ᵀ = 0.
This agrees with the finite-angle example. Increasing roll and yaw together can change the displayed coordinates while leaving the orientation stationary. The lab's “Euler rate rank” describes E, whose role differs from the rotation matrix R.
Understand the trouble near the singularity
Near the singular pitch, converting angular velocity back into Euler rates can require large numbers. For roll = 0° and a desired body angular velocity ω_B = (0, 0, 1) rad/s, the equations give:
φ̇ = tan θ, θ̇ = 0, ψ̇ = 1 / cos θ
At pitch = 89°, the required roll and yaw rates are about 57.290 and 57.299 rad/s. The actual angular velocity still has magnitude 1 rad/s. The large rates come from the angle-coordinate mapping.
At 90°, this tuple cannot express that angular velocity through finite Euler rates. The body can still move through the orientation. A different representation can describe the motion without this singular rate conversion.
A pitch of 89° does not make R singular or invalidate the orientation. The inverse rate mapping becomes sensitive as cos θ approaches zero. Drake documents the division by cos θ and its numerical consequences.
Choose how to store and compare orientations
Euler angles are useful for entering or displaying orientation when the convention is clear. Compare complete rotations when deciding whether two orientations agree. Coordinate differences alone can mislead, both at singularities and across an angle wrap such as +180° to −180°.
For calculations that must pass through these pitches, unit quaternions provide another representation. They require a unit norm and defined component and multiplication conventions. Converting them back to Euler angles still encounters the Euler ambiguity.
SciPy's as_euler reports gimbal lock and chooses zero for its third returned angle, while preserving the represented rotation. Its documentation explains that choice.
An axis-angle representation describes one net rotation axis and angle. That axis is a different object from an Euler tuple. Likewise, adding Euler tuples does not generally compose their rotations; use the chosen representation's composition rule.
Check equivalent orientations in Python
This standard-library example builds Rz Ry Rx and compares every entry. It rounds only printed values. The equality threshold accommodates floating-point trigonometry.
from math import cos, sin, radians
def euler_matrix(roll, pitch, yaw):
r, p, y = map(radians, (roll, pitch, yaw))
cr, sr = cos(r), sin(r)
cp, sp = cos(p), sin(p)
cy, sy = cos(y), sin(y)
return [
[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 maximum_difference(a, b):
return max(abs(x - y) for row_a, row_b in zip(a, b)
for x, y in zip(row_a, row_b))
for pitch, yaw_change in [(90, 30), (85, 30), (0, 30), (-90, -30)]:
original = euler_matrix(20, pitch, 50)
comparison = euler_matrix(50, pitch, 50 + yaw_change)
difference = maximum_difference(original, comparison)
print(f"pitch={pitch:3d}: difference={difference:.6f}, "
f"same={difference <= 1e-12}")
matrix = euler_matrix(20, 90, 50)
for row in matrix:
print("[" + ", ".join(f"{x:.3f}" for x in row) + "]")
Expected output:
pitch= 90: difference=0.000000, same=True
pitch= 85: difference=0.040888, same=False
pitch= 0: difference=0.492404, same=False
pitch=-90: difference=0.000000, same=True
[0.000, -0.500, 0.866]
[0.000, 0.866, 0.500]
[-1.000, 0.000, 0.000]
Try it yourself
Exercise 1. Start with (roll, pitch, yaw) = (10°, −90°, 40°). Increase roll to 30°. Which yaw preserves the full orientation? Why would adding 20° to yaw fail?
Show solution: preserve the negative-pitch sum
The original roll-plus-yaw sum is 50°. Set yaw to 20°, giving the equivalent tuple (30°, −90°, 20°).
Adding 20° to yaw would give a sum of 90°, which changes the orientation. The opposite-sign rule applies at −90° pitch.
Exercise 2. Let roll = 0°, pitch = 60°, and desired body angular velocity ω_B = (0, 0, 1) rad/s. Find roll, pitch, and yaw rates. Does the body rotate at the magnitude of the Euler-rate tuple?
Show solution: use the angular-velocity map
Since cos 60° = 1/2 and tan 60° = √3, the rates are (√3, 0, 2) rad/s, approximately (1.732, 0, 2).
Substituting into E gives ω_B = (√3 − (√3/2) × 2, 0, (1/2) × 2) = (0, 0, 1). The body has angular speed 1 rad/s. The Euler-rate tuple's Euclidean magnitude is √7 rad/s; it is not the body's angular speed.
Sources and further study
- Drake: RollPitchYaw. Fixed XYZ and body ZYX conventions, angular-rate maps, and singular pitch values.
- SciPy: Rotation.from_euler. Intrinsic and extrinsic sequence notation and angle units.
- SciPy: Rotation.as_euler. Angle ranges, nonunique angles, and gimbal-lock handling.
- Bernardes and Viollet: Quaternion to Euler angles conversion. A primary research paper covering proper Euler and Tait-Bryan sequences.