Appendix C. Quaternion Operations

This appendix implements a class called Quaternion that encapsulates all of the operations you need to handle quaternions when writing 3D rigid-body simulations.

Quaternion Class

The Quaternion class is defined with a scalar component, n, and vector component, v, where v is the vector, × i + y j + z k. The class has two constructors, one of which initializes the quaternion to 0, and the other of which initializes the elements to those passed to the constructor:

class Quaternion {
public:
     float      n;     // number (scalar) part
     Vector     v;     // vector part: v.x, v.y, v.z

     Quaternion(void);
     Quaternion(float e0, float e1, float e2, float e3);

     float      Magnitude(void);
     Vector     GetVector(void);
     float      GetScalar(void);
     Quaternion operator+=(Quaternion q);
     Quaternion operator-=(Quaternion q);
     Quaternion operator*=(float s);
     Quaternion operator/=(float s);
     Quaternion operator~(void) const { return Quaternion( n,
                                                           -v.x,
                                                           -v.y,
                                                           -v.z);}
};

// Constructor
inline     Quaternion::Quaternion(void)
{
     n   = 0;
     v.x = 0;
     v.y = 0;
     v.z = 0;
}

// Constructor
inline     Quaternion::Quaternion(float e0, float e1, float e2, float e3)
{
     n   = e0;
     v.x = e1;
     v.y = e2;
     v.z = e3;
}

Magnitude

The method Magnitude returns the magnitude of the quaternion according to the following formula:

|q| =

This is similar to calculating the magnitude of a vector except that for quaternions you have to take the fourth term, the scalar n, into account.

Here’s the code that calculates the magnitude for our Quaternion class:

inline     float     Quaternion::Magnitude(void)
{
     return (float) sqrt(n*n + v.x*v.x + v.y*v.y + v.z*v.z);
}

GetVector

The method GetVector returns the vector part of the quaternion. This method uses the Vector class defined in Appendix A:

inline     Vector     Quaternion::GetVector(void)
{
     return Vector(v.x, v.y, v.z);
}

GetScalar

The method GetScalar returns the scalar part of the quaternion:

inline     float     Quaternion::GetScalar(void)
{
     return n;
}

Quaternion Addition: The += Operator

This operator performs quaternion addition by simply adding the quaternion, q, to the current quaternion on a component-by-component basis.

If q and p are two quaternions, then:

q + p = [nq + np, (xq + xp) i + (yq + yp) j + (zq + zp) k]

Here, nq + np is the scalar part of the resulting quaternion, while (xq + xp) i + (yq + yp) j + (zq + zp) k is the vector part.

Quaternion addition is both associative and commutative; thus:

q + (p + h) = (q + p) + h
q + p = p + q

Here’s the code that adds the quaternion q to our Quaternion class:

inline     Quaternion     Quaternion::operator+=(Quaternion q)
{
     n += q.n;
     v.x += q.v.x;
     v.y += q.v.y;
     v.z += q.v.z;
     return *this;
}

Quaternion Subtraction: The −= Operator

This operator performs quaternion subtraction by simply subtracting the quaternion, q, from the current quaternion on a component-by-component basis.

If q and p are two quaternions, then:

qp = q + (−p) = [nq − np, (xq − xp) i + (yq − yp) j + (zq − zp) k]

Here, nqnp is the scalar part of the resulting quaternion, while (xqxp) i + (yqyp) j + (zqzp) k is the vector part.

Here’s the code that subtracts the quaternion q from our Quaternion class:

inline     Quaternion     Quaternion::operator-=(Quaternion q)
{
     n -= q.n;
     v.x -= q.v.x;
     v.y -= q.v.y;
     v.z -= q.v.z;
     return *this;
}

Scalar Multiplication: The *= Operator

This operator simply multiplies each component in the quaternion by the scalar s. This operation is similar to scaling a vector, as described in Appendix A:

inline     Quaternion Quaternion::operator*=(float s)
{
     n *= s;
     v.x *= s;
     v.y *= s;
     v.z *= s;
     return *this;
}

Scalar Division: The /= Operator

