I am working on some code to convert a quaternion Q into euler angles.
I came across Paul Heckbert's section in Graphics Gems 4:
http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html
The short of it is that his code should convert between any euler
angles representation to rotation matrices and quaternions.
However, I am confused as to how the output should come out. I wrote a
little test program to make sure it works:
// Create a quaternion rotation of 90 degrees about a diagonal
axis
Quat q = AxisAngle( Vector( 1, 1, 1 ), M_PI * 0.5 );
// Convert to ZXY euler angles with static frame of reference
int enc = EulOrdZXYs;
EulerAngles ea = Eul_FromQuat( q, enc );
// Generate quaternions for each indivudual axes
Quat qx = AxisAngle( Vector( 1, 0, 0 ), ea.x );
Quat qy = AxisAngle( Vector( 0, 1, 0 ), ea.y );
Quat qz = AxisAngle( Vector( 0, 0, 1 ), ea.z );
// Combine the individual rotations
const Quat qAct = qy * qx * qz;
// See how close the combined rotations are to the original
quaternion
const Quat qDiff = qAct.inverse() * q;
// Print out the variables and their values
cout << STREAM_VAR( q ) << endl;
cout << STREAM_VAR( ea ) << endl;
cout << STREAM_VAR( qx ) << endl;
cout << STREAM_VAR( qy ) << endl;
cout << STREAM_VAR( qz ) << endl;
cout << STREAM_VAR( qAct ) << endl;
cout << STREAM_VAR( qDiff ) << endl;
cout << STREAM_VAR( acos( qDiff.w ) * 180 / M_PI ) << endl;
I didn't include the AxisAngle function or the STREAM_VAR macro, but
basically the first one converts from an axis-angle representation to
a quaternion, and the second prints out a variable's name followed by
its value. I also didn't give the operator*(Quat,Quat) or Quat::inverse
() functions, but they are standard quaternion multiplication and
inversion methods. If you like, I'll post all that code.
The output I get is:
q (0.707107 0.408248 0.408248 0.408248)
ea (1.21992 0.246506 1.21992 16 )
qx (0.819672 0.572833 0 0)
qy (0.992414 0 0.122941 0)
qz (0.819672 0 0 0.572833)
qAct (0.707107 0.523699 -0.243049 0.408248)
qDiff (0.781241 0.184255 0.507669 -0.313024)
acos( qDiff.w ) * 180 / M_PI (38.6256)
So, the effect of combining the angles in ZXY order gives me a ~39
degree discrepancy between that combined rotation and the original
one.
However, if I change the order in which I combine the rotations to be
XYZ, like so:
const Quat qAct = qz * qy * qx;
I get the correct answer:
q (0.707107 0.408248 0.408248 0.408248)
ea (1.21992 0.246506 1.21992 16)
qx (0.819672 0.572833 0 0)
qy (0.992414 0 0.122941 0)
qz (0.819672 0 0 0.572833)
qAct (0.707107 0.408248 0.408248 0.408248)
qDiff (1 0 0 0)
acos( qDiff.w ) * 180 / M_PI (0)
So, what about this code am I not understanding? It seems to always
give me the rotation in XYZ order.
Thanks.
Dave
> However, if I change the order in which I combine the rotations to be
> XYZ, like so:
>
> const Quat qAct = qz * qy * qx;
>
> I get the correct answer:
What does "*" mean in this context? Did you define multiplication
in C++ for quaternions? The code in Graphics Gems does not.
See "http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation"
for the theory of quaternion rotation.
John Nagle
As I explained in the original post, I defined operator*(Quat,Quat)
and Quat::inverse() to be proper quaternion multiplication and
inversion code, but here is that code if you want to inspect it
yourself.
inline Quat operator*(const Quat &a, const Quat &b)
{
return Quat(a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z,
a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y,
a.w*b.y + a.y*b.w + a.z*b.x - a.x*b.z,
a.w*b.z + a.z*b.w + a.x*b.y - a.y*b.x);
}
inline Quat Quat::inverse() const
{
return Quat(w, -x, -y, -z);
}
As far as theory, I think I'm good on that, but if you see any holes
in my reasoning, by all means let me know. My thoughts were that, if I
decompose the quaternion into ZXY-order euler angles, then I can
reconstruct the original quaternion by rotating first about the Z
axis, then the X, then the Y. So, if qz, qx, and qy are quaternions
for those rotations, I should be able to get the original by
multiplying in the reverse order (like is done with rotation matrices
when operating on column vectors):
q == qy * qx * qz
Correct so far? So what else might I be missing? Am I using Shoemake's
code incorrectly, or am I making a mistake on my theory?
I have written a paper on converting a quaternion to Euler angles of
any rotation sequence. You can find it at noelhughes.net. There is a
typographic error in it that I recently became aware of. Let me know
if you find it.
Noel Hughes
noel.h...@gmail.com