2 from dynamic_graph.sot.tools.se3
import SO3
3 from numpy
import linalg
11 A quaternion has a scalar part and a vector part.
12 In this class the quaternion is represented as an array of 4 elements :
13 - the first element is the scalar part
14 - the next 3 elements represents the vector part
16 One can acces to the array directly with the attribute "array"
17 e.g. q1=Quaternion(1,0,0,0) --> q1.array
19 A quaternion can be instanciated with 1, 2 or 4 elements
20 (see : __init__() for more information).
22 It can also return a rotation vector, a rotation matrix, or a SO3
23 (see the methods : to...() for more information).
28 Instanciation of the quaternion with 1, 2 or 4 arguments :
29 -----------------------------------------------------------
30 This creates a 4-sized array (self.array) representing the quaternion
31 with the first element representing the scalar part
32 and the 3 others the vector part.
36 - the first one is used as the scalar part,
37 the other three as the vector part.
41 - the 1-sized argument is used as the scalar part,
42 the 3-sized argument is used as the vector part.
46 - if it is a quaternion it will create a copy of this quaternion.
47 - if it is a scalar, the scalar will be used as the scalar part
48 and the vector part will be set at (0,0,0).
49 - if it is an array, matrix, tuple or list of 4 elements,
50 the first element is used as the scalar part
51 and the rest as the vector part.
52 - if it is an array, matrix, tuple or list of 3 elements,
53 the 3 elements are interpreted as a rotation vector,
54 this creates a quaternion representing the same rotation.
55 - if it is a to 2 dimension array convertible array, matrix, tuple
56 or list with at least (3*3) elements,
57 the upper left (3*3) elements are interpreted as a rotation matrix,
58 this creates a quaternion representing the same rotation.
62 If no argument is given, than the quaternion will be set by default
63 to with the scalar part set to 1 and the vector part to (0,0,0).
64 (this is the neutral element for multiplication in quaternion space)
66 To create a quaternion from Roll, Pitch, Yaw angles :
67 -----------------------------------------------------
68 first instanciate a quaternion and than use the method fromRPY()
69 to change the values of it to the dezired ones.
70 e.g. : quat().fromRPY(R,P,Y)
75 self.
array = np.array([1.0, 0.0, 0.0, 0.0])
77 if np.array(args).size == 4:
78 self.
array = np.double(np.array(args))
82 if type(args[0]) == Quaternion:
83 self.
array = args[0].array.copy()
84 elif np.array(args[0]).size == 1:
87 self.
array = np.double(
88 np.hstack([np.array(args[0]), np.array([0, 0, 0])])
90 elif np.array(args[0]).size == 4
and max(np.array(args[0]).shape) == 4:
92 self.
array = np.double(np.array(args[0])).reshape(
95 elif np.array(args[0]).size == 3
and max(np.array(args[0]).shape) == 3:
98 rV = np.double(np.array(args[0])).reshape(
101 alpha = np.double(linalg.norm(rV))
106 self.
array = np.hstack([np.cos(alpha / 2.0), np.sin(alpha / 2.0) * e])
108 len(np.array(args[0]).shape) == 2
109 and np.array(args[0]).shape[0] >= 3
110 and np.array(args[0]).shape[1] >= 3
114 rM = np.double(np.array(args[0])[:3, :3])
116 selec[0] = 1 + rM[0, 0] + rM[1, 1] + rM[2, 2]
117 selec[1] = 1 + rM[0, 0] - rM[1, 1] - rM[2, 2]
118 selec[2] = 1 - rM[0, 0] + rM[1, 1] - rM[2, 2]
119 selec[3] = 1 - rM[0, 0] - rM[1, 1] + rM[2, 2]
120 param = selec.argmax()
124 q[0] = np.sqrt(selec[param])
125 q[1] = (rM[2, 1] - rM[1, 2]) / q[0]
126 q[2] = (rM[0, 2] - rM[2, 0]) / q[0]
127 q[3] = (rM[1, 0] - rM[0, 1]) / q[0]
131 q[1] = np.sqrt(selec[param])
132 q[0] = (rM[2, 1] - rM[1, 2]) / q[1]
133 q[2] = (rM[1, 0] + rM[0, 1]) / q[1]
134 q[3] = (rM[0, 2] + rM[2, 0]) / q[1]
138 q[2] = np.sqrt(selec[param])
139 q[0] = (rM[0, 2] - rM[2, 0]) / q[2]
140 q[1] = (rM[1, 0] + rM[0, 1]) / q[2]
141 q[3] = (rM[2, 1] + rM[1, 2]) / q[2]
145 q[3] = np.sqrt(selec[param])
146 q[0] = (rM[1, 0] - rM[0, 1]) / q[3]
147 q[1] = (rM[0, 2] + rM[2, 0]) / q[3]
148 q[2] = (rM[2, 1] + rM[1, 2]) / q[3]
158 arg0 = np.double(np.array(args[0]))
159 arg1 = np.double(np.array(args[1]))
160 if arg0.size == 1
and arg1.size == 3:
161 self.
array = np.zeros(4)
163 self.
array[1:4] = arg1[:]
164 elif arg0.size == 3
and arg1.size == 1:
165 self.
array = np.zeros(4)
167 self.
array[1:4] = arg0[:]
174 if not error
and self.
array.shape != (4,):
179 "Impossible to instanciate the Quaternion object "
180 "with the given arguments"
185 String representation of the quaternion.
188 aff += str(self.
array[0]) +
" + "
189 aff += str(self.
array[1]) +
" i + "
190 aff += str(self.
array[2]) +
" j + "
191 aff += str(self.
array[3]) +
" k ]"
196 Returns a quaternion which elements are the opposite of the original,
197 (this opposite quaternion represents the same rotation).
203 If other is not a quaternion it is casted to a quaternion,
204 the elements are added one to one.
206 if type(other) != Quaternion:
214 If other is not a quaternion it is casted to a quaternion,
215 the elements are substracted one to one.
217 if type(other) != Quaternion:
225 If other is not a quaternion it is casted to a quaternion,
226 the result of the quaternion multiplication is returned.
228 if type(other) != Quaternion:
233 qr[0] = self.
array[0] * q2.array[0] - np.vdot(self.
array[1:], q2.array[1:])
235 np.cross(self.
array[1:4], q2.array[1:4])
236 + self.
array[0] * q2.array[1:4]
237 + q2.array[0] * self.
array[1:4]
243 other is casted to a quaternion,
244 the result of the quaternion multiplication is returned.
250 Returns the norm of the quaternion.
252 return np.double(linalg.norm(self.
array))
256 Returns the conjugate of the quaternion.
262 Returns the inverse of the quaternion.
268 If other is not a quaternion it is casted to a quaternion,
269 the result of the quaternion multiplication with the inverse of other
272 if type(other) != Quaternion:
276 return self * q2.inv()
280 Returns quaternion**n with quaternion**0 = Quaternion(1,0,0,0).
289 Changes the values of the quaternion to make it a unit quaternion
290 representing the same rotation as the original one
291 and returns the updated version.
293 self.
array /= abs(self)
298 Returns the unit quaternion representation of the quaternion
299 without changing the original.
307 Returns the corresponding SO3.
313 Returns a (3*3) array (rotation matrix)
314 representing the same rotation as the (normalized) quaternion.
317 rm = np.zeros((3, 3))
318 rm[0, 0] = 1 - 2 * (q[2] ** 2 + q[3] ** 2)
319 rm[0, 1] = 2 * q[1] * q[2] - 2 * q[0] * q[3]
320 rm[0, 2] = 2 * q[1] * q[3] + 2 * q[0] * q[2]
321 rm[1, 0] = 2 * q[1] * q[2] + 2 * q[0] * q[3]
322 rm[1, 1] = 1 - 2 * (q[1] ** 2 + q[3] ** 2)
323 rm[1, 2] = 2 * q[2] * q[3] - 2 * q[0] * q[1]
324 rm[2, 0] = 2 * q[1] * q[3] - 2 * q[0] * q[2]
325 rm[2, 1] = 2 * q[2] * q[3] + 2 * q[0] * q[1]
326 rm[2, 2] = 1 - 2 * (q[1] ** 2 + q[2] ** 2)
331 Returns a 3-sized array (rotation vector)
332 representing the same rotation as the (normalized) quaternion.
336 alpha = 2 * np.arccos(q[0])
337 if linalg.norm(q[1:4]) != 0:
338 rV = alpha * q[1:4] / linalg.norm(q[1:4])
343 Returns a copy of the quaternion.
349 Returns a 3-sized array with representing the same rotation
350 as the (normalized) quaternion. With :
351 - the first element representing the Roll,
352 - the second the Pitch
354 Where Roll Pitch and Yaw are the angles so that the rotation
355 with the quaternion represents the same rotation as :
356 - A rotation of R (Roll) about the original x-axis,
357 followed by a rotation of P (Pitch) about the original y-axis,
358 followed by a rotation of Y (Yaw) about the original z-axis.
359 - Or otherwise a rotation of Y about the original z-axis,
360 followed by a rotation of P about the new y-axis,
361 followed by a rotation of R about the new x-axis.
364 r = np.arctan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] ** 2 + q[2] ** 2))
366 2 * (q[0] * q[2] - q[3] * q[1]),
368 (2 * (q[0] * q[1] + q[2] * q[3])) ** 2
369 + (1 - 2 * (q[1] ** 2 + q[2] ** 2)) ** 2
372 y = np.arctan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] ** 2 + q[3] ** 2))
373 return np.array([r, p, y])
377 Set the values of the quaternion to the values of a unit quaternion
378 representing the same rotation as the one performed by Roll Pitch Yaw :
379 - A rotation of R (Roll) about the original x-axis,
380 followed by a rotation of P (Pitch) about the original y-axis,
381 followed by a rotation of Y (Yaw) about the original z-axis.
382 - Or otherwise a rotation of Y about the original z-axis,
383 followed by a rotation of P about the new y-axis,
384 followed by a rotation of R about the new x-axis.
389 self.
array[0] = np.cos(r) * np.cos(p) * np.cos(y) + np.sin(r) * np.sin(
392 self.
array[1] = np.sin(r) * np.cos(p) * np.cos(y) - np.cos(r) * np.sin(
395 self.
array[2] = np.cos(r) * np.sin(p) * np.cos(y) + np.sin(r) * np.cos(
398 self.
array[3] = np.cos(r) * np.cos(p) * np.sin(y) - np.sin(r) * np.sin(