models.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #!/usr/bin/env python
  2. """This module contains some sample symbolic models used for testing and
  3. examples."""
  4. # Internal imports
  5. from sympy.core import backend as sm
  6. import sympy.physics.mechanics as me
  7. def multi_mass_spring_damper(n=1, apply_gravity=False,
  8. apply_external_forces=False):
  9. r"""Returns a system containing the symbolic equations of motion and
  10. associated variables for a simple multi-degree of freedom point mass,
  11. spring, damper system with optional gravitational and external
  12. specified forces. For example, a two mass system under the influence of
  13. gravity and external forces looks like:
  14. ::
  15. ----------------
  16. | | | | g
  17. \ | | | V
  18. k0 / --- c0 |
  19. | | | x0, v0
  20. --------- V
  21. | m0 | -----
  22. --------- |
  23. | | | |
  24. \ v | | |
  25. k1 / f0 --- c1 |
  26. | | | x1, v1
  27. --------- V
  28. | m1 | -----
  29. ---------
  30. | f1
  31. V
  32. Parameters
  33. ==========
  34. n : integer
  35. The number of masses in the serial chain.
  36. apply_gravity : boolean
  37. If true, gravity will be applied to each mass.
  38. apply_external_forces : boolean
  39. If true, a time varying external force will be applied to each mass.
  40. Returns
  41. =======
  42. kane : sympy.physics.mechanics.kane.KanesMethod
  43. A KanesMethod object.
  44. """
  45. mass = sm.symbols('m:{}'.format(n))
  46. stiffness = sm.symbols('k:{}'.format(n))
  47. damping = sm.symbols('c:{}'.format(n))
  48. acceleration_due_to_gravity = sm.symbols('g')
  49. coordinates = me.dynamicsymbols('x:{}'.format(n))
  50. speeds = me.dynamicsymbols('v:{}'.format(n))
  51. specifieds = me.dynamicsymbols('f:{}'.format(n))
  52. ceiling = me.ReferenceFrame('N')
  53. origin = me.Point('origin')
  54. origin.set_vel(ceiling, 0)
  55. points = [origin]
  56. kinematic_equations = []
  57. particles = []
  58. forces = []
  59. for i in range(n):
  60. center = points[-1].locatenew('center{}'.format(i),
  61. coordinates[i] * ceiling.x)
  62. center.set_vel(ceiling, points[-1].vel(ceiling) +
  63. speeds[i] * ceiling.x)
  64. points.append(center)
  65. block = me.Particle('block{}'.format(i), center, mass[i])
  66. kinematic_equations.append(speeds[i] - coordinates[i].diff())
  67. total_force = (-stiffness[i] * coordinates[i] -
  68. damping[i] * speeds[i])
  69. try:
  70. total_force += (stiffness[i + 1] * coordinates[i + 1] +
  71. damping[i + 1] * speeds[i + 1])
  72. except IndexError: # no force from below on last mass
  73. pass
  74. if apply_gravity:
  75. total_force += mass[i] * acceleration_due_to_gravity
  76. if apply_external_forces:
  77. total_force += specifieds[i]
  78. forces.append((center, total_force * ceiling.x))
  79. particles.append(block)
  80. kane = me.KanesMethod(ceiling, q_ind=coordinates, u_ind=speeds,
  81. kd_eqs=kinematic_equations)
  82. kane.kanes_equations(particles, forces)
  83. return kane
  84. def n_link_pendulum_on_cart(n=1, cart_force=True, joint_torques=False):
  85. r"""Returns the system containing the symbolic first order equations of
  86. motion for a 2D n-link pendulum on a sliding cart under the influence of
  87. gravity.
  88. ::
  89. |
  90. o y v
  91. \ 0 ^ g
  92. \ |
  93. --\-|----
  94. | \| |
  95. F-> | o --|---> x
  96. | |
  97. ---------
  98. o o
  99. Parameters
  100. ==========
  101. n : integer
  102. The number of links in the pendulum.
  103. cart_force : boolean, default=True
  104. If true an external specified lateral force is applied to the cart.
  105. joint_torques : boolean, default=False
  106. If true joint torques will be added as specified inputs at each
  107. joint.
  108. Returns
  109. =======
  110. kane : sympy.physics.mechanics.kane.KanesMethod
  111. A KanesMethod object.
  112. Notes
  113. =====
  114. The degrees of freedom of the system are n + 1, i.e. one for each
  115. pendulum link and one for the lateral motion of the cart.
  116. M x' = F, where x = [u0, ..., un+1, q0, ..., qn+1]
  117. The joint angles are all defined relative to the ground where the x axis
  118. defines the ground line and the y axis points up. The joint torques are
  119. applied between each adjacent link and the between the cart and the
  120. lower link where a positive torque corresponds to positive angle.
  121. """
  122. if n <= 0:
  123. raise ValueError('The number of links must be a positive integer.')
  124. q = me.dynamicsymbols('q:{}'.format(n + 1))
  125. u = me.dynamicsymbols('u:{}'.format(n + 1))
  126. if joint_torques is True:
  127. T = me.dynamicsymbols('T1:{}'.format(n + 1))
  128. m = sm.symbols('m:{}'.format(n + 1))
  129. l = sm.symbols('l:{}'.format(n))
  130. g, t = sm.symbols('g t')
  131. I = me.ReferenceFrame('I')
  132. O = me.Point('O')
  133. O.set_vel(I, 0)
  134. P0 = me.Point('P0')
  135. P0.set_pos(O, q[0] * I.x)
  136. P0.set_vel(I, u[0] * I.x)
  137. Pa0 = me.Particle('Pa0', P0, m[0])
  138. frames = [I]
  139. points = [P0]
  140. particles = [Pa0]
  141. forces = [(P0, -m[0] * g * I.y)]
  142. kindiffs = [q[0].diff(t) - u[0]]
  143. if cart_force is True or joint_torques is True:
  144. specified = []
  145. else:
  146. specified = None
  147. for i in range(n):
  148. Bi = I.orientnew('B{}'.format(i), 'Axis', [q[i + 1], I.z])
  149. Bi.set_ang_vel(I, u[i + 1] * I.z)
  150. frames.append(Bi)
  151. Pi = points[-1].locatenew('P{}'.format(i + 1), l[i] * Bi.y)
  152. Pi.v2pt_theory(points[-1], I, Bi)
  153. points.append(Pi)
  154. Pai = me.Particle('Pa' + str(i + 1), Pi, m[i + 1])
  155. particles.append(Pai)
  156. forces.append((Pi, -m[i + 1] * g * I.y))
  157. if joint_torques is True:
  158. specified.append(T[i])
  159. if i == 0:
  160. forces.append((I, -T[i] * I.z))
  161. if i == n - 1:
  162. forces.append((Bi, T[i] * I.z))
  163. else:
  164. forces.append((Bi, T[i] * I.z - T[i + 1] * I.z))
  165. kindiffs.append(q[i + 1].diff(t) - u[i + 1])
  166. if cart_force is True:
  167. F = me.dynamicsymbols('F')
  168. forces.append((P0, F * I.x))
  169. specified.append(F)
  170. kane = me.KanesMethod(I, q_ind=q, u_ind=u, kd_eqs=kindiffs)
  171. kane.kanes_equations(particles, forces)
  172. return kane