Skip to article frontmatterSkip to article content
%%capture
'''
(C) Copyright 2020-2025 Murilo Marques Marinho (murilomarinho@ieee.org)

     This file is licensed in the terms of the
     Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
     license.

 Derivative work of:
 https://github.com/dqrobotics/learning-dqrobotics-in-matlab/tree/master/robotic_manipulators
 Contributors to this file:
     Murilo Marques Marinho (murilomarinho@ieee.org)
'''

DQ2 Quaternion Basics using DQ Robotics

I found an issue

Thank you! Please report it at https://github.com/MarinhoLab/OpenExecutableBooksRobotics/issues

Introduction

Before going into the topic of quaternions, it is important to review some high-school level math. And, of course, we need to install the library! This is easy to do. The library is available in PyPI.

%%capture
%pip install dqrobotics dqrobotics-pyplot
%pip install dqrobotics dqrobotics-pyplot --break-system-packages
from dqrobotics_extensions.pyplot import plot
import matplotlib.pyplot as plt
from math import pi, cos, sin

Quick review on complex numbers

The set of complex numbers C\mathbb{C} , can be understood as an extention of the set of real numbers R\mathbb{R} . Any complex number can always be written in the form

c=a+b  ı  ,^\mathit{\mathbf{c}}=a+b\hat{\;\imath \;,}

where a,b  a,b\in \; R\mathbb{R} . In addition, the imaginary unit,   ı  ^\hat{\;\imath \;} has the following property

  ı  ^2=1.{\hat{\;\imath \;} }^2 =-1\ldotp

For instance, the complex number

c1=5+28ı  ^  {\mathit{\mathbf{c}}}_1 =5+28\hat{\imath \;} \;

can be written using DQ Robotics as follows.

# Import essential DQ algebra
from dqrobotics import *

# Define c1
c1 = 5 + 28*i_
print(f"c1 = {c1}")
c1 = 5 + 28i

Notice that the imaginary unit property holds:

# Imaginary unit property
i_ * i_
- 1

Note: Do not confuse Python’s “j” with DQ Robotics “j_”. They are different.

import numpy as np

if j_ != 1j:
    print("DQ Robotics j_ is not equal to Python's j")
DQ Robotics j_ is not equal to Python's j

Operations on complex numbers

The operations on complex numbers are very similar to the operations on real numbers. We just need to respect the property   ı  ^2=1.{\hat{\;\imath \;} }^2 =-1\ldotp

For instance, for the complex numbers

c1=a1+b1ı  ^,c_1 =a_1 +b_1 \hat{\imath \;},
c2=a2+b2ı  ^.c_2 =a_2 +b_2 \hat{\imath \;}.
c1 = 5  + 28*i_
print(f"c1 = {c1}")
c1 = 5 + 28i
c2 = 25 + 88*i_
print(f"c2 = {c2}")
c2 = 25 + 88i

Sum/Subtraction

c3=c1+c2=(a1+a2)+(b1+b2)  ı  ^.c_3 =c_1 +c_2 =\left(a_1 +a_2 \right)+\left(b_1 +b_2 \right)\hat{\;\imath \;} \ldotp
c3 = c1 + c2
print(f"c3 = {c3}")
c3 = 30 + 116i

The subtraction is defined accordingly.

  c1c2=(a1a2)+(b1b2)  ı  ^.{\;c}_1 -c_2 =\left(a_1 -a_2 \right)+\left(b_1 -b_2 \right)\hat{\;\imath \;} \ldotp
c1 - c2
- 20 - 60i

Product

The product will be

  c1c2=(a1a2b1b2)+(a1b2+b1a2)  ı  ^.{\;c}_1 c_2 =\left(a_1 a_2 -b_1 b_2 \right)+\left(a_1 b_2 +b_1 a_2 \right)\hat{\;\imath \;}.
c1 * c2
- 2339 + 1140i

Conjugation

The conjugate of a complex number is defined as

