3drotationquaternions

Efficient quaternion angular velocity


I have an orientation expressed with a quaternion and an angular velocity expressed as either a quaternion or a number (radians per second around the original orientation). I understand how to do this using conversion to axis-angle but that method is rather computationally expensive and is not a realistic option. How would I go about modifying the orientation quaternion given a time interval (in seconds)? I need a solution for both cases (the quaternion and the number). However, converting one case into the other is acceptable and may be preferable depending on the computational complexity of the various algorithms/formulae required for conversions.


Solution

  • To update orientation, you typically multiply the current orientation by a delta rotation. This is a computationally comparable operation to axis–angle conversion.

    A common way to represent angular velocity is via the exponential map — a 3D vector aligned with the rotation axis, with magnitude equal to the rotation rate in radians per second. The conversion to a delta rotation quaternion looks like this:

    Quaternion deltaRotation(const Vector3& angularVelocity, double deltaTime)
    {
       Vector3 ha = angularVelocity * (deltaTime * 0.5); // vector of half angle
       double l = ha.norm(); // magnitude
       if (l > 0) {
          ha *= sin(l) / l; //sine cardinal
       }
       return Quaternion(cos(l), ha.x(), ha.y(), ha.z());
    }
    

    If deltaTime and the angular velocity are small, you can use a first-order Taylor approximation. However, you should normalize the resulting quaternion to avoid numerical drift over time:

    Quaternion deltaRotationAppx1(const Vector3& angularVelocity, double deltaTime)
    {
       Vector3 ha = angularVelocity* (deltaTime * 0.5); // vector of half angle
       return Quaternion(1.0, ha.x(), ha.y(), ha.z());
    }
    

    Updating the orientation usually looks like:

    orientation = orientation * deltaRotation;
    orientation.normalize();
    

    If the linear approximation is used (i.e., w=1), then the quaternion multiplication simplifies, and you can write the update step in a more familiar integration form. Note:

    orientation = orientation * deltaRotation 
    orientation = orientation * [1, ha]
    orientation = orientation + orientation * ha 
    orientation += orientation * ha 
    orientation += orientation * angularVelocity * deltaTime * 0.5