Writing Code to Convert Quaternions to Z-Y-X Euler Angles in Python

This article is a translated version of my original post on Qiita. Original (Japanese): https://qiita.com/segur/items/1772f0b842bfabab3c6e

Writing Code to Convert Quaternions to Z-Y-X Euler Angles in Python

I needed to calculate Z-Y-X Euler angles from quaternions.

Moreover, the task required me to write the code in Python 2.7, so I couldn't use handy tools like scipy.spatial.transform.Rotation.

When I did some research, I found a sample code in C++ on Wikipedia.

Wikipedia / Quaternion to Euler Angles Conversion

Writing in Python

Based on the Wikipedia sample code, I created a code in Python 2.7.

import math
import numpy as np
from pyquaternion import Quaternion

def quaternion_to_euler_zyx(q):
    """
    Convert quaternion to Z-Y-X Euler angles.

    Parameters
    ----------
    q : Quaternion
        Quaternion (in pyquaternion format)

    Returns
    -------
    np.array
        Z-Y-X Euler angles
    """

    # roll : rotation around x-axis
    sinr_cosp = 2 * (q[0] * q[1] + q[2] * q[3])
    cosr_cosp = 1 - 2 * (q[1] * q[1] + q[2] * q[2])
    roll = math.atan2(sinr_cosp, cosr_cosp)

    # pitch : rotation around y-axis
    sinp = 2 * (q[0] * q[2] - q[3] * q[1])
    if math.fabs(sinp) >= 1:
        pitch = math.copysign(math.pi / 2, sinp)
    else:
        pitch = math.asin(sinp)

    # yaw : rotation around z-axis
    siny_cosp = 2 * (q[0] * q[3] + q[1] * q[2])
    cosy_cosp = 1 - 2 * (q[2] * q[2] + q[3] * q[3])
    yaw = math.atan2(siny_cosp, cosy_cosp)

    # Euler angles
    return np.array([
        math.degrees(roll), 
        math.degrees(pitch), 
        math.degrees(yaw)
    ])

Dependencies

NumPy 1.16.6

Used for vector calculations. Version 1.16.6 seems to be the latest one supporting Python 2.7.

PyQuaternion 0.9.5

PyQuaternion is used for quaternion calculations.

While numpy-quaternion might work better with NumPy, it could not be installed in the Python 2.7 environment.

Easier with SciPy in Python 3

Using scipy.spatial.transform.Rotation makes it simpler if you're using Python 3 or later.

import numpy as np
from pyquaternion import Quaternion
from scipy.spatial.transform import Rotation as R

def quaternion_to_euler_zyx(q):
    r = R.from_quat([q[0], q[1], q[2], q[3]])
    return r.as_euler('zyx', degrees=True)

Unfortunately, scipy.spatial.transform.Rotation does not support Python 2.7.

Conclusion

I'm mostly accustomed to dealing with Unity's Z-X-Y Euler angles, so working with Z-Y-X was quite confusing. I referred to the following articles while creating this post. Thank you for the detailed insights!