Decomposing and composing a 3×3 rotation matrix

This post shows how to decompose a 3×3 rotation matrix into the 3 elementary Euler angles, sometimes referred to as yaw/pitch/roll, and going the other way around. The technique I’m presenting is based off http://planning.cs.uiuc.edu/node102.html.

If you have ever seen Wikipedia’s entry on Rotation matrix or Euler angles, you will have no doubt been swamped to your neck with maths equations all over the place, depending how tall your neck is. It turns out there is no single correct answer on defining a rotation matrix in terms of Euler angles. There are a few ways to accomplish it and all of them are valid. But that’s okay, I’ll just show one way, which should be adequate for most applications. I won’t go into any maths derivation, aiming to keep this post implementation friendly.

Decomposing a rotation matrix

Given a 3×3 rotation matrix

$R = \left[ \begin{array}{ccc} r_{11} & r_{12} & r_{13} \\ r_{21} & r_{22} & r_{23} \\ r_{31} & r_{32} & r_{33} \end{array} \right]$

The 3 Euler angles are

$\theta_{x} = atan2\left(r_{32}, r_{33}\right)$

$\theta_{y} = atan2\left(-r_{31}, \sqrt{r_{32}^2 + r_{33}^2}\right)$

$\theta_{z} = atan2\left(r_{21}, r_{11}\right)$

Here atan2 is the same arc tangent function, with quadrant checking, you typically find in C or Matlab.

Composing a rotation matrix

Given 3 Euler angles $\theta_{x}, \theta_{y}, \theta_{z}$, the rotation matrix is calculated as follows:

$X = \left[ \begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos\left(\theta_{x}\right) & -\sin\left(\theta_{x}\right) \\ 0 & \sin\left(\theta_x\right) & \cos\left(\theta_{x}\right) \end{array} \right]$

$Y = \left[ \begin{array}{ccc} \cos\left(\theta_{y}\right) & 0 & \sin\left(\theta_{y}\right) \\ 0 & 1 & 0 \\ -\sin\left(\theta_{y}\right) & 0 &\cos\left(\theta_{y}\right) \end{array} \right]$

$Z = \left[ \begin{array}{ccc} \cos\left(\theta_{z}\right) & -\sin\left(\theta_{z}\right) & 0 \\ \sin\left(\theta_{z}\right) & \cos\left(\theta_{z}\right) & 0 \\ 0 & 0 & 1 \end{array} \right]$

$R = ZYX$

Note on angle ranges

The Euler angles returned when doing a decomposition will be in the following ranges:

$\theta_{x} \rightarrow \left(-\pi, \pi \right)$

$\theta_{y} \rightarrow \left(-\frac{pi}{2}, \frac{pi}{2} \right)$

$\theta_{z} \rightarrow \left(-\pi, \pi \right)$

If you keep your angles within these ranges, then you will get the same angles on decomposition. Conversely, if your angles are outside these ranges you will still get the correct rotation matrix, but the decomposed values will be different to your original angles.

Code

Download rotation_matrix_demo.m

The Octave/Matlab script contains the decompose/compose function and a demo on using it. It picks random Euler angles, makes a rotation matrix, decomposes it and verifies the results are the same. An example output

octave:1> rotation_matrix_demo
Picking random Euler angles (radians)
x = -2.6337
y = -0.47158
z = -1.2795

Rotation matrix is:
R =

0.25581  -0.77351   0.57986
-0.85333  -0.46255  -0.24057
0.45429  -0.43327  -0.77839

Decomposing R
x2 = -2.6337
y2 = -0.47158
z2 = -1.2795

err = 0
Results are correct!

12 thoughts on “Decomposing and composing a 3×3 rotation matrix”

1. Hi Nghia,

I believe you misplaced number 1 in the Y rotation matrix. Number 1 should be at the centre of Y. As a result, your derivation of the angles is not generally correct. From the RHS of equation 3.42 of the link you cited (http://planning.cs.uiuc.edu/node102.html), one can easily obtain yaw, pitch and roll angles as follows:

\theta_x = arctan(r_{3,2}/r_{3,3})
\theta_y = -arcsin(r_{3,1})
\theta_z = arctan(r_{2,1}/r_{1,1})

By the way, you other posts and source codes on computer vision are amazing. They are really useful to me.

Cheers,
Chuong

• Hi,

Well spotted!

I came to the same result for theta_y, using arcsin instead of atan2. I wasn’t sure if there was any ‘gotchas’ at the time, so I left it using the original equation I cited.

Glad you found something useful on the site

2. Hi Nghia,

I think this approach has one singularity. If theta_y is +-90 degrees, it will come that r_{1,1}, r_{2,1}, r_{3,2} and r_{3,3} will evaluate to zero due to the cos(theta_y) term. So you end up to atan(0) which, will return always 0, independent of theta_x and theta_z.

If you try this in your matlab code, make sure that you do not have numerical errors on r_{1,1}, r_{2,1}, r_{3,2} and r_{3,3}, after generating the rotation matrix.

Do you have the solution for that case also?

Cheers,
Andre

• I’ve tried putting 90 degrees in my demo program, at line 6
y = pi;

x,z are still random between -180,180 degrees. The matrix changes accordingly. The returned values however in the x,z axis are out by 180 degrees.

Try editing the demo code and see if you get the same results as me.

• I’ve tried to put in line 6:
y = pi*0.5;

and after the line “R = compose_rotation(x,y,z)”, the lines:
R(1,1) = 0;
R(2,1) = 0;
R(3,2) = 0;
R(3,3) = 0
to kill numerical errors of the composition process. If you read these values before, you will get values very close to zero (e-17 in my machine). They actually should be zero, due to the “cos(pi*0.5)” term in that specific case.

You can let x and z random.

• Ooops my bad, wasn’t thinking straight. You’re very right.

I guess the only one around it is to explicitly check for the +- 90 degree situation and use a different rotation composition. Probably one of the many reasons why people hate using matrices to represent rotation.

3. Hi Nghia!

Thank you very much for your article. It helped me very much. For about 2 days I couldn’t solve my problem, I’ve messed up in these rotation-conventions. But when I’ve used your formulas, it worked fine!

4. It’s important to note that the Euler angles described above are applied in the order theta_X -> theta_Y -> theta_Z relative to the world axis, due to the non-commutativity of 3D rotation.

5. thank you nghiaho, you excelent, i see much your paper. your’s result very good.

6. Hello: I have a visualization program which expects 3 values for rotation (x, y, z) between 0 and 360 degrees. Now, my fusion library (reading data from sensors) provides either euler angles or quaternions. I didin’t know how to use quaternions to feed the visualization application, so I ‘m working with euler angles.
The problem is I see euler angles values range from (0 to 360), (-180 to 180), and (-90 to 90). For the first case is Ok. For the second, I can add 180 and I’m done (range 0 to 360). Now for the last one, I see if doing a 360 movement with the board (sensors), the values are changing from -90 to +90 and +90 to -90 again, so in this case I’m not sure what to do. Adding 90 will end in (0 to 180) range.

How can I convert the euler angles ranges to the right (0 to 360) value range the visualization application requires?

Many thanks in advance.
Gus

• If you want to convert all your angles to [0,360] correctly then I would simply do:

if(angle < 0)
angle += 360;