m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1438 lines
52 KiB

7 months ago
  1. from sympy.core.backend import (diff, expand, sin, cos, sympify, eye, symbols,
  2. ImmutableMatrix as Matrix, MatrixBase)
  3. from sympy.core.symbol import (Dummy, Symbol)
  4. from sympy.simplify.trigsimp import trigsimp
  5. from sympy.solvers.solvers import solve
  6. from sympy.physics.vector.vector import Vector, _check_vector
  7. from sympy.utilities.misc import translate
  8. from warnings import warn
  9. __all__ = ['CoordinateSym', 'ReferenceFrame']
  10. class CoordinateSym(Symbol):
  11. """
  12. A coordinate symbol/base scalar associated wrt a Reference Frame.
  13. Ideally, users should not instantiate this class. Instances of
  14. this class must only be accessed through the corresponding frame
  15. as 'frame[index]'.
  16. CoordinateSyms having the same frame and index parameters are equal
  17. (even though they may be instantiated separately).
  18. Parameters
  19. ==========
  20. name : string
  21. The display name of the CoordinateSym
  22. frame : ReferenceFrame
  23. The reference frame this base scalar belongs to
  24. index : 0, 1 or 2
  25. The index of the dimension denoted by this coordinate variable
  26. Examples
  27. ========
  28. >>> from sympy.physics.vector import ReferenceFrame, CoordinateSym
  29. >>> A = ReferenceFrame('A')
  30. >>> A[1]
  31. A_y
  32. >>> type(A[0])
  33. <class 'sympy.physics.vector.frame.CoordinateSym'>
  34. >>> a_y = CoordinateSym('a_y', A, 1)
  35. >>> a_y == A[1]
  36. True
  37. """
  38. def __new__(cls, name, frame, index):
  39. # We can't use the cached Symbol.__new__ because this class depends on
  40. # frame and index, which are not passed to Symbol.__xnew__.
  41. assumptions = {}
  42. super()._sanitize(assumptions, cls)
  43. obj = super().__xnew__(cls, name, **assumptions)
  44. _check_frame(frame)
  45. if index not in range(0, 3):
  46. raise ValueError("Invalid index specified")
  47. obj._id = (frame, index)
  48. return obj
  49. @property
  50. def frame(self):
  51. return self._id[0]
  52. def __eq__(self, other):
  53. #Check if the other object is a CoordinateSym of the same frame
  54. #and same index
  55. if isinstance(other, CoordinateSym):
  56. if other._id == self._id:
  57. return True
  58. return False
  59. def __ne__(self, other):
  60. return not self == other
  61. def __hash__(self):
  62. return tuple((self._id[0].__hash__(), self._id[1])).__hash__()
  63. class ReferenceFrame:
  64. """A reference frame in classical mechanics.
  65. ReferenceFrame is a class used to represent a reference frame in classical
  66. mechanics. It has a standard basis of three unit vectors in the frame's
  67. x, y, and z directions.
  68. It also can have a rotation relative to a parent frame; this rotation is
  69. defined by a direction cosine matrix relating this frame's basis vectors to
  70. the parent frame's basis vectors. It can also have an angular velocity
  71. vector, defined in another frame.
  72. """
  73. _count = 0
  74. def __init__(self, name, indices=None, latexs=None, variables=None):
  75. """ReferenceFrame initialization method.
  76. A ReferenceFrame has a set of orthonormal basis vectors, along with
  77. orientations relative to other ReferenceFrames and angular velocities
  78. relative to other ReferenceFrames.
  79. Parameters
  80. ==========
  81. indices : tuple of str
  82. Enables the reference frame's basis unit vectors to be accessed by
  83. Python's square bracket indexing notation using the provided three
  84. indice strings and alters the printing of the unit vectors to
  85. reflect this choice.
  86. latexs : tuple of str
  87. Alters the LaTeX printing of the reference frame's basis unit
  88. vectors to the provided three valid LaTeX strings.
  89. Examples
  90. ========
  91. >>> from sympy.physics.vector import ReferenceFrame, vlatex
  92. >>> N = ReferenceFrame('N')
  93. >>> N.x
  94. N.x
  95. >>> O = ReferenceFrame('O', indices=('1', '2', '3'))
  96. >>> O.x
  97. O['1']
  98. >>> O['1']
  99. O['1']
  100. >>> P = ReferenceFrame('P', latexs=('A1', 'A2', 'A3'))
  101. >>> vlatex(P.x)
  102. 'A1'
  103. symbols() can be used to create multiple Reference Frames in one step, for example:
  104. >>> from sympy.physics.vector import ReferenceFrame
  105. >>> from sympy import symbols
  106. >>> A, B, C = symbols('A B C', cls=ReferenceFrame)
  107. >>> D, E = symbols('D E', cls=ReferenceFrame, indices=('1', '2', '3'))
  108. >>> A[0]
  109. A_x
  110. >>> D.x
  111. D['1']
  112. >>> E.y
  113. E['2']
  114. >>> type(A) == type(D)
  115. True
  116. """
  117. if not isinstance(name, str):
  118. raise TypeError('Need to supply a valid name')
  119. # The if statements below are for custom printing of basis-vectors for
  120. # each frame.
  121. # First case, when custom indices are supplied
  122. if indices is not None:
  123. if not isinstance(indices, (tuple, list)):
  124. raise TypeError('Supply the indices as a list')
  125. if len(indices) != 3:
  126. raise ValueError('Supply 3 indices')
  127. for i in indices:
  128. if not isinstance(i, str):
  129. raise TypeError('Indices must be strings')
  130. self.str_vecs = [(name + '[\'' + indices[0] + '\']'),
  131. (name + '[\'' + indices[1] + '\']'),
  132. (name + '[\'' + indices[2] + '\']')]
  133. self.pretty_vecs = [(name.lower() + "_" + indices[0]),
  134. (name.lower() + "_" + indices[1]),
  135. (name.lower() + "_" + indices[2])]
  136. self.latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
  137. indices[0])), (r"\mathbf{\hat{%s}_{%s}}" %
  138. (name.lower(), indices[1])),
  139. (r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
  140. indices[2]))]
  141. self.indices = indices
  142. # Second case, when no custom indices are supplied
  143. else:
  144. self.str_vecs = [(name + '.x'), (name + '.y'), (name + '.z')]
  145. self.pretty_vecs = [name.lower() + "_x",
  146. name.lower() + "_y",
  147. name.lower() + "_z"]
  148. self.latex_vecs = [(r"\mathbf{\hat{%s}_x}" % name.lower()),
  149. (r"\mathbf{\hat{%s}_y}" % name.lower()),
  150. (r"\mathbf{\hat{%s}_z}" % name.lower())]
  151. self.indices = ['x', 'y', 'z']
  152. # Different step, for custom latex basis vectors
  153. if latexs is not None:
  154. if not isinstance(latexs, (tuple, list)):
  155. raise TypeError('Supply the indices as a list')
  156. if len(latexs) != 3:
  157. raise ValueError('Supply 3 indices')
  158. for i in latexs:
  159. if not isinstance(i, str):
  160. raise TypeError('Latex entries must be strings')
  161. self.latex_vecs = latexs
  162. self.name = name
  163. self._var_dict = {}
  164. #The _dcm_dict dictionary will only store the dcms of adjacent parent-child
  165. #relationships. The _dcm_cache dictionary will store calculated dcm along with
  166. #all content of _dcm_dict for faster retrieval of dcms.
  167. self._dcm_dict = {}
  168. self._dcm_cache = {}
  169. self._ang_vel_dict = {}
  170. self._ang_acc_dict = {}
  171. self._dlist = [self._dcm_dict, self._ang_vel_dict, self._ang_acc_dict]
  172. self._cur = 0
  173. self._x = Vector([(Matrix([1, 0, 0]), self)])
  174. self._y = Vector([(Matrix([0, 1, 0]), self)])
  175. self._z = Vector([(Matrix([0, 0, 1]), self)])
  176. #Associate coordinate symbols wrt this frame
  177. if variables is not None:
  178. if not isinstance(variables, (tuple, list)):
  179. raise TypeError('Supply the variable names as a list/tuple')
  180. if len(variables) != 3:
  181. raise ValueError('Supply 3 variable names')
  182. for i in variables:
  183. if not isinstance(i, str):
  184. raise TypeError('Variable names must be strings')
  185. else:
  186. variables = [name + '_x', name + '_y', name + '_z']
  187. self.varlist = (CoordinateSym(variables[0], self, 0), \
  188. CoordinateSym(variables[1], self, 1), \
  189. CoordinateSym(variables[2], self, 2))
  190. ReferenceFrame._count += 1
  191. self.index = ReferenceFrame._count
  192. def __getitem__(self, ind):
  193. """
  194. Returns basis vector for the provided index, if the index is a string.
  195. If the index is a number, returns the coordinate variable correspon-
  196. -ding to that index.
  197. """
  198. if not isinstance(ind, str):
  199. if ind < 3:
  200. return self.varlist[ind]
  201. else:
  202. raise ValueError("Invalid index provided")
  203. if self.indices[0] == ind:
  204. return self.x
  205. if self.indices[1] == ind:
  206. return self.y
  207. if self.indices[2] == ind:
  208. return self.z
  209. else:
  210. raise ValueError('Not a defined index')
  211. def __iter__(self):
  212. return iter([self.x, self.y, self.z])
  213. def __str__(self):
  214. """Returns the name of the frame. """
  215. return self.name
  216. __repr__ = __str__
  217. def _dict_list(self, other, num):
  218. """Returns an inclusive list of reference frames that connect this
  219. reference frame to the provided reference frame.
  220. Parameters
  221. ==========
  222. other : ReferenceFrame
  223. The other reference frame to look for a connecting relationship to.
  224. num : integer
  225. ``0``, ``1``, and ``2`` will look for orientation, angular
  226. velocity, and angular acceleration relationships between the two
  227. frames, respectively.
  228. Returns
  229. =======
  230. list
  231. Inclusive list of reference frames that connect this reference
  232. frame to the other reference frame.
  233. Examples
  234. ========
  235. >>> from sympy.physics.vector import ReferenceFrame
  236. >>> A = ReferenceFrame('A')
  237. >>> B = ReferenceFrame('B')
  238. >>> C = ReferenceFrame('C')
  239. >>> D = ReferenceFrame('D')
  240. >>> B.orient_axis(A, A.x, 1.0)
  241. >>> C.orient_axis(B, B.x, 1.0)
  242. >>> D.orient_axis(C, C.x, 1.0)
  243. >>> D._dict_list(A, 0)
  244. [D, C, B, A]
  245. Raises
  246. ======
  247. ValueError
  248. When no path is found between the two reference frames or ``num``
  249. is an incorrect value.
  250. """
  251. connect_type = {0: 'orientation',
  252. 1: 'angular velocity',
  253. 2: 'angular acceleration'}
  254. if num not in connect_type.keys():
  255. raise ValueError('Valid values for num are 0, 1, or 2.')
  256. possible_connecting_paths = [[self]]
  257. oldlist = [[]]
  258. while possible_connecting_paths != oldlist:
  259. oldlist = possible_connecting_paths[:] # make a copy
  260. for frame_list in possible_connecting_paths:
  261. frames_adjacent_to_last = frame_list[-1]._dlist[num].keys()
  262. for adjacent_frame in frames_adjacent_to_last:
  263. if adjacent_frame not in frame_list:
  264. connecting_path = frame_list + [adjacent_frame]
  265. if connecting_path not in possible_connecting_paths:
  266. possible_connecting_paths.append(connecting_path)
  267. for connecting_path in oldlist:
  268. if connecting_path[-1] != other:
  269. possible_connecting_paths.remove(connecting_path)
  270. possible_connecting_paths.sort(key=len)
  271. if len(possible_connecting_paths) != 0:
  272. return possible_connecting_paths[0] # selects the shortest path
  273. msg = 'No connecting {} path found between {} and {}.'
  274. raise ValueError(msg.format(connect_type[num], self.name, other.name))
  275. def _w_diff_dcm(self, otherframe):
  276. """Angular velocity from time differentiating the DCM. """
  277. from sympy.physics.vector.functions import dynamicsymbols
  278. dcm2diff = otherframe.dcm(self)
  279. diffed = dcm2diff.diff(dynamicsymbols._t)
  280. angvelmat = diffed * dcm2diff.T
  281. w1 = trigsimp(expand(angvelmat[7]), recursive=True)
  282. w2 = trigsimp(expand(angvelmat[2]), recursive=True)
  283. w3 = trigsimp(expand(angvelmat[3]), recursive=True)
  284. return Vector([(Matrix([w1, w2, w3]), otherframe)])
  285. def variable_map(self, otherframe):
  286. """
  287. Returns a dictionary which expresses the coordinate variables
  288. of this frame in terms of the variables of otherframe.
  289. If Vector.simp is True, returns a simplified version of the mapped
  290. values. Else, returns them without simplification.
  291. Simplification of the expressions may take time.
  292. Parameters
  293. ==========
  294. otherframe : ReferenceFrame
  295. The other frame to map the variables to
  296. Examples
  297. ========
  298. >>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
  299. >>> A = ReferenceFrame('A')
  300. >>> q = dynamicsymbols('q')
  301. >>> B = A.orientnew('B', 'Axis', [q, A.z])
  302. >>> A.variable_map(B)
  303. {A_x: B_x*cos(q(t)) - B_y*sin(q(t)), A_y: B_x*sin(q(t)) + B_y*cos(q(t)), A_z: B_z}
  304. """
  305. _check_frame(otherframe)
  306. if (otherframe, Vector.simp) in self._var_dict:
  307. return self._var_dict[(otherframe, Vector.simp)]
  308. else:
  309. vars_matrix = self.dcm(otherframe) * Matrix(otherframe.varlist)
  310. mapping = {}
  311. for i, x in enumerate(self):
  312. if Vector.simp:
  313. mapping[self.varlist[i]] = trigsimp(vars_matrix[i], method='fu')
  314. else:
  315. mapping[self.varlist[i]] = vars_matrix[i]
  316. self._var_dict[(otherframe, Vector.simp)] = mapping
  317. return mapping
  318. def ang_acc_in(self, otherframe):
  319. """Returns the angular acceleration Vector of the ReferenceFrame.
  320. Effectively returns the Vector:
  321. ^N alpha ^B
  322. which represent the angular acceleration of B in N, where B is self, and
  323. N is otherframe.
  324. Parameters
  325. ==========
  326. otherframe : ReferenceFrame
  327. The ReferenceFrame which the angular acceleration is returned in.
  328. Examples
  329. ========
  330. >>> from sympy.physics.vector import ReferenceFrame
  331. >>> N = ReferenceFrame('N')
  332. >>> A = ReferenceFrame('A')
  333. >>> V = 10 * N.x
  334. >>> A.set_ang_acc(N, V)
  335. >>> A.ang_acc_in(N)
  336. 10*N.x
  337. """
  338. _check_frame(otherframe)
  339. if otherframe in self._ang_acc_dict:
  340. return self._ang_acc_dict[otherframe]
  341. else:
  342. return self.ang_vel_in(otherframe).dt(otherframe)
  343. def ang_vel_in(self, otherframe):
  344. """Returns the angular velocity Vector of the ReferenceFrame.
  345. Effectively returns the Vector:
  346. ^N omega ^B
  347. which represent the angular velocity of B in N, where B is self, and
  348. N is otherframe.
  349. Parameters
  350. ==========
  351. otherframe : ReferenceFrame
  352. The ReferenceFrame which the angular velocity is returned in.
  353. Examples
  354. ========
  355. >>> from sympy.physics.vector import ReferenceFrame
  356. >>> N = ReferenceFrame('N')
  357. >>> A = ReferenceFrame('A')
  358. >>> V = 10 * N.x
  359. >>> A.set_ang_vel(N, V)
  360. >>> A.ang_vel_in(N)
  361. 10*N.x
  362. """
  363. _check_frame(otherframe)
  364. flist = self._dict_list(otherframe, 1)
  365. outvec = Vector(0)
  366. for i in range(len(flist) - 1):
  367. outvec += flist[i]._ang_vel_dict[flist[i + 1]]
  368. return outvec
  369. def dcm(self, otherframe):
  370. r"""Returns the direction cosine matrix relative to the provided
  371. reference frame.
  372. The returned matrix can be used to express the orthogonal unit vectors
  373. of this frame in terms of the orthogonal unit vectors of
  374. ``otherframe``.
  375. Parameters
  376. ==========
  377. otherframe : ReferenceFrame
  378. The reference frame which the direction cosine matrix of this frame
  379. is formed relative to.
  380. Examples
  381. ========
  382. The following example rotates the reference frame A relative to N by a
  383. simple rotation and then calculates the direction cosine matrix of N
  384. relative to A.
  385. >>> from sympy import symbols, sin, cos
  386. >>> from sympy.physics.vector import ReferenceFrame
  387. >>> q1 = symbols('q1')
  388. >>> N = ReferenceFrame('N')
  389. >>> A = N.orientnew('A', 'Axis', (q1, N.x))
  390. >>> N.dcm(A)
  391. Matrix([
  392. [1, 0, 0],
  393. [0, cos(q1), -sin(q1)],
  394. [0, sin(q1), cos(q1)]])
  395. The second row of the above direction cosine matrix represents the
  396. ``N.y`` unit vector in N expressed in A. Like so:
  397. >>> Ny = 0*A.x + cos(q1)*A.y - sin(q1)*A.z
  398. Thus, expressing ``N.y`` in A should return the same result:
  399. >>> N.y.express(A)
  400. cos(q1)*A.y - sin(q1)*A.z
  401. Notes
  402. =====
  403. It is import to know what form of the direction cosine matrix is
  404. returned. If ``B.dcm(A)`` is called, it means the "direction cosine
  405. matrix of B relative to A". This is the matrix :math:`^{\mathbf{A}} \mathbf{R} ^{\mathbf{B}}`
  406. shown in the following relationship:
  407. .. math::
  408. \begin{bmatrix}
  409. \hat{\mathbf{b}}_1 \\
  410. \hat{\mathbf{b}}_2 \\
  411. \hat{\mathbf{b}}_3
  412. \end{bmatrix}
  413. =
  414. {}^A\mathbf{R}^B
  415. \begin{bmatrix}
  416. \hat{\mathbf{a}}_1 \\
  417. \hat{\mathbf{a}}_2 \\
  418. \hat{\mathbf{a}}_3
  419. \end{bmatrix}.
  420. :math:`{}^A\mathbf{R}^B` is the matrix that expresses the B unit
  421. vectors in terms of the A unit vectors.
  422. """
  423. _check_frame(otherframe)
  424. # Check if the dcm wrt that frame has already been calculated
  425. if otherframe in self._dcm_cache:
  426. return self._dcm_cache[otherframe]
  427. flist = self._dict_list(otherframe, 0)
  428. outdcm = eye(3)
  429. for i in range(len(flist) - 1):
  430. outdcm = outdcm * flist[i]._dcm_dict[flist[i + 1]]
  431. # After calculation, store the dcm in dcm cache for faster future
  432. # retrieval
  433. self._dcm_cache[otherframe] = outdcm
  434. otherframe._dcm_cache[self] = outdcm.T
  435. return outdcm
  436. def _dcm(self, parent, parent_orient):
  437. # If parent.oreint(self) is already defined,then
  438. # update the _dcm_dict of parent while over write
  439. # all content of self._dcm_dict and self._dcm_cache
  440. # with new dcm relation.
  441. # Else update _dcm_cache and _dcm_dict of both
  442. # self and parent.
  443. frames = self._dcm_cache.keys()
  444. dcm_dict_del = []
  445. dcm_cache_del = []
  446. if parent in frames:
  447. for frame in frames:
  448. if frame in self._dcm_dict:
  449. dcm_dict_del += [frame]
  450. dcm_cache_del += [frame]
  451. # Reset the _dcm_cache of this frame, and remove it from the
  452. # _dcm_caches of the frames it is linked to. Also remove it from the
  453. # _dcm_dict of its parent
  454. for frame in dcm_dict_del:
  455. del frame._dcm_dict[self]
  456. for frame in dcm_cache_del:
  457. del frame._dcm_cache[self]
  458. # Reset the _dcm_dict
  459. self._dcm_dict = self._dlist[0] = {}
  460. # Reset the _dcm_cache
  461. self._dcm_cache = {}
  462. else:
  463. #Check for loops and raise warning accordingly.
  464. visited = []
  465. queue = list(frames)
  466. cont = True #Flag to control queue loop.
  467. while queue and cont:
  468. node = queue.pop(0)
  469. if node not in visited:
  470. visited.append(node)
  471. neighbors = node._dcm_dict.keys()
  472. for neighbor in neighbors:
  473. if neighbor == parent:
  474. warn('Loops are defined among the orientation of frames.' + \
  475. ' This is likely not desired and may cause errors in your calculations.')
  476. cont = False
  477. break
  478. queue.append(neighbor)
  479. # Add the dcm relationship to _dcm_dict
  480. self._dcm_dict.update({parent: parent_orient.T})
  481. parent._dcm_dict.update({self: parent_orient})
  482. # Update the dcm cache
  483. self._dcm_cache.update({parent: parent_orient.T})
  484. parent._dcm_cache.update({self: parent_orient})
  485. def orient_axis(self, parent, axis, angle):
  486. """Sets the orientation of this reference frame with respect to a
  487. parent reference frame by rotating through an angle about an axis fixed
  488. in the parent reference frame.
  489. Parameters
  490. ==========
  491. parent : ReferenceFrame
  492. Reference frame that this reference frame will be rotated relative
  493. to.
  494. axis : Vector
  495. Vector fixed in the parent frame about about which this frame is
  496. rotated. It need not be a unit vector and the rotation follows the
  497. right hand rule.
  498. angle : sympifiable
  499. Angle in radians by which it the frame is to be rotated.
  500. Warns
  501. ======
  502. UserWarning
  503. If the orientation creates a kinematic loop.
  504. Examples
  505. ========
  506. Setup variables for the examples:
  507. >>> from sympy import symbols
  508. >>> from sympy.physics.vector import ReferenceFrame
  509. >>> q1 = symbols('q1')
  510. >>> N = ReferenceFrame('N')
  511. >>> B = ReferenceFrame('B')
  512. >>> B.orient_axis(N, N.x, q1)
  513. The ``orient_axis()`` method generates a direction cosine matrix and
  514. its transpose which defines the orientation of B relative to N and vice
  515. versa. Once orient is called, ``dcm()`` outputs the appropriate
  516. direction cosine matrix:
  517. >>> B.dcm(N)
  518. Matrix([
  519. [1, 0, 0],
  520. [0, cos(q1), sin(q1)],
  521. [0, -sin(q1), cos(q1)]])
  522. >>> N.dcm(B)
  523. Matrix([
  524. [1, 0, 0],
  525. [0, cos(q1), -sin(q1)],
  526. [0, sin(q1), cos(q1)]])
  527. The following two lines show that the sense of the rotation can be
  528. defined by negating the vector direction or the angle. Both lines
  529. produce the same result.
  530. >>> B.orient_axis(N, -N.x, q1)
  531. >>> B.orient_axis(N, N.x, -q1)
  532. """
  533. from sympy.physics.vector.functions import dynamicsymbols
  534. _check_frame(parent)
  535. if not isinstance(axis, Vector) and isinstance(angle, Vector):
  536. axis, angle = angle, axis
  537. axis = _check_vector(axis)
  538. amount = sympify(angle)
  539. theta = amount
  540. parent_orient_axis = []
  541. if not axis.dt(parent) == 0:
  542. raise ValueError('Axis cannot be time-varying.')
  543. unit_axis = axis.express(parent).normalize()
  544. unit_col = unit_axis.args[0][0]
  545. parent_orient_axis = (
  546. (eye(3) - unit_col * unit_col.T) * cos(theta) +
  547. Matrix([[0, -unit_col[2], unit_col[1]],
  548. [unit_col[2], 0, -unit_col[0]],
  549. [-unit_col[1], unit_col[0], 0]]) *
  550. sin(theta) + unit_col * unit_col.T)
  551. self._dcm(parent, parent_orient_axis)
  552. thetad = (amount).diff(dynamicsymbols._t)
  553. wvec = thetad*axis.express(parent).normalize()
  554. self._ang_vel_dict.update({parent: wvec})
  555. parent._ang_vel_dict.update({self: -wvec})
  556. self._var_dict = {}
  557. def orient_explicit(self, parent, dcm):
  558. """Sets the orientation of this reference frame relative to a parent
  559. reference frame by explicitly setting the direction cosine matrix.
  560. Parameters
  561. ==========
  562. parent : ReferenceFrame
  563. Reference frame that this reference frame will be rotated relative
  564. to.
  565. dcm : Matrix, shape(3, 3)
  566. Direction cosine matrix that specifies the relative rotation
  567. between the two reference frames.
  568. Warns
  569. ======
  570. UserWarning
  571. If the orientation creates a kinematic loop.
  572. Examples
  573. ========
  574. Setup variables for the examples:
  575. >>> from sympy import symbols, Matrix, sin, cos
  576. >>> from sympy.physics.vector import ReferenceFrame
  577. >>> q1 = symbols('q1')
  578. >>> A = ReferenceFrame('A')
  579. >>> B = ReferenceFrame('B')
  580. >>> N = ReferenceFrame('N')
  581. A simple rotation of ``A`` relative to ``N`` about ``N.x`` is defined
  582. by the following direction cosine matrix:
  583. >>> dcm = Matrix([[1, 0, 0],
  584. ... [0, cos(q1), -sin(q1)],
  585. ... [0, sin(q1), cos(q1)]])
  586. >>> A.orient_explicit(N, dcm)
  587. >>> A.dcm(N)
  588. Matrix([
  589. [1, 0, 0],
  590. [0, cos(q1), sin(q1)],
  591. [0, -sin(q1), cos(q1)]])
  592. This is equivalent to using ``orient_axis()``:
  593. >>> B.orient_axis(N, N.x, q1)
  594. >>> B.dcm(N)
  595. Matrix([
  596. [1, 0, 0],
  597. [0, cos(q1), sin(q1)],
  598. [0, -sin(q1), cos(q1)]])
  599. **Note carefully that** ``N.dcm(B)`` **(the transpose) would be passed
  600. into** ``orient_explicit()`` **for** ``A.dcm(N)`` **to match**
  601. ``B.dcm(N)``:
  602. >>> A.orient_explicit(N, N.dcm(B))
  603. >>> A.dcm(N)
  604. Matrix([
  605. [1, 0, 0],
  606. [0, cos(q1), sin(q1)],
  607. [0, -sin(q1), cos(q1)]])
  608. """
  609. _check_frame(parent)
  610. # amounts must be a Matrix type object
  611. # (e.g. sympy.matrices.dense.MutableDenseMatrix).
  612. if not isinstance(dcm, MatrixBase):
  613. raise TypeError("Amounts must be a SymPy Matrix type object.")
  614. parent_orient_dcm = []
  615. parent_orient_dcm = dcm
  616. self._dcm(parent, parent_orient_dcm)
  617. wvec = self._w_diff_dcm(parent)
  618. self._ang_vel_dict.update({parent: wvec})
  619. parent._ang_vel_dict.update({self: -wvec})
  620. self._var_dict = {}
  621. def _rot(self, axis, angle):
  622. """DCM for simple axis 1,2,or 3 rotations."""
  623. if axis == 1:
  624. return Matrix([[1, 0, 0],
  625. [0, cos(angle), -sin(angle)],
  626. [0, sin(angle), cos(angle)]])
  627. elif axis == 2:
  628. return Matrix([[cos(angle), 0, sin(angle)],
  629. [0, 1, 0],
  630. [-sin(angle), 0, cos(angle)]])
  631. elif axis == 3:
  632. return Matrix([[cos(angle), -sin(angle), 0],
  633. [sin(angle), cos(angle), 0],
  634. [0, 0, 1]])
  635. def orient_body_fixed(self, parent, angles, rotation_order):
  636. """Rotates this reference frame relative to the parent reference frame
  637. by right hand rotating through three successive body fixed simple axis
  638. rotations. Each subsequent axis of rotation is about the "body fixed"
  639. unit vectors of a new intermediate reference frame. This type of
  640. rotation is also referred to rotating through the `Euler and Tait-Bryan
  641. Angles`_.
  642. .. _Euler and Tait-Bryan Angles: https://en.wikipedia.org/wiki/Euler_angles
  643. Parameters
  644. ==========
  645. parent : ReferenceFrame
  646. Reference frame that this reference frame will be rotated relative
  647. to.
  648. angles : 3-tuple of sympifiable
  649. Three angles in radians used for the successive rotations.
  650. rotation_order : 3 character string or 3 digit integer
  651. Order of the rotations about each intermediate reference frames'
  652. unit vectors. The Euler rotation about the X, Z', X'' axes can be
  653. specified by the strings ``'XZX'``, ``'131'``, or the integer
  654. ``131``. There are 12 unique valid rotation orders (6 Euler and 6
  655. Tait-Bryan): zxz, xyx, yzy, zyz, xzx, yxy, xyz, yzx, zxy, xzy, zyx,
  656. and yxz.
  657. Warns
  658. ======
  659. UserWarning
  660. If the orientation creates a kinematic loop.
  661. Examples
  662. ========
  663. Setup variables for the examples:
  664. >>> from sympy import symbols
  665. >>> from sympy.physics.vector import ReferenceFrame
  666. >>> q1, q2, q3 = symbols('q1, q2, q3')
  667. >>> N = ReferenceFrame('N')
  668. >>> B = ReferenceFrame('B')
  669. >>> B1 = ReferenceFrame('B1')
  670. >>> B2 = ReferenceFrame('B2')
  671. >>> B3 = ReferenceFrame('B3')
  672. For example, a classic Euler Angle rotation can be done by:
  673. >>> B.orient_body_fixed(N, (q1, q2, q3), 'XYX')
  674. >>> B.dcm(N)
  675. Matrix([
  676. [ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
  677. [sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
  678. [sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
  679. This rotates reference frame B relative to reference frame N through
  680. ``q1`` about ``N.x``, then rotates B again through ``q2`` about
  681. ``B.y``, and finally through ``q3`` about ``B.x``. It is equivalent to
  682. three successive ``orient_axis()`` calls:
  683. >>> B1.orient_axis(N, N.x, q1)
  684. >>> B2.orient_axis(B1, B1.y, q2)
  685. >>> B3.orient_axis(B2, B2.x, q3)
  686. >>> B3.dcm(N)
  687. Matrix([
  688. [ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
  689. [sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
  690. [sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
  691. Acceptable rotation orders are of length 3, expressed in as a string
  692. ``'XYZ'`` or ``'123'`` or integer ``123``. Rotations about an axis
  693. twice in a row are prohibited.
  694. >>> B.orient_body_fixed(N, (q1, q2, 0), 'ZXZ')
  695. >>> B.orient_body_fixed(N, (q1, q2, 0), '121')
  696. >>> B.orient_body_fixed(N, (q1, q2, q3), 123)
  697. """
  698. _check_frame(parent)
  699. amounts = list(angles)
  700. for i, v in enumerate(amounts):
  701. if not isinstance(v, Vector):
  702. amounts[i] = sympify(v)
  703. approved_orders = ('123', '231', '312', '132', '213', '321', '121',
  704. '131', '212', '232', '313', '323', '')
  705. # make sure XYZ => 123
  706. rot_order = translate(str(rotation_order), 'XYZxyz', '123123')
  707. if rot_order not in approved_orders:
  708. raise TypeError('The rotation order is not a valid order.')
  709. parent_orient_body = []
  710. if not (len(amounts) == 3 & len(rot_order) == 3):
  711. raise TypeError('Body orientation takes 3 values & 3 orders')
  712. a1 = int(rot_order[0])
  713. a2 = int(rot_order[1])
  714. a3 = int(rot_order[2])
  715. parent_orient_body = (self._rot(a1, amounts[0]) *
  716. self._rot(a2, amounts[1]) *
  717. self._rot(a3, amounts[2]))
  718. self._dcm(parent, parent_orient_body)
  719. try:
  720. from sympy.polys.polyerrors import CoercionFailed
  721. from sympy.physics.vector.functions import kinematic_equations
  722. q1, q2, q3 = amounts
  723. u1, u2, u3 = symbols('u1, u2, u3', cls=Dummy)
  724. templist = kinematic_equations([u1, u2, u3], [q1, q2, q3],
  725. 'body', rot_order)
  726. templist = [expand(i) for i in templist]
  727. td = solve(templist, [u1, u2, u3])
  728. u1 = expand(td[u1])
  729. u2 = expand(td[u2])
  730. u3 = expand(td[u3])
  731. wvec = u1 * self.x + u2 * self.y + u3 * self.z
  732. except (CoercionFailed, AssertionError):
  733. wvec = self._w_diff_dcm(parent)
  734. self._ang_vel_dict.update({parent: wvec})
  735. parent._ang_vel_dict.update({self: -wvec})
  736. self._var_dict = {}
  737. def orient_space_fixed(self, parent, angles, rotation_order):
  738. """Rotates this reference frame relative to the parent reference frame
  739. by right hand rotating through three successive space fixed simple axis
  740. rotations. Each subsequent axis of rotation is about the "space fixed"
  741. unit vectors of the parent reference frame.
  742. Parameters
  743. ==========
  744. parent : ReferenceFrame
  745. Reference frame that this reference frame will be rotated relative
  746. to.
  747. angles : 3-tuple of sympifiable
  748. Three angles in radians used for the successive rotations.
  749. rotation_order : 3 character string or 3 digit integer
  750. Order of the rotations about the parent reference frame's unit
  751. vectors. The order can be specified by the strings ``'XZX'``,
  752. ``'131'``, or the integer ``131``. There are 12 unique valid
  753. rotation orders.
  754. Warns
  755. ======
  756. UserWarning
  757. If the orientation creates a kinematic loop.
  758. Examples
  759. ========
  760. Setup variables for the examples:
  761. >>> from sympy import symbols
  762. >>> from sympy.physics.vector import ReferenceFrame
  763. >>> q1, q2, q3 = symbols('q1, q2, q3')
  764. >>> N = ReferenceFrame('N')
  765. >>> B = ReferenceFrame('B')
  766. >>> B1 = ReferenceFrame('B1')
  767. >>> B2 = ReferenceFrame('B2')
  768. >>> B3 = ReferenceFrame('B3')
  769. >>> B.orient_space_fixed(N, (q1, q2, q3), '312')
  770. >>> B.dcm(N)
  771. Matrix([
  772. [ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
  773. [-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
  774. [ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
  775. is equivalent to:
  776. >>> B1.orient_axis(N, N.z, q1)
  777. >>> B2.orient_axis(B1, N.x, q2)
  778. >>> B3.orient_axis(B2, N.y, q3)
  779. >>> B3.dcm(N).simplify()
  780. Matrix([
  781. [ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
  782. [-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
  783. [ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
  784. It is worth noting that space-fixed and body-fixed rotations are
  785. related by the order of the rotations, i.e. the reverse order of body
  786. fixed will give space fixed and vice versa.
  787. >>> B.orient_space_fixed(N, (q1, q2, q3), '231')
  788. >>> B.dcm(N)
  789. Matrix([
  790. [cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
  791. [ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
  792. [sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
  793. >>> B.orient_body_fixed(N, (q3, q2, q1), '132')
  794. >>> B.dcm(N)
  795. Matrix([
  796. [cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
  797. [ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
  798. [sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
  799. """
  800. _check_frame(parent)
  801. amounts = list(angles)
  802. for i, v in enumerate(amounts):
  803. if not isinstance(v, Vector):
  804. amounts[i] = sympify(v)
  805. approved_orders = ('123', '231', '312', '132', '213', '321', '121',
  806. '131', '212', '232', '313', '323', '')
  807. # make sure XYZ => 123
  808. rot_order = translate(str(rotation_order), 'XYZxyz', '123123')
  809. if rot_order not in approved_orders:
  810. raise TypeError('The supplied order is not an approved type')
  811. parent_orient_space = []
  812. if not (len(amounts) == 3 & len(rot_order) == 3):
  813. raise TypeError('Space orientation takes 3 values & 3 orders')
  814. a1 = int(rot_order[0])
  815. a2 = int(rot_order[1])
  816. a3 = int(rot_order[2])
  817. parent_orient_space = (self._rot(a3, amounts[2]) *
  818. self._rot(a2, amounts[1]) *
  819. self._rot(a1, amounts[0]))
  820. self._dcm(parent, parent_orient_space)
  821. try:
  822. from sympy.polys.polyerrors import CoercionFailed
  823. from sympy.physics.vector.functions import kinematic_equations
  824. q1, q2, q3 = amounts
  825. u1, u2, u3 = symbols('u1, u2, u3', cls=Dummy)
  826. templist = kinematic_equations([u1, u2, u3], [q1, q2, q3],
  827. 'space', rot_order)
  828. templist = [expand(i) for i in templist]
  829. td = solve(templist, [u1, u2, u3])
  830. u1 = expand(td[u1])
  831. u2 = expand(td[u2])
  832. u3 = expand(td[u3])
  833. wvec = u1 * self.x + u2 * self.y + u3 * self.z
  834. except (CoercionFailed, AssertionError):
  835. wvec = self._w_diff_dcm(parent)
  836. self._ang_vel_dict.update({parent: wvec})
  837. parent._ang_vel_dict.update({self: -wvec})
  838. self._var_dict = {}
  839. def orient_quaternion(self, parent, numbers):
  840. """Sets the orientation of this reference frame relative to a parent
  841. reference frame via an orientation quaternion. An orientation
  842. quaternion is defined as a finite rotation a unit vector, ``(lambda_x,
  843. lambda_y, lambda_z)``, by an angle ``theta``. The orientation
  844. quaternion is described by four parameters:
  845. - ``q0 = cos(theta/2)``
  846. - ``q1 = lambda_x*sin(theta/2)``
  847. - ``q2 = lambda_y*sin(theta/2)``
  848. - ``q3 = lambda_z*sin(theta/2)``
  849. See `Quaternions and Spatial Rotation
  850. <https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation>`_ on
  851. Wikipedia for more information.
  852. Parameters
  853. ==========
  854. parent : ReferenceFrame
  855. Reference frame that this reference frame will be rotated relative
  856. to.
  857. numbers : 4-tuple of sympifiable
  858. The four quaternion scalar numbers as defined above: ``q0``,
  859. ``q1``, ``q2``, ``q3``.
  860. Warns
  861. ======
  862. UserWarning
  863. If the orientation creates a kinematic loop.
  864. Examples
  865. ========
  866. Setup variables for the examples:
  867. >>> from sympy import symbols
  868. >>> from sympy.physics.vector import ReferenceFrame
  869. >>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
  870. >>> N = ReferenceFrame('N')
  871. >>> B = ReferenceFrame('B')
  872. Set the orientation:
  873. >>> B.orient_quaternion(N, (q0, q1, q2, q3))
  874. >>> B.dcm(N)
  875. Matrix([
  876. [q0**2 + q1**2 - q2**2 - q3**2, 2*q0*q3 + 2*q1*q2, -2*q0*q2 + 2*q1*q3],
  877. [ -2*q0*q3 + 2*q1*q2, q0**2 - q1**2 + q2**2 - q3**2, 2*q0*q1 + 2*q2*q3],
  878. [ 2*q0*q2 + 2*q1*q3, -2*q0*q1 + 2*q2*q3, q0**2 - q1**2 - q2**2 + q3**2]])
  879. """
  880. from sympy.physics.vector.functions import dynamicsymbols
  881. _check_frame(parent)
  882. numbers = list(numbers)
  883. for i, v in enumerate(numbers):
  884. if not isinstance(v, Vector):
  885. numbers[i] = sympify(v)
  886. parent_orient_quaternion = []
  887. if not (isinstance(numbers, (list, tuple)) & (len(numbers) == 4)):
  888. raise TypeError('Amounts are a list or tuple of length 4')
  889. q0, q1, q2, q3 = numbers
  890. parent_orient_quaternion = (
  891. Matrix([[q0**2 + q1**2 - q2**2 - q3**2,
  892. 2 * (q1 * q2 - q0 * q3),
  893. 2 * (q0 * q2 + q1 * q3)],
  894. [2 * (q1 * q2 + q0 * q3),
  895. q0**2 - q1**2 + q2**2 - q3**2,
  896. 2 * (q2 * q3 - q0 * q1)],
  897. [2 * (q1 * q3 - q0 * q2),
  898. 2 * (q0 * q1 + q2 * q3),
  899. q0**2 - q1**2 - q2**2 + q3**2]]))
  900. self._dcm(parent, parent_orient_quaternion)
  901. t = dynamicsymbols._t
  902. q0, q1, q2, q3 = numbers
  903. q0d = diff(q0, t)
  904. q1d = diff(q1, t)
  905. q2d = diff(q2, t)
  906. q3d = diff(q3, t)
  907. w1 = 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1)
  908. w2 = 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2)
  909. w3 = 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3)
  910. wvec = Vector([(Matrix([w1, w2, w3]), self)])
  911. self._ang_vel_dict.update({parent: wvec})
  912. parent._ang_vel_dict.update({self: -wvec})
  913. self._var_dict = {}
  914. def orient(self, parent, rot_type, amounts, rot_order=''):
  915. """Sets the orientation of this reference frame relative to another
  916. (parent) reference frame.
  917. .. note:: It is now recommended to use the ``.orient_axis,
  918. .orient_body_fixed, .orient_space_fixed, .orient_quaternion``
  919. methods for the different rotation types.
  920. Parameters
  921. ==========
  922. parent : ReferenceFrame
  923. Reference frame that this reference frame will be rotated relative
  924. to.
  925. rot_type : str
  926. The method used to generate the direction cosine matrix. Supported
  927. methods are:
  928. - ``'Axis'``: simple rotations about a single common axis
  929. - ``'DCM'``: for setting the direction cosine matrix directly
  930. - ``'Body'``: three successive rotations about new intermediate
  931. axes, also called "Euler and Tait-Bryan angles"
  932. - ``'Space'``: three successive rotations about the parent
  933. frames' unit vectors
  934. - ``'Quaternion'``: rotations defined by four parameters which
  935. result in a singularity free direction cosine matrix
  936. amounts :
  937. Expressions defining the rotation angles or direction cosine
  938. matrix. These must match the ``rot_type``. See examples below for
  939. details. The input types are:
  940. - ``'Axis'``: 2-tuple (expr/sym/func, Vector)
  941. - ``'DCM'``: Matrix, shape(3,3)
  942. - ``'Body'``: 3-tuple of expressions, symbols, or functions
  943. - ``'Space'``: 3-tuple of expressions, symbols, or functions
  944. - ``'Quaternion'``: 4-tuple of expressions, symbols, or
  945. functions
  946. rot_order : str or int, optional
  947. If applicable, the order of the successive of rotations. The string
  948. ``'123'`` and integer ``123`` are equivalent, for example. Required
  949. for ``'Body'`` and ``'Space'``.
  950. Warns
  951. ======
  952. UserWarning
  953. If the orientation creates a kinematic loop.
  954. """
  955. _check_frame(parent)
  956. approved_orders = ('123', '231', '312', '132', '213', '321', '121',
  957. '131', '212', '232', '313', '323', '')
  958. rot_order = translate(str(rot_order), 'XYZxyz', '123123')
  959. rot_type = rot_type.upper()
  960. if rot_order not in approved_orders:
  961. raise TypeError('The supplied order is not an approved type')
  962. if rot_type == 'AXIS':
  963. self.orient_axis(parent, amounts[1], amounts[0])
  964. elif rot_type == 'DCM':
  965. self.orient_explicit(parent, amounts)
  966. elif rot_type == 'BODY':
  967. self.orient_body_fixed(parent, amounts, rot_order)
  968. elif rot_type == 'SPACE':
  969. self.orient_space_fixed(parent, amounts, rot_order)
  970. elif rot_type == 'QUATERNION':
  971. self.orient_quaternion(parent, amounts)
  972. else:
  973. raise NotImplementedError('That is not an implemented rotation')
  974. def orientnew(self, newname, rot_type, amounts, rot_order='',
  975. variables=None, indices=None, latexs=None):
  976. r"""Returns a new reference frame oriented with respect to this
  977. reference frame.
  978. See ``ReferenceFrame.orient()`` for detailed examples of how to orient
  979. reference frames.
  980. Parameters
  981. ==========
  982. newname : str
  983. Name for the new reference frame.
  984. rot_type : str
  985. The method used to generate the direction cosine matrix. Supported
  986. methods are:
  987. - ``'Axis'``: simple rotations about a single common axis
  988. - ``'DCM'``: for setting the direction cosine matrix directly
  989. - ``'Body'``: three successive rotations about new intermediate
  990. axes, also called "Euler and Tait-Bryan angles"
  991. - ``'Space'``: three successive rotations about the parent
  992. frames' unit vectors
  993. - ``'Quaternion'``: rotations defined by four parameters which
  994. result in a singularity free direction cosine matrix
  995. amounts :
  996. Expressions defining the rotation angles or direction cosine
  997. matrix. These must match the ``rot_type``. See examples below for
  998. details. The input types are:
  999. - ``'Axis'``: 2-tuple (expr/sym/func, Vector)
  1000. - ``'DCM'``: Matrix, shape(3,3)
  1001. - ``'Body'``: 3-tuple of expressions, symbols, or functions
  1002. - ``'Space'``: 3-tuple of expressions, symbols, or functions
  1003. - ``'Quaternion'``: 4-tuple of expressions, symbols, or
  1004. functions
  1005. rot_order : str or int, optional
  1006. If applicable, the order of the successive of rotations. The string
  1007. ``'123'`` and integer ``123`` are equivalent, for example. Required
  1008. for ``'Body'`` and ``'Space'``.
  1009. indices : tuple of str
  1010. Enables the reference frame's basis unit vectors to be accessed by
  1011. Python's square bracket indexing notation using the provided three
  1012. indice strings and alters the printing of the unit vectors to
  1013. reflect this choice.
  1014. latexs : tuple of str
  1015. Alters the LaTeX printing of the reference frame's basis unit
  1016. vectors to the provided three valid LaTeX strings.
  1017. Examples
  1018. ========
  1019. >>> from sympy import symbols
  1020. >>> from sympy.physics.vector import ReferenceFrame, vlatex
  1021. >>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
  1022. >>> N = ReferenceFrame('N')
  1023. Create a new reference frame A rotated relative to N through a simple
  1024. rotation.
  1025. >>> A = N.orientnew('A', 'Axis', (q0, N.x))
  1026. Create a new reference frame B rotated relative to N through body-fixed
  1027. rotations.
  1028. >>> B = N.orientnew('B', 'Body', (q1, q2, q3), '123')
  1029. Create a new reference frame C rotated relative to N through a simple
  1030. rotation with unique indices and LaTeX printing.
  1031. >>> C = N.orientnew('C', 'Axis', (q0, N.x), indices=('1', '2', '3'),
  1032. ... latexs=(r'\hat{\mathbf{c}}_1',r'\hat{\mathbf{c}}_2',
  1033. ... r'\hat{\mathbf{c}}_3'))
  1034. >>> C['1']
  1035. C['1']
  1036. >>> print(vlatex(C['1']))
  1037. \hat{\mathbf{c}}_1
  1038. """
  1039. newframe = self.__class__(newname, variables=variables,
  1040. indices=indices, latexs=latexs)
  1041. approved_orders = ('123', '231', '312', '132', '213', '321', '121',
  1042. '131', '212', '232', '313', '323', '')
  1043. rot_order = translate(str(rot_order), 'XYZxyz', '123123')
  1044. rot_type = rot_type.upper()
  1045. if rot_order not in approved_orders:
  1046. raise TypeError('The supplied order is not an approved type')
  1047. if rot_type == 'AXIS':
  1048. newframe.orient_axis(self, amounts[1], amounts[0])
  1049. elif rot_type == 'DCM':
  1050. newframe.orient_explicit(self, amounts)
  1051. elif rot_type == 'BODY':
  1052. newframe.orient_body_fixed(self, amounts, rot_order)
  1053. elif rot_type == 'SPACE':
  1054. newframe.orient_space_fixed(self, amounts, rot_order)
  1055. elif rot_type == 'QUATERNION':
  1056. newframe.orient_quaternion(self, amounts)
  1057. else:
  1058. raise NotImplementedError('That is not an implemented rotation')
  1059. return newframe
  1060. def set_ang_acc(self, otherframe, value):
  1061. """Define the angular acceleration Vector in a ReferenceFrame.
  1062. Defines the angular acceleration of this ReferenceFrame, in another.
  1063. Angular acceleration can be defined with respect to multiple different
  1064. ReferenceFrames. Care must be taken to not create loops which are
  1065. inconsistent.
  1066. Parameters
  1067. ==========
  1068. otherframe : ReferenceFrame
  1069. A ReferenceFrame to define the angular acceleration in
  1070. value : Vector
  1071. The Vector representing angular acceleration
  1072. Examples
  1073. ========
  1074. >>> from sympy.physics.vector import ReferenceFrame
  1075. >>> N = ReferenceFrame('N')
  1076. >>> A = ReferenceFrame('A')
  1077. >>> V = 10 * N.x
  1078. >>> A.set_ang_acc(N, V)
  1079. >>> A.ang_acc_in(N)
  1080. 10*N.x
  1081. """
  1082. if value == 0:
  1083. value = Vector(0)
  1084. value = _check_vector(value)
  1085. _check_frame(otherframe)
  1086. self._ang_acc_dict.update({otherframe: value})
  1087. otherframe._ang_acc_dict.update({self: -value})
  1088. def set_ang_vel(self, otherframe, value):
  1089. """Define the angular velocity vector in a ReferenceFrame.
  1090. Defines the angular velocity of this ReferenceFrame, in another.
  1091. Angular velocity can be defined with respect to multiple different
  1092. ReferenceFrames. Care must be taken to not create loops which are
  1093. inconsistent.
  1094. Parameters
  1095. ==========
  1096. otherframe : ReferenceFrame
  1097. A ReferenceFrame to define the angular velocity in
  1098. value : Vector
  1099. The Vector representing angular velocity
  1100. Examples
  1101. ========
  1102. >>> from sympy.physics.vector import ReferenceFrame
  1103. >>> N = ReferenceFrame('N')
  1104. >>> A = ReferenceFrame('A')
  1105. >>> V = 10 * N.x
  1106. >>> A.set_ang_vel(N, V)
  1107. >>> A.ang_vel_in(N)
  1108. 10*N.x
  1109. """
  1110. if value == 0:
  1111. value = Vector(0)
  1112. value = _check_vector(value)
  1113. _check_frame(otherframe)
  1114. self._ang_vel_dict.update({otherframe: value})
  1115. otherframe._ang_vel_dict.update({self: -value})
  1116. @property
  1117. def x(self):
  1118. """The basis Vector for the ReferenceFrame, in the x direction. """
  1119. return self._x
  1120. @property
  1121. def y(self):
  1122. """The basis Vector for the ReferenceFrame, in the y direction. """
  1123. return self._y
  1124. @property
  1125. def z(self):
  1126. """The basis Vector for the ReferenceFrame, in the z direction. """
  1127. return self._z
  1128. def partial_velocity(self, frame, *gen_speeds):
  1129. """Returns the partial angular velocities of this frame in the given
  1130. frame with respect to one or more provided generalized speeds.
  1131. Parameters
  1132. ==========
  1133. frame : ReferenceFrame
  1134. The frame with which the angular velocity is defined in.
  1135. gen_speeds : functions of time
  1136. The generalized speeds.
  1137. Returns
  1138. =======
  1139. partial_velocities : tuple of Vector
  1140. The partial angular velocity vectors corresponding to the provided
  1141. generalized speeds.
  1142. Examples
  1143. ========
  1144. >>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
  1145. >>> N = ReferenceFrame('N')
  1146. >>> A = ReferenceFrame('A')
  1147. >>> u1, u2 = dynamicsymbols('u1, u2')
  1148. >>> A.set_ang_vel(N, u1 * A.x + u2 * N.y)
  1149. >>> A.partial_velocity(N, u1)
  1150. A.x
  1151. >>> A.partial_velocity(N, u1, u2)
  1152. (A.x, N.y)
  1153. """
  1154. partials = [self.ang_vel_in(frame).diff(speed, frame, var_in_dcm=False)
  1155. for speed in gen_speeds]
  1156. if len(partials) == 1:
  1157. return partials[0]
  1158. else:
  1159. return tuple(partials)
  1160. def _check_frame(other):
  1161. from .vector import VectorTypeError
  1162. if not isinstance(other, ReferenceFrame):
  1163. raise VectorTypeError(other, ReferenceFrame('A'))