jointsmethod.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. from sympy.physics.mechanics import (Body, Lagrangian, KanesMethod, LagrangesMethod,
  2. RigidBody, Particle)
  3. from sympy.physics.mechanics.method import _Methods
  4. from sympy.core.backend import Matrix
  5. __all__ = ['JointsMethod']
  6. class JointsMethod(_Methods):
  7. """Method for formulating the equations of motion using a set of interconnected bodies with joints.
  8. Parameters
  9. ==========
  10. newtonion : Body or ReferenceFrame
  11. The newtonion(inertial) frame.
  12. *joints : Joint
  13. The joints in the system
  14. Attributes
  15. ==========
  16. q, u : iterable
  17. Iterable of the generalized coordinates and speeds
  18. bodies : iterable
  19. Iterable of Body objects in the system.
  20. loads : iterable
  21. Iterable of (Point, vector) or (ReferenceFrame, vector) tuples
  22. describing the forces on the system.
  23. mass_matrix : Matrix, shape(n, n)
  24. The system's mass matrix
  25. forcing : Matrix, shape(n, 1)
  26. The system's forcing vector
  27. mass_matrix_full : Matrix, shape(2*n, 2*n)
  28. The "mass matrix" for the u's and q's
  29. forcing_full : Matrix, shape(2*n, 1)
  30. The "forcing vector" for the u's and q's
  31. method : KanesMethod or Lagrange's method
  32. Method's object.
  33. kdes : iterable
  34. Iterable of kde in they system.
  35. Examples
  36. ========
  37. This is a simple example for a one degree of freedom translational
  38. spring-mass-damper.
  39. >>> from sympy import symbols
  40. >>> from sympy.physics.mechanics import Body, JointsMethod, PrismaticJoint
  41. >>> from sympy.physics.vector import dynamicsymbols
  42. >>> c, k = symbols('c k')
  43. >>> x, v = dynamicsymbols('x v')
  44. >>> wall = Body('W')
  45. >>> body = Body('B')
  46. >>> J = PrismaticJoint('J', wall, body, coordinates=x, speeds=v)
  47. >>> wall.apply_force(c*v*wall.x, reaction_body=body)
  48. >>> wall.apply_force(k*x*wall.x, reaction_body=body)
  49. >>> method = JointsMethod(wall, J)
  50. >>> method.form_eoms()
  51. Matrix([[-B_mass*Derivative(v(t), t) - c*v(t) - k*x(t)]])
  52. >>> M = method.mass_matrix_full
  53. >>> F = method.forcing_full
  54. >>> rhs = M.LUsolve(F)
  55. >>> rhs
  56. Matrix([
  57. [ v(t)],
  58. [(-c*v(t) - k*x(t))/B_mass]])
  59. Notes
  60. =====
  61. ``JointsMethod`` currently only works with systems that do not have any
  62. configuration or motion constraints.
  63. """
  64. def __init__(self, newtonion, *joints):
  65. if isinstance(newtonion, Body):
  66. self.frame = newtonion.frame
  67. else:
  68. self.frame = newtonion
  69. self._joints = joints
  70. self._bodies = self._generate_bodylist()
  71. self._loads = self._generate_loadlist()
  72. self._q = self._generate_q()
  73. self._u = self._generate_u()
  74. self._kdes = self._generate_kdes()
  75. self._method = None
  76. @property
  77. def bodies(self):
  78. """List of bodies in they system."""
  79. return self._bodies
  80. @property
  81. def loads(self):
  82. """List of loads on the system."""
  83. return self._loads
  84. @property
  85. def q(self):
  86. """List of the generalized coordinates."""
  87. return self._q
  88. @property
  89. def u(self):
  90. """List of the generalized speeds."""
  91. return self._u
  92. @property
  93. def kdes(self):
  94. """List of the generalized coordinates."""
  95. return self._kdes
  96. @property
  97. def forcing_full(self):
  98. """The "forcing vector" for the u's and q's."""
  99. return self.method.forcing_full
  100. @property
  101. def mass_matrix_full(self):
  102. """The "mass matrix" for the u's and q's."""
  103. return self.method.mass_matrix_full
  104. @property
  105. def mass_matrix(self):
  106. """The system's mass matrix."""
  107. return self.method.mass_matrix
  108. @property
  109. def forcing(self):
  110. """The system's forcing vector."""
  111. return self.method.forcing
  112. @property
  113. def method(self):
  114. """Object of method used to form equations of systems."""
  115. return self._method
  116. def _generate_bodylist(self):
  117. bodies = []
  118. for joint in self._joints:
  119. if joint.child not in bodies:
  120. bodies.append(joint.child)
  121. if joint.parent not in bodies:
  122. bodies.append(joint.parent)
  123. return bodies
  124. def _generate_loadlist(self):
  125. load_list = []
  126. for body in self.bodies:
  127. load_list.extend(body.loads)
  128. return load_list
  129. def _generate_q(self):
  130. q_ind = []
  131. for joint in self._joints:
  132. for coordinate in joint.coordinates:
  133. if coordinate in q_ind:
  134. raise ValueError('Coordinates of joints should be unique.')
  135. q_ind.append(coordinate)
  136. return Matrix(q_ind)
  137. def _generate_u(self):
  138. u_ind = []
  139. for joint in self._joints:
  140. for speed in joint.speeds:
  141. if speed in u_ind:
  142. raise ValueError('Speeds of joints should be unique.')
  143. u_ind.append(speed)
  144. return Matrix(u_ind)
  145. def _generate_kdes(self):
  146. kd_ind = Matrix(1, 0, []).T
  147. for joint in self._joints:
  148. kd_ind = kd_ind.col_join(joint.kdes)
  149. return kd_ind
  150. def _convert_bodies(self):
  151. # Convert `Body` to `Particle` and `RigidBody`
  152. bodylist = []
  153. for body in self.bodies:
  154. if body.is_rigidbody:
  155. rb = RigidBody(body.name, body.masscenter, body.frame, body.mass,
  156. (body.central_inertia, body.masscenter))
  157. rb.potential_energy = body.potential_energy
  158. bodylist.append(rb)
  159. else:
  160. part = Particle(body.name, body.masscenter, body.mass)
  161. part.potential_energy = body.potential_energy
  162. bodylist.append(part)
  163. return bodylist
  164. def form_eoms(self, method=KanesMethod):
  165. """Method to form system's equation of motions.
  166. Parameters
  167. ==========
  168. method : Class
  169. Class name of method.
  170. Returns
  171. ========
  172. Matrix
  173. Vector of equations of motions.
  174. Examples
  175. ========
  176. This is a simple example for a one degree of freedom translational
  177. spring-mass-damper.
  178. >>> from sympy import S, symbols
  179. >>> from sympy.physics.mechanics import LagrangesMethod, dynamicsymbols, Body
  180. >>> from sympy.physics.mechanics import PrismaticJoint, JointsMethod
  181. >>> q = dynamicsymbols('q')
  182. >>> qd = dynamicsymbols('q', 1)
  183. >>> m, k, b = symbols('m k b')
  184. >>> wall = Body('W')
  185. >>> part = Body('P', mass=m)
  186. >>> part.potential_energy = k * q**2 / S(2)
  187. >>> J = PrismaticJoint('J', wall, part, coordinates=q, speeds=qd)
  188. >>> wall.apply_force(b * qd * wall.x, reaction_body=part)
  189. >>> method = JointsMethod(wall, J)
  190. >>> method.form_eoms(LagrangesMethod)
  191. Matrix([[b*Derivative(q(t), t) + k*q(t) + m*Derivative(q(t), (t, 2))]])
  192. We can also solve for the states using the 'rhs' method.
  193. >>> method.rhs()
  194. Matrix([
  195. [ Derivative(q(t), t)],
  196. [(-b*Derivative(q(t), t) - k*q(t))/m]])
  197. """
  198. bodylist = self._convert_bodies()
  199. if issubclass(method, LagrangesMethod): #LagrangesMethod or similar
  200. L = Lagrangian(self.frame, *bodylist)
  201. self._method = method(L, self.q, self.loads, bodylist, self.frame)
  202. else: #KanesMethod or similar
  203. self._method = method(self.frame, q_ind=self.q, u_ind=self.u, kd_eqs=self.kdes,
  204. forcelist=self.loads, bodies=bodylist)
  205. soln = self.method._form_eoms()
  206. return soln
  207. def rhs(self, inv_method=None):
  208. """Returns equations that can be solved numerically.
  209. Parameters
  210. ==========
  211. inv_method : str
  212. The specific sympy inverse matrix calculation method to use. For a
  213. list of valid methods, see
  214. :meth:`~sympy.matrices.matrices.MatrixBase.inv`
  215. Returns
  216. ========
  217. Matrix
  218. Numerically solvable equations.
  219. See Also
  220. ========
  221. sympy.physics.mechanics.kane.KanesMethod.rhs:
  222. KanesMethod's rhs function.
  223. sympy.physics.mechanics.lagrange.LagrangesMethod.rhs:
  224. LagrangesMethod's rhs function.
  225. """
  226. return self.method.rhs(inv_method=inv_method)