(c1)=ab  ı  ^.{\left(c_1 \right)}^* =a-b\hat{\;\imath \;}.
conj(c1)
5 - 28i

Norm

The norm of a complex number can be obtained as

c1=c1c1  =(a1+b1ı  ^)(a1b1ı  ^)  =a12+b12.\left|\right|c_1 \left|\right|=\sqrt{c_1 c_1^* \;}=\sqrt{\left(a_1 +b_1 \hat{\imath \;} \right)\left(a_1 -b_1 \hat{\imath \;} \right)\;}=\sqrt{a_1^2 +b_1^2 }.
norm(c1)
28.442925

Imaginary part

The imaginary part is

Im(c1)=b  ı  ^.\textrm{Im}\left(c_1 \right)=b\hat{\;\imath \;}.
Im(c1)
28i

Real

The real part is

Re(c1)=a.\textrm{Re}\left(c_1 \right)=a.
Re(c1)
5

Quaternions

When we extend the concept of complex numbers to four dimensions, we have what we call quaternions. They compose the set H\mathbb{H} and can always be written in the form

h=a+b  ı  ^+c  ȷ  ^+d  k^,\mathit{\mathbf{h}}=a+b\hat{\;\imath \;} +c\hat{\;\jmath \;} +d\hat{\;k},

where a,b,c,d  a,b,c,d\in \; R\mathbb{R} . In addition, the imaginary units   ı  ^\hat{\;\imath \;} ,   ȷ  ^\hat{\;\jmath \;} , and   k  ^\hat{\;k\;} have the following properties

  ı  ^2=  ȷ  ^2=  k^2=  ı  ^  ȷ  ^  k^=1.{\hat{\;\imath \;} }^2 ={\hat{\;\jmath \;} }^2 ={\hat{\;k} }^2 =\hat{\;\imath \;} \hat{\;\jmath \;} \hat{\;k} =-1.

Note that every complex number is a quaternion, but not every quaternion is a complex number ( CH\mathbb{C}\subset \mathbb{H} ).

For instance, the quaternion

c1=5+28ı  ^+85  ȷ  ^+99  k^{\mathit{\mathbf{c}}}_1 =5+28\hat{\imath \;} +85\hat{\;\jmath \;} +99\hat{\;k}

can be written using DQ Robotics as follows

# Define c1
c1 = 5 + 28*i_ + 86*j_ + 99*k_
print(f"c1 = {c1}")
c1 = 5 + 28i + 86j + 99k

We can verify the properties of the imaginary units in DQ Robotics as follows

i_ * i_
- 1
j_ * j_
- 1
k_ * k_
- 1
i_ * j_ * k_
- 1

Operations on quaternions

Considering h1,h2  {\mathit{\mathbf{h}}}_1 ,{\mathit{\mathbf{h}}}_2 \in \; H\mathbb{H} , for example

h1 = 5 + 28*i_ + 86*j_ + 99*k_
print(f"h1 = {h1}")
h1 = 5 + 28i + 86j + 99k

h2 = -2 + 25*k_
print(f"h2 = {h1}")
h2 = 5 + 28i + 86j + 99k

Sum/Subtraction

h1±h2=(a1±a2)+(b1±b2)  ı  ^+(c1±c2)  ȷ  ^+(d1±d2)  k  ^.{\mathit{\mathbf{h}}}_1 \pm {\mathit{\mathbf{h}}}_2 =\left(a_1 \pm a_2 \right)+\left(b_1 \pm b_2 \right)\hat{\;\imath \;} +\left(c_1 \pm c_2 \right)\hat{\;\jmath \;} +\left(d_1 \pm d_2 \right)\hat{\;k\;}.
h1 + h2
3 + 28i + 86j + 124k
h1 - h2
7 + 28i + 86j + 74k

Multiplication

