@@ -75,30 +75,37 @@ def check_dual_quaternion(dq, unit=True):
7575 return dq
7676
7777
78- def dual_quaternion_requires_renormalization (dq , tolerance = 1e-6 ):
79- r """Check if dual quaternion requires renormalization .
78+ def dual_quaternion_squared_norm (dq ):
79+ """Compute squared norm of dual quaternion .
8080
81- Dual quaternions that represent transformations in 3D should have unit
82- norm. Since the real and the dual quaternion are orthogonal, their dot
83- product should be 0. In addition, :math:`\epsilon^2 = 0`. Hence,
81+ Parameters
82+ ----------
83+ dq : array-like, shape (8)
84+ Dual quaternion to represent transform:
85+ (pw, px, py, pz, qw, qx, qy, qz)
8486
85- .. math::
87+ Returns
88+ -------
89+ squared_norm : array, shape (2,)
90+ Squared norm of dual quaternion, which is a dual number with a real and
91+ a dual part.
92+ """
93+ dq = np .asarray (dq )
94+ prod = concatenate_dual_quaternions (
95+ dq , dq_q_conj (dq , unit = False ), unit = False
96+ )
97+ return prod [[0 , 4 ]]
8698
87- ||\boldsymbol{p} + \epsilon \boldsymbol{q}||
88- =
89- \sqrt{(\boldsymbol{p} + \epsilon \boldsymbol{q}) \cdot
90- (\boldsymbol{p} + \epsilon \boldsymbol{q})}
91- =
92- \sqrt{\boldsymbol{p}\cdot\boldsymbol{p}
93- + 2\epsilon \boldsymbol{p}\cdot\boldsymbol{q}
94- + \epsilon^2 \boldsymbol{q}\cdot\boldsymbol{q}}
95- =
96- \sqrt{\boldsymbol{p}\cdot\boldsymbol{p}},
9799
98- i.e., the norm only depends on the real quaternion.
100+ def dual_quaternion_requires_renormalization (dq , tolerance = 1e-6 ):
101+ r"""Check if dual quaternion requires renormalization.
102+
103+ Dual quaternions that represent transformations in 3D should have unit
104+ norm (:math:`1 + 0 \epsilon`), that is the real quaternion must have unit
105+ norm and the real and the dual quaternion must be orthogonal (their dot
106+ product should be 0).
99107
100- This function checks unit norm and orthogonality of the real and dual
101- part.
108+ This function checks unit norm and orthogonality of the real and dual part.
102109
103110 Parameters
104111 ----------
@@ -127,11 +134,11 @@ def dual_quaternion_requires_renormalization(dq, tolerance=1e-6):
127134 assert_unit_dual_quaternion
128135 Checks unit norm and orthogonality of real and dual quaternion.
129136 """
130- real = dq [: 4 ]
131- dual = dq [ 4 :]
132- real_norm = np . linalg . norm ( real )
133- real_dual_dot = np . dot ( real , dual )
134- return abs ( real_norm - 1.0 ) > tolerance or abs ( real_dual_dot ) > tolerance
137+ squared_norm = dual_quaternion_squared_norm ( dq )
138+ return (
139+ abs ( squared_norm [ 0 ] - 1.0 ) > tolerance
140+ or abs ( squared_norm [ 1 ]) > tolerance
141+ )
135142
136143
137144def norm_dual_quaternion (dq ):
@@ -169,30 +176,12 @@ def norm_dual_quaternion(dq):
169176 .. [1] enki (2023). Properly normalizing a dual quaternion.
170177 https://stackoverflow.com/a/76313524
171178 """
172- dq = check_dual_quaternion (dq , unit = False )
173- dq_prod = concatenate_dual_quaternions (dq , dq_q_conj (dq ), unit = False )
174-
175- prod_real = dq_prod [:4 ]
176- prod_dual = dq_prod [4 :]
177-
178- real = np .copy (dq [:4 ])
179+ # 1. ensure unit norm of real quaternion
180+ dq = check_dual_quaternion (dq , unit = True )
181+ # 2. ensure orthogonality of real and dual quaternion
182+ real = dq [:4 ]
179183 dual = dq [4 :]
180-
181- prod_real_norm = np .linalg .norm (prod_real )
182- if prod_real_norm == 0.0 :
183- real = np .array ([1.0 , 0.0 , 0.0 , 0.0 ])
184- prod_real_norm = 1.0
185- valid_dq = np .hstack ((real , dual ))
186- prod_dual = concatenate_dual_quaternions (
187- valid_dq , dq_q_conj (valid_dq ), unit = False
188- )[4 :]
189-
190- real_inv_sqrt = 1.0 / prod_real_norm
191- dual_inv_sqrt = - 0.5 * prod_dual * real_inv_sqrt ** 3
192-
193- real = real_inv_sqrt * real
194- dual = real_inv_sqrt * dual + concatenate_quaternions (dual_inv_sqrt , real )
195-
184+ dual = dual - np .dot (real , dual ) * real
196185 return np .hstack ((real , dual ))
197186
198187
@@ -229,14 +218,9 @@ def assert_unit_dual_quaternion(dq, *args, **kwargs):
229218 Normalization that enforces unit norm and orthogonality of the real and
230219 dual quaternion.
231220 """
232- real = dq [:4 ]
233- dual = dq [4 :]
234-
235- real_norm = np .linalg .norm (real )
236- assert_array_almost_equal (real_norm , 1.0 , * args , ** kwargs )
237-
238- real_dual_dot = np .dot (real , dual )
239- assert_array_almost_equal (real_dual_dot , 0.0 , * args , ** kwargs )
221+ real_sq_norm , dual_sq_norm = dual_quaternion_squared_norm (dq )
222+ assert_array_almost_equal (real_sq_norm , 1.0 , * args , ** kwargs )
223+ assert_array_almost_equal (dual_sq_norm , 0.0 , * args , ** kwargs )
240224
241225 # The two previous checks are consequences of the unit norm requirement.
242226 # The norm of a dual quaternion is defined as the product of a dual
@@ -307,7 +291,7 @@ def dual_quaternion_double(dq):
307291 return - check_dual_quaternion (dq , unit = True )
308292
309293
310- def dq_conj (dq ):
294+ def dq_conj (dq , unit = True ):
311295 """Conjugate of dual quaternion.
312296
313297 There are three different conjugates for dual quaternions. The one that we
@@ -321,6 +305,12 @@ def dq_conj(dq):
321305 Unit dual quaternion to represent transform:
322306 (pw, px, py, pz, qw, qx, qy, qz)
323307
308+ unit : bool, optional (default: True)
309+ Normalize the dual quaternion so that it is a unit dual quaternion.
310+ A unit dual quaternion has the properties
311+ :math:`p_w^2 + p_x^2 + p_y^2 + p_z^2 = 1` and
312+ :math:`p_w q_w + p_x q_x + p_y q_y + p_z q_z = 0`.
313+
324314 Returns
325315 -------
326316 dq_conjugate : array, shape (8,)
@@ -331,11 +321,11 @@ def dq_conj(dq):
331321 dq_q_conj
332322 Quaternion conjugate of dual quaternion.
333323 """
334- dq = check_dual_quaternion (dq )
324+ dq = check_dual_quaternion (dq , unit = unit )
335325 return np .r_ [dq [0 ], - dq [1 :5 ], dq [5 :]]
336326
337327
338- def dq_q_conj (dq ):
328+ def dq_q_conj (dq , unit = True ):
339329 """Quaternion conjugate of dual quaternion.
340330
341331 For unit dual quaternions that represent transformations, this function
@@ -352,6 +342,12 @@ def dq_q_conj(dq):
352342 Unit dual quaternion to represent transform:
353343 (pw, px, py, pz, qw, qx, qy, qz)
354344
345+ unit : bool, optional (default: True)
346+ Normalize the dual quaternion so that it is a unit dual quaternion.
347+ A unit dual quaternion has the properties
348+ :math:`p_w^2 + p_x^2 + p_y^2 + p_z^2 = 1` and
349+ :math:`p_w q_w + p_x q_x + p_y q_y + p_z q_z = 0`.
350+
355351 Returns
356352 -------
357353 dq_q_conjugate : array, shape (8,)
@@ -362,7 +358,7 @@ def dq_q_conj(dq):
362358 dq_conj
363359 Conjugate of a dual quaternion.
364360 """
365- dq = check_dual_quaternion (dq )
361+ dq = check_dual_quaternion (dq , unit = unit )
366362 return np .r_ [dq [0 ], - dq [1 :4 ], dq [4 ], - dq [5 :]]
367363
368364
0 commit comments