Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/roboticstoolbox/mobile/QuinticPolyPlanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def quintic_polynomials_planner(
:param MAX_T: maximum path time [s]
:raises NoPathError: no path time in the range meets the acceleration
and jerk limits
:raises ValueError: ``dt`` is not positive and finite
:return: time, path, velocity, acceleration and jerk
:rtype: tuple(list, ndarray(N,3), list, list, list)

Expand All @@ -140,6 +141,9 @@ def quintic_polynomials_planner(
step.
"""

if not math.isfinite(dt) or dt <= 0:
raise ValueError("dt must be a positive finite number")

vxs = sv * math.cos(syaw)
vys = sv * math.sin(syaw)
vxg = gv * math.cos(gyaw)
Expand All @@ -164,7 +168,8 @@ def quintic_polynomials_planner(

time, rx, ry, ryaw, rv, ra, rj = [], [], [], [], [], [], []

for t in np.arange(0.0, T + dt, dt):
samples = np.arange(0.0, T, dt)
for t in np.r_[samples[samples < T], T]:
time.append(t)
rx.append(xqp.calc_point(t))
ry.append(yqp.calc_point(t))
Expand Down
62 changes: 62 additions & 0 deletions tests/test_quintic_planner_terminal_sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Quintic sampling preserves its terminal boundary conditions."""

import numpy as np
import numpy.testing as npt
import pytest

from roboticstoolbox.mobile.QuinticPolyPlanner import quintic_polynomials_planner


@pytest.mark.parametrize(
"duration,step",
[(1.0, 0.3), (1.1, 0.3), (1.0, 2.0), (1.0, 0.25), (3 * 0.1, 0.1)],
)
@pytest.mark.parametrize("heading", [-0.4, 0.7])
def test_terminal_boundary_is_sampled_once(
duration: float, step: float, heading: float
) -> None:
times, path, velocity, acceleration, jerk = quintic_polynomials_planner(
sx=0.2,
sy=-0.3,
syaw=0.1,
sv=0.6,
sa=0.0,
gx=1.2,
gy=0.8,
gyaw=heading,
gv=0.4,
ga=0.0,
max_accel=np.inf,
max_jerk=np.inf,
dt=step,
MIN_T=duration,
MAX_T=2 * duration,
)

assert times[0] == 0.0
assert times[-1] == duration
assert np.count_nonzero(np.asarray(times) == duration) == 1
assert np.all(np.diff(times) > 0)
assert np.all(np.asarray(times) <= duration)
assert np.all(np.diff(times) <= step + 1e-15)
assert path.shape == (len(times), 3)
assert len(velocity) == len(acceleration) == len(jerk) == len(times)
npt.assert_allclose(path[0], [0.2, -0.3, 0.1], atol=1e-12)
npt.assert_allclose(path[-1], [1.2, 0.8, heading], atol=1e-10)
npt.assert_allclose(velocity[-1], 0.4, atol=1e-10)
npt.assert_allclose(acceleration[-1], 0.0, atol=1e-9)


def test_exact_grid_spacing_is_preserved() -> None:
times, *_ = quintic_polynomials_planner(
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 100, 100, 0.25, 1.0, 2.0
)
npt.assert_array_equal(times, [0.0, 0.25, 0.5, 0.75, 1.0])


@pytest.mark.parametrize("step", [-1.0, -0.1, 0.0, np.inf, -np.inf, np.nan])
def test_invalid_time_step_is_rejected(step: float) -> None:
with pytest.raises(ValueError):
quintic_polynomials_planner(
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 100, 100, step, 1.0, 2.0
)
Loading