28

Is there an existing algorithm for converting a quaternion representation of a rotation to an Euler angle representation? The rotation order for the Euler representation is known and can be any of the six permutations (i.e. xyz, xzy, yxz, yzx, zxy, zyx). I've seen algorithms for a fixed rotation order (usually the NASA heading, bank, roll convention) but not for arbitrary rotation order.

Furthermore, because there are multiple Euler angle representations of a single orientation, this result is going to be ambiguous. This is acceptable (because the orientation is still valid, it just may not be the one the user is expecting to see), however it would be even better if there was an algorithm which took rotation limits (i.e. the number of degrees of freedom and the limits on each degree of freedom) into account and yielded the 'most sensible' Euler representation given those constraints.

I have a feeling this problem (or something similar) may exist in the IK or rigid body dynamics domains.


Solved: I just realised that it might not be clear that I solved this problem by following Ken Shoemake's algorithms from Graphics Gems. I did answer my own question at the time, but it occurs to me it may not be clear that I did so. See the answer, below, for more detail.


Just to clarify - I know how to convert from a quaternion to the so-called 'Tait-Bryan' representation - what I was calling the 'NASA' convention. This is a rotation order (assuming the convention that the 'Z' axis is up) of zxy. I need an algorithm for all rotation orders.

Possibly the solution, then, is to take the zxy order conversion and derive from it five other conversions for the other rotation orders. I guess I was hoping there was a more 'overarching' solution. In any case, I am surprised that I haven't been able to find existing solutions out there.

In addition, and this perhaps should be a separate question altogether, any conversion (assuming a known rotation order, of course) is going to select one Euler representation, but there are in fact many. For example, given a rotation order of yxz, the two representations (0,0,180) and (180,180,0) are equivalent (and would yield the same quaternion). Is there a way to constrain the solution using limits on the degrees of freedom? Like you do in IK and rigid body dynamics? i.e. in the example above if there were only one degree of freedom about the Z axis then the second representation can be disregarded.


I have tracked down one paper which could be an algorithm in this pdf but I must confess I find the logic and math a little hard to follow. Surely there are other solutions out there? Is arbitrary rotation order really so rare? Surely every major 3D package that allows skeletal animation together with quaternion interpolation (i.e. Maya, Max, Blender, etc) must have solved exactly this problem?

