Jump to content
  • Advertisement
Sign in to follow this  
Sindharta Tanuwijaya

Quaternion to Euler Angles (2006)

This topic is 4368 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, Following previous thread about converting quaternion into euler angles: http://www.gamedev.net/community/forums/topic.asp?topic_id=305326&whichpage=1� I have already tried using Dave Eberly's code and Shoemake's code and it still won't work. Actually, I am making a program to edit motion capture data using Visual C++. I am using BVH files as input files, and I converted the rotation data from those BVH Files, which has ZXY Euler Angle rotation format, into quaternions. After editing, I would like to be able to save the output as BVH files too, but I am having trouble converting the quaternions back into ZXY Euler angles. I also had the thought that perhaps Dave Eberly's code and Shoemake's code are using right handed coordinate system, while I am using left handed one (DirectX). But for Dave Eberly's code, after reading its documentation, I think that if I just give LH coordinate system as its input, then it will automatically give LH coordinate system Euler angles as output. For Shoemake's code, I think the documentation (from Graphic Gems 4, I believe) is a little bit too complicated for me :(. Anyway, another option would be to use a motion capture file format with quaternion values inside that file format, but I don't know if such file format even exists. I am wondering if someone could point me to the right direction? Sindharta T.

Share this post


Link to post
Share on other sites
Advertisement


void Quaternion::ToRadians(float *outX,float *outY,float *outZ)
{
double sqw = w*w;
double sqx = x*x;
double sqy = y*y;
double sqz = z*z;
double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
double test = x*y + z*w;
if (test > 0.499*unit)
{ // singularity at north pole
*outY = 2 * atan2(x,w);
*outZ = 1.570796327; //HALF PI
*outX = 0;
return;
}
if (test < -0.499*unit)
{ // singularity at south pole
*outY = -2 * atan2(x,w);
*outZ = -1.570796327; //HALFPI
*outX = 0;
return;
}
*outY = atan2(2*y*w-2*x*z , sqx - sqy - sqz + sqw);
*outX = asin(2*test/unit);
*outZ = atan2(2*x*w-2*y*z , -sqx + sqy - sqz + sqw);
}


Share this post


Link to post
Share on other sites
To tthibault,

Thank you for the code, but it still didn't work for me. Can you tell me what the order of rotation of the Euler angles generated by that code is? Or any documentations for it?

Sindharta T.

Share this post


Link to post
Share on other sites
As long as you use a 'parallel' set of conversion functions to convert between Euler angles and quaternions, then that shouldn't be a source of errors. I'm would imagine both Eberly's and Shoemake's code is correct and stable, as both have been around for a while. I also have code for this that I'm pretty sure is correct; it's quite similar to Shoemake's though.

As far as the conversion itself goes, handedness shouldn't matter (I've never been able to figure out why Shoemake's docs claim the example code is for a 'right-handed' matrix). You may be running into handedness problems in terms of how the rotations are interpreted or appear onscreen, but that's another issue.

In other words, I wouldn't automatically attribute whatever problem you're encountering to incorrect Euler-angle conversion. If you use Eberly's or Shoemake's code (and can rule out copy-and-paste or user errors), you should be fine.

Also, perhaps you could give a more detailed description of how it's not working. Are the rotations going in the wrong direction? Are they completely off? And so on.

Share this post


Link to post
Share on other sites
Hi,

This is my code for using Shoemake's code. m_quatRotation holds my rotation, and the resulting angles are put into pZXYRot. The original motions represents a walking motion.

//---------------------
//use shoemake
Quat q;
q.x = m_quatRotation.x;
q.y = m_quatRotation.y;
q.z = m_quatRotation.z;
q.w = m_quatRotation.w;

//Call Shoemake's function
EulerAngles angles = Eul_FromQuat(q,EulOrdZXYr);
pZXYRot->SetX(angles.x);
pZXYRot->SetY(angles.y);
pZXYRot->SetZ(angles.z);
//end shoemake
//---------------------

