joint.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. # coding=utf-8
  2. from abc import ABC, abstractmethod
  3. from sympy.core.numbers import pi
  4. from sympy.physics.mechanics.body import Body
  5. from sympy.physics.vector import Vector, dynamicsymbols, cross
  6. from sympy.physics.vector.frame import ReferenceFrame
  7. import warnings
  8. __all__ = ['Joint', 'PinJoint', 'PrismaticJoint']
  9. class Joint(ABC):
  10. """Abstract base class for all specific joints.
  11. Explanation
  12. ===========
  13. A joint subtracts degrees of freedom from a body. This is the base class
  14. for all specific joints and holds all common methods acting as an interface
  15. for all joints. Custom joint can be created by inheriting Joint class and
  16. defining all abstract functions.
  17. The abstract methods are:
  18. - ``_generate_coordinates``
  19. - ``_generate_speeds``
  20. - ``_orient_frames``
  21. - ``_set_angular_velocity``
  22. - ``_set_linar_velocity``
  23. Parameters
  24. ==========
  25. name : string
  26. A unique name for the joint.
  27. parent : Body
  28. The parent body of joint.
  29. child : Body
  30. The child body of joint.
  31. coordinates: List of dynamicsymbols, optional
  32. Generalized coordinates of the joint.
  33. speeds : List of dynamicsymbols, optional
  34. Generalized speeds of joint.
  35. parent_joint_pos : Vector, optional
  36. Vector from the parent body's mass center to the point where the parent
  37. and child are connected. The default value is the zero vector.
  38. child_joint_pos : Vector, optional
  39. Vector from the child body's mass center to the point where the parent
  40. and child are connected. The default value is the zero vector.
  41. parent_axis : Vector, optional
  42. Axis fixed in the parent body which aligns with an axis fixed in the
  43. child body. The default is x axis in parent's reference frame.
  44. child_axis : Vector, optional
  45. Axis fixed in the child body which aligns with an axis fixed in the
  46. parent body. The default is x axis in child's reference frame.
  47. Attributes
  48. ==========
  49. name : string
  50. The joint's name.
  51. parent : Body
  52. The joint's parent body.
  53. child : Body
  54. The joint's child body.
  55. coordinates : list
  56. List of the joint's generalized coordinates.
  57. speeds : list
  58. List of the joint's generalized speeds.
  59. parent_point : Point
  60. The point fixed in the parent body that represents the joint.
  61. child_point : Point
  62. The point fixed in the child body that represents the joint.
  63. parent_axis : Vector
  64. The axis fixed in the parent frame that represents the joint.
  65. child_axis : Vector
  66. The axis fixed in the child frame that represents the joint.
  67. kdes : list
  68. Kinematical differential equations of the joint.
  69. Notes
  70. =====
  71. The direction cosine matrix between the child and parent is formed using a
  72. simple rotation about an axis that is normal to both ``child_axis`` and
  73. ``parent_axis``. In general, the normal axis is formed by crossing the
  74. ``child_axis`` into the ``parent_axis`` except if the child and parent axes
  75. are in exactly opposite directions. In that case the rotation vector is chosen
  76. using the rules in the following table where ``C`` is the child reference
  77. frame and ``P`` is the parent reference frame:
  78. .. list-table::
  79. :header-rows: 1
  80. * - ``child_axis``
  81. - ``parent_axis``
  82. - ``rotation_axis``
  83. * - ``-C.x``
  84. - ``P.x``
  85. - ``P.z``
  86. * - ``-C.y``
  87. - ``P.y``
  88. - ``P.x``
  89. * - ``-C.z``
  90. - ``P.z``
  91. - ``P.y``
  92. * - ``-C.x-C.y``
  93. - ``P.x+P.y``
  94. - ``P.z``
  95. * - ``-C.y-C.z``
  96. - ``P.y+P.z``
  97. - ``P.x``
  98. * - ``-C.x-C.z``
  99. - ``P.x+P.z``
  100. - ``P.y``
  101. * - ``-C.x-C.y-C.z``
  102. - ``P.x+P.y+P.z``
  103. - ``(P.x+P.y+P.z) × P.x``
  104. """
  105. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  106. parent_joint_pos=None, child_joint_pos=None, parent_axis=None,
  107. child_axis=None):
  108. if not isinstance(name, str):
  109. raise TypeError('Supply a valid name.')
  110. self._name = name
  111. if not isinstance(parent, Body):
  112. raise TypeError('Parent must be an instance of Body.')
  113. self._parent = parent
  114. if not isinstance(child, Body):
  115. raise TypeError('Parent must be an instance of Body.')
  116. self._child = child
  117. self._coordinates = self._generate_coordinates(coordinates)
  118. self._speeds = self._generate_speeds(speeds)
  119. self._kdes = self._generate_kdes()
  120. self._parent_axis = self._axis(parent, parent_axis)
  121. self._child_axis = self._axis(child, child_axis)
  122. self._parent_point = self._locate_joint_pos(parent, parent_joint_pos)
  123. self._child_point = self._locate_joint_pos(child, child_joint_pos)
  124. self._orient_frames()
  125. self._set_angular_velocity()
  126. self._set_linear_velocity()
  127. def __str__(self):
  128. return self.name
  129. def __repr__(self):
  130. return self.__str__()
  131. @property
  132. def name(self):
  133. return self._name
  134. @property
  135. def parent(self):
  136. """Parent body of Joint."""
  137. return self._parent
  138. @property
  139. def child(self):
  140. """Child body of Joint."""
  141. return self._child
  142. @property
  143. def coordinates(self):
  144. """List generalized coordinates of the joint."""
  145. return self._coordinates
  146. @property
  147. def speeds(self):
  148. """List generalized coordinates of the joint.."""
  149. return self._speeds
  150. @property
  151. def kdes(self):
  152. """Kinematical differential equations of the joint."""
  153. return self._kdes
  154. @property
  155. def parent_axis(self):
  156. """The axis of parent frame."""
  157. return self._parent_axis
  158. @property
  159. def child_axis(self):
  160. """The axis of child frame."""
  161. return self._child_axis
  162. @property
  163. def parent_point(self):
  164. """The joint's point where parent body is connected to the joint."""
  165. return self._parent_point
  166. @property
  167. def child_point(self):
  168. """The joint's point where child body is connected to the joint."""
  169. return self._child_point
  170. @abstractmethod
  171. def _generate_coordinates(self, coordinates):
  172. """Generate list generalized coordinates of the joint."""
  173. pass
  174. @abstractmethod
  175. def _generate_speeds(self, speeds):
  176. """Generate list generalized speeds of the joint."""
  177. pass
  178. @abstractmethod
  179. def _orient_frames(self):
  180. """Orient frames as per the joint."""
  181. pass
  182. @abstractmethod
  183. def _set_angular_velocity(self):
  184. pass
  185. @abstractmethod
  186. def _set_linear_velocity(self):
  187. pass
  188. def _generate_kdes(self):
  189. kdes = []
  190. t = dynamicsymbols._t
  191. for i in range(len(self.coordinates)):
  192. kdes.append(-self.coordinates[i].diff(t) + self.speeds[i])
  193. return kdes
  194. def _axis(self, body, ax):
  195. if ax is None:
  196. ax = body.frame.x
  197. return ax
  198. if not isinstance(ax, Vector):
  199. raise TypeError("Axis must be of type Vector.")
  200. if not ax.dt(body.frame) == 0:
  201. msg = ('Axis cannot be time-varying when viewed from the '
  202. 'associated body.')
  203. raise ValueError(msg)
  204. return ax
  205. def _locate_joint_pos(self, body, joint_pos):
  206. if joint_pos is None:
  207. joint_pos = Vector(0)
  208. if not isinstance(joint_pos, Vector):
  209. raise ValueError('Joint Position must be supplied as Vector.')
  210. if not joint_pos.dt(body.frame) == 0:
  211. msg = ('Position Vector cannot be time-varying when viewed from '
  212. 'the associated body.')
  213. raise ValueError(msg)
  214. point_name = self._name + '_' + body.name + '_joint'
  215. return body.masscenter.locatenew(point_name, joint_pos)
  216. def _alignment_rotation(self, parent, child):
  217. # Returns the axis and angle between two axis(vectors).
  218. angle = parent.angle_between(child)
  219. axis = cross(child, parent).normalize()
  220. return angle, axis
  221. def _generate_vector(self):
  222. parent_frame = self.parent.frame
  223. components = self.parent_axis.to_matrix(parent_frame)
  224. x, y, z = components[0], components[1], components[2]
  225. if x != 0:
  226. if y!=0:
  227. if z!=0:
  228. return cross(self.parent_axis,
  229. parent_frame.x)
  230. if z!=0:
  231. return parent_frame.y
  232. return parent_frame.z
  233. if x == 0:
  234. if y!=0:
  235. if z!=0:
  236. return parent_frame.x
  237. return parent_frame.x
  238. return parent_frame.y
  239. def _set_orientation(self):
  240. #Helper method for `orient_axis()`
  241. self.child.frame.orient_axis(self.parent.frame, self.parent_axis, 0)
  242. angle, axis = self._alignment_rotation(self.parent_axis,
  243. self.child_axis)
  244. with warnings.catch_warnings():
  245. warnings.filterwarnings("ignore", category=UserWarning)
  246. if axis != Vector(0) or angle == pi:
  247. if angle == pi:
  248. axis = self._generate_vector()
  249. int_frame = ReferenceFrame('int_frame')
  250. int_frame.orient_axis(self.child.frame, self.child_axis, 0)
  251. int_frame.orient_axis(self.parent.frame, axis, angle)
  252. return int_frame
  253. return self.parent.frame
  254. class PinJoint(Joint):
  255. """Pin (Revolute) Joint.
  256. Explanation
  257. ===========
  258. A pin joint is defined such that the joint rotation axis is fixed in both
  259. the child and parent and the location of the joint is relative to the mass
  260. center of each body. The child rotates an angle, θ, from the parent about
  261. the rotation axis and has a simple angular speed, ω, relative to the
  262. parent. The direction cosine matrix between the child and parent is formed
  263. using a simple rotation about an axis that is normal to both ``child_axis``
  264. and ``parent_axis``, see the Notes section for a detailed explanation of
  265. this.
  266. Parameters
  267. ==========
  268. name : string
  269. A unique name for the joint.
  270. parent : Body
  271. The parent body of joint.
  272. child : Body
  273. The child body of joint.
  274. coordinates: dynamicsymbol, optional
  275. Generalized coordinates of the joint.
  276. speeds : dynamicsymbol, optional
  277. Generalized speeds of joint.
  278. parent_joint_pos : Vector, optional
  279. Vector from the parent body's mass center to the point where the parent
  280. and child are connected. The default value is the zero vector.
  281. child_joint_pos : Vector, optional
  282. Vector from the child body's mass center to the point where the parent
  283. and child are connected. The default value is the zero vector.
  284. parent_axis : Vector, optional
  285. Axis fixed in the parent body which aligns with an axis fixed in the
  286. child body. The default is x axis in parent's reference frame.
  287. child_axis : Vector, optional
  288. Axis fixed in the child body which aligns with an axis fixed in the
  289. parent body. The default is x axis in child's reference frame.
  290. Attributes
  291. ==========
  292. name : string
  293. The joint's name.
  294. parent : Body
  295. The joint's parent body.
  296. child : Body
  297. The joint's child body.
  298. coordinates : list
  299. List of the joint's generalized coordinates.
  300. speeds : list
  301. List of the joint's generalized speeds.
  302. parent_point : Point
  303. The point fixed in the parent body that represents the joint.
  304. child_point : Point
  305. The point fixed in the child body that represents the joint.
  306. parent_axis : Vector
  307. The axis fixed in the parent frame that represents the joint.
  308. child_axis : Vector
  309. The axis fixed in the child frame that represents the joint.
  310. kdes : list
  311. Kinematical differential equations of the joint.
  312. Examples
  313. =========
  314. A single pin joint is created from two bodies and has the following basic
  315. attributes:
  316. >>> from sympy.physics.mechanics import Body, PinJoint
  317. >>> parent = Body('P')
  318. >>> parent
  319. P
  320. >>> child = Body('C')
  321. >>> child
  322. C
  323. >>> joint = PinJoint('PC', parent, child)
  324. >>> joint
  325. PinJoint: PC parent: P child: C
  326. >>> joint.name
  327. 'PC'
  328. >>> joint.parent
  329. P
  330. >>> joint.child
  331. C
  332. >>> joint.parent_point
  333. PC_P_joint
  334. >>> joint.child_point
  335. PC_C_joint
  336. >>> joint.parent_axis
  337. P_frame.x
  338. >>> joint.child_axis
  339. C_frame.x
  340. >>> joint.coordinates
  341. [theta_PC(t)]
  342. >>> joint.speeds
  343. [omega_PC(t)]
  344. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  345. omega_PC(t)*P_frame.x
  346. >>> joint.child.frame.dcm(joint.parent.frame)
  347. Matrix([
  348. [1, 0, 0],
  349. [0, cos(theta_PC(t)), sin(theta_PC(t))],
  350. [0, -sin(theta_PC(t)), cos(theta_PC(t))]])
  351. >>> joint.child_point.pos_from(joint.parent_point)
  352. 0
  353. To further demonstrate the use of the pin joint, the kinematics of simple
  354. double pendulum that rotates about the Z axis of each connected body can be
  355. created as follows.
  356. >>> from sympy import symbols, trigsimp
  357. >>> from sympy.physics.mechanics import Body, PinJoint
  358. >>> l1, l2 = symbols('l1 l2')
  359. First create bodies to represent the fixed ceiling and one to represent
  360. each pendulum bob.
  361. >>> ceiling = Body('C')
  362. >>> upper_bob = Body('U')
  363. >>> lower_bob = Body('L')
  364. The first joint will connect the upper bob to the ceiling by a distance of
  365. ``l1`` and the joint axis will be about the Z axis for each body.
  366. >>> ceiling_joint = PinJoint('P1', ceiling, upper_bob,
  367. ... child_joint_pos=-l1*upper_bob.frame.x,
  368. ... parent_axis=ceiling.frame.z,
  369. ... child_axis=upper_bob.frame.z)
  370. The second joint will connect the lower bob to the upper bob by a distance
  371. of ``l2`` and the joint axis will also be about the Z axis for each body.
  372. >>> pendulum_joint = PinJoint('P2', upper_bob, lower_bob,
  373. ... child_joint_pos=-l2*lower_bob.frame.x,
  374. ... parent_axis=upper_bob.frame.z,
  375. ... child_axis=lower_bob.frame.z)
  376. Once the joints are established the kinematics of the connected bodies can
  377. be accessed. First the direction cosine matrices of pendulum link relative
  378. to the ceiling are found:
  379. >>> upper_bob.frame.dcm(ceiling.frame)
  380. Matrix([
  381. [ cos(theta_P1(t)), sin(theta_P1(t)), 0],
  382. [-sin(theta_P1(t)), cos(theta_P1(t)), 0],
  383. [ 0, 0, 1]])
  384. >>> trigsimp(lower_bob.frame.dcm(ceiling.frame))
  385. Matrix([
  386. [ cos(theta_P1(t) + theta_P2(t)), sin(theta_P1(t) + theta_P2(t)), 0],
  387. [-sin(theta_P1(t) + theta_P2(t)), cos(theta_P1(t) + theta_P2(t)), 0],
  388. [ 0, 0, 1]])
  389. The position of the lower bob's masscenter is found with:
  390. >>> lower_bob.masscenter.pos_from(ceiling.masscenter)
  391. l1*U_frame.x + l2*L_frame.x
  392. The angular velocities of the two pendulum links can be computed with
  393. respect to the ceiling.
  394. >>> upper_bob.frame.ang_vel_in(ceiling.frame)
  395. omega_P1(t)*C_frame.z
  396. >>> lower_bob.frame.ang_vel_in(ceiling.frame)
  397. omega_P1(t)*C_frame.z + omega_P2(t)*U_frame.z
  398. And finally, the linear velocities of the two pendulum bobs can be computed
  399. with respect to the ceiling.
  400. >>> upper_bob.masscenter.vel(ceiling.frame)
  401. l1*omega_P1(t)*U_frame.y
  402. >>> lower_bob.masscenter.vel(ceiling.frame)
  403. l1*omega_P1(t)*U_frame.y + l2*(omega_P1(t) + omega_P2(t))*L_frame.y
  404. """
  405. def __init__(self, name, parent, child, coordinates=None, speeds=None,
  406. parent_joint_pos=None, child_joint_pos=None, parent_axis=None,
  407. child_axis=None):
  408. super().__init__(name, parent, child, coordinates, speeds,
  409. parent_joint_pos, child_joint_pos, parent_axis,
  410. child_axis)
  411. def __str__(self):
  412. return (f'PinJoint: {self.name} parent: {self.parent} '
  413. f'child: {self.child}')
  414. def _generate_coordinates(self, coordinate):
  415. coordinates = []
  416. if coordinate is None:
  417. theta = dynamicsymbols('theta' + '_' + self._name)
  418. coordinate = theta
  419. coordinates.append(coordinate)
  420. return coordinates
  421. def _generate_speeds(self, speed):
  422. speeds = []
  423. if speed is None:
  424. omega = dynamicsymbols('omega' + '_' + self._name)
  425. speed = omega
  426. speeds.append(speed)
  427. return speeds
  428. def _orient_frames(self):
  429. frame = self._set_orientation()
  430. self.child.frame.orient_axis(frame, self.parent_axis,
  431. self.coordinates[0])
  432. def _set_angular_velocity(self):
  433. self.child.frame.set_ang_vel(self.parent.frame, self.speeds[0] *
  434. self.parent_axis.normalize())
  435. def _set_linear_velocity(self):
  436. self.parent_point.set_vel(self.parent.frame, 0)
  437. self.child_point.set_vel(self.parent.frame, 0)
  438. self.child_point.set_pos(self.parent_point, 0)
  439. self.child.masscenter.v2pt_theory(self.parent.masscenter,
  440. self.parent.frame, self.child.frame)
  441. class PrismaticJoint(Joint):
  442. """Prismatic (Sliding) Joint.
  443. Explanation
  444. ===========
  445. It is defined such that the child body translates with respect to the parent
  446. body along the body fixed parent axis. The location of the joint is defined
  447. by two points in each body which coincides when the generalized coordinate is zero. The direction cosine matrix between
  448. the child and parent is formed using a simple rotation about an axis that is normal to
  449. both ``child_axis`` and ``parent_axis``, see the Notes section for a detailed explanation of
  450. this.
  451. Parameters
  452. ==========
  453. name : string
  454. A unique name for the joint.
  455. parent : Body
  456. The parent body of joint.
  457. child : Body
  458. The child body of joint.
  459. coordinates: dynamicsymbol, optional
  460. Generalized coordinates of the joint.
  461. speeds : dynamicsymbol, optional
  462. Generalized speeds of joint.
  463. parent_joint_pos : Vector, optional
  464. Vector from the parent body's mass center to the point where the parent
  465. and child are connected. The default value is the zero vector.
  466. child_joint_pos : Vector, optional
  467. Vector from the child body's mass center to the point where the parent
  468. and child are connected. The default value is the zero vector.
  469. parent_axis : Vector, optional
  470. Axis fixed in the parent body which aligns with an axis fixed in the
  471. child body. The default is x axis in parent's reference frame.
  472. child_axis : Vector, optional
  473. Axis fixed in the child body which aligns with an axis fixed in the
  474. parent body. The default is x axis in child's reference frame.
  475. Attributes
  476. ==========
  477. name : string
  478. The joint's name.
  479. parent : Body
  480. The joint's parent body.
  481. child : Body
  482. The joint's child body.
  483. coordinates : list
  484. List of the joint's generalized coordinates.
  485. speeds : list
  486. List of the joint's generalized speeds.
  487. parent_point : Point
  488. The point fixed in the parent body that represents the joint.
  489. child_point : Point
  490. The point fixed in the child body that represents the joint.
  491. parent_axis : Vector
  492. The axis fixed in the parent frame that represents the joint.
  493. child_axis : Vector
  494. The axis fixed in the child frame that represents the joint.
  495. kdes : list
  496. Kinematical differential equations of the joint.
  497. Examples
  498. =========
  499. A single prismatic joint is created from two bodies and has the following basic
  500. attributes:
  501. >>> from sympy.physics.mechanics import Body, PrismaticJoint
  502. >>> parent = Body('P')
  503. >>> parent
  504. P
  505. >>> child = Body('C')
  506. >>> child
  507. C
  508. >>> joint = PrismaticJoint('PC', parent, child)
  509. >>> joint
  510. PrismaticJoint: PC parent: P child: C
  511. >>> joint.name
  512. 'PC'
  513. >>> joint.parent
  514. P
  515. >>> joint.child
  516. C
  517. >>> joint.parent_point
  518. PC_P_joint
  519. >>> joint.child_point
  520. PC_C_joint
  521. >>> joint.parent_axis
  522. P_frame.x
  523. >>> joint.child_axis
  524. C_frame.x
  525. >>> joint.coordinates
  526. [x_PC(t)]
  527. >>> joint.speeds
  528. [v_PC(t)]
  529. >>> joint.child.frame.ang_vel_in(joint.parent.frame)
  530. 0
  531. >>> joint.child.frame.dcm(joint.parent.frame)
  532. Matrix([
  533. [1, 0, 0],
  534. [0, 1, 0],
  535. [0, 0, 1]])
  536. >>> joint.child_point.pos_from(joint.parent_point)
  537. x_PC(t)*P_frame.x
  538. To further demonstrate the use of the prismatic joint, the kinematics of
  539. two masses sliding, one moving relative to a fixed body and the other relative to the
  540. moving body. about the X axis of each connected body can be created as follows.
  541. >>> from sympy.physics.mechanics import PrismaticJoint, Body
  542. First create bodies to represent the fixed ceiling and one to represent
  543. a particle.
  544. >>> wall = Body('W')
  545. >>> Part1 = Body('P1')
  546. >>> Part2 = Body('P2')
  547. The first joint will connect the particle to the ceiling and the
  548. joint axis will be about the X axis for each body.
  549. >>> J1 = PrismaticJoint('J1', wall, Part1)
  550. The second joint will connect the second particle to the first particle
  551. and the joint axis will also be about the X axis for each body.
  552. >>> J2 = PrismaticJoint('J2', Part1, Part2)
  553. Once the joint is established the kinematics of the connected bodies can
  554. be accessed. First the direction cosine matrices of Part relative
  555. to the ceiling are found:
  556. >>> Part1.dcm(wall)
  557. Matrix([
  558. [1, 0, 0],
  559. [0, 1, 0],
  560. [0, 0, 1]])
  561. >>> Part2.dcm(wall)
  562. Matrix([
  563. [1, 0, 0],
  564. [0, 1, 0],
  565. [0, 0, 1]])
  566. The position of the particles' masscenter is found with:
  567. >>> Part1.masscenter.pos_from(wall.masscenter)
  568. x_J1(t)*W_frame.x
  569. >>> Part2.masscenter.pos_from(wall.masscenter)
  570. x_J1(t)*W_frame.x + x_J2(t)*P1_frame.x
  571. The angular velocities of the two particle links can be computed with
  572. respect to the ceiling.
  573. >>> Part1.ang_vel_in(wall)
  574. 0
  575. >>> Part2.ang_vel_in(wall)
  576. 0
  577. And finally, the linear velocities of the two particles can be computed
  578. with respect to the ceiling.
  579. >>> Part1.masscenter_vel(wall)
  580. v_J1(t)*W_frame.x
  581. >>> Part2.masscenter.vel(wall.frame)
  582. v_J1(t)*W_frame.x + v_J2(t)*P1_frame.x
  583. """
  584. def __init__(self, name, parent, child, coordinates=None, speeds=None, parent_joint_pos=None,
  585. child_joint_pos=None, parent_axis=None, child_axis=None):
  586. super().__init__(name, parent, child, coordinates, speeds, parent_joint_pos,
  587. child_joint_pos, parent_axis, child_axis)
  588. def __str__(self):
  589. return (f'PrismaticJoint: {self.name} parent: {self.parent} '
  590. f'child: {self.child}')
  591. def _generate_coordinates(self, coordinate):
  592. coordinates = []
  593. if coordinate is None:
  594. x = dynamicsymbols('x' + '_' + self._name)
  595. coordinate = x
  596. coordinates.append(coordinate)
  597. return coordinates
  598. def _generate_speeds(self, speed):
  599. speeds = []
  600. if speed is None:
  601. y = dynamicsymbols('v' + '_' + self._name)
  602. speed = y
  603. speeds.append(speed)
  604. return speeds
  605. def _orient_frames(self):
  606. frame = self._set_orientation()
  607. self.child.frame.orient_axis(frame, self.parent_axis, 0)
  608. def _set_angular_velocity(self):
  609. self.child.frame.set_ang_vel(self.parent.frame, 0)
  610. def _set_linear_velocity(self):
  611. self.parent_point.set_vel(self.parent.frame, 0)
  612. self.child_point.set_vel(self.child.frame, 0)
  613. self.child_point.set_pos(self.parent_point, self.coordinates[0] * self.parent_axis.normalize())
  614. self.child_point.set_vel(self.parent.frame, self.speeds[0] * self.parent_axis.normalize())
  615. self.child.masscenter.set_vel(self.parent.frame, self.speeds[0] * self.parent_axis.normalize())