Will Baker
  • 2,519
  • 4
  • 19
  • 17
  • People are usually lazy enough to try to conform to some type of standard. My uneducated guess would therefore be that you hardly could find source to any more than two or three of the different permutations. – Jonas Byström Jun 24 '09 at 09:09
  • You might be looking for something else, since Euler angles has an infinite number representations for every solution. – Jonas Byström Jun 24 '09 at 09:11
  • I would _love_ not to be constrained to using Euler angles for representation, but for the domain I'm working in (3D animation) they are the standard way in which to present rotations to the user. And because of the problem inherent in them (gimbal lock, etc) it is necessary for the rotation order to be editable as well. – Will Baker Jun 25 '09 at 01:04
  • What is lacking with quaternions? [Gimbal lock is not an issue](http://en.wikipedia.org/wiki/Gimbal_lock#The_quaternion_solution) using 'em. – Jonas Byström Jun 25 '09 at 14:40
  • Yep, I'm aware of that - my problem is that I want to use quaternions to represent the rotations internally, but how do you present a rotation stored as a quaternion to the user? You can't present the values of the quaternion itself because they're not exactly intuitive. Euler angles are the standard for 3D animation, hence the artist will expect to see them reflected in the user interface. – Will Baker Jun 25 '09 at 22:02
  • 1
    My experience is that, if you work with stuff like robotics or animation, you are often constrained to work with Euler angles, and it is not up to you, and you have to comply to some obscure convention. Thus, this is a very good question. – Fredriku73 Jan 15 '10 at 09:37
  • It might be worth trying http://mathoverflow.net/ as well. – Simon Jan 15 '10 at 11:02

8 Answers8

13

This looks like a classic case of old technology being overlooked - I managed to dig out a copy of Graphics Gems IV from the garage and it looks like Ken Shoemake has not only an algorithm for converting from Euler angles of arbitrary rotation order, but also answers most of my other questions on the subject. Hooray for books. If only I could vote up Mr. Shoemake's answer and reward him with reputation points.

I guess a recommendation that anybody working with Euler angles should get a copy of Graphics Gems IV from their local library and read the section starting page 222 will have to do. It has to be the clearest and most concise explanation of the problem I have read yet.


Here's a useful link I have found since - http://www.cgafaq.info/wiki/Euler_angles_from_matrix - This follows the same system as Shoemake; the 24 different permutations of rotation order are encoded as four separate parameters - inner axis, parity, repetition and frame - which then allows you to reduce the algorithm from 24 cases to 2. Could be a useful wiki in general - I hadn't come across it before.

To old link provided seems to be broken here is another copy of "Computing Euler angles from a rotation matrix ".

Hong Ooi
  • 51,703
  • 12
  • 121
  • 162
Will Baker
  • 2,519
  • 4
  • 19
  • 17
  • 1
    (at the time of this writing) the cgafaq.info link is dead. [webarchive](http://web.archive.org/web/20110722193627/http://cgafaq.info/wiki/Euler_angles_from_matrix) is the best I could find. – João Portela Jun 28 '12 at 10:36
  • 1
    I can't upvote this enough. I've been using Shoemake's algorithm for 5 years, it's really great. It's efficient and completely flexible, implementing all 24 possible Euler/Tait-Bryan combinations and handles both rotating and stationary reference frames. – Kyle Simek Jun 07 '13 at 17:19
  • I independently invented what turned out to be something very similar to Shoemake's algorithm. My supervisor wanted me to publish it. I did some research, and that's when I found Shoemake's algorithm. No paper. Someone ported my code to Java; you can still find it at http://uahuntsville-siso-smackdown.googlecode.com/svn-history/r3/trunk/ez/siso/smackdown/utilities/EulerAngles.java (until google code shuts down in January 2016). – David Hammen Aug 25 '15 at 19:07
  • Both the added links are dead, I've added a live link to the book itself (or the publisher) – Hong Ooi Nov 10 '20 at 15:00
10

In a right-handed Cartesian coordinate system with Z axis pointing up, do this:

struct Quaternion
{
    double w, x, y, z;
};

void GetEulerAngles(Quaternion q, double& yaw, double& pitch, double& roll)
{
    const double w2 = q.w*q.w;
    const double x2 = q.x*q.x;
    const double y2 = q.y*q.y;
    const double z2 = q.z*q.z;
    const double unitLength = w2 + x2 + y2 + z2;    // Normalised == 1, otherwise correction divisor.
    const double abcd = q.w*q.x + q.y*q.z;
    const double eps = 1e-7;    // TODO: pick from your math lib instead of hardcoding.
    const double pi = 3.14159265358979323846;   // TODO: pick from your math lib instead of hardcoding.
    if (abcd > (0.5-eps)*unitLength)
    {
        yaw = 2 * atan2(q.y, q.w);
        pitch = pi;
        roll = 0;
    }
    else if (abcd < (-0.5+eps)*unitLength)
    {
        yaw = -2 * ::atan2(q.y, q.w);
        pitch = -pi;
        roll = 0;
    }
    else
    {
        const double adbc = q.w*q.z - q.x*q.y;
        const double acbd = q.w*q.y - q.x*q.z;
        yaw = ::atan2(2*adbc, 1 - 2*(z2+x2));
        pitch = ::asin(2*abcd/unitLength);
        roll = ::atan2(2*acbd, 1 - 2*(y2+x2));
    }
}
Jonas Byström
  • 22,233
  • 21
  • 93
  • 137
  • 1
    Yep, this uses the NASA convention - yaw, pitch and roll (or heading, pitch and roll if you like). This would be (assuming your right-handed cartesian with Z up) a rotation order of zxy. I'm after an algorithm which handles xyz, xzy, yxz, yzx, zxy and zyx. Perhaps the only option is to provide essentially six different conversions, derived from the one you have given? And would there be a way to extend this approach so that joint limits and degrees of freedom can be used to get a non-ambiguous Euler representation? – Will Baker Jun 23 '09 at 20:47
  • Using my interpretation of "non-ambigous" the short answer is "no". :) – Jonas Byström Jun 24 '09 at 09:13
  • What about if you take joint limits into account? For example, if you end up with two possible Euler representations one of them may be able to be eliminated because it is outside the range of motion of a particular joint. Wouldn't you have to solve this problem when generating skeletal animation from motion capture data? – Will Baker Jun 25 '09 at 22:49
  • You are assuming that you compare the result - "two possible Euler representations" - with something similar. Possibly another Euler representation? The only resolution is using a non-ambigous system. – Jonas Byström Jun 26 '09 at 08:42
  • 1
    According to Wikipedia these are not "Euler" angles, they are "Tait-Bryan" angles. – Ray Apr 26 '14 at 10:47
  • According to Wikipedia [this guy](https://en.wikipedia.org/wiki/Lal_Bihari) was dead for 20 years. – Jonas Byström May 19 '17 at 07:19
9

I've been looking for several days for a similar solution, and I finally ran across this website that has an algorithm for converting quaternions to arbitrary Euler and Tait-Bryan rotations!

Here's the link: http://bediyap.com/programming/convert-quaternion-to-euler-rotations/

And here's the code:

///////////////////////////////
// Quaternion to Euler
///////////////////////////////
enum RotSeq{zyx, zyz, zxy, zxz, yxz, yxy, yzx, yzy, xyz, xyx, xzy,xzx};

void twoaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r11, r12 );
  res[1] = acos ( r21 );
  res[2] = atan2( r31, r32 );
}

void threeaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){
  res[0] = atan2( r31, r32 );
  res[1] = asin ( r21 );
  res[2] = atan2( r11, r12 );
}

void quaternion2Euler(const Quaternion& q, double res[], RotSeq rotSeq)
{
    switch(rotSeq){
    case zyx:
      threeaxisrot( 2*(q.x*q.y + q.w*q.z),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    -2*(q.x*q.z - q.w*q.y),
                     2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                     res);
      break;

    case zyz:
      twoaxisrot( 2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.z + q.w*q.y),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.z - q.w*q.y),
                  res);
      break;

    case zxy:
      threeaxisrot( -2*(q.x*q.y - q.w*q.z),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      2*(q.y*q.z + q.w*q.x),
                     -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                      res);
      break;

    case zxz:
      twoaxisrot( 2*(q.x*q.z + q.w*q.y),
                  -2*(q.y*q.z - q.w*q.x),
                   q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                   2*(q.x*q.z - q.w*q.y),
                   2*(q.y*q.z + q.w*q.x),
                   res);
      break;

    case yxz:
      threeaxisrot( 2*(q.x*q.z + q.w*q.y),
                     q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    -2*(q.y*q.z - q.w*q.x),
                     2*(q.x*q.y + q.w*q.z),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                     res);
      break;

    case yxy:
      twoaxisrot( 2*(q.x*q.y - q.w*q.z),
                   2*(q.y*q.z + q.w*q.x),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.x*q.y + q.w*q.z),
                  -2*(q.y*q.z - q.w*q.x),
                  res);
      break;

    case yzx:
      threeaxisrot( -2*(q.x*q.z - q.w*q.y),
                      q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                      2*(q.x*q.y + q.w*q.z),
                     -2*(q.y*q.z - q.w*q.x),
                      q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                      res);
      break;

    case yzy:
      twoaxisrot( 2*(q.y*q.z + q.w*q.x),
                  -2*(q.x*q.y - q.w*q.z),
                   q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                   2*(q.y*q.z - q.w*q.x),
                   2*(q.x*q.y + q.w*q.z),
                   res);
      break;

    case xyz:
      threeaxisrot( -2*(q.y*q.z - q.w*q.x),
                    q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,
                    2*(q.x*q.z + q.w*q.y),
                   -2*(q.x*q.y - q.w*q.z),
                    q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                    res);
      break;

    case xyx:
      twoaxisrot( 2*(q.x*q.y + q.w*q.z),
                  -2*(q.x*q.z - q.w*q.y),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.y - q.w*q.z),
                   2*(q.x*q.z + q.w*q.y),
                   res);
      break;

    case xzy:
      threeaxisrot( 2*(q.y*q.z + q.w*q.x),
                     q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,
                    -2*(q.x*q.y - q.w*q.z),
                     2*(q.x*q.z + q.w*q.y),
                     q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                     res);
      break;

    case xzx:
      twoaxisrot( 2*(q.x*q.z - q.w*q.y),
                   2*(q.x*q.y + q.w*q.z),
                   q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,
                   2*(q.x*q.z + q.w*q.y),
                  -2*(q.x*q.y - q.w*q.z),
                  res);
      break;
    default:
      std::cout << "Unknown rotation sequence" << std::endl;
      break;
   }
}
frodo2975
  • 6,794
  • 1
  • 24
  • 34
3

I have posted my paper titled "Quaternion to Euler Angle Conversion for Arbitrary Rotation Sequence Using Geometric Methods" on my website at noelhughes.net. I also have algorithms for converting any set of Euler angles to a quaternion and quaternion to/from direction cosine matrix which I will post this weekend. These are also on Martin Bakers website, though a little difficult to find. Google my name, Noel Hughes, and quaternions and you should find it.

3

I solve it this way:

step 1: Make sure which convention for Euler rotation you want, say, zyx.

step 2: Compute the analytical rotation matrix for the rotation. For example, if you want R(zyx),

**R***zyx* = **R***x*( phi ) * **R***y*( theta ) * **R***z*( psi ), where the elements become

R11 =  cos(theta)*cos(psi)
R12 = -cos(theta)*sin(psi)
R13 =  sin(theta)
R21 =  sin(psi)*cos(phi) + sin(theta)*cos(psi)*sin(phi)
R22 =  cos(psi)*cos(phi) - sin(theta)*sin(psi)*sin(phi)
R23 = -cos(theta)*sin(phi)
R31 =  sin(psi)*sin(phi) - sin(theta)*cos(psi)*cos(phi)
R32 =  cos(psi)sin(phi) + sin(theta)*sin(psi)*cos(phi)
R33 =  cos(theta)*cos(phi) 

step 3: By inspection, you can find the sin or tan for the three angles using the elements above. In this example,

tan(phi) = -R23/R33

sin(theta) = -R13

tan(psi) = -R12/R11

step 4: Compute the rotation matrix from your quaternion (see wikipedia), for the elements you need to compute the angles as in 3) above.

Other conventions can be computed using the same procedure.

Fredriku73
  • 3,020
  • 4
  • 33
  • 32
3

Here is a paper I wrote on converting a quaternion to Euler angles.

Link 1

I have also put a number of documents at this location discussing various aspects of quaternions, Euler angles and rotation matrices (DCM).

Link 2

Rais Alam
  • 6,742
  • 11
  • 50
  • 84
3

For those that stumble upon this page while Googling, I recently found derivations for these conversions for all 12 intrinsic Tait-Bryan (1-2-3, 3-2-1, etc.) and Proper Euler (1-2-1, 3-1-3, etc.) rotation sequences in the following two references:

Thanks to frodo2975 for the second link.

Community
  • 1
  • 1
Cody Martin
  • 134
  • 2
  • 10
2

Wikipedia shows how you can use the parts of the quaternion and calculate the euler angles.

ralphtheninja
  • 107,622
  • 20
  • 101
  • 118
  • 1
    It does for so-called Tait-Bryan angles [http://en.wikipedia.org/wiki/Tait-Bryan_angles] but there isn't really any mention of the fact that Euler angles can have other rotation orders. – Will Baker Jun 23 '09 at 20:49
  • Merely posting a link isn't a significant contribution – bobobobo Aug 03 '10 at 17:34