10000 polish doco · suddrey-qut/spatialmath-python@5f19097 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5f19097

Browse files
committed
polish doco
add ability to multiply udq by 3D point
1 parent 9e114d6 commit 5f19097

File tree

1 file changed

+66
-17
lines changed

1 file changed

+66
-17
lines changed

spatialmath/DualQuaternion.py

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class DualQuaternion:
88
r"""
99
A dual number is an ordered pair :math:`\hat{a} = (a, b)` or written as
10-
:math:`a + \epsilon b` where :math:`\epsilon^2 = 0`.
10+
:math:`a + \epsilon b` where :math:`\epsilon^2 = 0`.
1111
1212
A dual quaternion can be considered as either:
1313
@@ -20,6 +20,11 @@ class DualQuaternion:
2020
2121
- http://web.cs.iastate.edu/~cs577/handouts/dual-quaternion.pdf
2222
- https://en.wikipedia.org/wiki/Dual_quaternion
23+
24+
.. warning:: Unlike the other spatial math classes, this class does not
25+
(yet) support multiple values per object.
26+
27+
:seealso: :func:`UnitDualQuaternion`
2328
"""
2429

2530
def __init__(self, real=None, dual=None):
@@ -42,6 +47,9 @@ def __init__(self, real=None, dual=None):
4247
>>> d = DualQuaternion([1, 2, 3, 4, 5, 6, 7, 8])
4348
>>> print(d)
4449
50+
The dual number is stored internally as two quaternion, respectively
51+
called ``real`` and ``dual``.
52+
4553
"""
4654

4755
if real is None and dual is None:
@@ -61,6 +69,11 @@ def __init__(self, real=None, dual=None):
6169
else:
6270
raise ValueError('expecting zero or two parameters')
6371

72+
@classmethod
73+
def Pure(cls, x):
74+
x = base.getvector(x, 3)
75+
return cls(UnitQuaternion(), Quaternion.Pure(x))
76+
6477
def __repr__(self):
6578
return str(self)
6679

@@ -88,6 +101,9 @@ def norm(self):
88101
:return: Norm as a dual number
89102
:rtype: 2-tuple
90103
104+
The norm of a ``UnitDualQuaternion`` is unity, represented by the dual
105+
number (1,0).
106+
91107
Example:
92108
93109
.. runblock:: pycon
@@ -157,10 +173,13 @@ def __sub__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
157173

158174
def __mul__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-argument
159175
"""
160-
Product of two dual quaternions
176+
Product of dual quaternion
161177
162-
:return: Product
163-
:rtype: DualQuaternion
178+
- ``dq1 * dq2`` is a dual quaternion representing the product of
179+
``dq1`` and ``dq2``. If both are unit dual quaternions, the product
180+
will be a unit dual quaternion.
181+
- ``dq * p`` transforms the point ``p`` (3) by the unit dual quaternion
182+
``dq``.
164183
165184
Example:
166185
@@ -170,13 +189,18 @@ def __mul__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
170189
>>> d = DualQuaternion(Quaternion([1,2,3,4]), Quaternion([5,6,7,8]))
171190
>>> d * d
172191
"""
173-
real = left.real * right.real
174-
dual = left.real * right.dual + left.dual * right.real
175-
176-
if isinstance(left, UnitDualQuaternion) and isinstance(left, UnitDualQuaternion):
177-
return UnitDualQuaternion(real, dual)
178-
else:
179-
return DualQuaternion(real, dual)
192+
if isinstance(right, DualQuaternion):
193+
real = left.real * right.real
194+
dual = left.real * right.dual + left.dual * right.real
195+
196+
if isinstance(left, UnitDualQuaternion) and isinstance(left, UnitDualQuaternion):
197+
return UnitDualQuaternion(real, dual)
198+
else:
199+
return DualQuaternion(real, dual)
200+
elif isinstance(left, UnitDualQuaternion) and base.isvector(right, 3):
201+
v = base.getvector(right, 3)
202+
vp = left * DualQuaternion.Pure(v) * left.conj()
203+
return vp.dual.v
180204

181205
def matrix(self):
182206
"""
@@ -226,9 +250,20 @@ def vec(self):
226250
# pass
227251

228252
class UnitDualQuaternion(DualQuaternion):
253+
"""[summary]
254+
255+
:param DualQuaternion: [description]
256+
:type DualQuaternion: [type]
257+
258+
259+
.. warning:: Unlike the other spatial math classes, this class does not
260+
(yet) support multiple values per object.
261+
262+
:seealso: :func:`UnitDualQuaternion`
263+
"""
229264

230265
def __init__(self, real=None, dual=None):
231-
"""
266+
r"""
232267
Create new unit dual quaternion
233268
234269
:param real: real quaternion or SE(3) matrix
@@ -245,13 +280,27 @@ def __init__(self, real=None, dual=None):
245280
246281
.. runblock:: pycon
247282
248-
>>> from spatialmath import DualQuaternion
283+
>>> from spatialmath import UnitDualQuaternion, SE3
249284
>>> T = SE3.Rand()
250285
>>> print(T)
251-
>>> d = UnitDualQuaternion(T))
286+
>>> d = UnitDualQuaternion(T)
252287
>>> print(d)
253288
>>> type(d)
254289
>>> print(d.norm()) # norm is (1, 0)
290+
291+
The dual number is stored internally as two quaternion, respectively
292+
called ``real`` and ``dual``. For a unit dual quaternion they are
293+
respectively:
294+
295+
.. math::
296+
297+
\q_r &\sim \mat{R}
298+
299+
q_d &= \frac{1}{2} q_t \q_r
300+
301+
where :math:`\mat{R}` is the rotational part of the rigid-body motion
302+
and :math:`q_t` is a pure quaternion formed from the translational part
303+
:math:`t`.
255304
"""
256305
if real is None and dual is None:
257306
self.real = None
@@ -268,7 +317,7 @@ def __init__(self, real=None, dual=None):
268317
self.real = S
269318
self.dual = 0.5 * D * S
270319

271-
def T(self):
320+
def SE3(self):
272321
"""
273322
Convert unit dual quaternion to SE(3) matrix
274323
@@ -279,10 +328,10 @@ def T(self):
279328
280329
.. runblock:: pycon
281330
282-
>>> from spatialmath import DualQuaternion
331+
>>> from spatialmath import DualQuaternion, SE3
283332
>>> T = SE3.Rand()
284333
>>> print(T)
285-
>>> d = UnitDualQuaternion(T))
334+
>>> d = UnitDualQuaternion(T)
286335
>>> print(d)
287336
>>> print(d.T)
288337
"""

0 commit comments

Comments
 (0)
0