This page was generated from doc/rotation/quaternions.ipynb. Interactive online version: Binder badge.

Quaternions§

splines.quaternion.Quaternion

splines.quaternion.UnitQuaternion

helper.py

[1]:
from helper import angles2quat, plot_rotation

Quaternion Representations§

Algebraic§

\begin{equation*} q = w + x\mathbf{i} + y\mathbf{j} + z\mathbf{k} \end{equation*}

where \(\mathbf{i}^2 = \mathbf{j}^2 = \mathbf{k}^2 = \mathbf{ijk} = -1\).

The order matters: \(\mathbf{ij} = \mathbf{k}\), \(\mathbf{ji} = -\mathbf{k}\).

Scalar and Vector§

\begin{equation*} q = (w, \vec{v}) = (w, (x, y, z)) \end{equation*}

Sometimes, the scalar and vector parts are also called “real” and “imaginary” parts, respectively.

4D Space§

Quaternions can also be imagined as four dimensional vector space with some additional operations.

\begin{equation*} q = (w, x, y, z) \quad\text{or}\quad q = (x, y, z, w) \end{equation*}

More Representations§

There are even more ways to represent quaterions, for example as 4x4 real matrices or as 2x2 complex matrices.

Quaternion Operations§

TODO

Unit Quaternions as Rotations§

Given a (normalized) rotation axis \(\vec{n}_i\) and a rotation angle \(\alpha_i\) (in radians), we can create a corresponding quaternion (which will have unit length):

\begin{equation*} q_i = (\cos \frac{\alpha_i}{2}, \vec{n}_i \sin \frac{\alpha_i}{2}) \end{equation*}

Quaternions are a double cover over the rotation group (a.k.a. SO(3)), which means that each rotation can be associated with two distinct quaternions. More concretely, the antipodal points \(q\) and \(-q\) represent the same rotation.

More information can be found on Wikipedia.

[2]:
identity = angles2quat(0, 0, 0)
[3]:
a = angles2quat(90, 0, 0)
b = angles2quat(0, 35, 0)
[4]:
plot_rotation({
    'identity': identity,
    'a': a,
    'b': b,
}, figsize=(6, 2));
[4]:
[<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2f4af50>,
 <mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a5068550>,
 <mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2f2c310>]
../_images/rotation_quaternions_13_1.svg
[5]:
identity
[5]:
UnitQuaternion(scalar=1.0, vector=(0.0, 0.0, 0.0))

Unit Quaternion Operations§

  • quaternion multiplication: \(q_1 q_0\)

  • rotation \(q_0\) followed by rotation \(q_1\) (read from right to left)

  • \(q_0 q_1 \ne q_1 q_0\) (except if the rotation axis is the same)

[6]:
plot_rotation({'a b': a * b, 'b a': b * a}, figsize=(5, 2));
[6]:
[<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2b7fe90>,
 <mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2b57450>]
../_images/rotation_quaternions_16_1.svg
  • inverse: \(q^{-1}\)

  • same rotation axis, negated angle

  • \(q q^{-1} = q^{-1} q = (1, (0, 0, 0))\)

[7]:
plot_rotation({'b': b, 'b$^{-1}$': b.inverse()}, figsize=(5, 2));
[7]:
[<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2a61350>,
 <mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f28a2e9c950>]
../_images/rotation_quaternions_18_1.svg

Relative Rotation (Global Frame of Reference)§

Given two rotations \(q_0\) and \(q_1\), we can try to find a third rotation \(q_{0,1}\) that rotates \(q_0\) into \(q_1\).

Since we are considering the global frame of reference, \(q_{0,1}\) must be left-multiplied with \(q_0\):

\begin{equation*} q_{0,1} q_0 = q_1 \end{equation*}

Now we can right-multiply both sides with \({q_0}^{-1}\):

\begin{equation*} q_{0,1} q_0 {q_0}^{-1} = q_1 {q_0}^{-1} \end{equation*}

\(q_0 {q_0}^{-1}\) cancels out and we get:

\begin{equation*} q_{0,1} = q_1 {q_0}^{-1} \end{equation*}

Relative Rotation (Local Frame of Reference)§

If \(q_{0,1}\) is supposed to be a rotation in the local frame of \(q_0\), we have to change the order of multiplication:

\begin{equation*} q_0 q_{0,1} = q_1 \end{equation*}

Now we can left-multiply both sides with \({q_0}^{-1}\):

\begin{equation*} {q_0}^{-1} q_0 q_{0,1} = {q_0}^{-1} q_1 \end{equation*}

\({q_0}^{-1} q_0\) cancels out and we get:

\begin{equation*} q_{0,1} = {q_0}^{-1} q_1 \end{equation*}