Quaternions are often used in attitude and heading systems to represent orientation. This post describes quaternions and how to use them in orientation and navigation systems.
If you type ‘quaternion’ into google, it’ll tell you something like this
a complex number of the form w + xi + yj + zk, where w, x, y, z are real numbers and i, j, k are imaginary units that satisfy certain conditions.
That’s a pretty frightening description, but with any luck, you’re a persistent type of person because once you’ve masterd quaternions (at least, unit quaternions used to represent orientation) you’ll probably find they are actually nowhere near as difficult as you may have been led to believe. For a start, you can ignore the imaginary components. They’re just a figment of your imagination, anyway.
From here on, we’ll use a representation with only four components. These are w, x, y and z.
These four components make quaternions similar to four-dimensional vectors in a lot of ways. For a start, normalising a quaternion is done in the same way.
magnitude = sqrt(w*w + x*x + y*y + z*z)
unit_q = q / magnitude
The quaternion library in ETK has magnitude and normalise functions
real_t magnitude = q.magnitude();
One of the most import quaternion operation is multiplication. It’s used to add the amount of rotation stored in one quaternion to another. For example, quaternion A represents straight, level and east facing ( 90 degrees). Quaternion B is straight, level and facing north east ( 45 degrees ). Multiplying A and B will give a quaternion that has a heading of 135 degrees – because multiplying A by B adds the rotation of B to A. Make sense?
Here’s the proof
using namespace std;
etk::Quaternion A, B;
//heading is 90, zero pitch or roll
etk::Vector<3> a(90, 0, 0);
//heading is 45
etk::Vector<3> b(45, 0, 0);
//make quaternions A and B from euler angle representations
//multiply A by B
etk::Quaternion C = A*B;
//convert C to an euler angle, because it's easier for mere mortals to comprehend
etk::Vector<3> euler = C.toEuler();
cout << euler.x() << " " << euler.y() << " " << euler.z() << endl;
The output from this program is 135 0 0.
One very important thing to note is that quaternion multiplication is non-communicative. That means
For unit quaternions, the conjugate is the same as the inverse. This is great because the conjugate is a lot easier to calculate than the inverse. To find the conjugate, simply invert the x, y and z components.
etk::Quaternion has a conjugate function
etk::Quaternion conj = q.conjugate();
The conjugate is great because it allows you to unrotate or substract a rotation from another quaternion. Multiplying quaternions adds rotation but multiplying by the conjugate subtracts it.
Quaternions can be used to rotate vectors. This is really useful for converting vectors to a different frame of reference. Example:
A pilot experiences acceleration, but a large component of this is due to gravity. We know that gravity always measures 9.8m/s/s upwards in the world frame of reference and we know the orientation of the aircraft. We can use the aircrafts orientation to rotate a gravity vector into the aircraft frame of reference, then simply subtract it from total acceleration felt by the pilot.
etk::Vector<3> euler(285, 2.5, 1.54);
q.fromEuler(euler); //this is the orientation quaternion
//total acceleration is force felt by accelerometers in the aircraft
etk::Vector<3> total_acceleration(1.5, 0.2, 10.5);
//gravity makes things feel like they are accelerating up at 9.8m/s/s on the vertical (z) axis
etk::Vector<3> gravity(0.0, 0.0, 9.8);
//rotate gravity so it's in the pilots frame of reference
auto gravity_pilot_frame = q.rotateVector(gravity);
//now we can subtract gravity from total acceleration
etk::Vector<3> acceleration = total_acceleration-gravity_pilot_frame;
Angular Rates To Quaternion
Converting angular velocity to a quaternion is easily done with ETK. Simply use the function fromAngularVelocity().
If angular velocity is regular velocity, then a quaternion represents position. It simply doesn’t make sense to convert velocity into a position unless you have a time delta. Velocity equates to a change in distance over a period of time. fromAngularVelocity() requires angular velocity in radians per second, and the sample rate of the gyroscopes in seconds. It will calculate a quaternion that represents that change in angular position.
You can also convert the quaternion back into an angular rate using toAngularVelocity().
Converting To Other Orientation Forms
etk::Quaternions can be converted to and from NED orientation matrices, axis angles and euler angles.