The output BVH file if I use Shoemake's code is very odd. It seems that the body itself is being turned around (head below, and feet above), and of course the rotations of the joints do not form a feasible motion, although the rhytm of the walking motion somehow exists there.

And this is my code if I use source code from Dave Eberly's site:
// this is zxy order
if (mat._32 < 1.0f)
{
if (mat._32 > -1.0f)
{
pZXYRot->SetZ(atan2(-mat._12,mat._22));
pZXYRot->SetX(asin((double)mat._32));
pZXYRot->SetY(atan2(-mat._31,mat._33));

return true;
}
else
{
// WARNING. Not unique. ZA - YA = -atan(r02,r00)
pZXYRot->SetZ(-atan2(mat._13,mat._11));
pZXYRot->SetX(-D3DX_PI/2.f);
pZXYRot->SetY(0.f);

return false;
}
}
else
{
// WARNING. Not unique. ZA + YA = atan2(r02,r00)
pZXYRot->SetZ(atan2(mat._13,mat._11));
pZXYRot->SetX(D3DX_PI/2.f);//D3DX_PI/2.f;
pZXYRot->SetY(0.f);
return false;
}
//end zxy order

The result is better than Shoemake's code in my case, and although the walking motion produced is probably feasible, some/many rotations are not correct (are not the same as the rotations of original motion), which are probably caused by "not unique" solutions.

These are some screenshots of the BVH Files which are displayed in Motion Builder.
http://www.on.cs.keio.ac.jp/~sin/original.jpg
http://www.on.cs.keio.ac.jp/~sin/shoemake.jpg
http://www.on.cs.keio.ac.jp/~sin/dave.jpg
http://www.on.cs.keio.ac.jp/~sin/my_program.jpg

Your words: "parallel set of conversion functions" captured my attention. I used DirectX function "D3DXQUATERNIONRotationYawPitchRoll" to obtain quaternion values from Euler angles at the beginning, which I believe, perform in LH coordinate system. I wonder if the errors are related to this?

Sindharta






Share this post


Link to post
Share on other sites
Typical DX docs--poorly written and many times lacking in technical quality. For the entry D3DXQuaternionRotationYawPitchRoll, "Builds a quaternion with the given yaw, pitch, and roll". There is no indication of the order of composition of the coordinate-axis rotation matrices.

The following example should indicate how to fix your code. This makes calls to DX code and to eberley's code:

float yaw0 = 0.1f;
float pitch0 = 0.2f;
float roll0 = 0.3f;
D3DXQUATERNION quat0;
D3DXQuaternionRotationYawPitchRoll(&quat,yaw0,pitch0,roll0);
D3DXMATRIX rot0;
D3DXMatrixRotationQuaternion(&rot0,&quat0);
Matrix3f rot1;
rot1[0][0] = rot0._11;
rot1[0][1] = rot0._12;
rot1[0][2] = rot0._13;
rot1[1][0] = rot0._21;
rot1[1][1] = rot0._22;
rot1[1][2] = rot0._23;
rot1[2][0] = rot0._31;
rot1[2][1] = rot0._32;
rot1[2][2] = rot0._33;
float yaw1, pitch1, roll1;
rot1.ToEulerAnglesZXY(roll1,pitch1,yaw1);
roll1 = -roll1;
pitch1 = -pitch1;
yaw1 = -yaw1;
// Now roll0==roll1, pitch0==pitch1, yaw0==yaw1.

Matrix3f rot2;
rot2.FromEulerAnglesYXZ(yaw0,pitch0,roll0);
// rot2 is the transpose of rot1 (DX's 3x3 rotation matrix in rot0)

Share this post


Link to post
Share on other sites
Hi,

Thanks for the code. It certainly works for that input, but in my case, some errors still occured, which just looks like http://www.on.cs.keio.ac.jp/~sin/dave.jpg.

