# Lesson 8: From quaternion to rotation matrix and Euler angles, and back

Source: https://quaternion.steppeschool.com/quaternion-rotation-matrix-euler-angles/ (part of "Quaternions, Visually" by Steppeschool, free)

Inside the program we keep a quaternion. But we still want roll, pitch and yaw on the screen, and sometimes a rotation matrix. Here are the three conversions, and the one trap in them.

## 8.1 Euler angles → quaternion

We call the three angles roll φ (phi, about x), pitch θ (theta, about y) and yaw ψ (psi, about z). Careful: here θ is the pitch angle, not the turn angle of lesson 4.

We multiply the three single turns from lesson 7, $q = q_{yaw} \otimes q_{pitch} \otimes q_{roll}$, and write it out. With $c_\phi = \cos\frac{\phi}{2}$, $s_\phi = \sin\frac{\phi}{2}$ and the same for θ and ψ:

$$\begin{aligned}
w &= c_\phi c_\theta c_\psi + s_\phi s_\theta s_\psi &\qquad x &= s_\phi c_\theta c_\psi - c_\phi s_\theta s_\psi\\
y &= c_\phi s_\theta c_\psi + s_\phi c_\theta s_\psi &\qquad z &= c_\phi c_\theta s_\psi - s_\phi s_\theta c_\psi
\end{aligned}$$

**Check with a simple case.** Only yaw: roll and pitch are zero, so $c_\phi = c_\theta = 1$ and $s_\phi = s_\theta = 0$. What is left is $q = [c_\psi, 0, 0, s_\psi] = [\cos\frac{\psi}{2}, 0, 0, \sin\frac{\psi}{2}]$, a turn about z, as it should be.

## 8.2 Quaternion → 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}$$

v_world = R · v_object. Row 1 gives the new x: R₁₁v_x + R₁₂v_y + R₁₃v_z. Rows 2 and 3 give the new y and z the same way.

A nice way to read R: each column is one axis of the object, written in world coordinates. Column 1 is where the object’s x axis points.

**Where does it come from?** We turn the three unit arrows (1, 0, 0), (0, 1, 0) and (0, 0, 1) with $q \otimes v \otimes q^*$ from lesson 6. The three results are the three columns.

**A simple case.** Take a turn by ψ about z only: w = cos(ψ/2), z = sin(ψ/2), x = y = 0. Most entries vanish, and with the double-angle rules $1 - 2\sin^2\frac{\psi}{2} = \cos\psi$ and $2\sin\frac{\psi}{2}\cos\frac{\psi}{2} = \sin\psi$ we get the 2D turn formula from lesson 3, with z untouched:

$$R = \begin{bmatrix}1-2z^2 & -2wz & 0\\ 2wz & 1-2z^2 & 0\\ 0 & 0 & 1\end{bmatrix} = \begin{bmatrix}\cos\psi & -\sin\psi & 0\\ \sin\psi & \cos\psi & 0\\ 0 & 0 & 1\end{bmatrix}$$

> **Interactive visual: The columns of R are the axes of the object.** **Try it:** pick a column. Its three numbers are exactly where the tip of one of the object's axes is. Press **New orientation** and check again.

> **Interactive visual: All three conversions, live.** **Try it:** move the sliders and follow the three steps in the panel. The coloured columns of R are the coloured arrows on the object. Then set pitch to 90° and move roll and yaw: the angles we get back no longer match the sliders (roll comes back as 0), while q and R stay perfectly fine.

## 8.3 Quaternion → Euler angles

We read the angles from the matrix. Written with roll φ, pitch θ and yaw ψ, the ZYX rotation matrix has a few very simple entries:

$$R_{31} = -\sin\theta \qquad R_{32} = \sin\phi\cos\theta \qquad R_{33} = \cos\phi\cos\theta \qquad R_{21} = \cos\theta\sin\psi \qquad R_{11} = \cos\theta\cos\psi$$

R₃₁ is row 3, column 1. Dividing R₃₂ by R₃₃ leaves tan φ, and R₂₁ by R₁₁ leaves tan ψ.

So pitch comes from R₃₁ alone, roll from R₃₂ and R₃₃, yaw from R₂₁ and R₁₁. Now we replace each entry by its quaternion form from 8.2. We use atan2(a, b): it is the angle whose tangent is a/b, but it also looks at the signs of a and b, so it covers the full range from −180° to 180°.

$$\phi = \operatorname{atan2}\!\big(2(wx+yz),\ 1-2(x^2+y^2)\big) \qquad
\theta = \arcsin\!\big(2(wy - zx)\big) \qquad
\psi = \operatorname{atan2}\!\big(2(wz+xy),\ 1-2(y^2+z^2)\big)$$

**Trap:** Because of rounding, $2(wy - zx)$ can come out as 1.0000001. Then arcsin has no answer. In C, `asinf` returns NaN, "not a number", and every later result becomes NaN too. So we always limit the value to the range −1 to 1 first. This one line prevents a bug that is hard to find.

And at pitch ±90° the gimbal lock from lesson 1 is back, but only here, in the conversion for display. Roll and yaw turn about the same line there, so only their combined turn can be found. Our code then returns roll = 0 and puts the whole turn into yaw. The quaternion itself never has this problem. So the rule is simple: keep q inside the program, convert to Euler angles only to show them.

## ✓ Check yourself

**Q: q = [1, 0, 0, 0]. What is R?**

The identity matrix: the object’s axes are the world axes.

**Q: Why use atan2 and not atan for roll and yaw?**

atan2 looks at the signs of both inputs, so it gives the full −180° to 180° range. atan only gives −90° to 90°.

## Sources and further reading

- J. Diebel, "Representing Attitude: Euler Angles, Unit Quaternions, and Rotation Vectors", Stanford University, 2006 <https://www.astro.rug.nl/software/kapteyn-beta/_downloads/attitude.pdf>: all conversions between Euler angles, matrices and quaternions. It uses the opposite, world-to-object direction, so its matrices are the transpose of ours
- H. A. Hashim, "Special Orthogonal Group SO(3), Euler Angles, Angle-axis, Rodriguez Vector and Unit-Quaternion: Overview, Mapping and Challenges", 2019 <https://arxiv.org/abs/1909.06669>: conversion tables
