m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
13 KiB

7 months ago
  1. from sympy.core.backend import (cos, expand, Matrix, sin, symbols, tan, sqrt, S,
  2. zeros)
  3. from sympy.simplify.simplify import simplify
  4. from sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point,
  5. RigidBody, KanesMethod, inertia, Particle,
  6. dot)
  7. from sympy.testing.pytest import raises
  8. def test_one_dof():
  9. # This is for a 1 dof spring-mass-damper case.
  10. # It is described in more detail in the KanesMethod docstring.
  11. q, u = dynamicsymbols('q u')
  12. qd, ud = dynamicsymbols('q u', 1)
  13. m, c, k = symbols('m c k')
  14. N = ReferenceFrame('N')
  15. P = Point('P')
  16. P.set_vel(N, u * N.x)
  17. kd = [qd - u]
  18. FL = [(P, (-k * q - c * u) * N.x)]
  19. pa = Particle('pa', P, m)
  20. BL = [pa]
  21. KM = KanesMethod(N, [q], [u], kd)
  22. KM.kanes_equations(BL, FL)
  23. assert KM.bodies == BL
  24. assert KM.loads == FL
  25. MM = KM.mass_matrix
  26. forcing = KM.forcing
  27. rhs = MM.inv() * forcing
  28. assert expand(rhs[0]) == expand(-(q * k + u * c) / m)
  29. assert simplify(KM.rhs() -
  30. KM.mass_matrix_full.LUsolve(KM.forcing_full)) == zeros(2, 1)
  31. assert (KM.linearize(A_and_B=True, )[0] == Matrix([[0, 1], [-k/m, -c/m]]))
  32. def test_two_dof():
  33. # This is for a 2 d.o.f., 2 particle spring-mass-damper.
  34. # The first coordinate is the displacement of the first particle, and the
  35. # second is the relative displacement between the first and second
  36. # particles. Speeds are defined as the time derivatives of the particles.
  37. q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2')
  38. q1d, q2d, u1d, u2d = dynamicsymbols('q1 q2 u1 u2', 1)
  39. m, c1, c2, k1, k2 = symbols('m c1 c2 k1 k2')
  40. N = ReferenceFrame('N')
  41. P1 = Point('P1')
  42. P2 = Point('P2')
  43. P1.set_vel(N, u1 * N.x)
  44. P2.set_vel(N, (u1 + u2) * N.x)
  45. kd = [q1d - u1, q2d - u2]
  46. # Now we create the list of forces, then assign properties to each
  47. # particle, then create a list of all particles.
  48. FL = [(P1, (-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2) * N.x), (P2, (-k2 *
  49. q2 - c2 * u2) * N.x)]
  50. pa1 = Particle('pa1', P1, m)
  51. pa2 = Particle('pa2', P2, m)
  52. BL = [pa1, pa2]
  53. # Finally we create the KanesMethod object, specify the inertial frame,
  54. # pass relevant information, and form Fr & Fr*. Then we calculate the mass
  55. # matrix and forcing terms, and finally solve for the udots.
  56. KM = KanesMethod(N, q_ind=[q1, q2], u_ind=[u1, u2], kd_eqs=kd)
  57. KM.kanes_equations(BL, FL)
  58. MM = KM.mass_matrix
  59. forcing = KM.forcing
  60. rhs = MM.inv() * forcing
  61. assert expand(rhs[0]) == expand((-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2)/m)
  62. assert expand(rhs[1]) == expand((k1 * q1 + c1 * u1 - 2 * k2 * q2 - 2 *
  63. c2 * u2) / m)
  64. assert simplify(KM.rhs() -
  65. KM.mass_matrix_full.LUsolve(KM.forcing_full)) == zeros(4, 1)
  66. # Make sure an error is raised if nonlinear kinematic differential
  67. # equations are supplied.
  68. kd = [q1d - u1**2, sin(q2d) - cos(u2)]
  69. raises(ValueError, lambda: KanesMethod(N, q_ind=[q1, q2],
  70. u_ind=[u1, u2], kd_eqs=kd))
  71. def test_pend():
  72. q, u = dynamicsymbols('q u')
  73. qd, ud = dynamicsymbols('q u', 1)
  74. m, l, g = symbols('m l g')
  75. N = ReferenceFrame('N')
  76. P = Point('P')
  77. P.set_vel(N, -l * u * sin(q) * N.x + l * u * cos(q) * N.y)
  78. kd = [qd - u]
  79. FL = [(P, m * g * N.x)]
  80. pa = Particle('pa', P, m)
  81. BL = [pa]
  82. KM = KanesMethod(N, [q], [u], kd)
  83. KM.kanes_equations(BL, FL)
  84. MM = KM.mass_matrix
  85. forcing = KM.forcing
  86. rhs = MM.inv() * forcing
  87. rhs.simplify()
  88. assert expand(rhs[0]) == expand(-g / l * sin(q))
  89. assert simplify(KM.rhs() -
  90. KM.mass_matrix_full.LUsolve(KM.forcing_full)) == zeros(2, 1)
  91. def test_rolling_disc():
  92. # Rolling Disc Example
  93. # Here the rolling disc is formed from the contact point up, removing the
  94. # need to introduce generalized speeds. Only 3 configuration and three
  95. # speed variables are need to describe this system, along with the disc's
  96. # mass and radius, and the local gravity (note that mass will drop out).
  97. q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3')
  98. q1d, q2d, q3d, u1d, u2d, u3d = dynamicsymbols('q1 q2 q3 u1 u2 u3', 1)
  99. r, m, g = symbols('r m g')
  100. # The kinematics are formed by a series of simple rotations. Each simple
  101. # rotation creates a new frame, and the next rotation is defined by the new
  102. # frame's basis vectors. This example uses a 3-1-2 series of rotations, or
  103. # Z, X, Y series of rotations. Angular velocity for this is defined using
  104. # the second frame's basis (the lean frame).
  105. N = ReferenceFrame('N')
  106. Y = N.orientnew('Y', 'Axis', [q1, N.z])
  107. L = Y.orientnew('L', 'Axis', [q2, Y.x])
  108. R = L.orientnew('R', 'Axis', [q3, L.y])
  109. w_R_N_qd = R.ang_vel_in(N)
  110. R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)
  111. # This is the translational kinematics. We create a point with no velocity
  112. # in N; this is the contact point between the disc and ground. Next we form
  113. # the position vector from the contact point to the disc's center of mass.
  114. # Finally we form the velocity and acceleration of the disc.
  115. C = Point('C')
  116. C.set_vel(N, 0)
  117. Dmc = C.locatenew('Dmc', r * L.z)
  118. Dmc.v2pt_theory(C, N, R)
  119. # This is a simple way to form the inertia dyadic.
  120. I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2)
  121. # Kinematic differential equations; how the generalized coordinate time
  122. # derivatives relate to generalized speeds.
  123. kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L]
  124. # Creation of the force list; it is the gravitational force at the mass
  125. # center of the disc. Then we create the disc by assigning a Point to the
  126. # center of mass attribute, a ReferenceFrame to the frame attribute, and mass
  127. # and inertia. Then we form the body list.
  128. ForceList = [(Dmc, - m * g * Y.z)]
  129. BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc))
  130. BodyList = [BodyD]
  131. # Finally we form the equations of motion, using the same steps we did
  132. # before. Specify inertial frame, supply generalized speeds, supply
  133. # kinematic differential equation dictionary, compute Fr from the force
  134. # list and Fr* from the body list, compute the mass matrix and forcing
  135. # terms, then solve for the u dots (time derivatives of the generalized
  136. # speeds).
  137. KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd)
  138. KM.kanes_equations(BodyList, ForceList)
  139. MM = KM.mass_matrix
  140. forcing = KM.forcing
  141. rhs = MM.inv() * forcing
  142. kdd = KM.kindiffdict()
  143. rhs = rhs.subs(kdd)
  144. rhs.simplify()
  145. assert rhs.expand() == Matrix([(6*u2*u3*r - u3**2*r*tan(q2) +
  146. 4*g*sin(q2))/(5*r), -2*u1*u3/3, u1*(-2*u2 + u3*tan(q2))]).expand()
  147. assert simplify(KM.rhs() -
  148. KM.mass_matrix_full.LUsolve(KM.forcing_full)) == zeros(6, 1)
  149. # This code tests our output vs. benchmark values. When r=g=m=1, the
  150. # critical speed (where all eigenvalues of the linearized equations are 0)
  151. # is 1 / sqrt(3) for the upright case.
  152. A = KM.linearize(A_and_B=True)[0]
  153. A_upright = A.subs({r: 1, g: 1, m: 1}).subs({q1: 0, q2: 0, q3: 0, u1: 0, u3: 0})
  154. import sympy
  155. assert sympy.sympify(A_upright.subs({u2: 1 / sqrt(3)})).eigenvals() == {S.Zero: 6}
  156. def test_aux():
  157. # Same as above, except we have 2 auxiliary speeds for the ground contact
  158. # point, which is known to be zero. In one case, we go through then
  159. # substitute the aux. speeds in at the end (they are zero, as well as their
  160. # derivative), in the other case, we use the built-in auxiliary speed part
  161. # of KanesMethod. The equations from each should be the same.
  162. q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3')
  163. q1d, q2d, q3d, u1d, u2d, u3d = dynamicsymbols('q1 q2 q3 u1 u2 u3', 1)
  164. u4, u5, f1, f2 = dynamicsymbols('u4, u5, f1, f2')
  165. u4d, u5d = dynamicsymbols('u4, u5', 1)
  166. r, m, g = symbols('r m g')
  167. N = ReferenceFrame('N')
  168. Y = N.orientnew('Y', 'Axis', [q1, N.z])
  169. L = Y.orientnew('L', 'Axis', [q2, Y.x])
  170. R = L.orientnew('R', 'Axis', [q3, L.y])
  171. w_R_N_qd = R.ang_vel_in(N)
  172. R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)
  173. C = Point('C')
  174. C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x))
  175. Dmc = C.locatenew('Dmc', r * L.z)
  176. Dmc.v2pt_theory(C, N, R)
  177. Dmc.a2pt_theory(C, N, R)
  178. I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2)
  179. kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L]
  180. ForceList = [(Dmc, - m * g * Y.z), (C, f1 * L.x + f2 * (Y.z ^ L.x))]
  181. BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc))
  182. BodyList = [BodyD]
  183. KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3, u4, u5],
  184. kd_eqs=kd)
  185. (fr, frstar) = KM.kanes_equations(BodyList, ForceList)
  186. fr = fr.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0})
  187. frstar = frstar.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0})
  188. KM2 = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd,
  189. u_auxiliary=[u4, u5])
  190. (fr2, frstar2) = KM2.kanes_equations(BodyList, ForceList)
  191. fr2 = fr2.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0})
  192. frstar2 = frstar2.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0})
  193. frstar.simplify()
  194. frstar2.simplify()
  195. assert (fr - fr2).expand() == Matrix([0, 0, 0, 0, 0])
  196. assert (frstar - frstar2).expand() == Matrix([0, 0, 0, 0, 0])
  197. def test_parallel_axis():
  198. # This is for a 2 dof inverted pendulum on a cart.
  199. # This tests the parallel axis code in KanesMethod. The inertia of the
  200. # pendulum is defined about the hinge, not about the center of mass.
  201. # Defining the constants and knowns of the system
  202. gravity = symbols('g')
  203. k, ls = symbols('k ls')
  204. a, mA, mC = symbols('a mA mC')
  205. F = dynamicsymbols('F')
  206. Ix, Iy, Iz = symbols('Ix Iy Iz')
  207. # Declaring the Generalized coordinates and speeds
  208. q1, q2 = dynamicsymbols('q1 q2')
  209. q1d, q2d = dynamicsymbols('q1 q2', 1)
  210. u1, u2 = dynamicsymbols('u1 u2')
  211. u1d, u2d = dynamicsymbols('u1 u2', 1)
  212. # Creating reference frames
  213. N = ReferenceFrame('N')
  214. A = ReferenceFrame('A')
  215. A.orient(N, 'Axis', [-q2, N.z])
  216. A.set_ang_vel(N, -u2 * N.z)
  217. # Origin of Newtonian reference frame
  218. O = Point('O')
  219. # Creating and Locating the positions of the cart, C, and the
  220. # center of mass of the pendulum, A
  221. C = O.locatenew('C', q1 * N.x)
  222. Ao = C.locatenew('Ao', a * A.y)
  223. # Defining velocities of the points
  224. O.set_vel(N, 0)
  225. C.set_vel(N, u1 * N.x)
  226. Ao.v2pt_theory(C, N, A)
  227. Cart = Particle('Cart', C, mC)
  228. Pendulum = RigidBody('Pendulum', Ao, A, mA, (inertia(A, Ix, Iy, Iz), C))
  229. # kinematical differential equations
  230. kindiffs = [q1d - u1, q2d - u2]
  231. bodyList = [Cart, Pendulum]
  232. forceList = [(Ao, -N.y * gravity * mA),
  233. (C, -N.y * gravity * mC),
  234. (C, -N.x * k * (q1 - ls)),
  235. (C, N.x * F)]
  236. km = KanesMethod(N, [q1, q2], [u1, u2], kindiffs)
  237. (fr, frstar) = km.kanes_equations(bodyList, forceList)
  238. mm = km.mass_matrix_full
  239. assert mm[3, 3] == Iz
  240. def test_input_format():
  241. # 1 dof problem from test_one_dof
  242. q, u = dynamicsymbols('q u')
  243. qd, ud = dynamicsymbols('q u', 1)
  244. m, c, k = symbols('m c k')
  245. N = ReferenceFrame('N')
  246. P = Point('P')
  247. P.set_vel(N, u * N.x)
  248. kd = [qd - u]
  249. FL = [(P, (-k * q - c * u) * N.x)]
  250. pa = Particle('pa', P, m)
  251. BL = [pa]
  252. KM = KanesMethod(N, [q], [u], kd)
  253. # test for input format kane.kanes_equations((body1, body2, particle1))
  254. assert KM.kanes_equations(BL)[0] == Matrix([0])
  255. # test for input format kane.kanes_equations(bodies=(body1, body 2), loads=(load1,load2))
  256. assert KM.kanes_equations(bodies=BL, loads=None)[0] == Matrix([0])
  257. # test for input format kane.kanes_equations(bodies=(body1, body 2), loads=None)
  258. assert KM.kanes_equations(BL, loads=None)[0] == Matrix([0])
  259. # test for input format kane.kanes_equations(bodies=(body1, body 2))
  260. assert KM.kanes_equations(BL)[0] == Matrix([0])
  261. # test for input format kane.kanes_equations(bodies=(body1, body2), loads=[])
  262. assert KM.kanes_equations(BL, [])[0] == Matrix([0])
  263. # test for error raised when a wrong force list (in this case a string) is provided
  264. raises(ValueError, lambda: KM._form_fr('bad input'))
  265. # 1 dof problem from test_one_dof with FL & BL in instance
  266. KM = KanesMethod(N, [q], [u], kd, bodies=BL, forcelist=FL)
  267. assert KM.kanes_equations()[0] == Matrix([-c*u - k*q])
  268. # 2 dof problem from test_two_dof
  269. q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2')
  270. q1d, q2d, u1d, u2d = dynamicsymbols('q1 q2 u1 u2', 1)
  271. m, c1, c2, k1, k2 = symbols('m c1 c2 k1 k2')
  272. N = ReferenceFrame('N')
  273. P1 = Point('P1')
  274. P2 = Point('P2')
  275. P1.set_vel(N, u1 * N.x)
  276. P2.set_vel(N, (u1 + u2) * N.x)
  277. kd = [q1d - u1, q2d - u2]
  278. FL = ((P1, (-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2) * N.x), (P2, (-k2 *
  279. q2 - c2 * u2) * N.x))
  280. pa1 = Particle('pa1', P1, m)
  281. pa2 = Particle('pa2', P2, m)
  282. BL = (pa1, pa2)
  283. KM = KanesMethod(N, q_ind=[q1, q2], u_ind=[u1, u2], kd_eqs=kd)
  284. # test for input format
  285. # kane.kanes_equations((body1, body2), (load1, load2))
  286. KM.kanes_equations(BL, FL)
  287. MM = KM.mass_matrix
  288. forcing = KM.forcing
  289. rhs = MM.inv() * forcing
  290. assert expand(rhs[0]) == expand((-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2)/m)
  291. assert expand(rhs[1]) == expand((k1 * q1 + c1 * u1 - 2 * k2 * q2 - 2 *
  292. c2 * u2) / m)