test_rotation_spline.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. from itertools import product
  2. import numpy as np
  3. from numpy.testing import assert_allclose
  4. from pytest import raises
  5. from scipy.spatial.transform import Rotation, RotationSpline
  6. from scipy.spatial.transform._rotation_spline import (
  7. _angular_rate_to_rotvec_dot_matrix,
  8. _rotvec_dot_to_angular_rate_matrix,
  9. _matrix_vector_product_of_stacks,
  10. _angular_acceleration_nonlinear_term,
  11. _create_block_3_diagonal_matrix)
  12. def test_angular_rate_to_rotvec_conversions():
  13. np.random.seed(0)
  14. rv = np.random.randn(4, 3)
  15. A = _angular_rate_to_rotvec_dot_matrix(rv)
  16. A_inv = _rotvec_dot_to_angular_rate_matrix(rv)
  17. # When the rotation vector is aligned with the angular rate, then
  18. # the rotation vector rate and angular rate are the same.
  19. assert_allclose(_matrix_vector_product_of_stacks(A, rv), rv)
  20. assert_allclose(_matrix_vector_product_of_stacks(A_inv, rv), rv)
  21. # A and A_inv must be reciprocal to each other.
  22. I_stack = np.empty((4, 3, 3))
  23. I_stack[:] = np.eye(3)
  24. assert_allclose(np.matmul(A, A_inv), I_stack, atol=1e-15)
  25. def test_angular_rate_nonlinear_term():
  26. # The only simple test is to check that the term is zero when
  27. # the rotation vector
  28. np.random.seed(0)
  29. rv = np.random.rand(4, 3)
  30. assert_allclose(_angular_acceleration_nonlinear_term(rv, rv), 0,
  31. atol=1e-19)
  32. def test_create_block_3_diagonal_matrix():
  33. np.random.seed(0)
  34. A = np.empty((4, 3, 3))
  35. A[:] = np.arange(1, 5)[:, None, None]
  36. B = np.empty((4, 3, 3))
  37. B[:] = -np.arange(1, 5)[:, None, None]
  38. d = 10 * np.arange(10, 15)
  39. banded = _create_block_3_diagonal_matrix(A, B, d)
  40. # Convert the banded matrix to the full matrix.
  41. k, l = list(zip(*product(np.arange(banded.shape[0]),
  42. np.arange(banded.shape[1]))))
  43. k = np.asarray(k)
  44. l = np.asarray(l)
  45. i = k - 5 + l
  46. j = l
  47. values = banded.ravel()
  48. mask = (i >= 0) & (i < 15)
  49. i = i[mask]
  50. j = j[mask]
  51. values = values[mask]
  52. full = np.zeros((15, 15))
  53. full[i, j] = values
  54. zero = np.zeros((3, 3))
  55. eye = np.eye(3)
  56. # Create the reference full matrix in the most straightforward manner.
  57. ref = np.block([
  58. [d[0] * eye, B[0], zero, zero, zero],
  59. [A[0], d[1] * eye, B[1], zero, zero],
  60. [zero, A[1], d[2] * eye, B[2], zero],
  61. [zero, zero, A[2], d[3] * eye, B[3]],
  62. [zero, zero, zero, A[3], d[4] * eye],
  63. ])
  64. assert_allclose(full, ref, atol=1e-19)
  65. def test_spline_2_rotations():
  66. times = [0, 10]
  67. rotations = Rotation.from_euler('xyz', [[0, 0, 0], [10, -20, 30]],
  68. degrees=True)
  69. spline = RotationSpline(times, rotations)
  70. rv = (rotations[0].inv() * rotations[1]).as_rotvec()
  71. rate = rv / (times[1] - times[0])
  72. times_check = np.array([-1, 5, 12])
  73. dt = times_check - times[0]
  74. rv_ref = rate * dt[:, None]
  75. assert_allclose(spline(times_check).as_rotvec(), rv_ref)
  76. assert_allclose(spline(times_check, 1), np.resize(rate, (3, 3)))
  77. assert_allclose(spline(times_check, 2), 0, atol=1e-16)
  78. def test_constant_attitude():
  79. times = np.arange(10)
  80. rotations = Rotation.from_rotvec(np.ones((10, 3)))
  81. spline = RotationSpline(times, rotations)
  82. times_check = np.linspace(-1, 11)
  83. assert_allclose(spline(times_check).as_rotvec(), 1, rtol=1e-15)
  84. assert_allclose(spline(times_check, 1), 0, atol=1e-17)
  85. assert_allclose(spline(times_check, 2), 0, atol=1e-17)
  86. assert_allclose(spline(5.5).as_rotvec(), 1, rtol=1e-15)
  87. assert_allclose(spline(5.5, 1), 0, atol=1e-17)
  88. assert_allclose(spline(5.5, 2), 0, atol=1e-17)
  89. def test_spline_properties():
  90. times = np.array([0, 5, 15, 27])
  91. angles = [[-5, 10, 27], [3, 5, 38], [-12, 10, 25], [-15, 20, 11]]
  92. rotations = Rotation.from_euler('xyz', angles, degrees=True)
  93. spline = RotationSpline(times, rotations)
  94. assert_allclose(spline(times).as_euler('xyz', degrees=True), angles)
  95. assert_allclose(spline(0).as_euler('xyz', degrees=True), angles[0])
  96. h = 1e-8
  97. rv0 = spline(times).as_rotvec()
  98. rvm = spline(times - h).as_rotvec()
  99. rvp = spline(times + h).as_rotvec()
  100. assert_allclose(rv0, 0.5 * (rvp + rvm), rtol=1e-15)
  101. r0 = spline(times, 1)
  102. rm = spline(times - h, 1)
  103. rp = spline(times + h, 1)
  104. assert_allclose(r0, 0.5 * (rm + rp), rtol=1e-14)
  105. a0 = spline(times, 2)
  106. am = spline(times - h, 2)
  107. ap = spline(times + h, 2)
  108. assert_allclose(a0, am, rtol=1e-7)
  109. assert_allclose(a0, ap, rtol=1e-7)
  110. def test_error_handling():
  111. raises(ValueError, RotationSpline, [1.0], Rotation.random())
  112. r = Rotation.random(10)
  113. t = np.arange(10).reshape(5, 2)
  114. raises(ValueError, RotationSpline, t, r)
  115. t = np.arange(9)
  116. raises(ValueError, RotationSpline, t, r)
  117. t = np.arange(10)
  118. t[5] = 0
  119. raises(ValueError, RotationSpline, t, r)
  120. t = np.arange(10)
  121. s = RotationSpline(t, r)
  122. raises(ValueError, s, 10, -1)
  123. raises(ValueError, s, np.arange(10).reshape(5, 2))