PDA

View Full Version : Quaternion to Euler Angles



Gabriel Sanmartin
01-25-2010, 11:19 AM
Hi all

I've got this IK problem with meeting DOF restrictions with a 3D skeleton character, I think my problem is in some bit of code where I do Quaternion->EulerAngles, I've tried searching around for a function to do this just in case mine was incorrect, but found none, so now I would like to ask another question in order to try a different approach. Please have in mind that I'm no math expert so make it easy ;)

I've got this skeleton system where DOF restrictions of the bones are saved as euler angles, in degrees (e.g. upper arm is restricted to +-45 deg in the z axis, +-90 in the x axis, etc.). So I'm calculating IK rotations of the limbs by using quaternions and the CCD method, but when it comes to verifying DOF restricts, I don't know how to "compare" the current rotation (quaternion) with the DOF restrictions (degrees) in order to make them meet the restrictions, without doing what I'm currently doing, that is converting quats to euler and then viceversa.

Currently I'm doing this:

void CSkeleton::CheckDOFRestrictions(CBone *link)
{
/// Local Variables ///////////////////////////////////////////////////////////
CVertex euler; // PLACE TO STORE EULER ANGLES
///////////////////////////////////////////////////////////////////////////////



//if(quat
// FIRST STEP IS TO CONVERT LINK QUATERNION BACK TO EULER ANGLES
QuatToEuler(&link->quat, &euler);
//prev = link->quat;

//printf("Ángulo %f,%f,%f\n", euler.x,euler.y,euler.z);

// CHECK THE DOF SETTINGS
if (euler.x > (float)link->max_rx)
euler.x = (float)link->max_rx;
if (euler.x < (float)link->min_rx)
euler.x = (float)link->min_rx;
if (euler.y > (float)link->max_ry)
euler.y = (float)link->max_ry;
if (euler.y < (float)link->min_ry)
euler.y = (float)link->min_ry;
if (euler.z > (float)link->max_rz)
euler.z = (float)link->max_rz;
if (euler.z < (float)link->min_rz)
euler.z = (float)link->min_rz;

// BACK TO QUATERNION
EulerToQuaternion(&amp;euler, &amp;link->quat);
}



But I'm getting no good results as when a limb reaches -180 degrees it goes back to 180 degs.


For you to see the effect, let me show you a video:
http://www.youtube.com/watch?v=XC2W4C-peD8

The tiny white dot on the left is the IK desired position, based on which the arm is rotated as to reach the point.


And this image shows the output which currently displays the angle of the main arm bone being rotated. As you can see as it reaches -180 on the z axis it goes back to +180 so DOF restricts return it to its original state at about -113 deg.
(DOF restrictions for that bone are set to -234 deg max, -111 deg min, so I don't think they're being a problem.)
http://img11.imageshack.us/img11/3306/salidak.jpg

Abdallah DIB
01-25-2010, 01:58 PM
I didnt understand exactly ur problem , but the only thing that i can help u with is to verify in the function that convert ur quaternion to euler angles that u use arctang2 instead of arctang, because with arctang u will not get all the orientations because actang generates angles between +-PI/2 while arctan2 generates angles between +-PI.

Gabriel Sanmartin
01-26-2010, 01:17 AM
Here is "my" Quaternion->Euler function (actually I got it from an example on the internet).


void QuatToEuler(const tQuaternion *quat, CVertex *euler)
{
/// Local Variables ///////////////////////////////////////////////////////////
float matrix[3][3];
float cx,sx,x;
float cy,sy,y,yr;
float cz,sz,z;
///////////////////////////////////////////////////////////////////////////////
// CONVERT QUATERNION TO MATRIX - I DON'T REALLY NEED ALL OF IT
matrix[0][0] = 1.0f - 2.0f * (quat->y * quat->y + quat->z * quat->z);
matrix[0][1] = (2.0f * quat->x * quat->y) - (2.0f * quat->w * quat->z);
// matrix[0][2] = (2.0f * quat->x * quat->z) + (2.0f * quat->w * quat->y);
matrix[1][0] = 2.0f * (quat->x * quat->y + quat->w * quat->z);
matrix[1][1] = 1.0f - (2.0f * quat->x * quat->x) - (2.0f * quat->z * quat->z);
// matrix[1][2] = (2.0f * quat->y * quat->z) - (2.0f * quat->w * quat->x);
matrix[2][0] = 2.0f * (quat->x * quat->z - quat->w * quat->y);
matrix[2][1] = 2.0f * (quat->y * quat->z + quat->w * quat->x);
matrix[2][2] = 1.0f - 2.0f * (quat->x * quat->x - quat->y * quat->y);


sy = -matrix[2][0];
cy = sqrt(1 - (sy * sy));
yr = (float)atan2(sy,cy);
euler->y = (yr * 180.0f) / (float)M_PI;

// AVOID DIVIDE BY ZERO ERROR ONLY WHERE Y= +-90 or +-270
// NOT CHECKING cy BECAUSE OF PRECISION ERRORS
if (sy != 1.0f &amp;&amp; sy != -1.0f)
{
cx = matrix[2][2] / cy;
sx = matrix[2][1] / cy;
euler->x = ((float)atan2(sx,cx) * 180.0f) / (float)M_PI; // RAD TO DEG

cz = matrix[0][0] / cy;
sz = matrix[1][0] / cy;
euler->z = ((float)atan2(sz,cz) * 180.0f) / (float)M_PI; // RAD TO DEG
}
else
{
// SINCE Cos(Y) IS 0, I AM SCREWED. ADOPT THE STANDARD Z = 0
// I THINK THERE IS A WAY TO FIX THIS BUT I AM NOT SURE. EULERS SUCK
// NEED SOME MORE OF THE MATRIX TERMS NOW
cx = matrix[1][1];
sx = -matrix[1][2];
euler->x = ((float)atan2(sx,cx) * 180.0f) / (float)M_PI; // RAD TO DEG

cz = 1.0f;
sz = 0.0f;
euler->z = ((float)atan2(sz,cz) * 180.0f) / (float)M_PI; // RAD TO DEG
}


}

sammie381
01-26-2010, 03:38 PM
DOF restrictions for that bone are set to -234 deg max, -111 deg min...


What exactly do you want Quaternion->Euler to return? An angle less than -180 degrees?
Because that's not going to happen using any math function. You are stuck with a -180 to 180 range
when converting quats to euler.

Maybe it is better to exclusively store all rotations as eulers, and convert to quats when you need them,
rather than doing it the other way around.