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

DQ4 Dual Quaternion Basics using DQ Robotics - Part 2

I found an issue

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

Introduction

The last lesson introduced dual quaternions, some basic dual quaternion operations; and, most importantly, unit dual quaternions and their ability to represent rigid body motion.

As always, before we start, make essential installations and imports.

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

Notation

In the last lesson, we learned that unit dual quaternions represent reference frames and pose transformations. In this lesson, we will see that dual quaternions can also represent points, lines, and planes.

Keep these in mind (we will also use this notation when writting papers to conferences and journals):

  • hHh\in \mathbb{H} : a quaternion. (Bold-face, lowercase character)
  • hH\underline{h} \in \mathcal{H} : a dual quaternion. (Bold-face, underlined, lowercase character)
  • p,t,Hpp,t,\cdots \in {\mathbb{H}}_p : pure quaternions. They represent points, positions, and translations. They are quaternions for which Re(h)=0\textrm{Re}\left(h\right)=0 .
  • rS3r\in {\mathbb{S}}^3 : unit quaternions. They represent orientations and rotations. They are quaternions for which h=1||h||=1 .
  • x\underline{x} \in S\underline{\mathcal{S}} : unit dual quaternions. They represent poses and pose transformations. They are dual quaternions for which h=1||h||=1 .

What we have seen so far

Points, positions, and translations

A pure quaternion, such as p,t\mathit{\mathbf{p}},\mathit{\mathbf{t}}\in Hp{\mathbb{H}}_p , can be used to represent points, positions, and translations. Pure quaternions can be always written in the form

t=xı  ^+yȷ  ^+zk^,\mathit{\mathbf{t}}=x\hat{\imath \;} +y\hat{\jmath \;} +z\hat{k} ,

where x,y,zx,y,z\in R\mathbb{R} represent the 3D coordinates.

Orientations and rotations

A unit quaternion, r\mathit{\mathbf{r}}\in S3{\mathbb{S}}^3 , can always be written in the form

r=cos(ϕ  2)+v  sin(ϕ  2),\mathit{\mathbf{r}}=\cos \left(\frac{\phi \;}{2}\right)+\mathit{\mathbf{v}}\;\sin \left(\frac{\phi \;}{2}\right),

where ϕ\phi \in R\mathbb{R} represents the rotation angle and v\mathit{\mathbf{v}}\in Hp{\mathbb{H}}_p represents the rotation axis.

Reference frames and pose transformations

A unit dual quaternion, x\underline{\mathit{\mathbf{x}}} \in S\underline{{\mathcal{S}}} , can always be written in the form

x=r+ε  12t  r,\underline{\mathit{\mathbf{x}}} =\mathit{\mathbf{r}}+\varepsilon \frac{\;1}{2}\mathit{\mathbf{t}}\;\mathit{\mathbf{r}},

where r\mathit{\mathbf{r}}\in S3{\mathbb{S}}^3 is the unit-norm quaternion representing the rotation; and tt Hp\in {\mathbb{H}}_p is the quaternion representing the translation.

Lines and planes

Unit dual quaternions, besides being used to represent rotations and translations, can also be used to represent lines and planes in 3D space. These geometric primitives are very useful in many robot control scenarios.

Plücker Lines

image_0.png

In high-school, you have been introduced to the line equation in 3D space using linear algebra. Using the concept of unit dual quaternions, we can also represent lines, in the form

l=l+εm,\underline{\mathit{\mathbf{l}}} =\mathit{\mathbf{l}}+\varepsilon \mathit{\mathbf{m}},

where l\mathit{\mathbf{l}}\in HpS3{\mathbb{H}}_p \cap {\mathbb{S}}^3 is a unit vector representing the line’s direction and m\mathit{\mathbf{m}}\in Hp{\mathbb{H}}_p is called the line’s moment. The moment is defined as m\mathit{\mathbf{m}} \triangleq pl×l{\mathit{\mathbf{p}}}_l \times \mathit{\mathbf{l}} , in which pl{\mathit{\mathbf{p}}}_l \in Hp{\mathbb{H}}_p is a point (any point) in the line. A line represented this way is called a Plücker line. Note that a Plücker line is a pure dual quaternion with unit norm, that is lHpS\underline{l} \in {\mathcal{H}}_p \cap \underline{S} .

Pure quaternion cross product

Note that ×  \times \; is the cross product between pure quaternions, and is defined as

h1×h2=  h1h2h2h12{\mathit{\mathbf{h}}}_1 \times {\mathit{\mathbf{h}}}_2 =\frac{\;{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_2 -{\mathit{\mathbf{h}}}_2 {\mathit{\mathbf{h}}}_1 }{2}

for any h1,h2{\mathit{\mathbf{h}}}_1 ,{\mathit{\mathbf{h}}}_2 \in Hp{\mathbb{H}}_p . Note that this operation is equivalent to the cross product between vectors in R3{\mathbb{R}}^3 .

Plücker line example

Suppose that we want the Plücker line that represents the x-axis. The direction of the line will be

l1=ı  ^{\mathit{\mathbf{l}}}_1 =\hat{\imath \;}
l1=i_
print(f"l1 = {l1}")
l1 = 1i

and a point in the line is

pl,1=0ı  ^+0ȷ  ^+0k^=0{\mathit{\mathbf{p}}}_{l,1} =0\hat{\imath \;} +0\hat{\jmath \;} +0\hat{k} =0 ,

p_l1 = DQ([0]) # We use the DQ() constructor so that Python knows its a DQ, not a real number.
print(f"p_l1 = {p_l1}")
p_l1 = 0

because the line crosses the origin. Hence, the moment of the line will be given by

m1=pl,1×l1{\mathit{\mathbf{m}}}_1 ={\mathit{\mathbf{p}}}_{l,1} \times {\mathit{\mathbf{l}}}_1
m1 = cross(p_l1,l1)
print(f"m1 = {m1}")
m1 = 0

Hence, the Plücker line representing the origin will be

l1=l1+εm1=ı  ^.{\underline{\mathit{\mathbf{l}}} }_1 ={\mathit{\mathbf{l}}}_1 +\varepsilon {\mathit{\mathbf{m}}}_1 =\hat{\imath \;} \ldotp
l1_dq = l1 + E_*m1
print(f"l1_dq = {l1_dq}")
l1_dq = 1i

Checking if a dual quaternion is a line using DQ Robotics

Let us check that the Plücker line we calculated is a pure dual quaternion with unit norm. The following two conditions have to hold simultaneously.

is_pure(l1_dq)
True
is_unit(l1_dq)
True

You can also test directly if those two conditions hold simultaneously with

is_line(l1_dq)
True

Plotting lines using DQ Robotics

When you plot lines using DQ Robotics you have to specify that you want to plot that unit dual quaternion as a line. For example

plt.figure()
ax = plt.axes(projection='3d')
plot(l1_dq,line=True,scale=5)
plt.show()
<Figure size 640x480 with 1 Axes>

Note that a Plücker line is, well, a line (not a line segment). This means that, mathematically, its length is infinite. When plotting, you have to choose how much of the line you want to show on screen. In this example, we are showing 5 meters only.

Planes

image_1.png

Also in high-school-level linear-algebra, you have been introduced to the equation of the plane. Unit dual quaternions can also be used to represent planes, in the form

π=nπ  +εdπ  ,\underline{\pi} ={\mathit{\mathbf{n}}}_{\pi \;} +\varepsilon d_{\pi \;} ,

where nπ  {\mathit{\mathbf{n}}}_{\pi \;} \in HpS3{\mathbb{H}}_p \cap {\mathbb{S}}^3 is the unit vector representing the plane’s normal. In addition, dπ  d_{\pi \;} \in R\mathbb{R} is the signed distance to the plane (with respect to the normal centered at the origin), that can be written as

dπ  =  <pπ  ,nπ  >d_{\pi \;} =\;<{\mathit{\mathbf{p}}}_{\pi \;} ,{\mathit{\mathbf{n}}}_{\pi \;} >

in which pπ    {\mathit{\mathbf{p}}}_{\pi \;\;} \in Hp{\mathbb{H}}_p is any point in the plane.

Pure quaternion inner product

Note that <,>  <,>\; is the inner product between pure quaternions, and is defined as

<h1,h2>=  h1h2+h2h12<{\mathit{\mathbf{h}}}_{1,} {\mathit{\mathbf{h}}}_2 >=-\frac{\;{\mathit{\mathbf{h}}}_1 {\mathit{\mathbf{h}}}_2 +{\mathit{\mathbf{h}}}_2 {\mathit{\mathbf{h}}}_1 }{2}

for any h1,h2{\mathit{\mathbf{h}}}_1 ,{\mathit{\mathbf{h}}}_2 \in Hp{\mathbb{H}}_p .

Plane example

Suppose that we want one of the unit dual quaternions that represents the x-y plane. One of the normals to the x-y plane (there are two), is given by

nπ,1=k^{\mathit{\mathbf{n}}}_{\pi ,1} =\hat{k}
n_pi1 = k_
print(f"n_pi1 = {n_pi1}")
n_pi1 = 1k

One of the points in the x-y plane is the origin, so we can use

pπ,1=0{\mathit{\mathbf{p}}}_{\pi ,1} =0
p_pi1 = DQ([0])  # We use the DQ() constructor so that Python knows its a DQ, not a real number.
print(f"p_pi1 = {p_pi1}")
p_pi1 = 0

Hence, the distance can be calculated as

dπ,1=  <pπ,1,nπ,1>  =0d_{\pi ,1} =\;<{\mathit{\mathbf{p}}}_{\pi ,1} ,{\mathit{\mathbf{n}}}_{\pi ,1} >\;=0
d_pi1 = dot(p_pi1, n_pi1)
print(f"d_pi1 = {d_pi1}")
d_pi1 = 0

Therefore, the x-y plane can be represented by the unit dual quaternion

π1=nπ,1+εdπ,1=k^.{\underline{\pi} }_1 ={\mathit{\mathbf{n}}}_{\pi ,1} +\varepsilon d_{\pi ,1} =\hat{k} \ldotp
pi1 = n_pi1 + E_*d_pi1
print(f"pi1 = {pi1}")
pi1 = 1k

Checking if a dual quaternion is a plane using DQ Robotics

We can check if a unit dual quaternion represents a plane if all the following conditions hold simultaneously.

is_unit(pi1)
True
is_pure(P(pi1))
True
is_real(D(pi1))
True

This can be done directly with one command

is_plane(pi1)
True

Plotting planes using DQ Robotics

When you plot planes using DQ Robotics, you have to specify that you want to plot that unit dual quaternion as a plane. For example

plt.figure()
ax = plt.axes(projection='3d')
plot(pi1,plane=True,scale=5)
plt.show()
<Figure size 640x480 with 1 Axes>

Note that a plane line is, mathematically, infinite. When plotting, you have specify its size, in this case 5, and a central location, in this case pπ,1{\mathit{\mathbf{p}}}_{\pi ,1} .

(Signed) distances

One important measure for controlling robotic systems is the measurement of distance. The distance is always a real number. In some contexts it might be signed. We will discuss some of those cases.

Point to point distance

The distance between two points is the most basic form of distance. Given p1,p2{\mathit{\mathbf{p}}}_1 ,{\mathit{\mathbf{p}}}_2 \in Hp{\mathbb{H}}_p , the distance between them is given by

dp1,p2=p1p2d_{p_1 ,p_2 } =\left|\right|{\mathit{\mathbf{p}}}_1 -{\mathit{\mathbf{p}}}_2 \left|\right|

For example, using DQ Robotics, this can be calculated as,

p1 = i_+j_
p2 = k_
d_p1_p2 = norm(p1-p2)
print(f"d_p1_p2 = {d_p1_p2}")
d_p1_p2 = 1.732051

Point to line distance

The distance between any point p\mathit{\mathbf{p}}\in Hp{\mathbb{H}}_p and a line l1HpS{\underline{l} }_1 \in {\mathcal{H}}_p \cap \underline{S} is

dp,l1=p×l1m1d_{p,l_1 } =\left|\right|\mathit{\mathbf{p}}\times {\mathit{\mathbf{l}}}_1 -{\mathit{\mathbf{m}}}_1 \left|\right|

For example, using DQ Robotics, this can be calculated as,

# Point coordinates
p = i_+j_
# Build the line
l1 = k_
m1 = cross(i_,k_)
l1_dq = l1 + E_*m1
# Calculate distance
d_p_l1 = norm(cross(p,l1)-m1)

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

The distance can also be calculated in using a dedicated function inside DQ_Geometry.

from dqrobotics.utils import DQ_Geometry

d_p_l1 = sqrt(DQ_Geometry.point_to_line_squared_distance(p,l1_dq))

print(f"d_p_l1 = {d_p_l1}")
d_p_l1 = 1.0

Note: when controlling robots, it is usually more convenient to use the squared norm instead of the norm.

Point to plane signed distance

The (signed) distance between a point p\mathit{\mathbf{p}}\in Hp{\mathbb{H}}_p and a plane π1{\underline{\pi} }_1 , with respect to the plane, is

dp,π  1=  <p,nπ  ,1>dπ  ,1.d_{p,\pi {\;}_1 } =\;<\mathit{\mathbf{p}},{\mathit{\mathbf{n}}}_{\pi \;,1} >-d_{\pi \;,1} \ldotp

Note that the sign of the distance indicates if the point is “above” the plane or “below” the plane.

For example, using DQ Robotics, this can be calculated as,

# Point coordinates
p = k_
# Build the plane
n_pi1 = k_
p_pi1 = DQ([0])
d_pi1 = dot(p_pi1,n_pi1)
pi1 = n_pi1 + E_*d_pi1
# Calculate distance
d_p_pi1 = dot(p,n_pi1)-d_pi1

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

This distance can also be calculated with

from dqrobotics.utils import DQ_Geometry

d_p_pi1 = DQ_Geometry.point_to_plane_distance(p,pi1)

print(f"d_p_pi1 = {d_p_pi1}")
d_p_pi1 = 1.0

Line to line distance

Calculating the distance between two lines, l1,l2HpS{\underline{l} }_1 ,{\underline{l} }_2 \in {\mathcal{H}}_p \cap \underline{S} is somewhat more complicated than the ones mentioned above so we will skip the details for now. You can check the following paper for details:

\underline{ Dynamic Active Constraints for Surgical Robots using Vector Field Inequalities. }Marinho, M. M; Adorno, B. V; Harada, K.; and Mitsuishi, M. IEEE Transactions on Robotics (T-RO), 35(5): 1166–1185. October 2019.

For now, it suffices to know that the distance between two lines can be obtained, using DQ Robotics, as follows

from dqrobotics.utils import DQ_Geometry

# Build l1_dq
l1 = k_
p_l1 = DQ([0])
m1 = cross(p_l1,k_)
l1_dq = l1 + E_*m1
# Build l2_dq
l2 = k_
p_l2 = i_
m2 = cross(p_l2,k_)
l2_dq = l2 + E_*m2
# Get the distance
d_l1_l2 = sqrt(DQ_Geometry.line_to_line_squared_distance(l1_dq,l2_dq))

print(f"d_l1_l2 = {d_l1_l2}")
d_l1_l2 = 1.0

Homework

## Homework example
# Essential imports
from dqrobotics import *
from dqrobotics.utils import DQ_Geometry
from math import pi, sin, cos 

## Solutions
# Question 1

# Question 2

# Question 3

Following the template above to create a script called [dual_quaternion_basics_part2_homework.py], do the following.

  1. Find the Plücker line representing the y-axis using four different pl{\mathit{\mathbf{p}}}_l and store them in l1,l2,l3,and  l4{\underline{\mathit{\mathbf{l}}} }_1 ,{\underline{\mathit{\mathbf{l}}} }_2 ,{\underline{\mathit{\mathbf{l}}} }_3 ,\textrm{and}\;{\underline{\mathit{\mathbf{l}}} }_4 . Are all Plücker lines the same?
  2. Define the point p1=2ı  ^+ȷ  ^+k^{\mathit{\mathbf{p}}}_1 =2\hat{\imath \;} +\hat{\jmath \;} +\hat{k} and calculate its distance to l1{\underline{\mathit{\mathbf{l}}} }_1 .
  3. Find the y-z plane using the two possible plane normals, nπ{\mathit{\mathbf{n}}}_{\pi } , and store them in π1{\underline{\pi} }_1 and π2{\underline{\pi} }_2 .
  4. Calculate the distance between p1{\mathit{\mathbf{p}}}_1 and π1{\underline{\pi} }_1 , and between p1{\mathit{\mathbf{p}}}_1 and π2{\underline{\pi} }_2 . Are the signs different? Why?
  5. Build a 5mx5mx5m cubic region centered in the origin delineated by 6 planes. Call them π3{\underline{\pi} }_3 , π4{\underline{\pi} }_4 , π5{\underline{\pi} }_5 , π6{\underline{\pi} }_6 , π7{\underline{\pi} }_7 , π8{\underline{\pi} }_8 . Make sure that all normals point towards the origin. Plot this cubic region.
  6. Among π3{\underline{\pi} }_3 , π4{\underline{\pi} }_4 , π5{\underline{\pi} }_5 , π6{\underline{\pi} }_6 , π7{\underline{\pi} }_7 , π8{\underline{\pi} }_8 , which one is the plane closest to p1{\mathit{\mathbf{p}}}_1 ? What is the distance between p1{\mathit{\mathbf{p}}}_1 and the closest plane?