13

I'm using lib glm (http://glm.g-truc.net/) for test quaternion but I've a problem; when I convert euler angle to quaternion then immediatly quaternion to euler angles, my result are totally different from my initial euler angles. Is this normal? Could it be because the rotations are not communative?

Code test:

#include <glm\quaternion.hpp>
#include <math.h>

#define PI M_PI
#define RADTODEG(x) ( (x) * 180.0 / PI )
#define DEGTORAD(x) ( (x) * PI / 180.0 )

int         main( void )
{
    float RotX = 90.f;
    float RotY = 180.f;
    float RotZ = -270.f;

    if ( RotX || RotY || RotZ )
    {
        std::cout << "Init: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "\n";
        glm::quat key_quat(glm::detail::tvec3<float>(DEGTORAD( RotX ),
                                                     DEGTORAD( RotY ),
                                                     DEGTORAD( RotZ )));
        glm::detail::tvec3<float> v = glm::eulerAngles(key_quat);

        /*  // the result is even worse with this code here
        RotX = RADTODEG(v.x);
        RotY = RADTODEG(v.y);
        RotZ = RADTODEG(v.z);
        */

        RotX = v.x;
        RotY = v.y;
        RotZ = v.z;

        std::cout << "Final: x= " << RotX << ", y= " << RotY << ", z= " << RotZ << "\n";
    }
    return (0);
}

Result:

Init: x= 90, y= 180, z= -270
Final: x= -90, y= -3.41509e-006, z= -90

thank you in advance o/

Amir
  • 9,058
  • 9
  • 39
  • 68
user1466739
  • 133
  • 1
  • 1
  • 4

4 Answers4

16

Yes, it is normal. There are 2 ways to represent the same rotation with Euler angles.

I personally don't like Euler angles, they mess up the stability of your app. I would avoid them. Plus, they are not very handy either.

Ali
  • 51,545
  • 25
  • 157
  • 246
  • 1
    thanks to this usefull answer. effectively, in my engine, I use quaternion to rotate my objects. So, users can use fonction; SetRotation and GetRotation (with 3 euler-angles). In this fonction, I operate on object quaternion and I save euler-angles for user. you will probably agree with me if I say it is much simpler to specify its rotations with Euler angles that with quaternions ... (I'm going to read/watch all that you link. I'll come back) – user1466739 Jun 19 '12 at 21:49
  • Yes, I agree, Euler angles can be useful when communcating with the user. – Ali Jun 19 '12 at 22:19
  • Okay. I read the book you told me and now I understood everything. Thank you very much. – user1466739 Jun 20 '12 at 11:04
  • Glad to hear it helped. Good luck! :) – Ali Jun 20 '12 at 13:15
  • 1
    What? They are not evil... totally context sensitive. – zezba9000 Mar 02 '13 at 21:31
  • 1
    Two ways? There are 24 different beasts that qualify as "Euler angles", a boatload more if you allow left-handed coordinate systems. Euler angles aren't evil. That's understating things. They are evil incarnate. But evil as they are, they can be useful when communicating with the user. – David Hammen Aug 01 '15 at 04:46
11

Have a look at this page. It has everything you need (even some code samples!) for dealing with 3D transformations.

Quaternion to Euler Angles

Euler Angles to Quaternion

All rotation conversions

Jav_Rock
  • 21,011
  • 18
  • 115
  • 164
11

If you end up needing quaternion's to Euler angles, but you need an arbitrary rotation order, I came across a site with conversion code. Sometimes the trick is just finding the right rotation order. (Btw, the orders that have the same letter twice, like XYX, are proper Euler angles, but the ones like XYZ are Tait-Bryan angles).

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
  • How do you read a rotation sequence? for example `zyx`, does this mean apply z then apply y then x (elemental matrix multiplication order is `Rx*Ry*Rz`) or does `zyx` means it's `Rz*Ry*Rx` ? – MohamedEzz May 16 '18 at 15:25
  • Not sure about the actual matrix multiplies, but from an intuitive perspective, you rotate in order, like for `zyx`, you'd rotate about the object's local z-axis, then y, then x. If you're having trouble visualizing it, it can be useful to download a 3d modeling program like Blender so that you can see the rotations. Pretty much all 3d programs have a gimbal rotate mode that'll let you rotate an object around just 1 of its tait-bryan axes. – frodo2975 May 17 '18 at 18:05
-1

Euler -> Quaternion

Extracted from Three.js.

Here's a piece of code which works for me:

function eulerToQuaternion(eulerXYZ) {
  var c1 = Math.cos(eulerXYZ[0] / 2),
    c2 = Math.cos(eulerXYZ[1] / 2),
    c3 = Math.cos(eulerXYZ[2] / 2),
    s1 = Math.sin(eulerXYZ[0] / 2),
    s2 = Math.sin(eulerXYZ[1] / 2),
    s3 = Math.sin(eulerXYZ[2] / 2),
    x = s1 * c2 * c3 + c1 * s2 * s3,
    y = c1 * s2 * c3 - s1 * c2 * s3,
    z = c1 * c2 * s3 + s1 * s2 * c3,
    w = c1 * c2 * c3 - s1 * s2 * s3;

  return [x, y, z, w];
};

function calculate() {
  var quat = eulerToQuaternion([document.querySelector('#x').value, document.querySelector('#y').value, document.querySelector('#z').value]);

  document.querySelector('#result').innerHTML = quat.join(' &nbsp; ');
}
<h3>Euler radians in XYZ order:</h3>
<fieldset>
  <label>X:
    <input id="x" value="1.5" />
  </label>
  <label>Y:
    <input id="y" value="1" />
  </label>
  <label>Z:
    <input id="z" value="0" />
  </label>
  <button onClick="calculate()">To Quaternion</button>
</fieldset>
<h3>X Y Z W result:</h3>
<div id="result"></div>
Pawel
  • 10,190
  • 4
  • 56
  • 60