https://www.anyleaf.org/blog/quaternions:-a-practical-guide Home ELRS full diversity RX ELRS RX H7 Quad FC G4 Quad FC Stove Thermometer Water Monitor pH Conductivity Temperature About Checkout Blog Contact AnyLeaf Blog Quaternions: A practical guide Written on Aug. 6, 2022, 7:53 a.m. Updated Aug. 14, 2022, 4:41 a.m. Overview This article demonstrates how to use quaternions for practical applications. In it, we build intuition about how to use quaternions as building blocks to solve engineering and geometry problems. Applications include computer graphics, attitude systems for air and spacecraft, and structural chemistry models. You may have read that quaternions have advantages over Euler angles to represent orientations, including preventing gimbal lock, and advantages over rotation matrices, such as more efficient computation, and compact form. This is true. You may have also read that they're complicated, difficult to use, or counter-intuitive. This isn't! This article outlines several operations between quaternions and vectors, and describes how to perform them. It doesn't describe why they work. There are many articles and videos online where you can learn about this, including perspectives from numerical algorithms, visualizations (including stereographic projections of 4d space!), and symbolic operations. These sources are important for achieving a deep understanding - some examples are included in the References section at the bottom. This article, in contrast, contains concise information that will make you effective at using quaternions as an engineering tool. Why are we taking this approach instead of building deeper knowledge? I'm aiming for a specific level of detail that's suitable for engineering purposes. We're building intuition so you'll be able to learn a few general tools, then be able to apply them to a broad range of applications. You should be able to recognize when they're appropriate, how to combine them, and how to apply them to physical systems and models. A deeper approach might describe why the arithmetic we define them with works. A shallower approach might provide complete orientation and rotation systems, like in a computer graphics library. Hopefully we've hit a sweet spot. This article uses mathematical notation, in a format that can easily be translated to any procedural programming language. It includes the information you need to turn these operations into code, in a language-agnostic format. It uses some mathematical conventions, with limited abstractness. The key takeaway is that quaternions are a powerful, elegant tool that can be used whenever modelling rotations or orientations in 3D. When viewed this way, they're neither complicated to use, nor to understand. If you'd like an interactive description, check out this 3Blue1Brown + Ben Eater collaboration. It's a page containing a number of interactive videos that explain and visualize the significance of quaternions, including for rotations. It lets you pause the videos, then interact with the visualizations. Further, this article doesn't describe the algebra of quaternions; if you're curious, this article goes into detail. --------------------------------------------------------------------- Basics A quaternion is described by a set of 4 numbers, labeled \(w\), \(x \), \(y\), and \(z\). In code, this may be represented by a struct (or class) with 4 floating-point fields, and functions (eg methods) that perform various operations, and overload arithmetic operators. All quaternions used in this article are unit quaternions; they have a magnitude of 1. These are also known as versors. Magnitude in this context is defined in a similar way as with vectors: The square root of the sum of squares of each component: $$ || Q || = \sqrt{Q_w^2 + Q_x^2 + Q_y^2 + Q_z^2} $$ All vectors in this article are unit vectors in 3 dimensions. Many of the operations involve vectors; some knowledge of vectors and linear algebra is required. Note that unit quaternions double-cover the set of all possible 3d rotations; every rotation can be represented by either of 2 unique quaternions. For details on this, check out this paper by Neil Bickford. --------------------------------------------------------------------- Rotation and orientation Orientation and rotation quaternions are equivalent: The distinction is that ones used for orientation describe the operation that rotates the identity quaternion to the orientation. The identity quaternion is defined as having a \(w\) component equal to 1, and all others equal to 0. The distinction is how you use them; it's important to keep track of which quaternions you use for orientation vice rotations, even though the mathematics is the same. As a convention, we'll use \( O \) for quaternions that represent orientations, and \( R \) for ones that represent rotations. Quaternions are sometimes compared with matrices as ways to transform space. We'll go over this with more detail below; the key takeaway is that matrices represent linear transforms; quaternions represent a special case of linear transform: rotations in 3 dimensions. An important distinction: Quaternions directly describe rotations, and aren't tied to any coordinate system or absolute orientation. Conversely, most computations need a result tied to absolute orientation; a coordinate system. Examples: An aircraft's upright attitude; a 3D model's up orientation; a vector defined by global coordinates. --------------------------------------------------------------------- Anchoring quaternions to coordinates and directions Let's examine a 3d model of a boat. Its orientation is stored as a quaternion; one that represents the result of its rotation on the unit quaternion. How do we orient it so that its hull is pointed down? The key in how the model is defined, and how the graphics engine is set up. If you'd like the unit quaternion to represent an upright and level orientation, the boat's model should be defined using an up direction that's the same as the graphics engine expects. If not, you can use a different quaternion to represent up. In an aircraft's attitude system, something similar applies: We move from relative quaternions to absolute orientation by anchoring the system's inputs to, for example, the earth's gravitational field. We could define the up orientation as a unit quaternion where an accelerometer's z axis reads +1G. With this in mind, how the accelerometer is oriented within the aircraft provides the anchor. As you read through the operations below, you'll notice quaternions can be used to define a rotation between vectors, or rotate a vector to a new position. The theme is that quaternions represent transitions between 2 or more vectors. You may have seen functions in graphics libraries that construct quaternions that point in a direction - note how they always include one or more reference vectors as arguments - eg up or forward. What about quaternions operating on other unanchored quaternions? These aren't yet anchored, and in most systems, will ultimately act on something else, directly or indirectly. A common example is describing rotations relative to an anchored orientation, or a vector. --------------------------------------------------------------------- Rotating a vector using a quaternion Rotating a vector is one of the most common applications of quaternions, and is a building block for other operations. Here's how it's done, assuming a unit quaternion and unit vector: $$ \vec{v}' = Q \vec{v} Q^{-1} $$ Where \( \vec{v} \) is the original vector, \( \vec{v}' \) is the rotated vector, \( Q \) is the quaternion that defines the rotation, and \( Q^{-1} \) is its inverse. The inverse of a quaternion is that quaternion with the same w component, and the negation of the others: $$ Q^{-1} = [Q_w, -Q_x, -Q_y, -Q_z] $$ This is also known as the quaternion's conjugate, written as \( Q^* \). For unit quaternions, the conjugate and inverse is the same. An example: if \(Q = [1, 2, 0, -1] \), \(Q^{-1} = [1, -2, 0, 1] \). Quaternion multiplication, used above, is defined as follows. Note that to multiply a quaternion by a vector, convert the vector to a quaternion with \(w=0\), and with \(x\), \(y\), and \(z\) as the vector's respective components. $$ w = L_w R_w - L_x R_x - L_y R_y - L_z R_z $$ $$ x = L_w R_x + L_x R_w + L_y R_z - L_z R_y $$ $$ y = L_w R_y - L_x R_z + L_y R_w + L_z R_x $$ $$ z = L_w R_z + L_x R_y - L_y R_x + L_z R_w $$ $$ Q_{LR} = L R = [w, x, y, z] $$ --------------------------------------------------------------------- Specifying a quaternion that describes the rotation from one vector to another There are multiple rotations that can describe this; the equation above describes the shortest rotation. \( R \) is the quaternion that rotates unit vector \( \vec{v1} \) to unit vector \( \vec{v2} \). $$ w = 1 + \vec{v1} \cdot \vec{v2} $$ $$ [x, y, z] = \vec{v1} \times \vec{v2} $$ $$ R = \frac{ [w, x, y, z] }{ ||[w, x, y, z]|| } $$ Note: The above approach will fail if the vectors are parallel; to correct this in code, check that the dot product is equal to 1 or -1. If equal to 1, return the identity quaternion. If equal to -1, return the identity quaternion rotated by p radians. To do this, construct a rotation-around-axis (described below), using a vector orthonormal to either input vector as the axis, and p as the angle. You can construct the orthonormal vector by taking the cross product between our first bond, and an arbitrary unit vector. This is a versatile operation! Earlier, we mentioned how quaternions aren't anchored to coordinates or directions. Let's say we'd like a 3d model to align with a certain directional vector. To do this, we use the operation above, with \( \vec{v1} \) as the up vector the model uses, and \( \vec{v2} \) as the vector defining the direction. --------------------------------------------------------------------- Specifying a quaternion that describes rotation around an axis To create a quaternion \( R \) that describes the rotation of amount \( \theta \) around axis \( \vec{v} \), use this equation: $$ w = \cos{\frac{\theta}{2}} $$ $$ x = \sin{\frac{\theta}{2}} \vec {v} _x $$ $$ y = \sin{\frac{\theta}{2}} \vec{v} _y $$ $$ z = \sin{\ frac{\theta}{2}} \vec{v} _z $$ $$ R = [w, x, y, z] $$ One example of this use is changing the orientation of computer-graphics objects in response to user input. For example, modifying camera orientation in response to mouse movements. You could create a rotation around the X axis ( \(\vec{1, 0, 0} \) ) with one around the y axis ( \(\vec{1, 0, 0} \) ), then apply both of these to the current orientation: \( O_{new} = R_x R_y O \) The rotation amount would be the degree of input in the given directions. --------------------------------------------------------------------- Rotating an orientation, and composing rotations To rotate a quaternion representing an orientation, perform quaternion multiplication with the rotation quaternion on the left: $$ O' = R O $$ To compose multiple rotations, perform quaternion multiplication with the first rotation to apply on the right, and the last on the left. When multiple rotations are composed in this way, they are applied right to left. For example, this is how you would apply 2 rotations to an orientation: $$ R_{12} = R_2 R_1 $$ Here's an example applying both of those techniques; \( O' \) is the orientation as if \( R_1 \), were applied to \( O \), then \( R_2 \), then \( R_3 \). $$ O' = R_3 R_2 R_1 O $$ Where \( O \) is the initial orientation, and \( O' \) is the orientation after applying \(R_{r1} \) followed by \( R_{r2} \). Note that equivalently, you could compose the rotations first, then apply them. (eg by storing as a variable, or using parentheses to group the rotations) Quaternion multiplication is non-commutative, meaning order matters. --------------------------------------------------------------------- Quaternions for change of basis Matrices are the most common way to describe change-of-basis operations in linear algebra. For change of basis operations that are purely rotational (don't stretch or skew the space), quaternions are a more efficient (an arguably more intuitive) approach. To do this, create a rotation quaternion that rotates between 2 vectors, as described above. Set the first vector to the starting basis vector you wish wish to change, and the second vector to its equivalent in the new basis. --------------------------------------------------------------------- Construction from Euler angles Creating an orientation quaternion from Euler angles follows from 2 points above: #1: Orientation quaternions are rotations of the identity quaternion. #2: We can construct a rotation around an axis using an axis vector and rotation angle. So, to construct an orientation from angles, we can combine rotations from each basis vector. The \( fromAxisAngle \) function below is the procedure described in the Specifying a quaternion that describes rotation around an axis section above. $$ O_x = fromAxisAngle([1, 0, 0], \phi) $$ $$ O_y = fromAxisAngle([0, 1, 0], \psi) $$ $$ O_z = fromAxisAngle([0, 0, 1], \theta) $$ $$ O = O_x O_y O_z $$ Where \( \phi \), \( \psi \), and \( \theta \) are Euler angles around \(x\), \(y\), and \(z\) axes respectively. Alternatively, we can combine some operations with the following approach: $$ cy = \cos(\frac{\theta}{2}) $$ $$ sy = \sin(\frac{\theta}{2}) $$ $$ cp = \cos(\frac{\psi}{2}) $$ $$ sp = \sin(\frac{\psi}{2}) $$ $$ cr = \cos(\frac{\phi}{2}) $$ $$ sr = \sin(\frac{\phi}{2}) $$ $$ w = cr \cdot cp \cdot cy + sr \cdot sp \cdot sy $$ $$ x = sr \cdot cp \cdot cy - cr \cdot sp \cdot sy $$ $$ y = cr \cdot sp \cdot cy + sr \cdot cp \cdot sy $$ $$ z = cr \cdot cp \cdot sy - sr \cdot sp \ cdot cy $$ $$ O = [w, x, y, z] $$ --------------------------------------------------------------------- Conversion to Euler angles ( todo) --------------------------------------------------------------------- Spherical linear interpretation (SLERP) Given two quaternions that represent orientation, this is how you calculate an intermediate orientation a given portion between the two: $$ (Q_2 Q_1^{-1})^t Q_1 $$ Where t is the portion between the two quaternions, on a scale between 0 and 1. Of note, the inverse of a quaternion, as described earlier, is itself with its x, y, and k components negated. To take the exponent of a quaternion with a number, (to be continued!) Interpolation is useful when creating animations., since it lets you determine the orientation at arbitrary steps between starting and ending orientations. --------------------------------------------------------------------- An example using several of the above techniques We'll now go over an example of applying the techniques above to a practical problem. Note how we use a few tools to do a variety of manipulations on 3d objects. Let's consider the case where we are modelling a chain of atoms connected to each other by chemical bonds. We'll focus on a single bond; we can iterate this procedure to make additional bonds. We'll ignore physical subtleties, and model the situation like this: * \( O_{prev} \): An atom of known orientation, with its orientation represented by a quaternion, and its position represented by a vector. Its orientation is the output of the previous iteration of our process (and perhaps a known anchor point for the first atom, like a unit quaternion) * \(\theta \): The rotation amount around the bond axis. This is called the dihedral (or torsion) angle, and defines the protein's degrees of freedom when folding. (These are conventionally called \( \phi \), \( \psi \), and \( \omega \) depending on their location within an amino acid. These angles are anchored in terms of the neighboring 4 atom around a bond, but we'll skip this for simplicity.) We can break this problem down into determining the orientation and position of each individual atom, using the previous atom's orientation and position, and the bond geometry. We will define 2 unit vectors that represent the bonds to, and from this atom; these are called bond angles. These will be defined in relation to an atom; these are in local space, not global coordinates (worldspace). Their relative positions are what's important, so we'll pick an arbitrary unit vector for the first: $$ \vec{bondToNext} = [1, 0, 0] $$ To find the other angle, we can construct a quaternion from the angle between the bonds, and an arbitrary axis orthonormal to the first bond. Note that we can use an arbitrary orthnormal axis because we only care about the relative bond positions. We create it by taking the cross product between our first bond, and an arbitrary unit vector. $$ \vec{axis} = \vec{bondToNext} \times \vec{anyUnitVec} $$ $$ Q_ {bonds} = fromAxisAngle( \vec{axis}, \theta_{bonds} ) $$ Then, we create the other bond vector (The one defined as being at a given angle from the other) by rotating the first using the rotation we created: $$ \vec{bondToPrev} = Q_{bonds} \vec{bondToNext} Q_{bonds}^{-1} $$ The result is 2 vectors that are aligned relative to each other at the specified bond angle, \( \theta_{bonds} \) We will use these local vectors for all atoms in the chain. We then align the local bond vector of the previous atom to worldspace based on the prev atom's orientation. We do this by aligning the \( toNext \) local bond from the previous atom to worldspace, then aligning the \( toPrev \) local bond of this atom to it. $$ bondToThisWorldspace = O_{prev} \vec{bondToNext} O_{prev}^{-1} $$ We apply the rotation quaternion that aligns the (inverse of) the local-space bond to the prev atom with the world-space "to" bond of the previous atom. This is also the orientation of our atom, without applying the dihedral angle (rotation around the bond). $$ R_{bondAlignment}= fromUnitVecs(- \vec{bondFromThis}, \vec {bondToThisWorldspace}) $$ Now, we rotate the orientation along the dihedral angle, which is a rotation along the bond axis to a specified angle: $$ R_{aroundAxis} = fromAxisAngle(\vec{bondToThisWorldspace}, \theta) $$ We find our orientation for this atom by applying the 2 rotations sequentially to create our orientation using quaternion multiplication. As we mentioned earlier, order matters here, and operations are ordered right-to-left. So, we apply the bond alignment rotation before the around-axis one. $$ O = R_{aroundAxis} R_{bondAlignment} O_{prev} $$ To continue the chain, we repeat the process, using this new orientation as \( O_{prev} \) in the next iteration. The next atom's position is \( \vec{bondToNext} \) rotated by this atom's orientation \( O \), multiplied by the bond's length. --------------------------------------------------------------------- On information content Let's compare quaternions, rotation matrices, and Euler angles from the perspective of information. You might notice that a 3D rotation matrix has 9 numerical values, while a quaternion, which can represent the same rotation, has only 4. This begs the question: given that a quaternion with 4 values can represent any 3d rotation, why do rotation matrices have 5 extra values? The answer is that matrices can be used to represent more than rotations: They can represent any linear transformation, of which rotations are a subset. Matrices can be used to stretch, skew, sheer, and rotate, in any combination, making them more flexible. If we are only rotating, matrices encode extra information - this results in more values to store, and less-efficient computation. It also means they're an under-constrained system. With this in mind: Use Quaternions if only modeling rotation. Use matrices if modelling other linear transforms. Note that compared to Euler angles, quaternions store an extra value. (4 value in a quaternion, vs 3 in Euler angles) This extra value is due to the constraint that the ones we're using are strictly unit quaternions; this explains the extra degree of freedom. --------------------------------------------------------------------- A note on rotors and bivectors A recent article by Marc Ten Bosch makes a case that quaternions are an obfuscated, unintuitive, specialized case of Rotors. This line from that article highlights one of its main points: "I have always found it important to actually understand the things I am using". In our article, we've explicitly avoided the deeper understanding Mr. Bosch alludes to; this is partly due to his point - it's difficult to understand why quaternion operations work. In his article and accompanying video, Mr. Bosch describes how it's notoriously difficult to achieve this deeper geometric understanding using quaternions, and is easier with rotors. Rotors can be used for everything quaternions can. For the practical approach we've taken here, rotors don't have a tangible advantage; their operations are equivalent. Quaternions have the distinct advantage of being standardized on domains like computer graphics and attitude determination. With this in mind, I don't agree with the article's title of Let's remove Quaternions from every 3D Engine, but agree that rotors are a worthwhile alternative to explore and implement. From the article: "We can notice that 3D Rotors look a lot like Quaternions... In fact the code/math is basically the same! The main difference is that \( i \), \( j\) and \( k \) get replaced by \( y[?]z \), \( x[?]z \) and \( x[?]y \), but they work mostly the same way... However, as we have seen, 3D Rotors are a 3D concept that does not require the use of '4D double rotations' or 'stereographic projection' to visualize." --------------------------------------------------------------------- An exercise: Eigenvalues and eigenvectors For a given linear transform, eigenvectors are vectors that don't change direction when the transform is applied. Eigenvalues are the scale factor the length of these vectors. These are usually discussed in contexts of matrices, but apply to quaternions as well. * 1: For a unit quaternion, how many eigenvectors are there? What do they represent? What are the associated eigenvalues? * 2: How would you answer differ for a non-unit quaternion? To solve this, you need information outside the scope of this article, but it may give you insight into why we're using unit quaternions only. This 3Blue1Brown video has information that might help. It has info on the significance of eigenvectors on rotations. It also includes an alternative way of defining linear transforms using them, that maps directly to one of the techniques we described above. --------------------------------------------------------------------- Conclusion If you're modeling something that uses 3D rotations or orientation, quaternions should be in your toolkit. You should know what operations they can be used for, and be able to look up how to apply them. --------------------------------------------------------------------- References * Grant Sanderson (3Blue1Brown) and Ben Eater: Visualizing Quaternions * Wikipedia: Quaternions and Spacial Rotations * AHRS: Algebraic Quaternion Algorithm * Marcten Bosch: Quaternions * Opengl Tutorial: Quaternions * Robert Eisele: Proof: Quaternion from two vectors * All about Circuits: Don't Get Lost in Deep Space Understanding Quaternions * 3D Game Engine Programming: Understanding Quaternions * Neil Bickford: Why Do the Unit Quaternions Double-Cover the Space of Rotations? Assembled in Raleigh, NC, USA. Privacy policy Terms and conditions All AnyLeaf products comply with the Restriction of Hazardous Substances (RoHS) Directive. (c) 2022 AnyLeaf