Reputation: 133
I have a simple quaternion implementation for object rotation. If I create two quaternions representing rotations about the same axis-aligned vector (i.e. along the X, Y, or Z axis) the result is the same as a single rotation by the sum of their magnitudes (i.e. rotating by PI/2 then PI/2 again is the same as rotating by PI). That's good.
As soon as the axis of rotation is not axis-aligned, then the concatenations diverge from the expected (rotating by PI/2 then PI/2 again is not the same as rotating by PI). That's not good.
After poring over my code for a couple days, I'm not seeing anything wrong, so let me ask now: Am I in some way fundamentally misunderstanding how quaternions work? I'm reasoning about the quaternions in terms of the axis-angle rotations they represent, because frankly I don't completely understand quaternions.
If not, can you look at my code? :-) I just pushed the whole thing (written in Java--I'm targeting Android) to GitHub: https://github.com/wtracy/quaternions Under the Quaternions directory is an Eclipse project. (You shouldn't need Eclipse to read anything, but it's convenient.) The quaternion class is under the src/ folder. In the test/ folder are JUnit tests and stubs of the classes needed to run my Quaternion class.
I've done my best to make my code and tests easy to follow. I feel dumb asking the internet to find a bug in my code, but I am clean out of ideas. :-P
Upvotes: 0
Views: 254
Reputation: 33126
As soon as the axis of rotation is not axis-aligned, then the concatenations diverge from the expected (rotating by PI/2 then PI/2 again is not the same as rotating by PI). That's not good.
Even after you get the math right, you will still find this not to be the case. Rotations in three dimensional space in general are not commutative. If you performing rotation A and then rotation B you will in general end up with a different orientation than would result from first performing rotation B followed by rotation A.
Upvotes: 0
Reputation: 183978
You have a sign error in your quaternion multiplication.
public Quaternion times(Quaternion q2) {
Quaternion q1 = this;
float w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
float x = q1.x*q2.w + q1.w*q2.x + q1.z*q2.y - q1.y*q2.z;
float y = q1.w*q2.y - q1.x*q2.z + q1.y*q2.w + q1.z*q2.x;
float z = q1.w*q2.z - q2.x*q2.y + q1.y*q2.x + q1.z*q2.w;
return new Quaternion(w, x, y, z);
}
The product, written out, is
(w1 + x1*i + y1*j + z1*k)*(w2 + x2*i + y2*j + z2*k)
= w1*w2 - x1*x2 - y1*y2 - z1*z2
+ (w1*x2 + x1*w2 + y1*z2 - z1*y2)*i
+ (w1*y2 - x1*z2 + y1*w2 + z1*x2)*j
+ (w1*z2 + x1*y2 - y1*x2 + z1*w2)*k
since
i*j = k j*i = -k
j*k = i k*j = -i
k*i = j i*k = -j
You have the wrong terms with a minus in the equations for x
and z
- that wouldn't make a difference if the two axes are the same, because one can also write (as a shorthand)
(r + v)*(s + w) = r*s - <v|w> + r*w + s*v + v×w
and v×w = 0
for collinear vectors, but if the axes are different it would show up.
Further, in the equation for z
,
float z = q1.w*q2.z - q2.x*q2.y + q1.y*q2.x + q1.z*q2.w;
^^^^^^^^^
you have a typo, using q2
for both factors once.
It should be
public Quaternion times(Quaternion q2) {
Quaternion q1 = this;
float w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
float x = q1.x*q2.w + q1.w*q2.x - q1.z*q2.y + q1.y*q2.z;
float y = q1.w*q2.y - q1.x*q2.z + q1.y*q2.w + q1.z*q2.x;
float z = q1.w*q2.z + q1.x*q2.y - q1.y*q2.x + q1.z*q2.w;
return new Quaternion(w, x, y, z);
}
Upvotes: 3