22
22
from spatialmath .pose3d import SO3 , SE3
23
23
from spatialmath .smuserlist import SMUserList
24
24
25
+ _eps = np .finfo (np .float64 ).eps
26
+
25
27
class Quaternion (SMUserList ):
26
28
r"""
27
29
Quaternion class
@@ -106,7 +108,7 @@ def Pure(cls, v):
106
108
.. runblock:: pycon
107
109
108
110
>>> from spatialmath import Quaternion
109
- >>> print(Quaternion.pure ([1,2,3]))
111
+ >>> print(Quaternion.Pure ([1,2,3]))
110
112
"""
111
113
return cls (s = 0 , v = base .getvector (v , 3 ))
112
114
@@ -267,7 +269,7 @@ def conj(self):
267
269
.. runblock:: pycon
268
270
269
271
>>> from spatialmath import Quaternion
270
- >>> print(Quaternion.pure ([1,2,3]).conj())
272
+ >>> print(Quaternion.Pure ([1,2,3]).conj())
271
273
272
274
:seealso: :func:`~spatialmath.base.quaternions.conj`
273
275
"""
@@ -335,19 +337,26 @@ def log(self):
335
337
336
338
.. math::
337
339
338
- \ln \| q \| \langle \frac{\mathb{v}}{\| \mathbf{v} \|} \acos \frac{s}{\| q \|} \rangle
340
+ \ln \| q \|, \langle \frac{\vec{v}}{\| \vec{v} \|} \cos^{-1} \frac{s}{\| q \|} \rangle
341
+
342
+ For a ``UnitQuaternion`` the logarithm is a pure quaternion whose vector
343
+ part :math:`\vec{v}` and :math:`\vec{v}/2` is a Euler vector: parallel
344
+ to the axis of rotation and whose norm is the magnitude of rotation.
339
345
340
346
Example:
341
347
342
348
.. runblock:: pycon
343
349
344
- >>> from spatialmath import Quaternion
350
+ >>> from spatialmath import Quaternion, UnitQuaternion
351
+ >>> from math import pi
345
352
>>> q = Quaternion([1, 2, 3, 4])
346
353
>>> print(q.log())
354
+ >>> q = UnitQuaternion.Rx(pi / 2)
355
+ >>> print(q.log())
347
356
348
357
:reference: `Wikipedia <https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions>`_
349
358
350
- :seealso: :func:`~spatialmath.quaternion.UnitQuaternion.log `, `~spatialmath.quaternion.Quaternion.exp`
359
+ :seealso: :func:`~spatialmath.quaternion.Quaternion.exp `, :func: `~spatialmath.quaternion.Quaternion.log`, :func:`~spatialmath.quaternion.UnitQuaternion.angvec`,
351
360
"""
352
361
norm = self .norm ()
353
362
s = math .log (norm )
@@ -364,24 +373,38 @@ def exp(self):
364
373
365
374
.. math::
366
375
367
- e^s \cos \| v \| \langle e^s \frac{\mathb{v}}{\| \mathbf{v} \|} \sin \| \mathbf{v} \| \rangle
376
+ e^s \cos \| v \|, \langle e^s \frac{\vec{v}}{\| \vec{v} \|} \sin \| \vec{v} \| \rangle
377
+
378
+ For a pure quaternion with vector value :math:`\vec{v}` the the result
379
+ is a unit quaternion equivalent to a rotation defined by
380
+ :math:`2\vec{v}` intepretted as an Euler vector, that is, parallel to
381
+ the axis of rotation and whose norm is the magnitude of rotation.
368
382
369
383
Example:
370
384
371
385
.. runblock:: pycon
372
386
373
387
>>> from spatialmath import Quaternion
388
+ >>> from math import pi
374
389
>>> q = Quaternion([1, 2, 3, 4])
375
390
>>> print(q.exp())
391
+ >>> q = Quaternion.Pure([pi / 4, 0, 0])
392
+ >>> print(q.exp()) # result is a UnitQuaternion
393
+ >>> print(q.exp().angvec())
376
394
377
395
:reference: `Wikipedia <https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions>`_
378
396
379
- :seealso: :func:`~spatialmath.quaternion.UnitQuaternion .log`, `~spatialmath.quaternion.Quaternion .log`
397
+ :seealso: :func:`~spatialmath.quaternion.Quaternion .log`, :func: `~spatialmath.quaternion.UnitQuaternion .log`, :func:`~spatialmath.quaternion.UnitQuaternion.AngVec`, :func:`~spatialmath.quaternion.UnitQuaternion.EulerVec `
380
398
"""
381
- es = math .exp (self .s )
382
- s = es * math .cos (base .norm (self .v ))
383
- v = es * base .unitvec (self .v ) * math .sin (base .norm (self .v ))
384
- return Quaternion (s = s , v = v )
399
+ exp_s = math .exp (self .s )
400
+ norm_v = base .norm (self .v )
401
+ s = exp_s * math .cos (norm_v )
402
+ v = exp_s * self .v / norm_v * math .sin (norm_v )
403
+ if abs (self .s ) < 100 * _eps :
404
+ # result will be a unit quaternion
405
+ return UnitQuaternion (s = s , v = v )
406
+ else :
407
+ return Quaternion (s = s , v = v )
385
408
386
409
387
410
def inner (self , other ):
@@ -936,7 +959,7 @@ def __init__(self, s: Any = None, v=None, norm=True, check=True):
936
959
937
960
.. runblock:: pycon
938
961
939
- >>> from spatialmath import UnitQuaternion as UQ as UQ
962
+ >>> from spatialmath import UnitQuaternion as UQ
940
963
>>> q = UQ()
941
964
>>> q # repr()
942
965
>>> print(q) # str()
@@ -1315,7 +1338,7 @@ def AngVec(cls, theta, v, *, unit='rad'):
1315
1338
.. note:: :math:`\theta = 0` the result in an identity quaternion, otherwise
1316
1339
``V`` must have a finite length, ie. :math:`|V| > 0`.
1317
1340
1318
- :seealso: :func:`~spatialmath.UnitQuaternion.angvec`, :func:`~spatialmath.base.transforms3d.angvec2r`
1341
+ :seealso: :func:`~spatialmath.UnitQuaternion.angvec`, :func:`~spatialmath.quaternion.UnitQuaternion.exp`, :func:`~spatialmath. base.transforms3d.angvec2r`
1319
1342
"""
1320
1343
v = base .getvector (v , 3 )
1321
1344
base .isscalar (theta )
@@ -1932,35 +1955,35 @@ def angvec(self, unit='rad'):
1932
1955
>>> UQ.Rz(0.3).angvec()
1933
1956
(0.3, array([0., 0., 1.]))
1934
1957
1935
- :seealso: :func:`~spatialmath.quaternion.AngVec`, :func:`~angvec2r`
1958
+ :seealso: :func:`~spatialmath.quaternion.AngVec`, :func:`~spatialmath.quaternion.UnitQuaternion.log`, :func:`~ angvec2r`
1936
1959
"""
1937
1960
return base .tr2angvec (self .R , unit = unit )
1938
1961
1939
- def log (self ):
1940
- r"""
1941
- Logarithm of unit quaternion
1962
+ # def log(self):
1963
+ # r"""
1964
+ # Logarithm of unit quaternion
1942
1965
1943
- :rtype: Quaternion instance
1966
+ # :rtype: Quaternion instance
1944
1967
1945
- ``q.log()`` is the logarithm of the unit quaternion ``q``, ie.
1968
+ # ``q.log()`` is the logarithm of the unit quaternion ``q``, ie.
1946
1969
1947
- .. math::
1970
+ # .. math::
1948
1971
1949
- 0 \langle \frac{\mathb{v}}{\| \mathbf{v} \|} \acos s \rangle
1972
+ # 0 \langle \frac{\mathb{v}}{\| \mathbf{v} \|} \acos s \rangle
1950
1973
1951
- Example:
1974
+ # Example:
1952
1975
1953
- .. runblock:: pycon
1976
+ # .. runblock:: pycon
1954
1977
A6E0
code>
1955
- >>> from spatialmath import UnitQuaternion
1956
- >>> q = UnitQuaternion.Rx(0.3)
1957
- >>> print(q.log())
1978
+ # >>> from spatialmath import UnitQuaternion
1979
+ # >>> q = UnitQuaternion.Rx(0.3)
1980
+ # >>> print(q.log())
1958
1981
1959
- :reference: `Wikipedia <https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions>`_
1982
+ # :reference: `Wikipedia <https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions>`_
1960
1983
1961
- :seealso: :func:`~spatialmath.quaternion.Quaternion.log`, `~spatialmath.quaternion.Quaternion.exp`
1962
- """
1963
- return Quaternion (s = 0 , v = math .acos (self .s ) * base .unitvec (self .v ))
1984
+ # :seealso: :func:`~spatialmath.quaternion.Quaternion.log`, `~spatialmath.quaternion.Quaternion.exp`
1985
+ # """
1986
+ # return Quaternion(s=0, v=math.acos(self.s) * base.unitvec(self.v))
1964
1987
1965
1988
def angle (self , other ):
1966
1989
"""
0 commit comments