Quaternion as angular velocity

Started by
4 comments, last by Dirk Gregorius 7 years, 10 months ago

Hello,

first, i don't really understand quaternions, so please keep it simple. I have 2 transformation matrices and i need one of them to move and rotate so, that over time, it becomes the exact same matrix as the second one. Moving this matrix is simple.

Let's say M1 has position P1 and M2 has position P2.

I want that over time t, P1 moves to P2.

I create vector v = P2 - P1

v = normalized(v) * (length(v) / t)

Now every frame i just have to

P1 += v*ElapsedTime

t -= ElapsedTime

while t>0

How do i do this with a quaternion? Until now i used axis based angles and rotation matrices to rotate an object.

This was simple M = RotationMatrix( alpha * ElapsedTime )*M

I'm using DirectXMath, to get quaternion from matrix there is XMQuaternionRotationMatrix method, to create rotation matrix from quaternion there is XMMatrixRotationQuaternion.

I need quaternion that i can use as M = XMMatrixRotationQuaternion( quaternion* ElapsedTime )*M

But i don't think i can simply multiply a quaternion with a scalar.

Questions are: How do i get a quaternion that can be used to rotate one matrix to another and how can i use this quaternion to rotate that matrix over time t?

Advertisement

It looks to me like you have asked two completely different questions here.

1. "How do I get a quaternion to move from one position to another?"

2. "How do I get a quaternion to move from one angle to another?"

The answer to question one is you don't/can't.

For game programming purposes, quaternions are pretty straight forward. Think of them as an arrow that lives in another plane of existence. You can't really see it or deal with it directly, but you can ask it where it points and tell it how to rotate. As long as you trust that it will be where it's supposed to be once it completes that rotation, you can use it to store orientation information.

The primary difference between a matrix and a quaternion is that a 4by4 matrix can not only contain an orientation, but a position and scale/skew info as well. A quaternion can't. It can only hold an orientation. So, anything regarding position is a non-starter. When working with quaternions, you have to store a position separately. With that as a given, the answer to your question is probably self evident, because the positions are likely stored as vectors and you can do the same thing you did with a matrix.

Quaternions are not something you really "understand". They are multi-dimensional imaginary numbers. Just the concept of imaginary numbers is enough to blow most people's minds, then kick it up several notches to many dimensions of imaginary numbers and you're just asking for your brain to explode and that's a mess no one wants to clean up.

In game programming we use a small sub-set of the quaternion possible values by forcing it to be a unit quaternion. By forcing it to be a unit quaternion, we take 4 dimensions that we can't really understand and force the value to live on the surface of a sphere. Because it now lives on the surface of a sphere, you can think of it as an arrow that points to that place on the sphere. (And maybe a second tiny arrow that lives on and describes the facing on the surface of the sphere. Really, you might just think of it as this second tiny arrow living on the surface of the sphere. But the main big arrow describes the orientation on two axes, while the tiny arrow describes the rotation around the third axis.)

It's that 4th dimension that makes a quaternion different from a vertex normal. A normal doesn't have any way to know the rotation around it's own axis. So, you need 3 of them - like you have in a matrix - to describe an orientation. This can be done with a 3by3 matrix, but by adding another column and row, you get the added benefit of storing a position with it. So, a 3by3 matrix of mutually perpendicular vectors can basically do the same work as a quaternion. Quaternions and matrices are in many ways similar in the way we use them.

Quaternions on the other hand do know the amount of rotation around themselves. Because of that 4th dimension - in order to stay on the sphere (remain a unit quaternion), all 4 numbers must be adjusted to maintain that length of 1 when you rotate it on it's own axis. This makes every orientation of a quaternion unique, much like 3 mutually perpendicular vectors in a 3by3 matrix.

Don't try looking at the 4 values stored in the quaternion. They live in a 4 dimensional reality that is not going to make sense. Using quaternions is like using a calculator. You don't know how it works. You don't care how it works. All you care about is that when you give it the correct input, it gives you the correct output.

Just think of the quaternion as storing an orientation, which can be thought of as an arrow, or even like a 3by3 matrix of 3 mutually perpendicular vectors/arrows.

So, now on to question two.

If you have one quaternion that describes one orientation and another that describes a second orientation, you can subtract one quaternion from the other to give you the rotation required to change one into the other. I assume you can use SLERP to interpolate one quaternion to another.

For DirectX, here's MSDN's page on Quaternion SLERP. Don't let LERP and SLERP scare you if you are not familiar with them. All they are is a weighted average. It's exactly what you're looking for here. You feed it two quaternions and a percentage value of how far it is transformed from one to the other. (1.0 is 100%, so 0.4 is 40%, etc.) Interpolation is just a weighted average. But that assumes a straight line between two points. Here we're describing a curve between two points on a circle/sphere and so the interpolation needs to be curved instead of straight which is why you use SLERP instead of LERP.

Your quaternion can then be converted to a rotation matrix, to get the value put back into a matrix afterwards. So, you can pull a quaternion out of a matrix, then convert the quaternion back into a matrix, so that you can then combine it with the original matrix. This allows you to use quaternions to rotate matrices.