h1h2=See  Bonus  Homework  1.{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_2 =\textrm{See}\;\textrm{Bonus}\;\textrm{Homework}\;1\ldotp
h1 * h2
- 2485 + 2094i - 872j - 73k

Notice that the multiplication between quaternions is, in general, NOT COMMUTATIVE.

h1h2h2h1.{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_2 \not= {{\mathit{\mathbf{h}}}_2 \mathit{\mathbf{h}}}_1.
h3 = h1 * h2
h4 = h2 * h1
if h3==h4:
    print('h1*h2 is equal to h2*h1')
else:
    print('h1*h2 is not equal to h2*h1')
h1*h2 is not equal to h2*h1

It can be commutative in some cases. For instance, consider the trivial case in which h1=h2h_1 =h_2 .

Real part

Re(h1)=a.\textrm{Re}\left({\mathit{\mathbf{h}}}_1 \right)=a.
Re(h1)
5

If the real part of a quaternion is zero, it is called a pure quaternion, and belongs to the set Hp{\mathbb{H}}_p .

Imaginary part

Im(h1)=b  ı  ^+c  ȷ  ^+d  k^.\textrm{Im}\left({\mathit{\mathbf{h}}}_1 \right)=b\hat{\;\imath \;} +c\hat{\;\jmath \;} +d\hat{\;k}.
Im(h1)
28i + 86j + 99k

Conjugation

(h1)=Re(h1)Im(h1)=ab  ı  ^c  ȷ  ^d  k^.{\left({\mathit{\mathbf{h}}}_1 \right)}^* =\textrm{Re}\left({\mathit{\mathbf{h}}}_1 \right)-\textrm{Im}\left({\mathit{\mathbf{h}}}_1 \right)=a-b\hat{\;\imath \;} -c\hat{\;\jmath \;} -d\hat{\;k}.
conj(h1)
5 - 28i - 86j - 99k

Norm

h1=h1h1  =See  Bonus  Homework  2.\left|\right|{\mathit{\mathbf{h}}}_1 \left|\right|=\sqrt{{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_1^* \;}=\textrm{See}\;\textrm{Bonus}\;\textrm{Homework}\;2\ldotp
norm(h1)
134.186437

Vec3

The imaginary part of the quaternion can be mapped to R3{\mathbb{R}}^3 as follows

vec3(h1)=[bcd].{\textrm{vec}}_3 \left({\mathit{\mathbf{h}}}_1 \right)=\left\lbrack \begin{array}{c} b\\ c\\ d \end{array}\right\rbrack.
vec3(h1)
array([28., 86., 99.])

Vec4

Quaternions can be mapped to R4{\mathbb{R}}^4 as follows

vec4(h1)=[abcd].{\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_1 \right)=\left\lbrack \begin{array}{c} a\\ b\\ c\\ d \end{array}\right\rbrack.
vec4(h1)
array([ 5., 28., 86., 99.])

Hamilton Operators

The hamilton operators are useful to provide a form of commutativity in the quaternion multiplication.

vec4(h1h2)=H4+(h1)vec4(h2)=H4(h2)vec4(h1).{\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_2 \right)=\overset{+}{{\mathit{\mathbf{H}}}_4 } \left({\mathit{\mathbf{h}}}_1 \right){\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_2 \right)=\overset{-}{{\mathit{\mathbf{H}}}_4 } \left({\mathit{\mathbf{h}}}_2 \right){\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_1 \right).
vec4(h1*h2)
array([-2485., 2094., -872., -73.])
hamiplus4(h1)*vec4(h2)
array([[ -10., -0., -0., -2475.], [ -56., 0., -0., 2150.], [ -172., 0., 0., -700.], [ -198., -0., 0., 125.]])
haminus4(h2)*vec4(h1)
array([[ -10., -0., -0., -2475.], [ 0., -56., 2150., -0.], [ 0., -700., -172., 0.], [ 125., 0., -0., -198.]])

Conjugate mapping matrix

The following matrix is useful when the quaternion conjugate is used. For quaternions it is defined as,

C4=[1000010000100001]{\mathit{\mathbf{C}}}_4 =\left\lbrack \begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & -1 & 0 & 0\\ 0 & 0 & -1 & 0\\ 0 & 0 & 0 & -1 \end{array}\right\rbrack

and has the following property

C4vec4(h1)=vec4(h1).{\mathit{\mathbf{C}}}_4 {\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_1 \right)={\textrm{vec}}_4 \left({\mathit{\mathbf{h}}}_1^* \right)\ldotp

Unit quaternions and the rotation of rigid bodies

Unit quaternions compose the set S3{\mathbb{S}}^3 , which represent rotations of the reference frame of rigid bodies in three-dimensional space. A unit quaternion can always be written in the form

r=cos(ϕ2)+vsin(  ϕ  2)(See  Bonus  Homework  3),\mathit{\mathbf{r}}=\cos \left(\frac{\phi }{2}\right)+\textrm{vsin}\left(\frac{\;\phi \;}{2}\right)\left(\textrm{See}\;\textrm{Bonus}\;\textrm{Homework}\;3\right),

where ϕ\phi \in R\mathbb{R} is the rotation angle around the rotation axis vv\in S3Hp{\mathbb{S}}^3 \cap {\mathbb{H}}_p (Remember that Hp{\mathbb{H}}_p are pure quaternions, that is, quaternions for which the real part is zero).

Unit-norm quaternions, as the name says, have unit norm

r=1.\left|\right|r\left|\right|=1\ldotp

For instance, to represent the rotation of π2\frac{\pi }{2} rad about the x-axis, the following quaternion can be used

r1=cos(  π  4)+  ı  ^sin(  π  4).r_1 =\cos \left(\frac{\;\pi \;}{4}\right)+\hat{\;\imath \;} \sin \left(\frac{\;\pi \;}{4}\right).
r1 = cos(pi/4) + i_*sin(pi/4)

We can check that r1r_1 indeed has unit norm

norm(r1)
1

Unit quaternion double-cover property

Note that the same rotation can be represented by two different quaternions, because

r1{\mathit{\mathbf{r}}}_1

and

r1-{\mathit{\mathbf{r}}}_1

represent the same resulting rotation but are different quaternions. For instance, see that even though the following two quaternions represent the same rotation.

r1 = 1
print(f"r1 = {r1}")
r1 = 1

r2 = cos(pi) + i_*sin(pi)
print(f"r2 = {r2}")
r2 =  - 1

they have opposite signs.

This is called the “double-cover” property. We will not address this now, but it is important to know that this property exists.

No rotation

The quaternion that represents that there is no rotation is

r1=1.r_1 = 1.
r1 = DQ([1])

Plotting quaternions

Using DQ Robotics, the rotation quaternions can be plotted on screen. See, for example

Note: for the unit quaternion r1=1r_1 =1 , you have to explicitly initialize it using the DQ constructor before plotting, otherwise MATLAB will not use the correct plot function.

print(f'Printing 1 as a quaternion: {r1}')
plt.figure()
ax = plt.axes(projection='3d')
plot(r1)
plt.show()
Printing 1 as a quaternion: 1
<Figure size 640x480 with 1 Axes>

Sequential rotations

Sequential rotations are obtained by post-multiplication. For example, the transformation between the neutral reference frame by r1{r_1 } followed by r2{r_2 } is

r3=r1r2.{\mathit{\mathbf{r}}}_3 ={\mathit{\mathbf{r}}}_1 {\mathit{\mathbf{r}}}_2.

For example

from math import pi, cos, sin

r1 = cos(pi/16) + i_*sin(pi/16)
r2 = cos(pi/4) + i_*sin(pi/4)
r3 = r1*r2

plt.figure()
ax = plt.axes(projection='3d')
plot(r3)
plt.show()
<Figure size 640x480 with 1 Axes>

We can also plot all intermediary rotations using subplot.

from math import pi, cos, sin

r1 = cos(pi/16) + i_*sin(pi/16)
r2 = cos(pi/32) + i_*sin(pi/32)
r3 = r1*r2

plt.figure(figsize=(15,5))
ax1 = plt.subplot(1,3,1, projection='3d')
plot(r1)
ax1.title.set_text('r1')
ax2 = plt.subplot(1,3,2, projection='3d')
plot(r2)
ax2.title.set_text('r2')
ax3 = plt.subplot(1,3,3, projection='3d')
plot(r3)
ax3.title.set_text('Rotation of r1 by r2')
plt.show()
<Figure size 1500x500 with 3 Axes>

Reverse rotation

The reverse rotation can be obtained using the conjugate operation, because unit quaternions have unit norm. Hence,

r(r)=1.\mathit{\mathbf{r}}{\left(\mathit{\mathbf{r}}\right)}^* = 1.

For example, for a given rotation quaternion

r1 = cos(pi/16) + i_*sin(pi/16)

The rotation quaternion that corresponds to the reverse rotation is given by its conjugate.

conj(r1)
0.980785 - 0.19509i

We can verify that sequentially multiplying one by the other gives us a “no rotation” quaternion.

r1 * conj(r1)
1

Homework

in the beginning of your script.

  1. Create a MATLAB script called [quaternion_basics_homework.m]. On it, do the following tasks.
  2. Store, in r1r_1 , the value of a rotation of ϕ=π    8  rad\phi =\frac{\pi \;\;}{8}\;\textrm{rad} about the x-axis.
  3. Store, in r2r_2 , the value of a rotation of ϕ=π    16  rad\phi =\frac{\pi \;\;}{16}\;\textrm{rad} about the y-axis.
  4. Store, in r3r_3 , the value of a rotation of ϕ=π    32  rad\phi =\frac{\pi \;\;}{32}\;\textrm{rad} about the z-axis.
  5. Calculate the result of the sequential rotation of the neutral reference-frame by r1r_1 , followed by r2r_2 , followed by r3r_3 , and store it in r4{\mathit{\mathbf{r}}}_4 . Plot r4{\mathit{\mathbf{r}}}_4 .
  6. Find the reverse rotation of r4{\mathit{\mathbf{r}}}_4 and store it in r5{\mathit{\mathbf{r}}}_5 .
  7. Rotate r5{\mathit{\mathbf{r}}}_5 by 360  {360}^{\circ \;} about the x-axis and store it in r6{\mathit{\mathbf{r}}}_6 . Is r5=r6{\mathit{\mathbf{r}}}_5 =-{\mathit{\mathbf{r}}}_6 ? Plot r5{\mathit{\mathbf{r}}}_5 and r6{\mathit{\mathbf{r}}}_6 to confirm that they represent the same rotation.

Bonus Homework

  1. What is the general form of the quaternion multiplication? Multiply h1=a1+b1  ı  ^+c1  ȷ  ^+d1  k^{\mathit{\mathbf{h}}}_1 =a_1 +b_1 \hat{\;\imath \;} +c_1 \hat{\;\jmath \;} +d_1 \hat{\;k} and h2=a2+b2  ı  ^+c2  ȷ  ^+d2  k^{\mathit{\mathbf{h}}}_2 =a_2 +b_2 \hat{\;\imath \;} +c_2 \hat{\;\jmath \;} +d_2 \hat{\;k} on pen and paper and find h3=a3+b3  ı  ^+c3  ȷ  ^+d3  k^{\mathit{\mathbf{h}}}_3 =a_3 +b_3 \hat{\;\imath \;} +c_3 \hat{\;\jmath \;} +d_3 \hat{\;k} .
  2. What is the general form of the quaternion norm? Simplify   h1h1\sqrt{\;{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_1^* } on pen and paper.
  3. Show that every unit quaternion, written as r=cos(ϕ2)+vsin(  ϕ  2)\mathit{\mathbf{r}}=\cos \left(\frac{\phi }{2}\right)+\textrm{vsin}\left(\frac{\;\phi \;}{2}\right) , has unit norm. Do that on pen and paper.