Motion Planning¶
Motion Planner¶
MotionPlanner(robot_name: str, config: PlannerConfig | None = None, pointcloud: np.ndarray | None = None, base_config: np.ndarray | None = None)
¶
Motion planner using OMPL + VAMP C++ backend.
All internals are private. The public API accepts and returns only numpy arrays.
For subgroup planners the helper methods :meth:extract_config,
:meth:embed_config, and :meth:embed_path convert between the
reduced DOF space used by the planner and the full 5-DOF body
configuration.
Source code in nav_planning/planning/motion_planner.py
bounds: tuple[np.ndarray, np.ndarray]
property
¶
(lower, upper) joint limits of the active DOF (the heading of
an active base spans [-pi, pi]).
joint_limits: dict[str, tuple[float, float]]
property
¶
{joint name: (lower, upper)} the planner samples in, for every
joint of the full body. :attr:bounds gives the active joints'
ranges as arrays.
joint_names: list[str]
property
¶
Joint names controlled by this planner, in DOF order.
is_subgroup: bool
property
¶
True if this planner controls a subset of the full body.
subgroup_indices: np.ndarray | None
property
¶
Indices of this planner's joints in the full 5-DOF config.
base_config: np.ndarray
property
¶
The 5-DOF stance frozen for joints outside this subgroup.
has_pointcloud: bool
property
¶
True if a pointcloud is currently registered.
set_joint_limits(limits: Mapping[str, tuple[float, float]] | None = None) -> None
¶
Set the joint ranges the planner samples in, by joint name.
Joints left out of limits take the range compiled into the
robot model, so None restores every compiled range. The
ranges cover the full body and are kept across
:meth:set_subgroup. Collision checking does not read them.
With the base active its heading is periodic, so the
base_theta_joint range is not used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limits
|
Mapping[str, tuple[float, float]] | None
|
|
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown joint name, or bounds that are not
finite with |
Source code in nav_planning/planning/motion_planner.py
add_pointcloud(pointcloud: np.ndarray) -> None
¶
Set the scene point cloud, replacing any previous one.
Every point is a sphere of config.point_radius (default 0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pointcloud
|
ndarray
|
|
required |
Source code in nav_planning/planning/motion_planner.py
remove_pointcloud() -> bool
¶
filter_pointcloud(pointcloud: np.ndarray, min_dist: float, max_range: float, origin: np.ndarray | list[float], workspace_min: np.ndarray | list[float], workspace_max: np.ndarray | list[float], cull: bool = True) -> np.ndarray
¶
Spatially downsample a point cloud via Morton-curve sorting.
Keeps one representative point per min_dist neighbourhood and
discards points farther than max_range from origin or
outside the [workspace_min, workspace_max] bounding box.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pointcloud
|
ndarray
|
|
required |
min_dist
|
float
|
Minimum distance between two retained points. |
required |
max_range
|
float
|
Maximum distance from |
required |
origin
|
ndarray | list[float]
|
|
required |
workspace_min
|
ndarray | list[float]
|
|
required |
workspace_max
|
ndarray | list[float]
|
|
required |
cull
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in nav_planning/planning/motion_planner.py
filter_self_from_pointcloud(pointcloud: np.ndarray, point_radius: float, config: np.ndarray) -> np.ndarray
¶
Remove points that collide with the robot body or environment.
Computes forward kinematics at config, then drops every
point whose sphere (radius point_radius) overlaps any robot
collision sphere or any registered obstacle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pointcloud
|
ndarray
|
|
required |
point_radius
|
float
|
Radius of each point. |
required |
config
|
ndarray
|
Active-DOF configuration (same space as |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in nav_planning/planning/motion_planner.py
set_subgroup(robot_name: str, base_config: np.ndarray | None = None) -> None
¶
Switch active joints without rebuilding the collision environment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
robot_name
|
str
|
Subgroup name of the same robot, e.g.
|
required |
base_config
|
ndarray | None
|
5-DOF frozen config for inactive joints. Defaults to the previously stored base config. |
None
|
Source code in nav_planning/planning/motion_planner.py
extract_config(full_config: np.ndarray) -> np.ndarray
¶
Extract this planner's joints from a full 5-DOF configuration.
Source code in nav_planning/planning/motion_planner.py
embed_config(config: np.ndarray, base_config: np.ndarray | None = None) -> np.ndarray
¶
Embed a subgroup config into a full 5-DOF configuration.
base_config defaults to the planner's stored base — the same
values the C++ collision checker injects for inactive joints — so
the embedded config matches what was validated.
Source code in nav_planning/planning/motion_planner.py
embed_path(path: np.ndarray, base_config: np.ndarray | None = None) -> np.ndarray
¶
Convert a subgroup path (N, sub_dof) to (N, 5).
base_config defaults to the planner's stored base — the same
values the C++ collision checker injects for inactive joints — so
the embedded path matches what was validated.
Source code in nav_planning/planning/motion_planner.py
plan(start: np.ndarray, goal: np.ndarray, time_limit: float | None = None) -> PlanningResult
¶
Plan a collision-free path from start to goal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
ndarray
|
Start configuration (active DOF). |
required |
goal
|
ndarray
|
Goal configuration (active DOF). |
required |
time_limit
|
float | None
|
Optional per-call override for the solver time
limit. Defaults to |
None
|
Source code in nav_planning/planning/motion_planner.py
plan_kinodynamic(start: np.ndarray, goal: np.ndarray, start_velocity: np.ndarray | None = None, config: KinodynamicConfig | None = None) -> KinodynamicResult
¶
Plan a dynamically feasible, time-parameterised trajectory (FLASK).
Kinodynamic RRT-Connect in the differentially flat output space
(Duong et al., T-RO 2026): every tree edge is the closed-form
minimum-effort cubic between two (position, velocity) states,
validated with SIMD-batched collision checks and the joint /
base velocity and acceleration limits from the robot module.
The LoCoBot base follows unicycle kinematics — heading tangent to
the path, forward or reverse, rotate-in-place allowed while
parked; the Ridgeback base moves in x, y and theta independently.
The output can be tracked directly with feed-forward + PID; see
:meth:~nav_planning.trajectory.KinodynamicTrajectory.base_twist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
ndarray
|
Start configuration (active DOF). |
required |
goal
|
ndarray
|
Goal configuration (active DOF). Reached at rest. |
required |
start_velocity
|
ndarray | None
|
Optional active-DOF velocity at |
None
|
config
|
KinodynamicConfig | None
|
Kinodynamic planner parameters. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
KinodynamicResult
|
class: |
KinodynamicResult
|
|
|
KinodynamicResult
|
class: |
Source code in nav_planning/planning/motion_planner.py
simplify_path(path: np.ndarray, time_limit: float = 1.0) -> np.ndarray
¶
Run OMPL's shortcut-based path simplifier on path.
Same pipeline plan(..., simplify=True) uses internally, but
detached so you can apply it to any path you already have — e.g.
replay an old plan with a different collision environment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
ndarray
|
|
required |
time_limit
|
float
|
Wall-clock budget for the simplifier, seconds. |
1.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in nav_planning/planning/motion_planner.py
interpolate_path(path: np.ndarray, count: int = 0, resolution: float = 64.0) -> np.ndarray
¶
Densify path with uniform waypoints along every edge.
Three modes (pick one; the other must be zero):
* ``count > 0`` — exactly that many total waypoints
distributed proportionally to edge length.
* ``resolution > 0.0`` — ``ceil(edge_length * resolution)``
waypoints per edge (uniform density in state-space
distance — the default).
* both ``0`` — OMPL's default longest-valid-segment
fraction.
Uses the state space's own interpolation, so the inserted states follow the LoCoBot's Reeds-Shepp curves and the Ridgeback's shortest-arc heading. No collision check is performed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
ndarray
|
|
required |
count
|
int
|
Exact total waypoint count if |
0
|
resolution
|
float
|
Waypoints per unit state-space distance if
|
64.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in nav_planning/planning/motion_planner.py
validate(configuration: np.ndarray) -> bool
¶
validate_batch(configurations: np.ndarray) -> np.ndarray
¶
Batched collision check — one SIMD block per rake configs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
configurations
|
ndarray
|
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ndarray
|
|
Source code in nav_planning/planning/motion_planner.py
sample_valid(max_samples: int = 100000) -> np.ndarray
¶
Sample a random collision-free configuration.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If none of |
Source code in nav_planning/planning/motion_planner.py
Factory Function¶
create_planner(robot_name: str, config: PlannerConfig | None = None, pointcloud: np.ndarray | None = None, base_config: np.ndarray | None = None) -> MotionPlanner
¶
Create a motion planner for a robot or subgroup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
robot_name
|
str
|
Robot or subgroup name: |
required |
config
|
PlannerConfig | None
|
Planner configuration (uses defaults if None). |
None
|
pointcloud
|
ndarray | None
|
|
None
|
base_config
|
ndarray | None
|
5-DOF values to inject for joints not controlled by
this planner (i.e. the camera joints of a base subgroup).
Defaults to the robot's |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
MotionPlanner
|
class: |
Source code in nav_planning/planning/motion_planner.py
Available Robots¶
available_robots() -> list[str]
¶
Return all available robot names for planning.
Source code in nav_planning/planning/motion_planner.py
Low-level C++ binding¶
OmplVampPlanner
¶
plan_kinodynamic(start: Sequence[float], start_velocity: Sequence[float], goal: Sequence[float], time_limit: float, settings: KinodynamicSettings) -> KinodynamicResult
¶
FLASK kinodynamic planning in the flat output space. Returns a KinodynamicResult whose trajectory is time-parameterised.