test_kane5.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. from sympy import (zeros, Matrix, symbols, lambdify, sqrt, pi,
  2. simplify)
  3. from sympy.physics.mechanics import (dynamicsymbols, cross, inertia, RigidBody,
  4. ReferenceFrame, KanesMethod)
  5. def _create_rolling_disc():
  6. # Define symbols and coordinates
  7. t = dynamicsymbols._t
  8. q1, q2, q3, q4, q5, u1, u2, u3, u4, u5 = dynamicsymbols('q1:6 u1:6')
  9. g, r, m = symbols('g r m')
  10. # Define bodies and frames
  11. ground = RigidBody('ground')
  12. disc = RigidBody('disk', mass=m)
  13. disc.inertia = (m * r ** 2 / 4 * inertia(disc.frame, 1, 2, 1),
  14. disc.masscenter)
  15. ground.masscenter.set_vel(ground.frame, 0)
  16. disc.masscenter.set_vel(disc.frame, 0)
  17. int_frame = ReferenceFrame('int_frame')
  18. # Orient frames
  19. int_frame.orient_body_fixed(ground.frame, (q1, q2, 0), 'zxy')
  20. disc.frame.orient_axis(int_frame, int_frame.y, q3)
  21. g_w_d = disc.frame.ang_vel_in(ground.frame)
  22. disc.frame.set_ang_vel(ground.frame,
  23. u1 * disc.x + u2 * disc.y + u3 * disc.z)
  24. # Define points
  25. cp = ground.masscenter.locatenew('contact_point',
  26. q4 * ground.x + q5 * ground.y)
  27. cp.set_vel(ground.frame, u4 * ground.x + u5 * ground.y)
  28. disc.masscenter.set_pos(cp, r * int_frame.z)
  29. disc.masscenter.set_vel(ground.frame, cross(
  30. disc.frame.ang_vel_in(ground.frame), disc.masscenter.pos_from(cp)))
  31. # Define kinematic differential equations
  32. kdes = [g_w_d.dot(disc.x) - u1, g_w_d.dot(disc.y) - u2,
  33. g_w_d.dot(disc.z) - u3, q4.diff(t) - u4, q5.diff(t) - u5]
  34. # Define nonholonomic constraints
  35. v0 = cp.vel(ground.frame) + cross(
  36. disc.frame.ang_vel_in(int_frame), cp.pos_from(disc.masscenter))
  37. fnh = [v0.dot(ground.x), v0.dot(ground.y)]
  38. # Define loads
  39. loads = [(disc.masscenter, -disc.mass * g * ground.z)]
  40. bodies = [disc]
  41. return {
  42. 'frame': ground.frame,
  43. 'q_ind': [q1, q2, q3, q4, q5],
  44. 'u_ind': [u1, u2, u3],
  45. 'u_dep': [u4, u5],
  46. 'kdes': kdes,
  47. 'fnh': fnh,
  48. 'bodies': bodies,
  49. 'loads': loads
  50. }
  51. def _verify_rolling_disc_numerically(kane, all_zero=False):
  52. q, u, p = dynamicsymbols('q1:6'), dynamicsymbols('u1:6'), symbols('g r m')
  53. eval_sys = lambdify((q, u, p), (kane.mass_matrix_full, kane.forcing_full),
  54. cse=True)
  55. solve_sys = lambda q, u, p: Matrix.LUsolve(
  56. *(Matrix(mat) for mat in eval_sys(q, u, p)))
  57. solve_u_dep = lambdify((q, u[:3], p), kane._Ars * Matrix(u[:3]), cse=True)
  58. eps = 1e-10
  59. p_vals = (9.81, 0.26, 3.43)
  60. # First numeric test
  61. q_vals = (0.3, 0.1, 1.97, -0.35, 2.27)
  62. u_vals = [-0.2, 1.3, 0.15]
  63. u_vals.extend(solve_u_dep(q_vals, u_vals, p_vals)[:2, 0])
  64. expected = Matrix([
  65. 0.126603940595934, 0.215942571601660, 1.28736069604936,
  66. 0.319764288376543, 0.0989146857254898, -0.925848952664489,
  67. -0.0181350656532944, 2.91695398184589, -0.00992793421754526,
  68. 0.0412861634829171])
  69. assert all(abs(x) < eps for x in
  70. (solve_sys(q_vals, u_vals, p_vals) - expected))
  71. # Second numeric test
  72. q_vals = (3.97, -0.28, 8.2, -0.35, 2.27)
  73. u_vals = [-0.25, -2.2, 0.62]
  74. u_vals.extend(solve_u_dep(q_vals, u_vals, p_vals)[:2, 0])
  75. expected = Matrix([
  76. 0.0259159090798597, 0.668041660387416, -2.19283799213811,
  77. 0.385441810852219, 0.420109283790573, 1.45030568179066,
  78. -0.0110924422400793, -8.35617840186040, -0.154098542632173,
  79. -0.146102664410010])
  80. assert all(abs(x) < eps for x in
  81. (solve_sys(q_vals, u_vals, p_vals) - expected))
  82. if all_zero:
  83. q_vals = (0, 0, 0, 0, 0)
  84. u_vals = (0, 0, 0, 0, 0)
  85. assert solve_sys(q_vals, u_vals, p_vals) == zeros(10, 1)
  86. def test_kane_rolling_disc_lu():
  87. props = _create_rolling_disc()
  88. kane = KanesMethod(props['frame'], props['q_ind'], props['u_ind'],
  89. props['kdes'], u_dependent=props['u_dep'],
  90. velocity_constraints=props['fnh'],
  91. bodies=props['bodies'], forcelist=props['loads'],
  92. explicit_kinematics=False, constraint_solver='LU')
  93. kane.kanes_equations()
  94. _verify_rolling_disc_numerically(kane)
  95. def test_kane_rolling_disc_kdes_callable():
  96. props = _create_rolling_disc()
  97. kane = KanesMethod(
  98. props['frame'], props['q_ind'], props['u_ind'], props['kdes'],
  99. u_dependent=props['u_dep'], velocity_constraints=props['fnh'],
  100. bodies=props['bodies'], forcelist=props['loads'],
  101. explicit_kinematics=False,
  102. kd_eqs_solver=lambda A, b: simplify(A.LUsolve(b)))
  103. q, u, p = dynamicsymbols('q1:6'), dynamicsymbols('u1:6'), symbols('g r m')
  104. qd = dynamicsymbols('q1:6', 1)
  105. eval_kdes = lambdify((q, qd, u, p), tuple(kane.kindiffdict().items()))
  106. eps = 1e-10
  107. # Test with only zeros. If 'LU' would be used this would result in nan.
  108. p_vals = (9.81, 0.25, 3.5)
  109. zero_vals = (0, 0, 0, 0, 0)
  110. assert all(abs(qdi - fui) < eps for qdi, fui in
  111. eval_kdes(zero_vals, zero_vals, zero_vals, p_vals))
  112. # Test with some arbitrary values
  113. q_vals = tuple(map(float, (pi / 6, pi / 3, pi / 2, 0.42, 0.62)))
  114. qd_vals = tuple(map(float, (4, 1 / 3, 4 - 2 * sqrt(3),
  115. 0.25 * (2 * sqrt(3) - 3),
  116. 0.25 * (2 - sqrt(3)))))
  117. u_vals = tuple(map(float, (-2, 4, 1 / 3, 0.25 * (-3 + 2 * sqrt(3)),
  118. 0.25 * (-sqrt(3) + 2))))
  119. assert all(abs(qdi - fui) < eps for qdi, fui in
  120. eval_kdes(q_vals, qd_vals, u_vals, p_vals))