Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Solving gimbal lock

Started by altra4u Jul 1, 2009 at 9:24 AM 13 replies 9.3k views
Original Post
altra4u
altra4u
Hey guys, So I need to do the solve the following and I am confused if someone can please help me. Issue:Solve gimbal lock for a Left hand coordinate system with first y, then x and then z order of rotation.So I want the rotations to be always around the global axis.i.e not the local axis of the object. Steps taken: -form a quaternion with with rotation order multiplied Q = Qz.Qx.Qy.This order of rotation as the reference axis is the global world axis.I have pictures of the math I did too but don't find a place to attach them. -multiply the new quaternion(based on key input) with the current quat.So we have newQuat = newQuat*Q -convert the final quat to euler angles. This quat to euler conversion gives me rotation in the local axes.I wonder why is the case as I am pre-multiplying both the quats and the rotation matrices(at the time of quat to euler conversion) as I am using the world axis as the absolute reference. -Since quattoeuler LH z.x.y did not work.I tried doing quattoeulerLHy.x.z which did not work too.Attaching math file quattoeulerLHy.x.z.This did not work too Can someone point out what I am doing wrong? Questions: -Although I don't see how but would the handedness of a system affect the quaternion derivation? The only thing I could think of now is creating the quaternion in Qy.Qx.Qz(although I feel this is wrong) and trying the z.x.y and y.x.z order of quat to euler. LH:left hand z.x.y:order in which multiplied Thanks, chit
vicviper
vicviper
I suggest you to do this:

keep a quaternion for every object you want to avoid gimbal lock

for each step:
multiply that quaternion by a quaternion representing the rotation change (usually a quaternion built with very small angle degrees)
renormalize the quaternion
convert the quaternion to euler angles or a matrix for rendering.

Notice the quaternion is kept all the time, you only multiply it by other quaternions and renormalize it, but you don't rebuild it fron scratch every frame.


Zakwayda
Zakwayda
I didn't quite follow all that, but maybe it's a multiplication order problem - did you try 'newQuat = Q*newQuat' instead of 'newQuat = newQuat*Q'?

Coordinate system handedness should not matter for what you're doing. Beyond that I can't give much in the way of advice, as I'm not quite clear on what sort of motion you're trying to achieve. If the multiplication order suggestion doesn't help, maybe you could provide some more details (such as what the object represents, how the user interface works, and exactly what sort of motion you want the object to undergo).
altra4u
altra4u
I am doing what you are saying.I am not creating the quat from scratch every time.Its saved.Only the quat based on key input is created new every time.I am converting the final quat to euler angles to give the angles to my engine which only accepts euler angles for rotation.This is the part that I feel is going wrong.
Also posting the code

Euler to Quat(Can I post pics here?I have the full math derivation)
thetaxHalf as Float = x/2.0
thetayHalf as Float = y/2.0
thetazHalf as Float = z/2.0
cx as Float = Cosine(thetaxHalf)
cy as Float = Cosine(thetayHalf)
cz as Float = Cosine(thetazHalf)
sx as Float = Sine(thetaxHalf)
sy as Float = Sine(thetayHalf)
sz as Float = Sine(thetazHalf)

me.kz_w = cz*cx*cy - sz*sx*sy
me.kz_x = cz*sx*cy - sz*cx*sy
me.kz_y = cz*cx*sy + sz*sx*cy
me.kz_z = cz*sx*sy + sz*cx*cy
me.NormalizeQuat()

Quat to Euler(yaw-y axis,pitch-x axis,roll-z axis)
//Order:Rz.Rx.Ry.Matrices for left hand coordinate system.Rotates in the local axes.

pitch as Float = ArcSine(2.0*(me.kz_y*me.kz_z + me.kz_w*me.kz_x))
yaw as Float = ArcCosine( (1.0 - 2.0*Power(me.kz_x,2) - 2.0*Power(me.kz_y,2))/Cosine(pitch) )
roll as Float = ArcCosine( (1.0 - 2.0*Power(me.kz_x,2) - 2.0*Power(me.kz_z,2))/Cosine(pitch) )

//Order:Ry.Rx.Rz.Matrices for left hand coordinate sytem.weird result
pitch as Float = ArcSine(-2.0*me.kz_y*me.kz_z + 2.0*me.kz_w*me.kz_x)
yaw as Float = ArcSine( 2.0*me.kz_x*me.kz_z + 2.0*me.kz_w*me.kz_y/Cosine(pitch) )
roll as Float = ArcSine( 2.0*me.kz_x*me.kz_y + 2.0*me.kz_w*me.kz_z/Cosine(pitch) )
altra4u
altra4u
Quote:
Original post by jyk
I didn't quite follow all that, but maybe it's a multiplication order problem - did you try 'newQuat = Q*newQuat' instead of 'newQuat = newQuat*Q'?

Coordinate system handedness should not matter for what you're doing. Beyond that I can't give much in the way of advice, as I'm not quite clear on what sort of motion you're trying to achieve. If the multiplication order suggestion doesn't help, maybe you could provide some more details (such as what the object represents, how the user interface works, and exactly what sort of motion you want the object to undergo).


Well in very simple terms assume I have a cube and when I use the up,down,left,right keys it should move around the global x and y axis irrespective of the orientation of its local axis.
Here is what I am doing with the quats.
quat = retrieve the saved value which tells of the current rotation.
keyQuat = create quat based on key input
quat = quat*keyQuat.
I believe this is the right way as I downloaded a gimbal lock code sample from gamedev itself which does it like this.

Thanks,
chit

Zakwayda
Zakwayda
Question: if it's your engine, why not just revise the interface so that it accepts orientations in quaternion or matrix form? Then you could bypass the Euler-angle conversion(s) entirely.
altra4u
altra4u
Quote:
Original post by jyk
Question: if it's your engine, why not just revise the interface so that it accepts orientations in quaternion or matrix form? Then you could bypass the Euler-angle conversion(s) entirely.


