Jump to content

  • Log In with Google      Sign In   
  • Create Account


Limit quaternion rotation speed


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
2 replies to this topic

#1 BleuBleu   Members   -  Reputation: 100

Like
0Likes
Like

Posted 27 February 2011 - 10:25 PM

Hi!

I'm looking for a simple way of clamping the rotation speed of bones in an animation. I want to prevent some bones to rotate at more than a given rate (smooth out bones that moves too fast).

When working with floats or vectors, I like to use something like this and i'm sort-of looking for the quaternion equivalent:

float approach(float v1, float v2, float max)
{
	if (v1 > v2)
    	return (v1 - v2 > max) ? v1 - max : v2;
	else
    	return (v2 - v1 > max) ? v1 + max : v2;
}

So, suppose I want to go from a rotation q1 to a rotation q2 and I don't want to rotate by more than x radians per frame.

I tried this, but for some reason, it seem to introduce some unexpected rotations from time to time, any idea why?

1) Find delta rotation that will transform q1 into q2: delta = q2 * q1.Inverse()
2) Convert delta rotation to axis + angle
3) Clamp the angle to x
4) Reconstruct new (clamped) delta rotation
5) My final, clamped rotaiton should now be: delta * v1

Ideas? Suggestions? Thanks!

-Mat

Sponsor:

#2 STufaro   Members   -  Reputation: 183

Like
0Likes
Like

Posted 28 February 2011 - 07:30 PM

So, suppose I want to go from a rotation q1 to a rotation q2 and I don't want to rotate by more than x radians per frame.

I tried this, but for some reason, it seem to introduce some unexpected rotations from time to time, any idea why?

1) Find delta rotation that will transform q1 into q2: delta = q2 * q1.Inverse()
2) Convert delta rotation to axis + angle
3) Clamp the angle to x
4) Reconstruct new (clamped) delta rotation
5) My final, clamped rotaiton should now be: delta * v1

Ideas? Suggestions? Thanks!

-Mat


Mat,

I'm not sure where the unexpected rotations come from--are you using Euler angles at any point? They might be because of a phenomenon known as gimbal lock--see this wiki article: http://en.wikipedia....ki/Gimbal_lock. This happens when you go to Euler angles from quaternions (quaternions avoid the problem), because you have a series of rotations, and depending on which order you apply them (e.g., X-Y-Z, Z-X-Y), it may change things. That shouldn't affect axis-angle, though, but my hunch is the way to get around it is just keep everything as quaternions.

I know that you can simply add two quaternions and divide them to "average" them out, resulting in a smoother transition. I had an application where there was a tank rolling over a heightmap--I noticed the tank "snapped" to the normals of each triangle it rolled over, so I averaged the normals between frames and smoothed the transitions a lot.

Quaternion approach (Quaternion initialRotation, Quaternion finalRotation)
{
	return (initialRotation + finalRotation)/2;
}

If you keep feeding that back into itself after each frame, you'll get a fast movement at first and a slow movement toward the end, which looks nice.

Using the same principle, you can get a quaternion somewhere between your initial and final:
Quaternion approach (Quaternion initialRotation, Quaternion finalRotation, float position)
{
	// position between 0 and 1
	return (1 - position) * initialRotation + position * finalRotation;
}

Correct me if I'm wrong, or let me know if that helps.

Best of luck,
-- Steve.

#3 BleuBleu   Members   -  Reputation: 100

Like
0Likes
Like

Posted 28 February 2011 - 07:52 PM

Quaternion approach (Quaternion initialRotation, Quaternion finalRotation)
{
	return (initialRotation + finalRotation)/2;
}


Oh that's great. Didnt think about that!
You tank example is very close to what i'm trying to do, except im trying to smooth out the feet of my characters on the ground when doing foot IK. Very similar.

My only worry about this technique is that im affraid its going to be a bit hard to predict how the smoothing will look using different timesteps.But it might be ok.

After posting, I started to look at the quaternion slerp math. Turns out it computes the angle between the quaternions and figures out the coefficients to use to lerp the quaternions. I wrote another version of slerp() that instead of taking a time (t) parameter, takes a maximum interpolation angle. This maximum angle is then used to clamp the angle between the quaternions and is used to compute t, if needed.

For reference, this is the barebones version of the slerp in Ogre (i removed a bunch of stuff for simplicity):

Quaternion Quaternion::Slerp (Real fT, const Quaternion& rkP, const Quaternion& rkQ)
{
	Real fCos = rkP.Dot(rkQ);
	Real fSin = Math::Sqrt(1 - Math::Sqr(fCos));
	Radian fAngle = Math::ATan2(fSin, fCos);
	Real fInvSin = 1.0f / fSin;
	Real fCoeff0 = Math::Sin((1.0f - fT) * fAngle) * fInvSin;
	Real fCoeff1 = Math::Sin(fT * fAngle) * fInvSin;
	return fCoeff0 * rkP + fCoeff1 * rkQ;
}

And, a simplified versin of what i did:

Quaternion Quaternion::SlerpLimit (const Quaternion& rkP, const Quaternion& rkQ, Radian maxAngle)
{
	Real fCos = rkP.Dot(rkQ);
	Real fSin = Math::Sqrt(1 - Math::Sqr(fCos));
	Radian fAngle = Math::ATan2(fSin, fCos);

	// Clamp angle if needed, and figure out time parameter.
	float fT = 1.0f;
	if (maxAngle.valueRadians() > 0) 
	{
    	fT = std::min(1.0f, maxAngle.valueRadians() / fAngle.valueRadians());
    	fAngle = std::min(maxAngle, fAngle);
	}

	Real fInvSin = 1.0f / fSin;
	Real fCoeff0 = Math::Sin((1.0f - fT) * fAngle) * fInvSin;
	Real fCoeff1 = Math::Sin(fT * fAngle) * fInvSin;
	return fCoeff0 * rkP + fCoeff1 * rkQ;
}

Seem to work fine so far... (fingers crossed)

-Mat




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS