7
7
class DualQuaternion :
8
8
r"""
9
9
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`.
11
11
12
12
A dual quaternion can be considered as either:
13
13
@@ -20,6 +20,11 @@ class DualQuaternion:
20
20
21
21
- http://web.cs.iastate.edu/~cs577/handouts/dual-quaternion.pdf
22
22
- 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`
23
28
"""
24
29
25
30
def __init__ (self , real = None , dual = None ):
@@ -42,6 +47,9 @@ def __init__(self, real=None, dual=None):
42
47
>>> d = DualQuaternion([1, 2, 3, 4, 5, 6, 7, 8])
43
48
>>> print(d)
44
49
50
+ The dual number is stored internally as two quaternion, respectively
51
+ called ``real`` and ``dual``.
52
+
45
53
"""
46
54
47
55
if real is None and dual is None :
@@ -61,6 +69,11 @@ def __init__(self, real=None, dual=None):
61
69
else :
62
70
raise ValueError ('expecting zero or two parameters' )
63
71
72
+ @classmethod
73
+ def Pure (cls , x ):
74
+ x = base .getvector (x , 3 )
75
+ return cls (UnitQuaternion (), Quaternion .Pure (x ))
76
+
64
77
def __repr__ (self ):
65
78
return str (self )
66
79
@@ -88,6 +101,9 @@ def norm(self):
88
101
:return: Norm as a dual number
89
102
:rtype: 2-tuple
90
103
104
+ The norm of a ``UnitDualQuaternion`` is unity, represented by the dual
105
+ number (1,0).
106
+
91
107
Example:
92
108
93
109
.. runblock:: pycon
@@ -157,10 +173,13 @@ def __sub__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
157
173
158
174
def __mul__ (left , right ): # lgtm[py/not-named-self] pylint: disable=no-self-argument
159
175
"""
160
- Product of two dual quaternions
176
+ Product of dual quaternion
161
177
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``.
164
183
165
184
Example:
166
185
@@ -170,13 +189,18 @@ def __mul__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
170
189
>>> d = DualQuaternion(Quaternion([1,2,3,4]), Quaternion([5,6,7,8]))
171
190
>>> d * d
172
191
"""
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
180
204
181
205
def matrix (self ):
182
206
"""
@@ -226,9 +250,20 @@ def vec(self):
226
250
# pass
227
251
228
252
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
+ """
229
264
230
265
def __init__ (self , real = None , dual = None ):
231
- """
266
+ r """
232
267
Create new unit dual quaternion
233
268
234
269
:param real: real quaternion or SE(3) matrix
@@ -245,13 +280,27 @@ def __init__(self, real=None, dual=None):
245
280
246
281
.. runblock:: pycon
247
282
248
- >>> from spatialmath import DualQuaternion
283
+ >>> from spatialmath import UnitDualQuaternion, SE3
249
284
>>> T = SE3.Rand()
250
285
>>> print(T)
251
- >>> d = UnitDualQuaternion(T))
286
+ >>> d = UnitDualQuaternion(T)
252
287
>>> print(d)
253
288
>>> type(d)
254
289
>>> 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`.
255
304
"""
256
305
if real is None and dual is None :
257
306
self .real = None
@@ -268,7 +317,7 @@ def __init__(self, real=None, dual=None):
268
317
self .real = S
269
318
self .dual = 0.5 * D * S
270
319
271
- def T (self ):
320
+ def SE3 (self ):
272
321
"""
273
322
Convert unit dual quaternion to SE(3) matrix
274
323
@@ -279,10 +328,10 @@ def T(self):
279
328
280
329
.. runblock:: pycon
281
330
282
- >>> from spatialmath import DualQuaternion
331
+ >>> from spatialmath import DualQuaternion, SE3
283
332
>>> T = SE3.Rand()
284
333
>>> print(T)
285
- >>> d = UnitDualQuaternion(T))
334
+ >>> d = UnitDualQuaternion(T)
286
335
>>> print(d)
287
336
>>> print(d.T)
288
337
"""
0 commit comments