Created
March 31, 2019 03:56
-
-
Save mhalber/8054459427c5a9bec32b5d42fe54fbe7 to your computer and use it in GitHub Desktop.
quaternion rotation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In this stackoverflow question[1] user ltjax provides an answer on the order of applying quaternions to | |
accumulate rotations. The answer makes an interesting claim that the proposed order of quaternion | |
multiplications: | |
(1) cameraOrientation = framePitch * cameraOrientation * frameYaw | |
is equivalent to storing the 'Yaw' and 'Pitch' angles separately. It is an interesting observation, | |
but no reasoning, or proof is given. | |
For the rest of this discussion we will assume lab 1-2-3 sequence[2], where we first roll the | |
body around x, then we nose it up around y(pitch), and finally rotate around z(yaw). | |
What we wish to show is that, for the case of just two Euler angles, converting from an accumulated | |
Euler Angles is in fact equal to left and right multiplying by the incremental change in the Euler | |
Angles. | |
Assuming E(roll,pitch,yaw) is a function that converts Euler into a quaternion, and we mark 'roll', | |
'pitch', 'yaw' as the accumulated angles, and d_roll, d_pitch, d_yaw as the incremental angle, | |
we wish to show following is true: | |
(2) E( 0, pitch, yaw ) = E( 0, d_pitch, 0) * E(0, pitch - d_pitch, yaw - d_yaw ) * E(0, 0, d_yaw) | |
For brevity we will now just mark 'roll', 'pitch', 'yaw' as 'x', 'y', 'z'. Similar treatment | |
is made for incremental angles. | |
In this document we will represent quaternions as [x,y,z,w], where [x,y,z] is the imaginary part and | |
w is the real part. We assume quaternion can be indexed as an array. | |
First lets define function that converts a Euler to a quaternion: | |
(3) E(x, y, z) = [ cos(0.5*x) * cos(0.5*y) * sin(0.5*z) - sin(0.5*x) * sin(0.5*y) * cos(0.5*z), | |
sin(0.5*x) * cos(0.5*y) * sin(0.5*z) + cos(0.5*x) * sin(0.5*y) * cos(0.5*z), | |
sin(0.5*x) * cos(0.5*y) * cos(0.5*z) - cos(0.5*x) * sin(0.5*y) * sin(0.5*z), | |
cos(0.5*x) * cos(0.5*y) * cos(0.5*z) + sin(0.5*x) * sin(0.5*y) * sin(0.5*z) ] | |
Without the loss of generality we can drop the 0.5 constant factor, to make our derivations slightly | |
more compact. | |
Secondly let us define quaternion multiplication: | |
(4) A*B = [ A[3]*B[0] + A[0]*B[3] + A[1]*B[2] - A[2]*B[1], | |
A[3]*B[1] + A[1]*B[3] + A[2]*B[0] - A[0]*B[2], | |
A[3]*B[2] + A[2]*B[3] + A[0]*B[1] - A[1]*B[0], | |
A[3]*B[3] - A[0]*B[0] - A[1]*B[1] - A[2]*B[2] ] | |
With all in place, we can start. Let us first calculate left hand side and repeat definition of | |
the right hand side ( from (3) ) | |
(5) LHS = E(0, y, z) = [sin(z)*cos(y), sin(y)*cos(z), -sin(y)*sin(z), cos(y)*cos(z)] | |
(6) RHS = E(0, d_y, 0) * E(0, y-d_y, z-d_z) * E(0, 0, d_z) | |
Let's start calculating RHS. Using our conversion formulas | |
(7) E(0, d_y, 0) = [0, sin(dy), 0, cos(dy)] | |
(8) E(0, 0, d_z) = [sin(dz), 0, 0, cos(dz)] | |
(9) E(0, y-d_y, z-d_z) = [-sin(dz - z)*cos(dy - y), -sin(dy - y)*cos(dz - z), -sin(dy - y)*sin(dz - z), cos(dy - y)*cos(dz - z)] | |
Now let's perform first quaternion multiplication | |
(10) Q_A = E(0, d_y, 0) * E(0, y-d_y, z-d_z) | |
Let us work out the first index of Q_A: | |
Q_A[0] = -sin(dy)*sin(dy - y)*sin(dz - z) - sin(dz - z)*cos(dy)*cos(dy - y) | |
Q_A[0] = -(sin(dy)*sin(dy - y) + cos(dy)*cos(dy - y))*sin(dz - z)// Factor out the common expression | |
Q_A[0] = -sin(dz - z)*cos(dy-dy+y) // Use Ptolemy’s identitiy | |
Q_A[0] = -sin(dz - z)*cos(y) // Simplify | |
The rest of the components can be simplified to get: | |
Q_A[1] = sin(y)*cos(dz - z) | |
Q_A[2] = sin(y)*sin(dz - z) | |
Q_A[3] = cos(y)*cos(dz - z) | |
Now the second quaternion multiplication | |
(11) RHS = Q_A * E(0, 0, d_z) | |
Similarly to calculating q_A we have: | |
RHS[0] = sin(dz)*cos(y)*cos(dz - z) - sin(dz - z)*cos(dz)*cos(y) | |
RHS[0] = (sin(dz)*cos(dz - z) - sin(dz - z)*cos(dz))*cos(y) // Factor out the common expression | |
RHS[0] = sin(dz - dz + z)*cos(y) // Use Ptolemy’s identity | |
RHS[0] = sin(z)*cos(y) // Simplify | |
RHS[1] = sin(y)*cos(z) | |
RHS[2] = -sin(y)*sin(z) | |
RHS[3] = cos(y)*cos(z) | |
Finally, we have: | |
LHS = [sin(z)*cos(y), sin(y)*cos(z), -sin(y)*sin(z), cos(y)*cos(z)] | |
RHS = [sin(z)*cos(y), sin(y)*cos(z), -sin(y)*sin(z), cos(y)*cos(z)] | |
As such we see that | |
LHS = RHS, as intended | |
------------------------------ | |
TODO / Questions: | |
[ ] What happens if there is roll? Provide additional derivation. | |
[ ] What is the advantage of the accumulating rotations in quaternion vs Euler angle accumulation? Gimbal loc | |
References | |
------------------ | |
[1] https://gamedev.stackexchange.com/questions/30644/how-to-keep-my-quaternion-using-fps-camera-from-tilting-and-messing-up/30654 | |
[2] https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment