joint.py 83 KB

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