First, let me address the title of the thread. Quaternions represent attitudes and rotations, not angular velocities: Angular velocities are regular vectors.

So you have an attitude represented by quaternion q1 and you want to change it to q2 over time. Because the notation used in quaternions is multiplicative, you need to perform the rotation inverse(q1)*q2 over some time T. inverse(q1) is the same as conj(q1) because |q1|=1. If the first frame will move time forward by t, the fraction of the rotation you need to perform is t/T. This means you need to multiply your original q1 by (conj(q1)*q2)^(t/T). The relevant formulas are here.


EDIT: Here's some code for your enjoyment.

#include <iostream>
#include <boost/math/quaternion.hpp>

typedef boost::math::quaternion<float> Quaternion;

// This function assumes abs(q) = 1
Quaternion log(Quaternion q) {
  float a = q.real();
  Quaternion v = q.unreal();
  
  // In case some rounding error buildup results in a real part that is too large
  if (a > 1.0f) a = 1.0f;
  if (a < -1.0f) a = -1.0f;
  
  return v * (std::acos(a) / abs(v));
}

int main() {
  Quaternion q1(0.0f, 1.0f, 0.0f, 0.0f);
  Quaternion q2(0.0f, 0.0f, 1.0f, 0.0f);
  
  for (float t = 0; t <= 1.0; t += 0.125)
    std::cout << q1 * exp(log(conj(q1) * q2) * t) << '\n';
}

Thanks for the replies, at first i thought i shouldn't use slerp as i couldn't simply transfer from one orientation to another, and really need angular velocity. Then i realized i can use slerp to create angular velocity if i simply lerp between identity quaternion and Q1->Q2 quaternion.

What i did:

get quaternion that points from matrix 1 to matrix 2: Q1 -> Q2

to get this i did q = Q1^(-1) * Q2

now q is orientation that is requires to rotate Matrix 1 to Matrix 2

to get a small part of this orientation every frame update

quatnow = XMQuaternionSlerp(XMQuaternionIdentity(), m_vAdjustQuat, fElapsedTime / CLIENT_POSITION_ADJUST_TIME);

m_mWorldMat = XMMatrixRotationQuaternion(quatnow)*m_mWorldMat;

This looks kinda promising but there are still some visible lags.

It may help if you treat quaternions as an 'optimized axis and angle' representation.

E.g. if we have axis and angle rotation of

vec3 axis (1,0,0), float angle 0.7

... converting this to quaternion gives

quat (axis * sin(angle / 2), cos(angle / 2)),

where the vector axis part goes to xyz and scalar angle part goes to w components.

... converting the same rotation to the vector representation used for angular velocity we get

axis * angle

so simply (0.7,0,0)

Notice that angular velocity can have any magnitude, but quaternion can't store a angle larger than 4 pi.

Here is a code snippet using this to integrate angular velocity to a body orientation stored as quaternion:

void IntegrateVelocity (const sQuat &curOrn, const sVec3 &angvel, float timeStep, sQuat &predictedOrn)
{
    sVec3 axis;
    float angle = angvel.Length();
    
    if ( angle < 0.00001f )
    {
        predictedOrn = curOrn;
        return;
    }
    else
    {
        axis = angvel * (sin (angle * timeStep * 0.5f) / angle);
    }
    sQuat rot (axis.x, axis.y, axis.z, cos (angle * timeStep * 0.5f) );
    predictedOrn = rot * curOrn;
    predictedOrn.Normalize()
}

To get the rotation from Q1 -> Q2,

you may want to care also to use the shortest arc by taking the dot product and doing the inversion accordingly:

inline sQuat QuatFromAToB (const sQuat &qA, const sQuat &qB) // in global space
{
    sQuat q;
    if (qA.Dot(qB) < 0.0f)
    {
        q[0] = qA[0]; q[1] = qA[1]; q[2] = qA[2];
        q[3] = -qA[3];
    }
    else
    {
        q[0] = -qA[0]; q[1] = -qA[1]; q[2] = -qA[2];
        q[3] = qA[3];
    }
            
    return qB * q;
}

And instead of a slerp you could simply scale the angle of this rotation:

quat q = QuatFromAToB (Q1, Q2);

vec3 axis, float angle;

q.ToAxisAndAngle (axis, angle);

angle *= 0.3; // assuming you want to rotate 30%

q.FromAxisAndAngle (axis, angle);

quat result = q * Q1;

Be warned that DirectX may have different multiplication order convention, e.g. my 'q * Q1' may be 'Q1 * q' for you.

You can do this all without trig functions. The quaternion derivative is defined as:

dq/dt = 0.5 * w * q

You can approximate this using the differential quotient:

(q2 - q1) / h = 0.5 * w * q1

Solving for w which gets you from q1 to q2 in h time yields:

w = 2 * (q2 - q1) * conj( q1 ) / h

Here w is pure quaternion containing the angular velocity omega. E.g. w = ( omega.x, omega.y, omega.z, 0 )

This method works great in practice e.g. to initialize ragdoll velocity given two keyframes when switching from animation to physics.

This topic is closed to new replies.

Advertisement