diff --git a/pytransform3d/batch_rotations.py b/pytransform3d/batch_rotations.py index dc441230..3ef7e382 100644 --- a/pytransform3d/batch_rotations.py +++ b/pytransform3d/batch_rotations.py @@ -393,12 +393,12 @@ def axis_angles_from_quaternions(qs): Parameters ---------- - q : array-like, shape (..., 4) + qs : array-like, shape (..., 4) Unit quaternion to represent rotation: (w, x, y, z) Returns ------- - a : array, shape (..., 4) + as : array, shape (..., 4) Axis of rotation and rotation angle: (x, y, z, angle). The angle is constrained to [0, pi) so that the mapping is unique. """ diff --git a/pytransform3d/test/test_batch_rotations.py b/pytransform3d/test/test_batch_rotations.py index cfea89c0..59a0a5e3 100644 --- a/pytransform3d/test/test_batch_rotations.py +++ b/pytransform3d/test/test_batch_rotations.py @@ -1,4 +1,5 @@ import numpy as np + from pytransform3d import rotations as pr from pytransform3d import batch_rotations as pbr from numpy.testing import assert_array_almost_equal @@ -342,6 +343,28 @@ def test_axis_angles_from_matrices_output_variable(): assert_array_almost_equal(A, A2_compact) +def test_axis_angles_from_quaternions(): + rng = np.random.default_rng(48322) + Q = pbr.norm_vectors(rng.standard_normal(size=(20, 4))) + + # 1D + pr.assert_quaternion_equal( + pbr.axis_angles_from_quaternions(Q[0]), + pr.axis_angle_from_quaternion(Q[0]) + ) + + # 2D + A = pbr.axis_angles_from_quaternions(Q) + for a, q in zip(A, Q): + pr.assert_quaternion_equal(a, pr.axis_angle_from_quaternion(q)) + + # 3D + Q3D = Q.reshape(5, 4, 4) + A3D = pbr.axis_angles_from_quaternions(Q3D) + for a, q in zip(A3D.reshape(20, 4), Q3D.reshape(20, 4)): + pr.assert_quaternion_equal(a, pr.axis_angle_from_quaternion(q)) + + def test_quaternion_slerp_batch_zero_angle(): rng = np.random.default_rng(228) q = pr.random_quaternion(rng)