Anyway, I tested playing with the input values, and it seems that there are some cases where we could not obtain the angles back, for example:

float yaw0 = 2.2f;
float pitch0 = 3.0f;
float roll0 = 1.5f;

I think I have to check the rotation angles that are generated for the incorrect joints and also check their original rotation angles.

Sindharta

Share this post


Link to post
Share on other sites
Quote:
Original post by Sindharta Tanuwijaya
It certainly works for that input, but in my case, some errors still occured,


What do you define as an "error"?

Quote:

Anyway, I tested playing with the input values, and it seems that there are some cases where we could not obtain the angles back, for example:

float yaw0 = 2.2f;
float pitch0 = 3.0f;
float roll0 = 1.5f;


In your example, notice that yaw0 - yaw1 = pi, pitch0 + pitch1 = pi, and roll0 - roll1 = pi. The important observation is that in the code I posted, rot2 is the transpose of rot1, so the rotation matrices agree. The problem is the nonuniqueness of Euler angles that form a particular rotation matrix. Shoemake's and Eberley's code make choices about the range of the Euler angles spit out by the factorization so that you get _some_ set of Euler angles.

Euler angles, although useful in a limited number of situations, are generally a curse.

Share this post


Link to post
Share on other sites
Hi,

I defined an error, when I found out that the Euler rotation angles obtained from quaternions differ quite a lot from the original Euler Rotation Angles.

Anyway, thanks to you, I think I solved the problem. I took a look into my code again and compare it with your code, and I found out that actually what I have been doing is not ZXY rotation, but YXZ rotation. I tested it by editing the motion capture data, save it into a bvh file, and import that bvh file into Motion Builder and it worked. But I am still wondering why you negated all the values at the end of the code? It works though, but how did you find it?

Sindharta T.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sindharta Tanuwijaya
I defined an error, when I found out that the Euler rotation angles obtained from quaternions differ quite a lot from the original Euler Rotation Angles.


This is not what I would consider an error. As noted, the nonuniqueness of Euler angles for a rotation matrix makes it difficult to expect that conversion from Euler angles to matrix followed by conversion back to Euler angles will produce the same numbers.

Quote:

I took a look into my code again and compare it with your code, and I found out that actually what I have been doing is not ZXY rotation, but YXZ rotation. But I am still wondering why you negated all the values at the end of the code? It works though, but how did you find it?


Direct3D uses the convention for matrix-vector multiplication U = V*M, where V is a 1x4 (row) vector and M is a 4x4 homogeneous matrix. The product U is also a 1x4 vector. WildMagic uses the convention U' = M'*V', where V' is a 4x1 (column) vector and M' is a 4x4 homogeneous matrix. The product U' is also a 4x1 vector. The ' symbol indicates transposition. M' is the transpose of M.

Assuming the WildMagic convention, you have Euler angles yaw ay, pitch ap, and roll ar and you have a composition R = Rz(ar)*Rx(ap)*Ry(ay), where Rx(t) is a coordinate-axis rotation about (1,0,0) by angle t, Ry(t) is a coordinate-axis rotation about (0,1,0) by angle t, and Rz(t) is a coordinate-axis rotation about (0,0,1) by angle t. You would apply R to vector V by R*V. To convert to how Direct3D operates, you want the transpose

R' = (Rz(ar)*Rx(ap)*Ry(ay))' = Ry(ay)'*Rx(ap)'*Rz(ar)' = Ry(-ay)*Rx(-ap)*Rz(-ar)

The angles are negated and the ZXY order is reversed to YXZ.

In the second example you posted, where you said there was an "error", the mathematical characterization of the nonuniqueness is: Ry(ay-pi)*Rx(pi-ap)*Rz(ar-pi) = Ry(ay)*Rx(ap)*Rz(ar) for _all_ angles ay, ap, and ar.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!