Its not my engine as in I made it.Its just an engine I have to use and it accepts only euler angles :(.
altra4u
altra4u
Some general questions that have been bugging me.
-if I am doing my rotations with respect to the global axis then I need to pre-multipy right.
So if I was doing y first then x then z then I need to do
Q = Qz.Qx.Qy

-I also get confused about quaternion multiplication.
So if I had a rotation q1 and if I wanted to apply another rotation q2 then should
the final quat be q2*q1 or should it be q1*q2?But then this would mean that my first question would have only one
valid solution irrespective of what axis(global or local)we choose.

-When I am converting the quat to euler
I need to compare the quaternion matrix form to the composite rotation matrix form.
So the question is should this composite rotation matrix be derived as
R = Rz.Rx.Ry or should it be R = Ry.Rx.Rz as we want the angles with respect to the local axis?
Thanks,
chit
clb
clb
Quote:
Original post by altra4u
Issue:Solve gimbal lock for a Left hand coordinate system with first y, then x and then z order of rotation.So I want the rotations to be always around the global axis.i.e not the local axis of the object.


What you're describing is not possible without changing the logic of how you do rotations. Gimbal lock is a problem about logic of combining several rotations, and not about the way you represent those rotations. So, your question is effectively "How can I use Euler's method for expressing and combining rotations that suffers from gimbal locking so that it doesn't exhibit gimbal locking?"

You ask to use the method v'=R*v, where R=Rz(a)*Rx(b)*Ry(c) and Rx, Ry and Rz rotate around the three world coordinate axes. Unfortunately you cannot escape gimbal locking if you just use three fixed world axes, no matter whether Rx, Ry and Rz are rotation matrices or quaternions. You can see this for example by setting b=pi/2 and computing R. It always happens (no matter whether you use Euler xyz, zyz, xyx, etc.) that in the resulting transformation matrix R, the scalars a and c adjust the rotation about the same axis, i.e. you have lost that one degree of freedom (see Euler Rotations Explained, or Wikipedia derivation of the R matrix during gimbal lock).

One solution that allows you to combine three successive rotations is to use the local object axes. I.e. do R=Ry(yaw)*Rp(pitch)*Rr(roll). This requires that you change the axis around which Ry and Rp rotate according to pitch and roll (i.e. the axis around which Ry rotates depends on pitch and roll, and the axis around which Rp rotates depends on roll). This maps nicely for example to keyboard/mouse input from player: mouse horizontal alters yaw, mouse vertical alters pitch and so on. Note that here the problem was solved by changing the logic of how you combine rotations, not by changing the representation of these rotations.

I know you mentioned you did not want to use local axes, but that's really the only way I know of that allows you to do this kind of simplistic R=R1*R2*R3 scheme.

Hope that was helpful,
altra4u
altra4u
Quote:
Original post by clb
Quote:
Original post by altra4u
Issue:Solve gimbal lock for a Left hand coordinate system with first y, then x and then z order of rotation.So I want the rotations to be always around the global axis.i.e not the local axis of the object.


What you're describing is not possible without changing the logic of how you do rotations. Gimbal lock is a problem about logic of combining several rotations, and not about the way you represent those rotations. So, your question is effectively "How can I use Euler's method for expressing and combining rotations that suffers from gimbal locking so that it doesn't exhibit gimbal locking?"

You ask to use the method v'=R*v, where R=Rz(a)*Rx(b)*Ry(c) and Rx, Ry and Rz rotate around the three world coordinate axes. Unfortunately you cannot escape gimbal locking if you just use three fixed world axes, no matter whether Rx, Ry and Rz are rotation matrices or quaternions. You can see this for example by setting b=pi/2 and computing R. It always happens (no matter whether you use Euler xyz, zyz, xyx, etc.) that in the resulting transformation matrix R, the scalars a and c adjust the rotation about the same axis, i.e. you have lost that one degree of freedom (see Euler Rotations Explained, or Wikipedia derivation of the R matrix during gimbal lock).

One solution that allows you to combine three successive rotations is to use the local object axes. I.e. do R=Ry(yaw)*Rp(pitch)*Rr(roll). This requires that you change the axis around which Ry and Rp rotate according to pitch and roll (i.e. the axis around which Ry rotates depends on pitch and roll, and the axis around which Rp rotates depends on roll). This maps nicely for example to keyboard/mouse input from player: mouse horizontal alters yaw, mouse vertical alters pitch and so on. Note that here the problem was solved by changing the logic of how you combine rotations, not by changing the representation of these rotations.

I know you mentioned you did not want to use local axes, but that's really the only way I know of that allows you to do this kind of simplistic R=R1*R2*R3 scheme.

Hope that was helpful,



I agree to that Gimbal lock is a problem about logic of combining several rotations, and not about the way you represent those rotations.
I believed that multiplication of two quaternions would solve the gimbal lock problem as they lie on sphere.At least that is what the explanation goes on this tutorial http://www.gamedev.net/reference/articles/article1095.asp

My whole math involves using quaternions so rotation matrices are not involved except at the very end where I try to extract euler angles from the quaternion to give to the Rotate function of my engine.
So the steps would be

-retrieve the quaternion representing the rotation
-create a new quaternion based on the key input
-multiply them(This should solve the gimbal lock cause of quat multiplication)
-fetch euler angles from this quat.

If we are talking about different things I would like to know more about this local axis approach of yours.I don't mind using local axis as long as it solves my problem.
clb
clb
Quote:
Original post by altra4u
I believed that multiplication of two quaternions would solve the gimbal lock problem as they lie on sphere.At least that is what the explanation goes on this tutorial http://www.gamedev.net/reference/articles/article1095.asp


Multiplication of two quaternions has exactly the same effect than multiplying two matrices that represent the equivalent rotations. The article is good in will, but that example is unfortunately rather poor. From the article:

Finally, we've reached what you all been waiting for: "How can quaternions avoid gimbal lock.?"The basic idea is1. Use a quaternion to represent the rotation. 2. Generate a temporary quaternion for the change from the current orientation to the new orientation. 3. PostMultiply the temp quaternion with the original quaternion. This results in a new orientation that combines both rotations. 4. Convert the quaternion to a matrix and use matrix multiplication as normal.


Well, the article is correct here. There's no gimbal lock in here. But the fact that there is none is nothing to do with quaternions, but that he got rid of the problematic logic of combining rotations. In fact, I can substitute the word 'matrix' wherever the word quaternion occurs and things are still true:

Finally, we've reached what you all been waiting for: "How can 3x3 rotation matrices avoid gimbal lock.?"The basic idea is1. Use a 3x3 rotation matrix to represent the rotation. 2. Generate a temporary 3x3 rotation matrix for the change from the current orientation to the new orientation. (This matrix will not in general rotate just about one fixed world axis,    but it has to be generated using a formula for arbitrary angle-axis rotation matrix).3. PostMultiply the temp matrix with the original matrix. This results in a new orientation that combines both rotations. 4. <strike>Convert the matrix to a matrix and</strike> use matrix multiplication as normal.


That's totally sound & sane, and it is so because of the logic of what we're doing. We're not performing rotations about fixed world axes like you would be when combining rotations using the Euler scheme. But you don't need quaternions to avoid that!

Google: Rotation arbitrary axis will show the way. I couldn't find a page quickly that would show the final formula, so I'll just paste the appropriate code from my library:

/** Sets the top-left 3x3 area of the matrix to the rotation matrix about an arbitrary axis. Elements outside the top-left 3x3 area are ignored. Apply the rotation matrix in the order v'=M*v.	@param m The matrix to store the result.	@param a The axis to rotate about. The axis must be normalized.	@param angle The rotation angle. */template<typename M, typename V, int Rows, int Cols, MatrixPackingOrder Pack>void SetRotationAxis3x3(Matrix<M, Rows, Cols, Pack> &m, const Vector<V, 3> &a, angle_t angle){//	assert(axis is normalized);	const typename M::elem_t c = Cos(angle);	const typename M::elem_t c1 = (typename M::elem_t(1)-c);	const typename M::elem_t s = Sin(angle);	m[0][0] = c+c1*a.x*a.x;	m[1][0] = c1*a.x*a.y+s*a.z;	m[2][0] = c1*a.x*a.z-s*a.y;	m[0][1] = c1*a.x*a.y-s*a.z;	m[1][1] = c+c1*a.y*a.y;	m[2][1] = c1*a.y*a.z+s*a.x;	m[0][2] = c1*a.x*a.z+s*a.y;	m[1][2] = c1*a.y*a.z-s*a.x;	m[2][2] = c+c1*a.z*a.z;}


To rotate about local axes you utilize this function that generates rotations about arbitrary axes.

The pseudo for yaw-pitch-roll goes something like this: (roll is applied first, then pitch, then yaw)
input: float yaw,pitch,roll;Rroll = RotationMatrix(axis(0,0,1), angle(roll));axis pitchAxis = Rroll * vec(1,0,0);Rpitch = RotationMatrix(pitchAxis, angle(pitch));axis yawAxis = Rpitch * Rroll * vec(0,1,0);Ryaw = RotationMatrix(yawAxis, angle(yaw));R = Ryaw*Rpitch*Rroll;

For comparison, the method of combining Euler XYZ angles would be something like
input: float x,y,z;Rx = RotationMatrix(axis(1,0,0), angle(x));Ry = RotationMatrix(axis(0,1,0), angle(y));Rz = RotationMatrix(axis(0,0,1), angle(z));R = Rx*Ry*Rz

When rotating about local axes, we just follow the directions our axes are pointing at after each rotation that we've applied, and the next rotations will be done about these rotated axes and not some global fixed axes. One of the reasons why people often favor the use of quaternions is that rotations about arbitrary axes are somewhat easier (and possibly faster) to generate than these matrices that rotate about an arbitrary axis. But then you have the conversion and other stuff going on, so it depends.

The usual disclaimer with correctness.. I just wrote that pseudo from the spot, so double-checking it is in order.
altra4u
altra4u
Quote:
Original post by clb
Quote:
Original post by altra4u
I believed that multiplication of two quaternions would solve the gimbal lock problem as they lie on sphere.At least that is what the explanation goes on this tutorial http://www.gamedev.net/reference/articles/article1095.asp


Multiplication of two quaternions has exactly the same effect than multiplying two matrices that represent the equivalent rotations. The article is good in will, but that example is unfortunately rather poor. From the article:

Finally, we've reached what you all been waiting for: "How can quaternions avoid gimbal lock.?"The basic idea is1. Use a quaternion to represent the rotation. 2. Generate a temporary quaternion for the change from the current orientation to the new orientation. 3. PostMultiply the temp quaternion with the original quaternion. This results in a new orientation that combines both rotations. 4. Convert the quaternion to a matrix and use matrix multiplication as normal.


Well, the article is correct here. There's no gimbal lock in here. But the fact that there is none is nothing to do with quaternions, but that he got rid of the problematic logic of combining rotations. In fact, I can substitute the word 'matrix' wherever the word quaternion occurs and things are still true:

Finally, we've reached what you all been waiting for: "How can 3x3 rotation matrices avoid gimbal lock.?"The basic idea is1. Use a 3x3 rotation matrix to represent the rotation. 2. Generate a temporary 3x3 rotation matrix for the change from the current orientation to the new orientation. (This matrix will not in general rotate just about one fixed world axis,    but it has to be generated using a formula for arbitrary angle-axis rotation matrix).3. PostMultiply the temp matrix with the original matrix. This results in a new orientation that combines both rotations. 4. <strike>Convert the matrix to a matrix and</strike> use matrix multiplication as normal.


That's totally sound & sane, and it is so because of the logic of what we're doing. We're not performing rotations about fixed world axes like you would be when combining rotations using the Euler scheme. But you don't need quaternions to avoid that!

Google: Rotation arbitrary axis will show the way. I couldn't find a page quickly that would show the final formula, so I'll just paste the appropriate code from my library:

*** Source Snippet Removed ***

To rotate about local axes you utilize this function that generates rotations about arbitrary axes.

The pseudo for yaw-pitch-roll goes something like this: (roll is applied first, then pitch, then yaw)
input: float yaw,pitch,roll;Rroll = RotationMatrix(axis(0,0,1), angle(roll));axis pitchAxis = Rroll * vec(1,0,0);Rpitch = RotationMatrix(pitchAxis, angle(pitch));axis yawAxis = Rpitch * Rroll * vec(0,1,0);Ryaw = RotationMatrix(yawAxis, angle(yaw));R = Ryaw*Rpitch*Rroll;

For comparison, the method of combining Euler XYZ angles would be something like
input: float x,y,z;Rx = RotationMatrix(axis(1,0,0), angle(x));Ry = RotationMatrix(axis(0,1,0), angle(y));Rz = RotationMatrix(axis(0,0,1), angle(z));R = Rx*Ry*Rz

When rotating about local axes, we just follow the directions our axes are pointing at after each rotation that we've applied, and the next rotations will be done about these rotated axes and not some global fixed axes. One of the reasons why people often favor the use of quaternions is that rotations about arbitrary axes are somewhat easier (and possibly faster) to generate than these matrices that rotate about an arbitrary axis. But then you have the conversion and other stuff going on, so it depends.

The usual disclaimer with correctness.. I just wrote that pseudo from the spot, so double-checking it is in order.


Wow thanks for the reply.Also gotten a bit confused so trying to clear up my head first a bit.I'll try and understand and get back on this.Thanks again.Wish I had so much of clear understanding.Its just so hard to visualize especially if you don't know and are considering all possible options.
altra4u
altra4u
Quote:
Original post by clb
Quote:
Original post by altra4u
I believed that multiplication of two quaternions would solve the gimbal lock problem as they lie on sphere.At least that is what the explanation goes on this tutorial http://www.gamedev.net/reference/articles/article1095.asp


Multiplication of two quaternions has exactly the same effect than multiplying two matrices that represent the equivalent rotations. The article is good in will, but that example is unfortunately rather poor. From the article:

Finally, we've reached what you all been waiting for: "How can quaternions avoid gimbal lock.?"The basic idea is1. Use a quaternion to represent the rotation. 2. Generate a temporary quaternion for the change from the current orientation to the new orientation. 3. PostMultiply the temp quaternion with the original quaternion. This results in a new orientation that combines both rotations. 4. Convert the quaternion to a matrix and use matrix multiplication as normal.


Well, the article is correct here. There's no gimbal lock in here. But the fact that there is none is nothing to do with quaternions, but that he got rid of the problematic logic of combining rotations. In fact, I can substitute the word 'matrix' wherever the word quaternion occurs and things are still true:

Finally, we've reached what you all been waiting for: "How can 3x3 rotation matrices avoid gimbal lock.?"The basic idea is1. Use a 3x3 rotation matrix to represent the rotation. 2. Generate a temporary 3x3 rotation matrix for the change from the current orientation to the new orientation. (This matrix will not in general rotate just about one fixed world axis,    but it has to be generated using a formula for arbitrary angle-axis rotation matrix).3. PostMultiply the temp matrix with the original matrix. This results in a new orientation that combines both rotations. 4. <strike>Convert the matrix to a matrix and</strike> use matrix multiplication as normal.


That's totally sound & sane, and it is so because of the logic of what we're doing. We're not performing rotations about fixed world axes like you would be when combining rotations using the Euler scheme. But you don't need quaternions to avoid that!

Google: Rotation arbitrary axis will show the way. I couldn't find a page quickly that would show the final formula, so I'll just paste the appropriate code from my library:

*** Source Snippet Removed ***

To rotate about local axes you utilize this function that generates rotations about arbitrary axes.

The pseudo for yaw-pitch-roll goes something like this: (roll is applied first, then pitch, then yaw)
input: float yaw,pitch,roll;Rroll = RotationMatrix(axis(0,0,1), angle(roll));axis pitchAxis = Rroll * vec(1,0,0);Rpitch = RotationMatrix(pitchAxis, angle(pitch));axis yawAxis = Rpitch * Rroll * vec(0,1,0);Ryaw = RotationMatrix(yawAxis, angle(yaw));R = Ryaw*Rpitch*Rroll;

For comparison, the method of combining Euler XYZ angles would be something like
input: float x,y,z;Rx = RotationMatrix(axis(1,0,0), angle(x));Ry = RotationMatrix(axis(0,1,0), angle(y));Rz = RotationMatrix(axis(0,0,1), angle(z));R = Rx*Ry*Rz

When rotating about local axes, we just follow the directions our axes are pointing at after each rotation that we've applied, and the next rotations will be done about these rotated axes and not some global fixed axes. One of the reasons why people often favor the use of quaternions is that rotations about arbitrary axes are somewhat easier (and possibly faster) to generate than these matrices that rotate about an arbitrary axis. But then you have the conversion and other stuff going on, so it depends.

The usual disclaimer with correctness.. I just wrote that pseudo from the spot, so double-checking it is in order.


Ok so this is what I understood from the explanation.Tell me if I'm wrong.
Scenario that the axis angle rotation matrix solves.
I have a left handed coordinate system and I rotate it around the z axis by 90 degrees.Now the y axis is where the x axis was and the x axis is where -y axis was.Now when I rotate around the y axis I want a rotation around (1,0,0) and not (0,1,0).This is what is happening for me anyways cause I have only 1 available function Rotate(x,y,z) to rotate an object and this rotates it around the object's local axes.

Scenario that I am trying to solve.
I have a left handed coordinate system and I rotate it around the z axis by 90 degrees.Now when I rotate around the y axis I want a rotation around (0,1,0)and not (1,0,0).I don't see how the arbitrary rotation matrix thing would help me here.

Going by the description though I feel like if I did
1. Use a quaternion/matrix to represent the rotation.
2. Generate a temporary quaternion for the change from the current orientation to the new orientation.
3. PostMultiply the temp quaternion/matrix with the original quaternion. This results in a new orientation that combines both rotations.
4. Derive the euler angles from this matrix and give it to my function RotateObject()

it would work.
I have already tried this approach but I am premutltiplying instead of postmultiplying.May be that is wrong.I'm sorry about sounding very methodical.I am trying to understand the logic but I gotta finish this stuff in time too.

Thanks,
chit

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.