Quaternions· Visually
Home / Part B / 8

From quaternion to rotation matrix and Euler angles, and back

Lesson 8 · Interactive

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.1Euler 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.2Quaternion → 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}\]
Visual 1 of 2 · 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.
Visual 2 of 2 · 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.3Quaternion → 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 = [1, 0, 0, 0]. What is R?

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

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

Next: A spinning object: updating q from the turn rate.