joint.py 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163
  1. # coding=utf-8
  2. from abc import ABC, abstractmethod
  3. from sympy.core.backend import pi, AppliedUndef, Derivative, Matrix
  4. from sympy.physics.mechanics.body import Body
  5. from sympy.physics.mechanics.functions import _validate_coordinates
  6. from sympy.physics.vector import (Vector, dynamicsymbols, cross, Point,
  7. ReferenceFrame)
  8. from sympy.utilities.iterables import iterable
  9. from sympy.utilities.exceptions import sympy_deprecation_warning
  10. __all__ = ['Joint', 'PinJoint', 'PrismaticJoint', 'CylindricalJoint',
  11. 'PlanarJoint', 'SphericalJoint', 'WeldJoint']
  12. class Joint(ABC):
  13. """Abstract base class for all specific joints.
  14. Explanation
  15. ===========
  16. A joint subtracts degrees of freedom from a body. This is the base class
  17. for all specific joints and holds all common methods acting as an interface
  18. for all joints. Custom joint can be created by inheriting Joint class and
  19. defining all abstract functions.
  20. The abstract methods are:
  21. - ``_generate_coordinates``
  22. - ``_generate_speeds``
  23. - ``_orient_frames``
  24. - ``_set_angular_velocity``
  25. - ``_set_linear_velocity``
  26. Parameters
  27. ==========
  28. name : string
  29. A unique name for the joint.
  30. parent : Body
  31. The parent body of joint.
  32. child : Body
  33. The child body of joint.
  34. coordinates : iterable of dynamicsymbols, optional
  35. Generalized coordinates of the joint.
  36. speeds : iterable of dynamicsymbols, optional
  37. Generalized speeds of joint.
  38. parent_point : Point or Vector, optional
  39. Attachment point where the joint is fixed to the parent body. If a
  40. vector is provided, then the attachment point is computed by adding the
  41. vector to the body's mass center. The default value is the parent's mass
  42. center.
  43. child_point : Point or Vector, optional
  44. Attachment point where the joint is fixed to the child body. If a
  45. vector is provided, then the attachment point is computed by adding the
  46. vector to the body's mass center. The default value is the child's mass
  47. center.
  48. parent_axis : Vector, optional
  49. .. deprecated:: 1.12
  50. Axis fixed in the parent body which aligns with an axis fixed in the
  51. child body. The default is the x axis of parent's reference frame.
  52. For more information on this deprecation, see
  53. :ref:`deprecated-mechanics-joint-axis`.
  54. child_axis : Vector, optional
  55. .. deprecated:: 1.12
  56. Axis fixed in the child body which aligns with an axis fixed in the
  57. parent body. The default is the x axis of child's reference frame.
  58. For more information on this deprecation, see
  59. :ref:`deprecated-mechanics-joint-axis`.
  60. parent_interframe : ReferenceFrame, optional
  61. Intermediate frame of the parent body with respect to which the joint
  62. transformation is formulated. If a Vector is provided then an interframe
  63. is created which aligns its X axis with the given vector. The default
  64. value is the parent's own frame.
  65. child_interframe : ReferenceFrame, optional
  66. Intermediate frame of the child body with respect to which the joint
  67. transformation is formulated. If a Vector is provided then an interframe
  68. is created which aligns its X axis with the given vector. The default
  69. value is the child's own frame.
  70. parent_joint_pos : Point or Vector, optional
  71. .. deprecated:: 1.12
  72. This argument is replaced by parent_point and will be removed in a
  73. future version.
  74. See :ref:`deprecated-mechanics-joint-pos` for more information.
  75. child_joint_pos : Point or Vector, optional
  76. .. deprecated:: 1.12
  77. This argument is replaced by child_point and will be removed in a
  78. future version.
  79. See :ref:`deprecated-mechanics-joint-pos` for more information.
  80. Attributes
  81. ==========
  82. name : string
  83. The joint's name.
  84. parent : Body
  85. The joint's parent body.
  86. child : Body
  87. The joint's child body.
  88. coordinates : Matrix
  89. Matrix of the joint's generalized coordinates.
  90. speeds : Matrix
  91. Matrix of the joint's generalized speeds.
  92. parent_point : Point
  93. Attachment point where the joint is fixed to the parent body.
  94. child_point : Point
  95. Attachment point where the joint is fixed to the child body.
  96. parent_axis : Vector
  97. The axis fixed in the parent frame that represents the joint.
  98. child_axis : Vector
  99. The axis fixed in the child frame that represents the joint.
  100. parent_interframe : ReferenceFrame
  101. Intermediate frame of the parent body with respect to which the joint
  102. transformation is formulated.
  103. child_interframe : ReferenceFrame
  104. Intermediate frame of the child body with respect to which the joint
  105. transformation is formulated.
  106. kdes : Matrix
  107. Kinematical differential equations of the joint.
  108. Notes
  109. =====
  110. When providing a vector as the intermediate frame, a new intermediate frame
  111. is created which aligns its X axis with the provided vector. This is done
  112. with a single fixed rotation about a rotation axis. This rotation axis is
  113. determined by taking the cross product of the ``body.x`` axis with the
  114. provided vector. In the case where the provided vector is in the ``-body.x``
  115. direction, the rotation is done about the ``body.y`` axis.
  116. """
  117. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  118. parent_point=None, child_point=None, parent_axis=None,
  119. child_axis=None, parent_interframe=None, child_interframe=None,
  120. parent_joint_pos=None, child_joint_pos=None):
  121. if not isinstance(name, str):
  122. raise TypeError('Supply a valid name.')
  123. self._name = name
  124. if not isinstance(parent, Body):
  125. raise TypeError('Parent must be an instance of Body.')
  126. self._parent = parent
  127. if not isinstance(child, Body):
  128. raise TypeError('Parent must be an instance of Body.')
  129. self._child = child
  130. self._coordinates = self._generate_coordinates(coordinates)
  131. self._speeds = self._generate_speeds(speeds)
  132. _validate_coordinates(self.coordinates, self.speeds)
  133. self._kdes = self._generate_kdes()
  134. self._parent_axis = self._axis(parent_axis, parent.frame)
  135. self._child_axis = self._axis(child_axis, child.frame)
  136. if parent_joint_pos is not None or child_joint_pos is not None:
  137. sympy_deprecation_warning(
  138. """
  139. The parent_joint_pos and child_joint_pos arguments for the Joint
  140. classes are deprecated. Instead use parent_point and child_point.
  141. """,
  142. deprecated_since_version="1.12",
  143. active_deprecations_target="deprecated-mechanics-joint-pos",
  144. stacklevel=4
  145. )
  146. if parent_point is None:
  147. parent_point = parent_joint_pos
  148. if child_point is None:
  149. child_point = child_joint_pos
  150. self._parent_point = self._locate_joint_pos(parent, parent_point)
  151. self._child_point = self._locate_joint_pos(child, child_point)
  152. if parent_axis is not None or child_axis is not None:
  153. sympy_deprecation_warning(
  154. """
  155. The parent_axis and child_axis arguments for the Joint classes
  156. are deprecated. Instead use parent_interframe, child_interframe.
  157. """,
  158. deprecated_since_version="1.12",
  159. active_deprecations_target="deprecated-mechanics-joint-axis",
  160. stacklevel=4
  161. )
  162. if parent_interframe is None:
  163. parent_interframe = parent_axis
  164. if child_interframe is None:
  165. child_interframe = child_axis
  166. self._parent_interframe = self._locate_joint_frame(parent,
  167. parent_interframe)
  168. self._child_interframe = self._locate_joint_frame(child,
  169. child_interframe)
  170. self._orient_frames()
  171. self._set_angular_velocity()
  172. self._set_linear_velocity()
  173. def __str__(self):
  174. return self.name
  175. def __repr__(self):
  176. return self.__str__()
  177. @property
  178. def name(self):
  179. """Name of the joint."""
  180. return self._name
  181. @property
  182. def parent(self):
  183. """Parent body of Joint."""
  184. return self._parent
  185. @property
  186. def child(self):
  187. """Child body of Joint."""
  188. return self._child
  189. @property
  190. def coordinates(self):
  191. """Matrix of the joint's generalized coordinates."""
  192. return self._coordinates
  193. @property
  194. def speeds(self):
  195. """Matrix of the joint's generalized speeds."""
  196. return self._speeds
  197. @property
  198. def kdes(self):
  199. """Kinematical differential equations of the joint."""
  200. return self._kdes
  201. @property
  202. def parent_axis(self):
  203. """The axis of parent frame."""
  204. # Will be removed with `deprecated-mechanics-joint-axis`
  205. return self._parent_axis
  206. @property
  207. def child_axis(self):
  208. """The axis of child frame."""
  209. # Will be removed with `deprecated-mechanics-joint-axis`
  210. return self._child_axis
  211. @property
  212. def parent_point(self):
  213. """Attachment point where the joint is fixed to the parent body."""
  214. return self._parent_point
  215. @property
  216. def child_point(self):
  217. """Attachment point where the joint is fixed to the child body."""
  218. return self._child_point
  219. @property
  220. def parent_interframe(self):
  221. return self._parent_interframe
  222. @property
  223. def child_interframe(self):
  224. return self._child_interframe
  225. @abstractmethod
  226. def _generate_coordinates(self, coordinates):
  227. """Generate Matrix of the joint's generalized coordinates."""
  228. pass
  229. @abstractmethod
  230. def _generate_speeds(self, speeds):
  231. """Generate Matrix of the joint's generalized speeds."""
  232. pass
  233. @abstractmethod
  234. def _orient_frames(self):
  235. """Orient frames as per the joint."""
  236. pass
  237. @abstractmethod
  238. def _set_angular_velocity(self):
  239. """Set angular velocity of the joint related frames."""
  240. pass
  241. @abstractmethod
  242. def _set_linear_velocity(self):
  243. """Set velocity of related points to the joint."""
  244. pass
  245. @staticmethod
  246. def _to_vector(matrix, frame):
  247. """Converts a matrix to a vector in the given frame."""
  248. return Vector([(matrix, frame)])
  249. @staticmethod
  250. def _axis(ax, *frames):
  251. """Check whether an axis is fixed in one of the frames."""
  252. if ax is None:
  253. ax = frames[0].x
  254. return ax
  255. if not isinstance(ax, Vector):
  256. raise TypeError("Axis must be a Vector.")
  257. ref_frame = None # Find a body in which the axis can be expressed
  258. for frame in frames:
  259. try:
  260. ax.to_matrix(frame)
  261. except ValueError:
  262. pass
  263. else:
  264. ref_frame = frame
  265. break
  266. if ref_frame is None:
  267. raise ValueError("Axis cannot be expressed in one of the body's "
  268. "frames.")
  269. if not ax.dt(ref_frame) == 0:
  270. raise ValueError('Axis cannot be time-varying when viewed from the '
  271. 'associated body.')
  272. return ax
  273. @staticmethod
  274. def _choose_rotation_axis(frame, axis):
  275. components = axis.to_matrix(frame)
  276. x, y, z = components[0], components[1], components[2]
  277. if x != 0:
  278. if y != 0:
  279. if z != 0:
  280. return cross(axis, frame.x)
  281. if z != 0:
  282. return frame.y
  283. return frame.z
  284. else:
  285. if y != 0:
  286. return frame.x
  287. return frame.y
  288. @staticmethod
  289. def _create_aligned_interframe(frame, align_axis, frame_axis=None,
  290. frame_name=None):
  291. """
  292. Returns an intermediate frame, where the ``frame_axis`` defined in
  293. ``frame`` is aligned with ``axis``. By default this means that the X
  294. axis will be aligned with ``axis``.
  295. Parameters
  296. ==========
  297. frame : Body or ReferenceFrame
  298. The body or reference frame with respect to which the intermediate
  299. frame is oriented.
  300. align_axis : Vector
  301. The vector with respect to which the intermediate frame will be
  302. aligned.
  303. frame_axis : Vector
  304. The vector of the frame which should get aligned with ``axis``. The
  305. default is the X axis of the frame.
  306. frame_name : string
  307. Name of the to be created intermediate frame. The default adds
  308. "_int_frame" to the name of ``frame``.
  309. Example
  310. =======
  311. An intermediate frame, where the X axis of the parent becomes aligned
  312. with ``parent.y + parent.z`` can be created as follows:
  313. >>> from sympy.physics.mechanics.joint import Joint
  314. >>> from sympy.physics.mechanics import Body
  315. >>> parent = Body('parent')
  316. >>> parent_interframe = Joint._create_aligned_interframe(
  317. ... parent, parent.y + parent.z)
  318. >>> parent_interframe
  319. parent_int_frame
  320. >>> parent.dcm(parent_interframe)
  321. Matrix([
  322. [ 0, -sqrt(2)/2, -sqrt(2)/2],
  323. [sqrt(2)/2, 1/2, -1/2],
  324. [sqrt(2)/2, -1/2, 1/2]])
  325. >>> (parent.y + parent.z).express(parent_interframe)
  326. sqrt(2)*parent_int_frame.x
  327. Notes
  328. =====
  329. The direction cosine matrix between the given frame and intermediate
  330. frame is formed using a simple rotation about an axis that is normal to
  331. both ``align_axis`` and ``frame_axis``. In general, the normal axis is
  332. formed by crossing the ``frame_axis`` with the ``align_axis``. The
  333. exception is if the axes are parallel with opposite directions, in which
  334. case the rotation vector is chosen using the rules in the following
  335. table with the vectors expressed in the given frame:
  336. .. list-table::
  337. :header-rows: 1
  338. * - ``align_axis``
  339. - ``frame_axis``
  340. - ``rotation_axis``
  341. * - ``-x``
  342. - ``x``
  343. - ``z``
  344. * - ``-y``
  345. - ``y``
  346. - ``x``
  347. * - ``-z``
  348. - ``z``
  349. - ``y``
  350. * - ``-x-y``
  351. - ``x+y``
  352. - ``z``
  353. * - ``-y-z``
  354. - ``y+z``
  355. - ``x``
  356. * - ``-x-z``
  357. - ``x+z``
  358. - ``y``
  359. * - ``-x-y-z``
  360. - ``x+y+z``
  361. - ``(x+y+z) × x``
  362. """
  363. if isinstance(frame, Body):
  364. frame = frame.frame
  365. if frame_axis is None:
  366. frame_axis = frame.x
  367. if frame_name is None:
  368. if frame.name[-6:] == '_frame':
  369. frame_name = f'{frame.name[:-6]}_int_frame'
  370. else:
  371. frame_name = f'{frame.name}_int_frame'
  372. angle = frame_axis.angle_between(align_axis)
  373. rotation_axis = cross(frame_axis, align_axis)
  374. if rotation_axis == Vector(0) and angle == 0:
  375. return frame
  376. if angle == pi:
  377. rotation_axis = Joint._choose_rotation_axis(frame, align_axis)
  378. int_frame = ReferenceFrame(frame_name)
  379. int_frame.orient_axis(frame, rotation_axis, angle)
  380. int_frame.set_ang_vel(frame, 0 * rotation_axis)
  381. return int_frame
  382. def _generate_kdes(self):
  383. """Generate kinematical differential equations."""
  384. kdes = []
  385. t = dynamicsymbols._t
  386. for i in range(len(self.coordinates)):
  387. kdes.append(-self.coordinates[i].diff(t) + self.speeds[i])
  388. return Matrix(kdes)
  389. def _locate_joint_pos(self, body, joint_pos):
  390. """Returns the attachment point of a body."""
  391. if joint_pos is None:
  392. return body.masscenter
  393. if not isinstance(joint_pos, (Point, Vector)):
  394. raise TypeError('Attachment point must be a Point or Vector.')
  395. if isinstance(joint_pos, Vector):
  396. point_name = f'{self.name}_{body.name}_joint'
  397. joint_pos = body.masscenter.locatenew(point_name, joint_pos)
  398. if not joint_pos.pos_from(body.masscenter).dt(body.frame) == 0:
  399. raise ValueError('Attachment point must be fixed to the associated '
  400. 'body.')
  401. return joint_pos
  402. def _locate_joint_frame(self, body, interframe):
  403. """Returns the attachment frame of a body."""
  404. if interframe is None:
  405. return body.frame
  406. if isinstance(interframe, Vector):
  407. interframe = Joint._create_aligned_interframe(
  408. body, interframe,
  409. frame_name=f'{self.name}_{body.name}_int_frame')
  410. elif not isinstance(interframe, ReferenceFrame):
  411. raise TypeError('Interframe must be a ReferenceFrame.')
  412. if not interframe.ang_vel_in(body.frame) == 0:
  413. raise ValueError(f'Interframe {interframe} is not fixed to body '
  414. f'{body}.')
  415. body.masscenter.set_vel(interframe, 0) # Fixate interframe to body
  416. return interframe
  417. def _fill_coordinate_list(self, coordinates, n_coords, label='q', offset=0,
  418. number_single=False):
  419. """Helper method for _generate_coordinates and _generate_speeds.
  420. Parameters
  421. ==========
  422. coordinates : iterable
  423. Iterable of coordinates or speeds that have been provided.
  424. n_coords : Integer
  425. Number of coordinates that should be returned.
  426. label : String, optional
  427. Coordinate type either 'q' (coordinates) or 'u' (speeds). The
  428. Default is 'q'.
  429. offset : Integer
  430. Count offset when creating new dynamicsymbols. The default is 0.
  431. number_single : Boolean
  432. Boolean whether if n_coords == 1, number should still be used. The
  433. default is False.
  434. """
  435. def create_symbol(number):
  436. if n_coords == 1 and not number_single:
  437. return dynamicsymbols(f'{label}_{self.name}')
  438. return dynamicsymbols(f'{label}{number}_{self.name}')
  439. name = 'generalized coordinate' if label == 'q' else 'generalized speed'
  440. generated_coordinates = []
  441. if coordinates is None:
  442. coordinates = []
  443. elif not iterable(coordinates):
  444. coordinates = [coordinates]
  445. if not (len(coordinates) == 0 or len(coordinates) == n_coords):
  446. raise ValueError(f'Expected {n_coords} {name}s, instead got '
  447. f'{len(coordinates)} {name}s.')
  448. # Supports more iterables, also Matrix
  449. for i, coord in enumerate(coordinates):
  450. if coord is None:
  451. generated_coordinates.append(create_symbol(i + offset))
  452. elif isinstance(coord, (AppliedUndef, Derivative)):
  453. generated_coordinates.append(coord)
  454. else:
  455. raise TypeError(f'The {name} {coord} should have been a '
  456. f'dynamicsymbol.')
  457. for i in range(len(coordinates) + offset, n_coords + offset):
  458. generated_coordinates.append(create_symbol(i))
  459. return Matrix(generated_coordinates)
  460. class PinJoint(Joint):
  461. """Pin (Revolute) Joint.
  462. .. image:: PinJoint.svg
  463. Explanation
  464. ===========
  465. A pin joint is defined such that the joint rotation axis is fixed in both
  466. the child and parent and the location of the joint is relative to the mass
  467. center of each body. The child rotates an angle, θ, from the parent about
  468. the rotation axis and has a simple angular speed, ω, relative to the
  469. parent. The direction cosine matrix between the child interframe and
  470. parent interframe is formed using a simple rotation about the joint axis.
  471. The page on the joints framework gives a more detailed explanation of the
  472. intermediate frames.
  473. Parameters
  474. ==========
  475. name : string
  476. A unique name for the joint.
  477. parent : Body
  478. The parent body of joint.
  479. child : Body
  480. The child body of joint.
  481. coordinates : dynamicsymbol, optional
  482. Generalized coordinates of the joint.
  483. speeds : dynamicsymbol, optional
  484. Generalized speeds of joint.
  485. parent_point : Point or Vector, optional
  486. Attachment point where the joint is fixed to the parent body. If a
  487. vector is provided, then the attachment point is computed by adding the
  488. vector to the body's mass center. The default value is the parent's mass
  489. center.
  490. child_point : Point or Vector, optional
  491. Attachment point where the joint is fixed to the child body. If a
  492. vector is provided, then the attachment point is computed by adding the
  493. vector to the body's mass center. The default value is the child's mass
  494. center.
  495. parent_axis : Vector, optional
  496. .. deprecated:: 1.12
  497. Axis fixed in the parent body which aligns with an axis fixed in the
  498. child body. The default is the x axis of parent's reference frame.
  499. For more information on this deprecation, see
  500. :ref:`deprecated-mechanics-joint-axis`.
  501. child_axis : Vector, optional
  502. .. deprecated:: 1.12
  503. Axis fixed in the child body which aligns with an axis fixed in the
  504. parent body. The default is the x axis of child's reference frame.
  505. For more information on this deprecation, see
  506. :ref:`deprecated-mechanics-joint-axis`.
  507. parent_interframe : ReferenceFrame, optional
  508. Intermediate frame of the parent body with respect to which the joint
  509. transformation is formulated. If a Vector is provided then an interframe
  510. is created which aligns its X axis with the given vector. The default
  511. value is the parent's own frame.
  512. child_interframe : ReferenceFrame, optional
  513. Intermediate frame of the child body with respect to which the joint
  514. transformation is formulated. If a Vector is provided then an interframe
  515. is created which aligns its X axis with the given vector. The default
  516. value is the child's own frame.
  517. joint_axis : Vector
  518. The axis about which the rotation occurs. Note that the components
  519. of this axis are the same in the parent_interframe and child_interframe.
  520. parent_joint_pos : Point or Vector, optional
  521. .. deprecated:: 1.12
  522. This argument is replaced by parent_point and will be removed in a
  523. future version.
  524. See :ref:`deprecated-mechanics-joint-pos` for more information.
  525. child_joint_pos : Point or Vector, optional
  526. .. deprecated:: 1.12
  527. This argument is replaced by child_point and will be removed in a
  528. future version.
  529. See :ref:`deprecated-mechanics-joint-pos` for more information.
  530. Attributes
  531. ==========
  532. name : string
  533. The joint's name.
  534. parent : Body
  535. The joint's parent body.
  536. child : Body
  537. The joint's child body.
  538. coordinates : Matrix
  539. Matrix of the joint's generalized coordinates. The default value is
  540. ``dynamicsymbols(f'q_{joint.name}')``.
  541. speeds : Matrix
  542. Matrix of the joint's generalized speeds. The default value is
  543. ``dynamicsymbols(f'u_{joint.name}')``.
  544. parent_point : Point
  545. Attachment point where the joint is fixed to the parent body.
  546. child_point : Point
  547. Attachment point where the joint is fixed to the child body.
  548. parent_axis : Vector
  549. The axis fixed in the parent frame that represents the joint.
  550. child_axis : Vector
  551. The axis fixed in the child frame that represents the joint.
  552. parent_interframe : ReferenceFrame
  553. Intermediate frame of the parent body with respect to which the joint
  554. transformation is formulated.
  555. child_interframe : ReferenceFrame
  556. Intermediate frame of the child body with respect to which the joint
  557. transformation is formulated.
  558. joint_axis : Vector
  559. The axis about which the rotation occurs. Note that the components of
  560. this axis are the same in the parent_interframe and child_interframe.
  561. kdes : Matrix
  562. Kinematical differential equations of the joint.
  563. Examples
  564. =========
  565. A single pin joint is created from two bodies and has the following basic
  566. attributes:
  567. >>> from sympy.physics.mechanics import Body, PinJoint
  568. >>> parent = Body('P')
  569. >>> parent
  570. P
  571. >>> child = Body('C')
  572. >>> child
  573. C
  574. >>> joint = PinJoint('PC', parent, child)
  575. >>> joint
  576. PinJoint: PC parent: P child: C
  577. >>> joint.name
  578. 'PC'
  579. >>> joint.parent
  580. P
  581. >>> joint.child
  582. C
  583. >>> joint.parent_point
  584. P_masscenter
  585. >>> joint.child_point
  586. C_masscenter
  587. >>> joint.parent_axis
  588. P_frame.x
  589. >>> joint.child_axis
  590. C_frame.x
  591. >>> joint.coordinates
  592. Matrix([[q_PC(t)]])
  593. >>> joint.speeds
  594. Matrix([[u_PC(t)]])
  595. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  596. u_PC(t)*P_frame.x
  597. >>> joint.child.frame.dcm(joint.parent.frame)
  598. Matrix([
  599. [1, 0, 0],
  600. [0, cos(q_PC(t)), sin(q_PC(t))],
  601. [0, -sin(q_PC(t)), cos(q_PC(t))]])
  602. >>> joint.child_point.pos_from(joint.parent_point)
  603. 0
  604. To further demonstrate the use of the pin joint, the kinematics of simple
  605. double pendulum that rotates about the Z axis of each connected body can be
  606. created as follows.
  607. >>> from sympy import symbols, trigsimp
  608. >>> from sympy.physics.mechanics import Body, PinJoint
  609. >>> l1, l2 = symbols('l1 l2')
  610. First create bodies to represent the fixed ceiling and one to represent
  611. each pendulum bob.
  612. >>> ceiling = Body('C')
  613. >>> upper_bob = Body('U')
  614. >>> lower_bob = Body('L')
  615. The first joint will connect the upper bob to the ceiling by a distance of
  616. ``l1`` and the joint axis will be about the Z axis for each body.
  617. >>> ceiling_joint = PinJoint('P1', ceiling, upper_bob,
  618. ... child_point=-l1*upper_bob.frame.x,
  619. ... joint_axis=ceiling.frame.z)
  620. The second joint will connect the lower bob to the upper bob by a distance
  621. of ``l2`` and the joint axis will also be about the Z axis for each body.
  622. >>> pendulum_joint = PinJoint('P2', upper_bob, lower_bob,
  623. ... child_point=-l2*lower_bob.frame.x,
  624. ... joint_axis=upper_bob.frame.z)
  625. Once the joints are established the kinematics of the connected bodies can
  626. be accessed. First the direction cosine matrices of pendulum link relative
  627. to the ceiling are found:
  628. >>> upper_bob.frame.dcm(ceiling.frame)
  629. Matrix([
  630. [ cos(q_P1(t)), sin(q_P1(t)), 0],
  631. [-sin(q_P1(t)), cos(q_P1(t)), 0],
  632. [ 0, 0, 1]])
  633. >>> trigsimp(lower_bob.frame.dcm(ceiling.frame))
  634. Matrix([
  635. [ cos(q_P1(t) + q_P2(t)), sin(q_P1(t) + q_P2(t)), 0],
  636. [-sin(q_P1(t) + q_P2(t)), cos(q_P1(t) + q_P2(t)), 0],
  637. [ 0, 0, 1]])
  638. The position of the lower bob's masscenter is found with:
  639. >>> lower_bob.masscenter.pos_from(ceiling.masscenter)
  640. l1*U_frame.x + l2*L_frame.x
  641. The angular velocities of the two pendulum links can be computed with
  642. respect to the ceiling.
  643. >>> upper_bob.frame.ang_vel_in(ceiling.frame)
  644. u_P1(t)*C_frame.z
  645. >>> lower_bob.frame.ang_vel_in(ceiling.frame)
  646. u_P1(t)*C_frame.z + u_P2(t)*U_frame.z
  647. And finally, the linear velocities of the two pendulum bobs can be computed
  648. with respect to the ceiling.
  649. >>> upper_bob.masscenter.vel(ceiling.frame)
  650. l1*u_P1(t)*U_frame.y
  651. >>> lower_bob.masscenter.vel(ceiling.frame)
  652. l1*u_P1(t)*U_frame.y + l2*(u_P1(t) + u_P2(t))*L_frame.y
  653. """
  654. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  655. parent_point=None, child_point=None, parent_axis=None,
  656. child_axis=None, parent_interframe=None, child_interframe=None,
  657. joint_axis=None, parent_joint_pos=None, child_joint_pos=None):
  658. self._joint_axis = joint_axis
  659. super().__init__(name, parent, child, coordinates, speeds, parent_point,
  660. child_point, parent_axis, child_axis,
  661. parent_interframe, child_interframe, parent_joint_pos,
  662. child_joint_pos)
  663. def __str__(self):
  664. return (f'PinJoint: {self.name} parent: {self.parent} '
  665. f'child: {self.child}')
  666. @property
  667. def joint_axis(self):
  668. """Axis about which the child rotates with respect to the parent."""
  669. return self._joint_axis
  670. def _generate_coordinates(self, coordinate):
  671. return self._fill_coordinate_list(coordinate, 1, 'q')
  672. def _generate_speeds(self, speed):
  673. return self._fill_coordinate_list(speed, 1, 'u')
  674. def _orient_frames(self):
  675. self._joint_axis = self._axis(self.joint_axis, self.parent_interframe)
  676. self.child_interframe.orient_axis(
  677. self.parent_interframe, self.joint_axis, self.coordinates[0])
  678. def _set_angular_velocity(self):
  679. self.child_interframe.set_ang_vel(self.parent_interframe, self.speeds[
  680. 0] * self.joint_axis.normalize())
  681. def _set_linear_velocity(self):
  682. self.child_point.set_pos(self.parent_point, 0)
  683. self.parent_point.set_vel(self.parent.frame, 0)
  684. self.child_point.set_vel(self.child.frame, 0)
  685. self.child.masscenter.v2pt_theory(self.parent_point,
  686. self.parent.frame, self.child.frame)
  687. class PrismaticJoint(Joint):
  688. """Prismatic (Sliding) Joint.
  689. .. image:: PrismaticJoint.svg
  690. Explanation
  691. ===========
  692. It is defined such that the child body translates with respect to the parent
  693. body along the body-fixed joint axis. The location of the joint is defined
  694. by two points, one in each body, which coincide when the generalized
  695. coordinate is zero. The direction cosine matrix between the
  696. parent_interframe and child_interframe is the identity matrix. Therefore,
  697. the direction cosine matrix between the parent and child frames is fully
  698. defined by the definition of the intermediate frames. The page on the joints
  699. framework gives a more detailed explanation of the intermediate frames.
  700. Parameters
  701. ==========
  702. name : string
  703. A unique name for the joint.
  704. parent : Body
  705. The parent body of joint.
  706. child : Body
  707. The child body of joint.
  708. coordinates : dynamicsymbol, optional
  709. Generalized coordinates of the joint. The default value is
  710. ``dynamicsymbols(f'q_{joint.name}')``.
  711. speeds : dynamicsymbol, optional
  712. Generalized speeds of joint. The default value is
  713. ``dynamicsymbols(f'u_{joint.name}')``.
  714. parent_point : Point or Vector, optional
  715. Attachment point where the joint is fixed to the parent body. If a
  716. vector is provided, then the attachment point is computed by adding the
  717. vector to the body's mass center. The default value is the parent's mass
  718. center.
  719. child_point : Point or Vector, optional
  720. Attachment point where the joint is fixed to the child body. If a
  721. vector is provided, then the attachment point is computed by adding the
  722. vector to the body's mass center. The default value is the child's mass
  723. center.
  724. parent_axis : Vector, optional
  725. .. deprecated:: 1.12
  726. Axis fixed in the parent body which aligns with an axis fixed in the
  727. child body. The default is the x axis of parent's reference frame.
  728. For more information on this deprecation, see
  729. :ref:`deprecated-mechanics-joint-axis`.
  730. child_axis : Vector, optional
  731. .. deprecated:: 1.12
  732. Axis fixed in the child body which aligns with an axis fixed in the
  733. parent body. The default is the x axis of child's reference frame.
  734. For more information on this deprecation, see
  735. :ref:`deprecated-mechanics-joint-axis`.
  736. parent_interframe : ReferenceFrame, optional
  737. Intermediate frame of the parent body with respect to which the joint
  738. transformation is formulated. If a Vector is provided then an interframe
  739. is created which aligns its X axis with the given vector. The default
  740. value is the parent's own frame.
  741. child_interframe : ReferenceFrame, optional
  742. Intermediate frame of the child body with respect to which the joint
  743. transformation is formulated. If a Vector is provided then an interframe
  744. is created which aligns its X axis with the given vector. The default
  745. value is the child's own frame.
  746. joint_axis : Vector
  747. The axis along which the translation occurs. Note that the components
  748. of this axis are the same in the parent_interframe and child_interframe.
  749. parent_joint_pos : Point or Vector, optional
  750. .. deprecated:: 1.12
  751. This argument is replaced by parent_point and will be removed in a
  752. future version.
  753. See :ref:`deprecated-mechanics-joint-pos` for more information.
  754. child_joint_pos : Point or Vector, optional
  755. .. deprecated:: 1.12
  756. This argument is replaced by child_point and will be removed in a
  757. future version.
  758. See :ref:`deprecated-mechanics-joint-pos` for more information.
  759. Attributes
  760. ==========
  761. name : string
  762. The joint's name.
  763. parent : Body
  764. The joint's parent body.
  765. child : Body
  766. The joint's child body.
  767. coordinates : Matrix
  768. Matrix of the joint's generalized coordinates.
  769. speeds : Matrix
  770. Matrix of the joint's generalized speeds.
  771. parent_point : Point
  772. Attachment point where the joint is fixed to the parent body.
  773. child_point : Point
  774. Attachment point where the joint is fixed to the child body.
  775. parent_axis : Vector
  776. The axis fixed in the parent frame that represents the joint.
  777. child_axis : Vector
  778. The axis fixed in the child frame that represents the joint.
  779. parent_interframe : ReferenceFrame
  780. Intermediate frame of the parent body with respect to which the joint
  781. transformation is formulated.
  782. child_interframe : ReferenceFrame
  783. Intermediate frame of the child body with respect to which the joint
  784. transformation is formulated.
  785. kdes : Matrix
  786. Kinematical differential equations of the joint.
  787. Examples
  788. =========
  789. A single prismatic joint is created from two bodies and has the following
  790. basic attributes:
  791. >>> from sympy.physics.mechanics import Body, PrismaticJoint
  792. >>> parent = Body('P')
  793. >>> parent
  794. P
  795. >>> child = Body('C')
  796. >>> child
  797. C
  798. >>> joint = PrismaticJoint('PC', parent, child)
  799. >>> joint
  800. PrismaticJoint: PC parent: P child: C
  801. >>> joint.name
  802. 'PC'
  803. >>> joint.parent
  804. P
  805. >>> joint.child
  806. C
  807. >>> joint.parent_point
  808. P_masscenter
  809. >>> joint.child_point
  810. C_masscenter
  811. >>> joint.parent_axis
  812. P_frame.x
  813. >>> joint.child_axis
  814. C_frame.x
  815. >>> joint.coordinates
  816. Matrix([[q_PC(t)]])
  817. >>> joint.speeds
  818. Matrix([[u_PC(t)]])
  819. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  820. 0
  821. >>> joint.child.frame.dcm(joint.parent.frame)
  822. Matrix([
  823. [1, 0, 0],
  824. [0, 1, 0],
  825. [0, 0, 1]])
  826. >>> joint.child_point.pos_from(joint.parent_point)
  827. q_PC(t)*P_frame.x
  828. To further demonstrate the use of the prismatic joint, the kinematics of two
  829. masses sliding, one moving relative to a fixed body and the other relative
  830. to the moving body. about the X axis of each connected body can be created
  831. as follows.
  832. >>> from sympy.physics.mechanics import PrismaticJoint, Body
  833. First create bodies to represent the fixed ceiling and one to represent
  834. a particle.
  835. >>> wall = Body('W')
  836. >>> Part1 = Body('P1')
  837. >>> Part2 = Body('P2')
  838. The first joint will connect the particle to the ceiling and the
  839. joint axis will be about the X axis for each body.
  840. >>> J1 = PrismaticJoint('J1', wall, Part1)
  841. The second joint will connect the second particle to the first particle
  842. and the joint axis will also be about the X axis for each body.
  843. >>> J2 = PrismaticJoint('J2', Part1, Part2)
  844. Once the joint is established the kinematics of the connected bodies can
  845. be accessed. First the direction cosine matrices of Part relative
  846. to the ceiling are found:
  847. >>> Part1.dcm(wall)
  848. Matrix([
  849. [1, 0, 0],
  850. [0, 1, 0],
  851. [0, 0, 1]])
  852. >>> Part2.dcm(wall)
  853. Matrix([
  854. [1, 0, 0],
  855. [0, 1, 0],
  856. [0, 0, 1]])
  857. The position of the particles' masscenter is found with:
  858. >>> Part1.masscenter.pos_from(wall.masscenter)
  859. q_J1(t)*W_frame.x
  860. >>> Part2.masscenter.pos_from(wall.masscenter)
  861. q_J1(t)*W_frame.x + q_J2(t)*P1_frame.x
  862. The angular velocities of the two particle links can be computed with
  863. respect to the ceiling.
  864. >>> Part1.ang_vel_in(wall)
  865. 0
  866. >>> Part2.ang_vel_in(wall)
  867. 0
  868. And finally, the linear velocities of the two particles can be computed
  869. with respect to the ceiling.
  870. >>> Part1.masscenter_vel(wall)
  871. u_J1(t)*W_frame.x
  872. >>> Part2.masscenter.vel(wall.frame)
  873. u_J1(t)*W_frame.x + Derivative(q_J2(t), t)*P1_frame.x
  874. """
  875. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  876. parent_point=None, child_point=None, parent_axis=None,
  877. child_axis=None, parent_interframe=None, child_interframe=None,
  878. joint_axis=None, parent_joint_pos=None, child_joint_pos=None):
  879. self._joint_axis = joint_axis
  880. super().__init__(name, parent, child, coordinates, speeds, parent_point,
  881. child_point, parent_axis, child_axis,
  882. parent_interframe, child_interframe, parent_joint_pos,
  883. child_joint_pos)
  884. def __str__(self):
  885. return (f'PrismaticJoint: {self.name} parent: {self.parent} '
  886. f'child: {self.child}')
  887. @property
  888. def joint_axis(self):
  889. """Axis along which the child translates with respect to the parent."""
  890. return self._joint_axis
  891. def _generate_coordinates(self, coordinate):
  892. return self._fill_coordinate_list(coordinate, 1, 'q')
  893. def _generate_speeds(self, speed):
  894. return self._fill_coordinate_list(speed, 1, 'u')
  895. def _orient_frames(self):
  896. self._joint_axis = self._axis(self.joint_axis, self.parent_interframe)
  897. self.child_interframe.orient_axis(
  898. self.parent_interframe, self.joint_axis, 0)
  899. def _set_angular_velocity(self):
  900. self.child_interframe.set_ang_vel(self.parent_interframe, 0)
  901. def _set_linear_velocity(self):
  902. axis = self.joint_axis.normalize()
  903. self.child_point.set_pos(self.parent_point, self.coordinates[0] * axis)
  904. self.parent_point.set_vel(self.parent.frame, 0)
  905. self.child_point.set_vel(self.child.frame, 0)
  906. self.child_point.set_vel(self.parent.frame, self.speeds[0] * axis)
  907. self.child.masscenter.set_vel(self.parent.frame, self.speeds[0] * axis)
  908. class CylindricalJoint(Joint):
  909. """Cylindrical Joint.
  910. .. image:: CylindricalJoint.svg
  911. :align: center
  912. :width: 600
  913. Explanation
  914. ===========
  915. A cylindrical joint is defined such that the child body both rotates about
  916. and translates along the body-fixed joint axis with respect to the parent
  917. body. The joint axis is both the rotation axis and translation axis. The
  918. location of the joint is defined by two points, one in each body, which
  919. coincide when the generalized coordinate corresponding to the translation is
  920. zero. The direction cosine matrix between the child interframe and parent
  921. interframe is formed using a simple rotation about the joint axis. The page
  922. on the joints framework gives a more detailed explanation of the
  923. intermediate frames.
  924. Parameters
  925. ==========
  926. name : string
  927. A unique name for the joint.
  928. parent : Body
  929. The parent body of joint.
  930. child : Body
  931. The child body of joint.
  932. rotation_coordinate : dynamicsymbol, optional
  933. Generalized coordinate corresponding to the rotation angle. The default
  934. value is ``dynamicsymbols(f'q0_{joint.name}')``.
  935. translation_coordinate : dynamicsymbol, optional
  936. Generalized coordinate corresponding to the translation distance. The
  937. default value is ``dynamicsymbols(f'q1_{joint.name}')``.
  938. rotation_speed : dynamicsymbol, optional
  939. Generalized speed corresponding to the angular velocity. The default
  940. value is ``dynamicsymbols(f'u0_{joint.name}')``.
  941. translation_speed : dynamicsymbol, optional
  942. Generalized speed corresponding to the translation velocity. The default
  943. value is ``dynamicsymbols(f'u1_{joint.name}')``.
  944. parent_point : Point or Vector, optional
  945. Attachment point where the joint is fixed to the parent body. If a
  946. vector is provided, then the attachment point is computed by adding the
  947. vector to the body's mass center. The default value is the parent's mass
  948. center.
  949. child_point : Point or Vector, optional
  950. Attachment point where the joint is fixed to the child body. If a
  951. vector is provided, then the attachment point is computed by adding the
  952. vector to the body's mass center. The default value is the child's mass
  953. center.
  954. parent_interframe : ReferenceFrame, optional
  955. Intermediate frame of the parent body with respect to which the joint
  956. transformation is formulated. If a Vector is provided then an interframe
  957. is created which aligns its X axis with the given vector. The default
  958. value is the parent's own frame.
  959. child_interframe : ReferenceFrame, optional
  960. Intermediate frame of the child body with respect to which the joint
  961. transformation is formulated. If a Vector is provided then an interframe
  962. is created which aligns its X axis with the given vector. The default
  963. value is the child's own frame.
  964. joint_axis : Vector, optional
  965. The rotation as well as translation axis. Note that the components of
  966. this axis are the same in the parent_interframe and child_interframe.
  967. Attributes
  968. ==========
  969. name : string
  970. The joint's name.
  971. parent : Body
  972. The joint's parent body.
  973. child : Body
  974. The joint's child body.
  975. rotation_coordinate : dynamicsymbol
  976. Generalized coordinate corresponding to the rotation angle.
  977. translation_coordinate : dynamicsymbol
  978. Generalized coordinate corresponding to the translation distance.
  979. rotation_speed : dynamicsymbol
  980. Generalized speed corresponding to the angular velocity.
  981. translation_speed : dynamicsymbol
  982. Generalized speed corresponding to the translation velocity.
  983. coordinates : Matrix
  984. Matrix of the joint's generalized coordinates.
  985. speeds : Matrix
  986. Matrix of the joint's generalized speeds.
  987. parent_point : Point
  988. Attachment point where the joint is fixed to the parent body.
  989. child_point : Point
  990. Attachment point where the joint is fixed to the child body.
  991. parent_interframe : ReferenceFrame
  992. Intermediate frame of the parent body with respect to which the joint
  993. transformation is formulated.
  994. child_interframe : ReferenceFrame
  995. Intermediate frame of the child body with respect to which the joint
  996. transformation is formulated.
  997. kdes : Matrix
  998. Kinematical differential equations of the joint.
  999. joint_axis : Vector
  1000. The axis of rotation and translation.
  1001. Examples
  1002. =========
  1003. A single cylindrical joint is created between two bodies and has the
  1004. following basic attributes:
  1005. >>> from sympy.physics.mechanics import Body, CylindricalJoint
  1006. >>> parent = Body('P')
  1007. >>> parent
  1008. P
  1009. >>> child = Body('C')
  1010. >>> child
  1011. C
  1012. >>> joint = CylindricalJoint('PC', parent, child)
  1013. >>> joint
  1014. CylindricalJoint: PC parent: P child: C
  1015. >>> joint.name
  1016. 'PC'
  1017. >>> joint.parent
  1018. P
  1019. >>> joint.child
  1020. C
  1021. >>> joint.parent_point
  1022. P_masscenter
  1023. >>> joint.child_point
  1024. C_masscenter
  1025. >>> joint.parent_axis
  1026. P_frame.x
  1027. >>> joint.child_axis
  1028. C_frame.x
  1029. >>> joint.coordinates
  1030. Matrix([
  1031. [q0_PC(t)],
  1032. [q1_PC(t)]])
  1033. >>> joint.speeds
  1034. Matrix([
  1035. [u0_PC(t)],
  1036. [u1_PC(t)]])
  1037. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  1038. u0_PC(t)*P_frame.x
  1039. >>> joint.child.frame.dcm(joint.parent.frame)
  1040. Matrix([
  1041. [1, 0, 0],
  1042. [0, cos(q0_PC(t)), sin(q0_PC(t))],
  1043. [0, -sin(q0_PC(t)), cos(q0_PC(t))]])
  1044. >>> joint.child_point.pos_from(joint.parent_point)
  1045. q1_PC(t)*P_frame.x
  1046. >>> child.masscenter.vel(parent.frame)
  1047. u1_PC(t)*P_frame.x
  1048. To further demonstrate the use of the cylindrical joint, the kinematics of
  1049. two cylindrical joints perpendicular to each other can be created as follows.
  1050. >>> from sympy import symbols
  1051. >>> from sympy.physics.mechanics import Body, CylindricalJoint
  1052. >>> r, l, w = symbols('r l w')
  1053. First create bodies to represent the fixed floor with a fixed pole on it.
  1054. The second body represents a freely moving tube around that pole. The third
  1055. body represents a solid flag freely translating along and rotating around
  1056. the Y axis of the tube.
  1057. >>> floor = Body('floor')
  1058. >>> tube = Body('tube')
  1059. >>> flag = Body('flag')
  1060. The first joint will connect the first tube to the floor with it translating
  1061. along and rotating around the Z axis of both bodies.
  1062. >>> floor_joint = CylindricalJoint('C1', floor, tube, joint_axis=floor.z)
  1063. The second joint will connect the tube perpendicular to the flag along the Y
  1064. axis of both the tube and the flag, with the joint located at a distance
  1065. ``r`` from the tube's center of mass and a combination of the distances
  1066. ``l`` and ``w`` from the flag's center of mass.
  1067. >>> flag_joint = CylindricalJoint('C2', tube, flag,
  1068. ... parent_point=r * tube.y,
  1069. ... child_point=-w * flag.y + l * flag.z,
  1070. ... joint_axis=tube.y)
  1071. Once the joints are established the kinematics of the connected bodies can
  1072. be accessed. First the direction cosine matrices of both the body and the
  1073. flag relative to the floor are found:
  1074. >>> tube.dcm(floor)
  1075. Matrix([
  1076. [ cos(q0_C1(t)), sin(q0_C1(t)), 0],
  1077. [-sin(q0_C1(t)), cos(q0_C1(t)), 0],
  1078. [ 0, 0, 1]])
  1079. >>> flag.dcm(floor)
  1080. Matrix([
  1081. [cos(q0_C1(t))*cos(q0_C2(t)), sin(q0_C1(t))*cos(q0_C2(t)), -sin(q0_C2(t))],
  1082. [ -sin(q0_C1(t)), cos(q0_C1(t)), 0],
  1083. [sin(q0_C2(t))*cos(q0_C1(t)), sin(q0_C1(t))*sin(q0_C2(t)), cos(q0_C2(t))]])
  1084. The position of the flag's center of mass is found with:
  1085. >>> flag.masscenter.pos_from(floor.masscenter)
  1086. q1_C1(t)*floor_frame.z + (r + q1_C2(t))*tube_frame.y + w*flag_frame.y - l*flag_frame.z
  1087. The angular velocities of the two tubes can be computed with respect to the
  1088. floor.
  1089. >>> tube.ang_vel_in(floor)
  1090. u0_C1(t)*floor_frame.z
  1091. >>> flag.ang_vel_in(floor)
  1092. u0_C1(t)*floor_frame.z + u0_C2(t)*tube_frame.y
  1093. Finally, the linear velocities of the two tube centers of mass can be
  1094. computed with respect to the floor, while expressed in the tube's frame.
  1095. >>> tube.masscenter.vel(floor.frame).to_matrix(tube.frame)
  1096. Matrix([
  1097. [ 0],
  1098. [ 0],
  1099. [u1_C1(t)]])
  1100. >>> flag.masscenter.vel(floor.frame).to_matrix(tube.frame).simplify()
  1101. Matrix([
  1102. [-l*u0_C2(t)*cos(q0_C2(t)) - r*u0_C1(t) - w*u0_C1(t) - q1_C2(t)*u0_C1(t)],
  1103. [ -l*u0_C1(t)*sin(q0_C2(t)) + Derivative(q1_C2(t), t)],
  1104. [ l*u0_C2(t)*sin(q0_C2(t)) + u1_C1(t)]])
  1105. """
  1106. def __init__(self, name, parent, child, rotation_coordinate=None,
  1107. translation_coordinate=None, rotation_speed=None,
  1108. translation_speed=None, parent_point=None, child_point=None,
  1109. parent_interframe=None, child_interframe=None,
  1110. joint_axis=None):
  1111. self._joint_axis = joint_axis
  1112. coordinates = (rotation_coordinate, translation_coordinate)
  1113. speeds = (rotation_speed, translation_speed)
  1114. super().__init__(name, parent, child, coordinates, speeds,
  1115. parent_point, child_point,
  1116. parent_interframe=parent_interframe,
  1117. child_interframe=child_interframe)
  1118. def __str__(self):
  1119. return (f'CylindricalJoint: {self.name} parent: {self.parent} '
  1120. f'child: {self.child}')
  1121. @property
  1122. def joint_axis(self):
  1123. """Axis about and along which the rotation and translation occurs."""
  1124. return self._joint_axis
  1125. @property
  1126. def rotation_coordinate(self):
  1127. """Generalized coordinate corresponding to the rotation angle."""
  1128. return self.coordinates[0]
  1129. @property
  1130. def translation_coordinate(self):
  1131. """Generalized coordinate corresponding to the translation distance."""
  1132. return self.coordinates[1]
  1133. @property
  1134. def rotation_speed(self):
  1135. """Generalized speed corresponding to the angular velocity."""
  1136. return self.speeds[0]
  1137. @property
  1138. def translation_speed(self):
  1139. """Generalized speed corresponding to the translation velocity."""
  1140. return self.speeds[1]
  1141. def _generate_coordinates(self, coordinates):
  1142. return self._fill_coordinate_list(coordinates, 2, 'q')
  1143. def _generate_speeds(self, speeds):
  1144. return self._fill_coordinate_list(speeds, 2, 'u')
  1145. def _orient_frames(self):
  1146. self._joint_axis = self._axis(self.joint_axis, self.parent_interframe)
  1147. self.child_interframe.orient_axis(
  1148. self.parent_interframe, self.joint_axis, self.rotation_coordinate)
  1149. def _set_angular_velocity(self):
  1150. self.child_interframe.set_ang_vel(
  1151. self.parent_interframe,
  1152. self.rotation_speed * self.joint_axis.normalize())
  1153. def _set_linear_velocity(self):
  1154. self.child_point.set_pos(
  1155. self.parent_point,
  1156. self.translation_coordinate * self.joint_axis.normalize())
  1157. self.parent_point.set_vel(self.parent.frame, 0)
  1158. self.child_point.set_vel(self.child.frame, 0)
  1159. self.child_point.set_vel(
  1160. self.parent.frame,
  1161. self.translation_speed * self.joint_axis.normalize())
  1162. self.child.masscenter.v2pt_theory(self.child_point, self.parent.frame,
  1163. self.child_interframe)
  1164. class PlanarJoint(Joint):
  1165. """Planar Joint.
  1166. .. image:: PlanarJoint.svg
  1167. :align: center
  1168. :width: 800
  1169. Explanation
  1170. ===========
  1171. A planar joint is defined such that the child body translates over a fixed
  1172. plane of the parent body as well as rotate about the rotation axis, which
  1173. is perpendicular to that plane. The origin of this plane is the
  1174. ``parent_point`` and the plane is spanned by two nonparallel planar vectors.
  1175. The location of the ``child_point`` is based on the planar vectors
  1176. ($\\vec{v}_1$, $\\vec{v}_2$) and generalized coordinates ($q_1$, $q_2$),
  1177. i.e. $\\vec{r} = q_1 \\hat{v}_1 + q_2 \\hat{v}_2$. The direction cosine
  1178. matrix between the ``child_interframe`` and ``parent_interframe`` is formed
  1179. using a simple rotation ($q_0$) about the rotation axis.
  1180. In order to simplify the definition of the ``PlanarJoint``, the
  1181. ``rotation_axis`` and ``planar_vectors`` are set to be the unit vectors of
  1182. the ``parent_interframe`` according to the table below. This ensures that
  1183. you can only define these vectors by creating a separate frame and supplying
  1184. that as the interframe. If you however would only like to supply the normals
  1185. of the plane with respect to the parent and child bodies, then you can also
  1186. supply those to the ``parent_interframe`` and ``child_interframe``
  1187. arguments. An example of both of these cases is in the examples section
  1188. below and the page on the joints framework provides a more detailed
  1189. explanation of the intermediate frames.
  1190. .. list-table::
  1191. * - ``rotation_axis``
  1192. - ``parent_interframe.x``
  1193. * - ``planar_vectors[0]``
  1194. - ``parent_interframe.y``
  1195. * - ``planar_vectors[1]``
  1196. - ``parent_interframe.z``
  1197. Parameters
  1198. ==========
  1199. name : string
  1200. A unique name for the joint.
  1201. parent : Body
  1202. The parent body of joint.
  1203. child : Body
  1204. The child body of joint.
  1205. rotation_coordinate : dynamicsymbol, optional
  1206. Generalized coordinate corresponding to the rotation angle. The default
  1207. value is ``dynamicsymbols(f'q0_{joint.name}')``.
  1208. planar_coordinates : iterable of dynamicsymbols, optional
  1209. Two generalized coordinates used for the planar translation. The default
  1210. value is ``dynamicsymbols(f'q1_{joint.name} q2_{joint.name}')``.
  1211. rotation_speed : dynamicsymbol, optional
  1212. Generalized speed corresponding to the angular velocity. The default
  1213. value is ``dynamicsymbols(f'u0_{joint.name}')``.
  1214. planar_speeds : dynamicsymbols, optional
  1215. Two generalized speeds used for the planar translation velocity. The
  1216. default value is ``dynamicsymbols(f'u1_{joint.name} u2_{joint.name}')``.
  1217. parent_point : Point or Vector, optional
  1218. Attachment point where the joint is fixed to the parent body. If a
  1219. vector is provided, then the attachment point is computed by adding the
  1220. vector to the body's mass center. The default value is the parent's mass
  1221. center.
  1222. child_point : Point or Vector, optional
  1223. Attachment point where the joint is fixed to the child body. If a
  1224. vector is provided, then the attachment point is computed by adding the
  1225. vector to the body's mass center. The default value is the child's mass
  1226. center.
  1227. parent_interframe : ReferenceFrame, optional
  1228. Intermediate frame of the parent body with respect to which the joint
  1229. transformation is formulated. If a Vector is provided then an interframe
  1230. is created which aligns its X axis with the given vector. The default
  1231. value is the parent's own frame.
  1232. child_interframe : ReferenceFrame, optional
  1233. Intermediate frame of the child body with respect to which the joint
  1234. transformation is formulated. If a Vector is provided then an interframe
  1235. is created which aligns its X axis with the given vector. The default
  1236. value is the child's own frame.
  1237. Attributes
  1238. ==========
  1239. name : string
  1240. The joint's name.
  1241. parent : Body
  1242. The joint's parent body.
  1243. child : Body
  1244. The joint's child body.
  1245. rotation_coordinate : dynamicsymbol
  1246. Generalized coordinate corresponding to the rotation angle.
  1247. planar_coordinates : Matrix
  1248. Two generalized coordinates used for the planar translation.
  1249. rotation_speed : dynamicsymbol
  1250. Generalized speed corresponding to the angular velocity.
  1251. planar_speeds : Matrix
  1252. Two generalized speeds used for the planar translation velocity.
  1253. coordinates : Matrix
  1254. Matrix of the joint's generalized coordinates.
  1255. speeds : Matrix
  1256. Matrix of the joint's generalized speeds.
  1257. parent_point : Point
  1258. Attachment point where the joint is fixed to the parent body.
  1259. child_point : Point
  1260. Attachment point where the joint is fixed to the child body.
  1261. parent_interframe : ReferenceFrame
  1262. Intermediate frame of the parent body with respect to which the joint
  1263. transformation is formulated.
  1264. child_interframe : ReferenceFrame
  1265. Intermediate frame of the child body with respect to which the joint
  1266. transformation is formulated.
  1267. kdes : Matrix
  1268. Kinematical differential equations of the joint.
  1269. rotation_axis : Vector
  1270. The axis about which the rotation occurs.
  1271. planar_vectors : list
  1272. The vectors that describe the planar translation directions.
  1273. Examples
  1274. =========
  1275. A single planar joint is created between two bodies and has the following
  1276. basic attributes:
  1277. >>> from sympy.physics.mechanics import Body, PlanarJoint
  1278. >>> parent = Body('P')
  1279. >>> parent
  1280. P
  1281. >>> child = Body('C')
  1282. >>> child
  1283. C
  1284. >>> joint = PlanarJoint('PC', parent, child)
  1285. >>> joint
  1286. PlanarJoint: PC parent: P child: C
  1287. >>> joint.name
  1288. 'PC'
  1289. >>> joint.parent
  1290. P
  1291. >>> joint.child
  1292. C
  1293. >>> joint.parent_point
  1294. P_masscenter
  1295. >>> joint.child_point
  1296. C_masscenter
  1297. >>> joint.rotation_axis
  1298. P_frame.x
  1299. >>> joint.planar_vectors
  1300. [P_frame.y, P_frame.z]
  1301. >>> joint.rotation_coordinate
  1302. q0_PC(t)
  1303. >>> joint.planar_coordinates
  1304. Matrix([
  1305. [q1_PC(t)],
  1306. [q2_PC(t)]])
  1307. >>> joint.coordinates
  1308. Matrix([
  1309. [q0_PC(t)],
  1310. [q1_PC(t)],
  1311. [q2_PC(t)]])
  1312. >>> joint.rotation_speed
  1313. u0_PC(t)
  1314. >>> joint.planar_speeds
  1315. Matrix([
  1316. [u1_PC(t)],
  1317. [u2_PC(t)]])
  1318. >>> joint.speeds
  1319. Matrix([
  1320. [u0_PC(t)],
  1321. [u1_PC(t)],
  1322. [u2_PC(t)]])
  1323. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  1324. u0_PC(t)*P_frame.x
  1325. >>> joint.child.frame.dcm(joint.parent.frame)
  1326. Matrix([
  1327. [1, 0, 0],
  1328. [0, cos(q0_PC(t)), sin(q0_PC(t))],
  1329. [0, -sin(q0_PC(t)), cos(q0_PC(t))]])
  1330. >>> joint.child_point.pos_from(joint.parent_point)
  1331. q1_PC(t)*P_frame.y + q2_PC(t)*P_frame.z
  1332. >>> child.masscenter.vel(parent.frame)
  1333. u1_PC(t)*P_frame.y + u2_PC(t)*P_frame.z
  1334. To further demonstrate the use of the planar joint, the kinematics of a
  1335. block sliding on a slope, can be created as follows.
  1336. >>> from sympy import symbols
  1337. >>> from sympy.physics.mechanics import PlanarJoint, Body, ReferenceFrame
  1338. >>> a, d, h = symbols('a d h')
  1339. First create bodies to represent the slope and the block.
  1340. >>> ground = Body('G')
  1341. >>> block = Body('B')
  1342. To define the slope you can either define the plane by specifying the
  1343. ``planar_vectors`` or/and the ``rotation_axis``. However it is advisable to
  1344. create a rotated intermediate frame, so that the ``parent_vectors`` and
  1345. ``rotation_axis`` will be the unit vectors of this intermediate frame.
  1346. >>> slope = ReferenceFrame('A')
  1347. >>> slope.orient_axis(ground.frame, ground.y, a)
  1348. The planar joint can be created using these bodies and intermediate frame.
  1349. We can specify the origin of the slope to be ``d`` above the slope's center
  1350. of mass and the block's center of mass to be a distance ``h`` above the
  1351. slope's surface. Note that we can specify the normal of the plane using the
  1352. rotation axis argument.
  1353. >>> joint = PlanarJoint('PC', ground, block, parent_point=d * ground.x,
  1354. ... child_point=-h * block.x, parent_interframe=slope)
  1355. Once the joint is established the kinematics of the bodies can be accessed.
  1356. First the ``rotation_axis``, which is normal to the plane and the
  1357. ``plane_vectors``, can be found.
  1358. >>> joint.rotation_axis
  1359. A.x
  1360. >>> joint.planar_vectors
  1361. [A.y, A.z]
  1362. The direction cosine matrix of the block with respect to the ground can be
  1363. found with:
  1364. >>> block.dcm(ground)
  1365. Matrix([
  1366. [ cos(a), 0, -sin(a)],
  1367. [sin(a)*sin(q0_PC(t)), cos(q0_PC(t)), sin(q0_PC(t))*cos(a)],
  1368. [sin(a)*cos(q0_PC(t)), -sin(q0_PC(t)), cos(a)*cos(q0_PC(t))]])
  1369. The angular velocity of the block can be computed with respect to the
  1370. ground.
  1371. >>> block.ang_vel_in(ground)
  1372. u0_PC(t)*A.x
  1373. The position of the block's center of mass can be found with:
  1374. >>> block.masscenter.pos_from(ground.masscenter)
  1375. d*G_frame.x + h*B_frame.x + q1_PC(t)*A.y + q2_PC(t)*A.z
  1376. Finally, the linear velocity of the block's center of mass can be
  1377. computed with respect to the ground.
  1378. >>> block.masscenter.vel(ground.frame)
  1379. u1_PC(t)*A.y + u2_PC(t)*A.z
  1380. In some cases it could be your preference to only define the normals of the
  1381. plane with respect to both bodies. This can most easily be done by supplying
  1382. vectors to the ``interframe`` arguments. What will happen in this case is
  1383. that an interframe will be created with its ``x`` axis aligned with the
  1384. provided vector. For a further explanation of how this is done see the notes
  1385. of the ``Joint`` class. In the code below, the above example (with the block
  1386. on the slope) is recreated by supplying vectors to the interframe arguments.
  1387. Note that the previously described option is however more computationally
  1388. efficient, because the algorithm now has to compute the rotation angle
  1389. between the provided vector and the 'x' axis.
  1390. >>> from sympy import symbols, cos, sin
  1391. >>> from sympy.physics.mechanics import PlanarJoint, Body
  1392. >>> a, d, h = symbols('a d h')
  1393. >>> ground = Body('G')
  1394. >>> block = Body('B')
  1395. >>> joint = PlanarJoint(
  1396. ... 'PC', ground, block, parent_point=d * ground.x,
  1397. ... child_point=-h * block.x, child_interframe=block.x,
  1398. ... parent_interframe=cos(a) * ground.x + sin(a) * ground.z)
  1399. >>> block.dcm(ground).simplify()
  1400. Matrix([
  1401. [ cos(a), 0, sin(a)],
  1402. [-sin(a)*sin(q0_PC(t)), cos(q0_PC(t)), sin(q0_PC(t))*cos(a)],
  1403. [-sin(a)*cos(q0_PC(t)), -sin(q0_PC(t)), cos(a)*cos(q0_PC(t))]])
  1404. """
  1405. def __init__(self, name, parent, child, rotation_coordinate=None,
  1406. planar_coordinates=None, rotation_speed=None,
  1407. planar_speeds=None, parent_point=None, child_point=None,
  1408. parent_interframe=None, child_interframe=None):
  1409. # A ready to merge implementation of setting the planar_vectors and
  1410. # rotation_axis was added and removed in PR #24046
  1411. coordinates = (rotation_coordinate, planar_coordinates)
  1412. speeds = (rotation_speed, planar_speeds)
  1413. super().__init__(name, parent, child, coordinates, speeds,
  1414. parent_point, child_point,
  1415. parent_interframe=parent_interframe,
  1416. child_interframe=child_interframe)
  1417. def __str__(self):
  1418. return (f'PlanarJoint: {self.name} parent: {self.parent} '
  1419. f'child: {self.child}')
  1420. @property
  1421. def rotation_coordinate(self):
  1422. """Generalized coordinate corresponding to the rotation angle."""
  1423. return self.coordinates[0]
  1424. @property
  1425. def planar_coordinates(self):
  1426. """Two generalized coordinates used for the planar translation."""
  1427. return self.coordinates[1:, 0]
  1428. @property
  1429. def rotation_speed(self):
  1430. """Generalized speed corresponding to the angular velocity."""
  1431. return self.speeds[0]
  1432. @property
  1433. def planar_speeds(self):
  1434. """Two generalized speeds used for the planar translation velocity."""
  1435. return self.speeds[1:, 0]
  1436. @property
  1437. def rotation_axis(self):
  1438. """The axis about which the rotation occurs."""
  1439. return self.parent_interframe.x
  1440. @property
  1441. def planar_vectors(self):
  1442. """The vectors that describe the planar translation directions."""
  1443. return [self.parent_interframe.y, self.parent_interframe.z]
  1444. def _generate_coordinates(self, coordinates):
  1445. rotation_speed = self._fill_coordinate_list(coordinates[0], 1, 'q',
  1446. number_single=True)
  1447. planar_speeds = self._fill_coordinate_list(coordinates[1], 2, 'q', 1)
  1448. return rotation_speed.col_join(planar_speeds)
  1449. def _generate_speeds(self, speeds):
  1450. rotation_speed = self._fill_coordinate_list(speeds[0], 1, 'u',
  1451. number_single=True)
  1452. planar_speeds = self._fill_coordinate_list(speeds[1], 2, 'u', 1)
  1453. return rotation_speed.col_join(planar_speeds)
  1454. def _orient_frames(self):
  1455. self.child_interframe.orient_axis(
  1456. self.parent_interframe, self.rotation_axis,
  1457. self.rotation_coordinate)
  1458. def _set_angular_velocity(self):
  1459. self.child_interframe.set_ang_vel(
  1460. self.parent_interframe,
  1461. self.rotation_speed * self.rotation_axis)
  1462. def _set_linear_velocity(self):
  1463. self.child_point.set_pos(
  1464. self.parent_point,
  1465. self.planar_coordinates[0] * self.planar_vectors[0] +
  1466. self.planar_coordinates[1] * self.planar_vectors[1])
  1467. self.parent_point.set_vel(self.parent_interframe, 0)
  1468. self.child_point.set_vel(self.child_interframe, 0)
  1469. self.child_point.set_vel(
  1470. self.parent.frame, self.planar_speeds[0] * self.planar_vectors[0] +
  1471. self.planar_speeds[1] * self.planar_vectors[1])
  1472. self.child.masscenter.v2pt_theory(self.child_point, self.parent.frame,
  1473. self.child.frame)
  1474. class SphericalJoint(Joint):
  1475. """Spherical (Ball-and-Socket) Joint.
  1476. .. image:: SphericalJoint.svg
  1477. :align: center
  1478. :width: 600
  1479. Explanation
  1480. ===========
  1481. A spherical joint is defined such that the child body is free to rotate in
  1482. any direction, without allowing a translation of the ``child_point``. As can
  1483. also be seen in the image, the ``parent_point`` and ``child_point`` are
  1484. fixed on top of each other, i.e. the ``joint_point``. This rotation is
  1485. defined using the :func:`parent_interframe.orient(child_interframe,
  1486. rot_type, amounts, rot_order)
  1487. <sympy.physics.vector.frame.ReferenceFrame.orient>` method. The default
  1488. rotation consists of three relative rotations, i.e. body-fixed rotations.
  1489. Based on the direction cosine matrix following from these rotations, the
  1490. angular velocity is computed based on the generalized coordinates and
  1491. generalized speeds.
  1492. Parameters
  1493. ==========
  1494. name : string
  1495. A unique name for the joint.
  1496. parent : Body
  1497. The parent body of joint.
  1498. child : Body
  1499. The child body of joint.
  1500. coordinates: iterable of dynamicsymbols, optional
  1501. Generalized coordinates of the joint.
  1502. speeds : iterable of dynamicsymbols, optional
  1503. Generalized speeds of joint.
  1504. parent_point : Point or Vector, optional
  1505. Attachment point where the joint is fixed to the parent body. If a
  1506. vector is provided, then the attachment point is computed by adding the
  1507. vector to the body's mass center. The default value is the parent's mass
  1508. center.
  1509. child_point : Point or Vector, optional
  1510. Attachment point where the joint is fixed to the child body. If a
  1511. vector is provided, then the attachment point is computed by adding the
  1512. vector to the body's mass center. The default value is the child's mass
  1513. center.
  1514. parent_interframe : ReferenceFrame, optional
  1515. Intermediate frame of the parent body with respect to which the joint
  1516. transformation is formulated. If a Vector is provided then an interframe
  1517. is created which aligns its X axis with the given vector. The default
  1518. value is the parent's own frame.
  1519. child_interframe : ReferenceFrame, optional
  1520. Intermediate frame of the child body with respect to which the joint
  1521. transformation is formulated. If a Vector is provided then an interframe
  1522. is created which aligns its X axis with the given vector. The default
  1523. value is the child's own frame.
  1524. rot_type : str, optional
  1525. The method used to generate the direction cosine matrix. Supported
  1526. methods are:
  1527. - ``'Body'``: three successive rotations about new intermediate axes,
  1528. also called "Euler and Tait-Bryan angles"
  1529. - ``'Space'``: three successive rotations about the parent frames' unit
  1530. vectors
  1531. The default method is ``'Body'``.
  1532. amounts :
  1533. Expressions defining the rotation angles or direction cosine matrix.
  1534. These must match the ``rot_type``. See examples below for details. The
  1535. input types are:
  1536. - ``'Body'``: 3-tuple of expressions, symbols, or functions
  1537. - ``'Space'``: 3-tuple of expressions, symbols, or functions
  1538. The default amounts are the given ``coordinates``.
  1539. rot_order : str or int, optional
  1540. If applicable, the order of the successive of rotations. The string
  1541. ``'123'`` and integer ``123`` are equivalent, for example. Required for
  1542. ``'Body'`` and ``'Space'``. The default value is ``123``.
  1543. Attributes
  1544. ==========
  1545. name : string
  1546. The joint's name.
  1547. parent : Body
  1548. The joint's parent body.
  1549. child : Body
  1550. The joint's child body.
  1551. coordinates : Matrix
  1552. Matrix of the joint's generalized coordinates.
  1553. speeds : Matrix
  1554. Matrix of the joint's generalized speeds.
  1555. parent_point : Point
  1556. Attachment point where the joint is fixed to the parent body.
  1557. child_point : Point
  1558. Attachment point where the joint is fixed to the child body.
  1559. parent_interframe : ReferenceFrame
  1560. Intermediate frame of the parent body with respect to which the joint
  1561. transformation is formulated.
  1562. child_interframe : ReferenceFrame
  1563. Intermediate frame of the child body with respect to which the joint
  1564. transformation is formulated.
  1565. kdes : Matrix
  1566. Kinematical differential equations of the joint.
  1567. Examples
  1568. =========
  1569. A single spherical joint is created from two bodies and has the following
  1570. basic attributes:
  1571. >>> from sympy.physics.mechanics import Body, SphericalJoint
  1572. >>> parent = Body('P')
  1573. >>> parent
  1574. P
  1575. >>> child = Body('C')
  1576. >>> child
  1577. C
  1578. >>> joint = SphericalJoint('PC', parent, child)
  1579. >>> joint
  1580. SphericalJoint: PC parent: P child: C
  1581. >>> joint.name
  1582. 'PC'
  1583. >>> joint.parent
  1584. P
  1585. >>> joint.child
  1586. C
  1587. >>> joint.parent_point
  1588. P_masscenter
  1589. >>> joint.child_point
  1590. C_masscenter
  1591. >>> joint.parent_interframe
  1592. P_frame
  1593. >>> joint.child_interframe
  1594. C_frame
  1595. >>> joint.coordinates
  1596. Matrix([
  1597. [q0_PC(t)],
  1598. [q1_PC(t)],
  1599. [q2_PC(t)]])
  1600. >>> joint.speeds
  1601. Matrix([
  1602. [u0_PC(t)],
  1603. [u1_PC(t)],
  1604. [u2_PC(t)]])
  1605. >>> child.frame.ang_vel_in(parent.frame).to_matrix(child.frame)
  1606. Matrix([
  1607. [ u0_PC(t)*cos(q1_PC(t))*cos(q2_PC(t)) + u1_PC(t)*sin(q2_PC(t))],
  1608. [-u0_PC(t)*sin(q2_PC(t))*cos(q1_PC(t)) + u1_PC(t)*cos(q2_PC(t))],
  1609. [ u0_PC(t)*sin(q1_PC(t)) + u2_PC(t)]])
  1610. >>> child.frame.x.to_matrix(parent.frame)
  1611. Matrix([
  1612. [ cos(q1_PC(t))*cos(q2_PC(t))],
  1613. [sin(q0_PC(t))*sin(q1_PC(t))*cos(q2_PC(t)) + sin(q2_PC(t))*cos(q0_PC(t))],
  1614. [sin(q0_PC(t))*sin(q2_PC(t)) - sin(q1_PC(t))*cos(q0_PC(t))*cos(q2_PC(t))]])
  1615. >>> joint.child_point.pos_from(joint.parent_point)
  1616. 0
  1617. To further demonstrate the use of the spherical joint, the kinematics of a
  1618. spherical joint with a ZXZ rotation can be created as follows.
  1619. >>> from sympy import symbols
  1620. >>> from sympy.physics.mechanics import Body, SphericalJoint
  1621. >>> l1 = symbols('l1')
  1622. First create bodies to represent the fixed floor and a pendulum bob.
  1623. >>> floor = Body('F')
  1624. >>> bob = Body('B')
  1625. The joint will connect the bob to the floor, with the joint located at a
  1626. distance of ``l1`` from the child's center of mass and the rotation set to a
  1627. body-fixed ZXZ rotation.
  1628. >>> joint = SphericalJoint('S', floor, bob, child_point=l1 * bob.y,
  1629. ... rot_type='body', rot_order='ZXZ')
  1630. Now that the joint is established, the kinematics of the connected body can
  1631. be accessed.
  1632. The position of the bob's masscenter is found with:
  1633. >>> bob.masscenter.pos_from(floor.masscenter)
  1634. - l1*B_frame.y
  1635. The angular velocities of the pendulum link can be computed with respect to
  1636. the floor.
  1637. >>> bob.frame.ang_vel_in(floor.frame).to_matrix(
  1638. ... floor.frame).simplify()
  1639. Matrix([
  1640. [u1_S(t)*cos(q0_S(t)) + u2_S(t)*sin(q0_S(t))*sin(q1_S(t))],
  1641. [u1_S(t)*sin(q0_S(t)) - u2_S(t)*sin(q1_S(t))*cos(q0_S(t))],
  1642. [ u0_S(t) + u2_S(t)*cos(q1_S(t))]])
  1643. Finally, the linear velocity of the bob's center of mass can be computed.
  1644. >>> bob.masscenter.vel(floor.frame).to_matrix(bob.frame)
  1645. Matrix([
  1646. [ l1*(u0_S(t)*cos(q1_S(t)) + u2_S(t))],
  1647. [ 0],
  1648. [-l1*(u0_S(t)*sin(q1_S(t))*sin(q2_S(t)) + u1_S(t)*cos(q2_S(t)))]])
  1649. """
  1650. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  1651. parent_point=None, child_point=None, parent_interframe=None,
  1652. child_interframe=None, rot_type='BODY', amounts=None,
  1653. rot_order=123):
  1654. self._rot_type = rot_type
  1655. self._amounts = amounts
  1656. self._rot_order = rot_order
  1657. super().__init__(name, parent, child, coordinates, speeds,
  1658. parent_point, child_point,
  1659. parent_interframe=parent_interframe,
  1660. child_interframe=child_interframe)
  1661. def __str__(self):
  1662. return (f'SphericalJoint: {self.name} parent: {self.parent} '
  1663. f'child: {self.child}')
  1664. def _generate_coordinates(self, coordinates):
  1665. return self._fill_coordinate_list(coordinates, 3, 'q')
  1666. def _generate_speeds(self, speeds):
  1667. return self._fill_coordinate_list(speeds, len(self.coordinates), 'u')
  1668. def _orient_frames(self):
  1669. supported_rot_types = ('BODY', 'SPACE')
  1670. if self._rot_type.upper() not in supported_rot_types:
  1671. raise NotImplementedError(
  1672. f'Rotation type "{self._rot_type}" is not implemented. '
  1673. f'Implemented rotation types are: {supported_rot_types}')
  1674. amounts = self.coordinates if self._amounts is None else self._amounts
  1675. self.child_interframe.orient(self.parent_interframe, self._rot_type,
  1676. amounts, self._rot_order)
  1677. def _set_angular_velocity(self):
  1678. t = dynamicsymbols._t
  1679. vel = self.child_interframe.ang_vel_in(self.parent_interframe).xreplace(
  1680. {q.diff(t): u for q, u in zip(self.coordinates, self.speeds)}
  1681. )
  1682. self.child_interframe.set_ang_vel(self.parent_interframe, vel)
  1683. def _set_linear_velocity(self):
  1684. self.child_point.set_pos(self.parent_point, 0)
  1685. self.parent_point.set_vel(self.parent.frame, 0)
  1686. self.child_point.set_vel(self.child.frame, 0)
  1687. self.child.masscenter.v2pt_theory(self.parent_point, self.parent.frame,
  1688. self.child.frame)
  1689. class WeldJoint(Joint):
  1690. """Weld Joint.
  1691. .. image:: WeldJoint.svg
  1692. :align: center
  1693. :width: 500
  1694. Explanation
  1695. ===========
  1696. A weld joint is defined such that there is no relative motion between the
  1697. child and parent bodies. The direction cosine matrix between the attachment
  1698. frame (``parent_interframe`` and ``child_interframe``) is the identity
  1699. matrix and the attachment points (``parent_point`` and ``child_point``) are
  1700. coincident. The page on the joints framework gives a more detailed
  1701. explanation of the intermediate frames.
  1702. Parameters
  1703. ==========
  1704. name : string
  1705. A unique name for the joint.
  1706. parent : Body
  1707. The parent body of joint.
  1708. child : Body
  1709. The child body of joint.
  1710. parent_point : Point or Vector, optional
  1711. Attachment point where the joint is fixed to the parent body. If a
  1712. vector is provided, then the attachment point is computed by adding the
  1713. vector to the body's mass center. The default value is the parent's mass
  1714. center.
  1715. child_point : Point or Vector, optional
  1716. Attachment point where the joint is fixed to the child body. If a
  1717. vector is provided, then the attachment point is computed by adding the
  1718. vector to the body's mass center. The default value is the child's mass
  1719. center.
  1720. parent_interframe : ReferenceFrame, optional
  1721. Intermediate frame of the parent body with respect to which the joint
  1722. transformation is formulated. If a Vector is provided then an interframe
  1723. is created which aligns its X axis with the given vector. The default
  1724. value is the parent's own frame.
  1725. child_interframe : ReferenceFrame, optional
  1726. Intermediate frame of the child body with respect to which the joint
  1727. transformation is formulated. If a Vector is provided then an interframe
  1728. is created which aligns its X axis with the given vector. The default
  1729. value is the child's own frame.
  1730. Attributes
  1731. ==========
  1732. name : string
  1733. The joint's name.
  1734. parent : Body
  1735. The joint's parent body.
  1736. child : Body
  1737. The joint's child body.
  1738. coordinates : Matrix
  1739. Matrix of the joint's generalized coordinates. The default value is
  1740. ``dynamicsymbols(f'q_{joint.name}')``.
  1741. speeds : Matrix
  1742. Matrix of the joint's generalized speeds. The default value is
  1743. ``dynamicsymbols(f'u_{joint.name}')``.
  1744. parent_point : Point
  1745. Attachment point where the joint is fixed to the parent body.
  1746. child_point : Point
  1747. Attachment point where the joint is fixed to the child body.
  1748. parent_interframe : ReferenceFrame
  1749. Intermediate frame of the parent body with respect to which the joint
  1750. transformation is formulated.
  1751. child_interframe : ReferenceFrame
  1752. Intermediate frame of the child body with respect to which the joint
  1753. transformation is formulated.
  1754. kdes : Matrix
  1755. Kinematical differential equations of the joint.
  1756. Examples
  1757. =========
  1758. A single weld joint is created from two bodies and has the following basic
  1759. attributes:
  1760. >>> from sympy.physics.mechanics import Body, WeldJoint
  1761. >>> parent = Body('P')
  1762. >>> parent
  1763. P
  1764. >>> child = Body('C')
  1765. >>> child
  1766. C
  1767. >>> joint = WeldJoint('PC', parent, child)
  1768. >>> joint
  1769. WeldJoint: PC parent: P child: C
  1770. >>> joint.name
  1771. 'PC'
  1772. >>> joint.parent
  1773. P
  1774. >>> joint.child
  1775. C
  1776. >>> joint.parent_point
  1777. P_masscenter
  1778. >>> joint.child_point
  1779. C_masscenter
  1780. >>> joint.coordinates
  1781. Matrix(0, 0, [])
  1782. >>> joint.speeds
  1783. Matrix(0, 0, [])
  1784. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  1785. 0
  1786. >>> joint.child.frame.dcm(joint.parent.frame)
  1787. Matrix([
  1788. [1, 0, 0],
  1789. [0, 1, 0],
  1790. [0, 0, 1]])
  1791. >>> joint.child_point.pos_from(joint.parent_point)
  1792. 0
  1793. To further demonstrate the use of the weld joint, two relatively-fixed
  1794. bodies rotated by a quarter turn about the Y axis can be created as follows:
  1795. >>> from sympy import symbols, pi
  1796. >>> from sympy.physics.mechanics import ReferenceFrame, Body, WeldJoint
  1797. >>> l1, l2 = symbols('l1 l2')
  1798. First create the bodies to represent the parent and rotated child body.
  1799. >>> parent = Body('P')
  1800. >>> child = Body('C')
  1801. Next the intermediate frame specifying the fixed rotation with respect to
  1802. the parent can be created.
  1803. >>> rotated_frame = ReferenceFrame('Pr')
  1804. >>> rotated_frame.orient_axis(parent.frame, parent.y, pi / 2)
  1805. The weld between the parent body and child body is located at a distance
  1806. ``l1`` from the parent's center of mass in the X direction and ``l2`` from
  1807. the child's center of mass in the child's negative X direction.
  1808. >>> weld = WeldJoint('weld', parent, child, parent_point=l1 * parent.x,
  1809. ... child_point=-l2 * child.x,
  1810. ... parent_interframe=rotated_frame)
  1811. Now that the joint has been established, the kinematics of the bodies can be
  1812. accessed. The direction cosine matrix of the child body with respect to the
  1813. parent can be found:
  1814. >>> child.dcm(parent)
  1815. Matrix([
  1816. [0, 0, -1],
  1817. [0, 1, 0],
  1818. [1, 0, 0]])
  1819. As can also been seen from the direction cosine matrix, the parent X axis is
  1820. aligned with the child's Z axis:
  1821. >>> parent.x == child.z
  1822. True
  1823. The position of the child's center of mass with respect to the parent's
  1824. center of mass can be found with:
  1825. >>> child.masscenter.pos_from(parent.masscenter)
  1826. l1*P_frame.x + l2*C_frame.x
  1827. The angular velocity of the child with respect to the parent is 0 as one
  1828. would expect.
  1829. >>> child.ang_vel_in(parent)
  1830. 0
  1831. """
  1832. def __init__(self, name, parent, child, parent_point=None, child_point=None,
  1833. parent_interframe=None, child_interframe=None):
  1834. super().__init__(name, parent, child, [], [], parent_point,
  1835. child_point, parent_interframe=parent_interframe,
  1836. child_interframe=child_interframe)
  1837. self._kdes = Matrix(1, 0, []).T # Removes stackability problems #10770
  1838. def __str__(self):
  1839. return (f'WeldJoint: {self.name} parent: {self.parent} '
  1840. f'child: {self.child}')
  1841. def _generate_coordinates(self, coordinate):
  1842. return Matrix()
  1843. def _generate_speeds(self, speed):
  1844. return Matrix()
  1845. def _orient_frames(self):
  1846. self.child_interframe.orient_axis(self.parent_interframe,
  1847. self.parent_interframe.x, 0)
  1848. def _set_angular_velocity(self):
  1849. self.child_interframe.set_ang_vel(self.parent_interframe, 0)
  1850. def _set_linear_velocity(self):
  1851. self.child_point.set_pos(self.parent_point, 0)
  1852. self.parent_point.set_vel(self.parent.frame, 0)
  1853. self.child_point.set_vel(self.child.frame, 0)
  1854. self.child.masscenter.set_vel(self.parent.frame, 0)