|
| 1 | +import numpy as np |
| 2 | +import pytest |
| 3 | +from scipy.linalg import expm |
| 4 | + |
| 5 | +from spatialmath import SE2, SE3, Twist2, Twist3 |
| 6 | + |
| 7 | + |
| 8 | +@pytest.mark.parametrize("twist,pose", [(Twist2, SE2), (Twist3, SE3)]) |
| 9 | +@pytest.mark.parametrize("method", ["exp", "pose"]) |
| 10 | +@pytest.mark.parametrize("theta", [None, 0, -0.4, [0.2, -0.7]]) |
| 11 | +def test_twist_sequence_exponential(twist, pose, method, theta): |
| 12 | + """A shared step and individual steps both integrate every twist.""" |
| 13 | + vectors = ( |
| 14 | + [[1, -2, 0.3], [2, 1, -0.4]] |
| 15 | + if twist is Twist2 |
| 16 | + else [[1, -2, 3, 0.2, 0.3, -0.4], [2, 1, -1, -0.1, 0.5, 0.2]] |
| 17 | + ) |
| 18 | + sequence = twist([np.array(v) for v in vectors]) |
| 19 | + convert = getattr(sequence, "exp" if method == "exp" else pose.__name__) |
| 20 | + result = convert() if theta is None else convert(theta) |
| 21 | + steps = np.broadcast_to(1 if theta is None else theta, (2,)) |
| 22 | + assert isinstance(result, pose) |
| 23 | + assert len(result) == 2 |
| 24 | + for value, source, step in zip(result, sequence, steps): |
| 25 | + np.testing.assert_allclose(value.A, expm(source.skewa() * step), atol=1e-14) |
| 26 | + |
| 27 | + |
| 28 | +@pytest.mark.parametrize("twist,pose", [(Twist2, SE2), (Twist3, SE3)]) |
| 29 | +@pytest.mark.parametrize("method", ["exp", "pose"]) |
| 30 | +def test_twist_sequence_degrees_and_singleton(twist, pose, method): |
| 31 | + """Degrees, singleton expansion and mismatched lengths retain their rules.""" |
| 32 | + source = twist([1, 2, 0.3] if twist is Twist2 else [1, 2, 3, 0.2, 0.3, 0.4]) |
| 33 | + name = "exp" if method == "exp" else pose.__name__ |
| 34 | + angles = [15, -30] |
| 35 | + expanded = getattr(source, name)(angles, unit="deg") |
| 36 | + assert len(expanded) == 2 |
| 37 | + for value, angle in zip(expanded, angles): |
| 38 | + np.testing.assert_allclose( |
| 39 | + value.A, expm(source.skewa() * np.deg2rad(angle)), atol=1e-14 |
| 40 | + ) |
| 41 | + sequence = twist([source, source]) |
| 42 | + shared = getattr(sequence, name)(30, unit="deg") |
| 43 | + assert len(shared) == 2 |
| 44 | + for value in shared: |
| 45 | + np.testing.assert_allclose( |
| 46 | + value.A, expm(source.skewa() * np.deg2rad(30)), atol=1e-14 |
| 47 | + ) |
| 48 | + with pytest.raises(ValueError): |
| 49 | + getattr(sequence, name)([0.1, 0.2, 0.3]) |
0 commit comments