%%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://
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 , can be understood as an extention of the set of real numbers . Any complex number can always be written in the form
where . In addition, the imaginary unit, has the following property
For instance, the complex number
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
For instance, for the complex numbers
c1 = 5 + 28*i_
print(f"c1 = {c1}")
c1 = 5 + 28i
c2 = 25 + 88*i_
print(f"c2 = {c2}")
c2 = 25 + 88i
c3 = c1 + c2
print(f"c3 = {c3}")
c3 = 30 + 116i
The subtraction is defined accordingly.
c1 - c2
- 20 - 60i
c1 * c2
- 2339 + 1140i
conj(c1)
5 - 28i
norm(c1)
28.442925
Im(c1)
28i
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 and can always be written in the form
where . In addition, the imaginary units , , and have the following properties
Note that every complex number is a quaternion, but not every quaternion is a complex number ( ).
For instance, the quaternion
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
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
h1 + h2
3 + 28i + 86j + 124k
h1 - h2
7 + 28i + 86j + 74k
h1 * h2
- 2485 + 2094i - 872j - 73k
Notice that the multiplication between quaternions is, in general, NOT COMMUTATIVE.
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 .
Real part¶
Re(h1)
5
If the real part of a quaternion is zero, it is called a pure quaternion, and belongs to the set .
Imaginary part¶
Im(h1)
28i + 86j + 99k
conj(h1)
5 - 28i - 86j - 99k
Norm
norm(h1)
134.186437
vec3(h1)
array([28., 86., 99.])
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(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,
and has the following property
Unit quaternions and the rotation of rigid bodies¶
Unit quaternions compose the set , which represent rotations of the reference frame of rigid bodies in three-dimensional space. A unit quaternion can always be written in the form
where is the rotation angle around the rotation axis (Remember that are pure quaternions, that is, quaternions for which the real part is zero).
Unit-norm quaternions, as the name says, have unit norm
For instance, to represent the rotation of rad about the x-axis, the following quaternion can be used
r1 = cos(pi/4) + i_*sin(pi/4)
We can check that 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
and
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 = DQ([1])
Plotting quaternions¶
Using DQ Robotics, the rotation quaternions can be plotted on screen. See, for example
Note: for the unit quaternion , 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

Sequential rotations¶
Sequential rotations are obtained by post-multiplication. For example, the transformation between the neutral reference frame by followed by is
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()

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()

Reverse rotation¶
The reverse rotation can be obtained using the conjugate operation, because unit quaternions have unit norm. Hence,
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.
- Create a MATLAB script called [quaternion_basics_homework.m]. On it, do the following tasks.
- Store, in , the value of a rotation of about the x-axis.
- Store, in , the value of a rotation of about the y-axis.
- Store, in , the value of a rotation of about the z-axis.
- Calculate the result of the sequential rotation of the neutral reference-frame by , followed by , followed by , and store it in . Plot .
- Find the reverse rotation of and store it in .
- Rotate by about the x-axis and store it in . Is ? Plot and to confirm that they represent the same rotation.
Bonus Homework¶
- What is the general form of the quaternion multiplication? Multiply and on pen and paper and find .
- What is the general form of the quaternion norm? Simplify on pen and paper.
- Show that every unit quaternion, written as , has unit norm. Do that on pen and paper.