This operator simply divides each component in the quaternion by the scalar s:

inline     Quaternion Quaternion::operator/=(float s)
{
     n /= s;
     v.x /= s;
     v.y /= s;
     v.z /= s;
     return *this;
}

Conjugate: The ~ Operator

This operator takes the conjugate of the quaternion, ~q, which is simply the negative of the vector part. If q = [n, x i + y j + z k], then ~q = [n, (−x) i + (−y) j + (−z) k].

The conjugate of the product of quaternions is equal to the product of the quaternion conjugates, but in reverse order:

~(qp) = (~p)(~q)

Here’s the code that computes the conjugate for our Quaternion class:

     Quaternion operator~(void) const { return Quaternion( n,
                                                           -v.x,
                                                           -v.y,
                                                           -v.z);}

Quaternion Functions and Operators

The functions and overloaded operators that follow are useful when you are performing operations with two quaternions, or with a quaternion and a scalar, or a quaternion and a vector. Here, the quaternions are assumed to be of the type Quaternion, and vectors of the type Vector, as discussed in Appendix A.

Quaternion Addition: The + Operator

This operator performs quaternion addition by simply adding the quaternion q1 to quaternion q2 on a component-by-component basis:

inline     Quaternion operator+(Quaternion q1, Quaternion q2)
{
     return     Quaternion(     q1.n + q2.n,
                                   q1.v.x + q2.v.x,
                                   q1.v.y + q2.v.y,
                                   q1.v.z + q2.v.z);
}

Quaternion Subtraction: The − Operator

This operator performs quaternion subtraction by simply subtracting the quaternion q2 from quaternion q1 on a component-by-component basis:

inline     Quaternion operator-(Quaternion q1, Quaternion q2)
{
     return     Quaternion(     q1.n - q2.n,
                                   q1.v.x - q2.v.x,
                                   q1.v.y - q2.v.y,
                                   q1.v.z - q2.v.z);
}

Quaternion Multiplication: The * Operator

This operator performs quaternion multiplication according to the following formula:

q p = nq npvqvp + nq vp + np vq + (vq × vp)

Here, nqnpvqvp is the scalar part of the result, while nq vp + np vq + (vq × vp) is the vector part. Also note that vq and vp are the vector parts of q and p, respectively, • is the vector dot-product operator, and × is the vector cross-product operator.

Quaternion multiplication is associative but not commutative; thus:

q(ph) = (qp)h
qppq

Here’s the code that multiplies two Quaternions, q1 and q2:

inline     Quaternion operator*(Quaternion q1, Quaternion q2)
{
     return     Quaternion(q1.n*q2.n - q1.v.x*q2.v.x
                               - q1.v.y*q2.v.y - q1.v.z*q2.v.z,
                           q1.n*q2.v.x + q1.v.x*q2.n
                               + q1.v.y*q2.v.z - q1.v.z*q2.v.y,
                           q1.n*q2.v.y + q1.v.y*q2.n
                               + q1.v.z*q2.v.x - q1.v.x*q2.v.z,
                           q1.n*q2.v.z + q1.v.z*q2.n
                               + q1.v.x*q2.v.y - q1.v.y*q2.v.x);
}

Scalar Multiplication: The * Operator

This operator simply multiplies each component in the quaternion by the scalar s. There are two forms of this operator, depending on the order in which the quaternion and scalar are encountered:

inline     Quaternion operator*(Quaternion q, float s)
{
     return     Quaternion(q.n*s, q.v.x*s, q.v.y*s, q.v.z*s);
}

inline     Quaternion operator*(float s, Quaternion q)
{
     return     Quaternion(q.n*s, q.v.x*s, q.v.y*s, q.v.z*s);
}

Vector Multiplication: The * Operator

This operator multiplies the quaternion q by the vector v as though the vector v were a quaternion with its scalar component equal to 0. There are two forms of this operator, depending on the order in which the quaternion and vector are encountered. Since v is assumed to be a quaternion with its scalar part equal to 0, the rules of multiplication follow those outlined earlier for quaternion multiplication:

inline     Quaternion operator*(Quaternion q, Vector v)
{
     return     Quaternion(     -(q.v.x*v.x + q.v.y*v.y + q.v.z*v.z),
                                   q.n*v.x + q.v.y*v.z - q.v.z*v.y,
                                   q.n*v.y + q.v.z*v.x - q.v.x*v.z,
                                   q.n*v.z + q.v.x*v.y - q.v.y*v.x);
}
inline     Quaternion operator*(Vector v, Quaternion q)
{
     return     Quaternion(     -(q.v.x*v.x + q.v.y*v.y + q.v.z*v.z),
                                   q.n*v.x + q.v.z*v.y - q.v.y*v.z,
                                   q.n*v.y + q.v.x*v.z - q.v.z*v.x,
                                   q.n*v.z + q.v.y*v.x - q.v.x*v.y);
}

Scalar Division: The / Operator

This operator simply divides each component in the quaternion by the scalar s:

inline     Quaternion operator/(Quaternion q, float s)
{
     return     Quaternion(q.n/s, q.v.x/s, q.v.y/s, q.v.z/s);
}

QGetAngle

This function[30] extracts the angle of rotation about the axis represented by the vector part of the quaternion:

inline     float QGetAngle(Quaternion q)
{
     return     (float) (2*acos(q.n));
}

QGetAxis

This function returns a unit vector along the axis of rotation represented by the vector part of the quaternion q:

inline     Vector QGetAxis(Quaternion q)
{
     Vector v;
     float m;

     v = q.GetVector();
     m = v.Magnitude();

     if (m <= tol)
          return Vector();
     else
          return v/m;
}

QRotate

This function rotates the quaternion p by q according to the formula:

p’ = (q)(p)(~q)

Here, ~q is the conjugate of the unit quaternion q. Here’s the code:

inline     Quaternion QRotate(Quaternion q1, Quaternion q2)
{
     return     q1*q2*(~q1);
}

QVRotate

This function rotates the vector v by the unit quaternion q according to the formula:

p’ = (q)(v)(~q)

Here, ~q is the conjugate of the unit quaternion q. Here’s the code:

inline     Vector     QVRotate(Quaternion q, Vector v)
{
     Quaternion t;


     t = q*v*(~q);

     return     t.GetVector();
}

MakeQFromEulerAngles

This function constructs a quaternion from a set of Euler angles.

For a given set of Euler angles, yaw (ψ), pitch (τ), and roll (φ), defining rotation about the z-axis, then the y-axis, and then the z-axis, you can construct the representative rotation quaternion. You do this by first constructing a quaternion for each Euler angle and then multiplying the three quaternions following the rules of quaternion multiplication. Here are the three quaternions representing each Euler rotation angle:

qroll = [cos(φ/2), (sin(φ/2)) i + 0 j + 0 k]
qpitch = [cos(τ /2), 0 i + (sin(τ /2)) j + 0 k]
qyaw = [cos(ψ /2), 0 i + 0 j + (sin(ψ /2)) k]

Each one of these quaternions is of unit length.[31]

Now you can multiply these quaternions to obtain a single one that represents the rotation, or orientation, defined by the three Euler angles:

q = qyaw qpitch qroll

Performing this multiplication yields:

q = [ {cos(φ/2) cos(τ /2) cos(ψ /2) + sin(φ/2) sin(τ /2) sin(ψ /2)},
{ sin(φ/2) cos(τ /2) cos(ψ /2) - cos(φ/2) sin(τ /2) sin(ψ /2) } i +
{ cos(φ/2) sin(τ /2) cos(ψ /2) + sin(φ/2) cos(τ /2) sin(ψ /2) } j +
{ cos(φ/2) cos(τ /2) sin(ψ /2) - sin(φ/2) sin(τ /2) cos(ψ /2) } k ]

Here’s the code that takes three Euler angles and returns a quaternion:

inline     Quaternion     MakeQFromEulerAngles(float x, float y, float z)
{
     Quaternion     q;
     double     roll = DegreesToRadians(x);
     double     pitch = DegreesToRadians(y);
     double     yaw = DegreesToRadians(z);

     double     cyaw, cpitch, croll, syaw, spitch, sroll;
     double     cyawcpitch, syawspitch, cyawspitch, syawcpitch;

     cyaw = cos(0.5f * yaw);
     cpitch = cos(0.5f * pitch);
     croll = cos(0.5f * roll);
     syaw = sin(0.5f * yaw);
     spitch = sin(0.5f * pitch);
     sroll = sin(0.5f * roll);

     cyawcpitch = cyaw*cpitch;
     syawspitch = syaw*spitch;
     cyawspitch = cyaw*spitch;
     syawcpitch = syaw*cpitch;

     q.n = (float) (cyawcpitch * croll + syawspitch * sroll);
     q.v.x = (float) (cyawcpitch * sroll - syawspitch * croll);
     q.v.y = (float) (cyawspitch * croll + syawcpitch * sroll);
     q.v.z = (float) (syawcpitch * croll - cyawspitch * sroll);

     return q;
}

MakeEulerAnglesFromQ

This function extracts the three Euler angles from a given quaternion.

You can extract the three Euler angles from a quaternion by first converting the quaternion to a rotation matrix and then extracting the Euler angles from the rotation matrix. Let R be a nine-element rotation matrix:

image with no caption

and let q be a quaternion:

q = [n, x i + y j + z k]

Then each element in R is calculated from q as follows:

r11 = n2 + x2 − y2 − z2
r21 = 2xy+2zn
r31 = 2zx − 2yn
r12 = 2xy − 2zn
r22 = n2 − x2 + y2 − z2
r32 = 2zy + 2xn
r13 = 2xz + 2yn
r23 = 2yz − 2xn
r33 = n2 − x2 − y2 + z2

To extract the Euler angles, yaw (ψ), pitch (τ), and roll (φ), from R, you can use these relations:

sin τ = −r31
tan φ = r32 / r33
tan ψ = r21 / r11

Here’s the code that extracts the three Euler angles, returned in the form of a Vector, from a given quaternion:

inline     Vector     MakeEulerAnglesFromQ(Quaternion q)
{
     double     r11, r21, r31, r32, r33, r12, r13;
     double     q00, q11, q22, q33;
     double     tmp;
     Vector     u;

     q00 = q.n * q.n;
     q11 = q.v.x * q.v.x;
     q22 = q.v.y * q.v.y;
     q33 = q.v.z * q.v.z;

     r11 = q00 + q11 - q22 - q33;
     r21 = 2 * (q.v.x*q.v.y + q.n*q.v.z);
     r31 = 2 * (q.v.x*q.v.z - q.n*q.v.y);
     r32 = 2 * (q.v.y*q.v.z + q.n*q.v.x);
     r33 = q00 - q11 - q22 + q33;

     tmp = fabs(r31);
     if(tmp > 0.999999)
     {
          r12 = 2 * (q.v.x*q.v.y - q.n*q.v.z);
          r13 = 2 * (q.v.x*q.v.z + q.n*q.v.y);

          u.x = RadiansToDegrees(0.0f); //roll
          u.y = RadiansToDegrees((float) (-(pi/2) * r31/tmp));   // pitch
          u.z = RadiansToDegrees((float) atan2(-r12, -r31*r13)); // yaw
          return u;
     }

     u.x = RadiansToDegrees((float) atan2(r32, r33)); // roll
     u.y = RadiansToDegrees((float) asin(-r31));      // pitch
     u.z = RadiansToDegrees((float) atan2(r21, r11)); // yaw
     return u;


}

Conversion Functions

These two functions are used to convert angles from degrees to radians and radians to degrees. They are not specific to quaternions but are used in some of the code samples shown earlier:

inline     float     DegreesToRadians(float deg)
{
     return deg * pi / 180.0f;
}

inline     float     RadiansToDegrees(float rad)
{
     return rad * 180.0f / pi;
}


[30] For a description of how quaternions are used to represent rotation, refer to the section Quaternions in Chapter 11.

[31] You can verify this by recalling the trigonometric relation cos2θ + sin2 θ = 1.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.163.238