ToppraPlanner#
ToppraPlanner time-parameterizes joint paths under velocity and acceleration
limits with the in-tree Warp implementation of
TOPPRA. CPU and CUDA use the same
calculation chain; the external toppra package is not required. For Cartesian goals, use
MotionGenerator to generate joint waypoints through IK.
Direct planning#
The example assumes a registered robot in sim. Supply at least a start
and goal as batched tensors; the direct planner does not insert the current
robot state.
from embodichain.lab.sim.motion.planners import (
PlanState,
ToppraPlanner,
ToppraPlannerCfg,
ToppraPlanOptions,
TrajectorySampleMethod,
)
planner = ToppraPlanner(
ToppraPlannerCfg(robot_uid=robot.uid, sim_instance_id=sim.instance_id)
)
start_qpos = robot.get_qpos(name="arm") # (B, DOF)
goal_qpos = start_qpos.clone()
goal_qpos[:, 0] += 0.1 # Choose a goal within this robot's limits.
result = planner.plan(
[PlanState.from_qpos(start_qpos), PlanState.from_qpos(goal_qpos)],
ToppraPlanOptions(
control_part="arm",
constraints={"velocity": 0.2, "acceleration": 0.5},
sample_method=TrajectorySampleMethod.TIME,
sample_interval=0.01,
),
)
assert result.is_all_success()
planner.close()
Constraints and sampling#
Set
constraintsonToppraPlanOptions; positive scalars apply to all controlled joints, vectors specify per-joint magnitudes, and(DOF, 2)arrays specify signed bounds containing zero. Explicit velocity requests are capped by the robot’s runtime speed limits.Noneuses robot speed limits and the default planning acceleration budget.control_partselects the trajectory’s joint order. A partial-joint path needs an explicit group when the mapping is ambiguous. Runtime zero-speed joints must remain constant at every waypoint, including points that would otherwise be removed by duplicate filtering.TIMEusessample_intervalas the requested time spacing in seconds; samples include both endpoints, so actual spacing can be smaller.QUANTITYusessample_intervalas the output count, with at least two samples.
Results follow the shared
PlanResult contract. Planner
results use float32 on the robot’s device. Internal spline fitting, interval
bounds, timing and sampling use float64. grid_size sets the minimum timing
grid size; segment alignment and path length can increase the actual size.
The interval bounds are conservative, so durations can differ from the
former external solver. Timing alone does not certify collision freedom.
CPU execution and configuration migration#
CPU inference automatically splits sufficiently large batches across a shared
pool of at most eight threads. Small requests and calls recording derivatives
run inline. CUDA numerical work remains on the input device. Planning does
not create a CPU process pool, and individual planners own no worker processes.
close() is an idempotent lifecycle hook; it does not close the shared CPU
thread executor.
Remove backend, max_workers, and mp_context from existing
ToppraPlannerCfg constructors and configuration dictionaries. These options
are no longer accepted. The robot selects the device, and Warp is the only
runtime implementation:
cfg = ToppraPlannerCfg(
robot_uid=robot.uid,
sim_instance_id=sim.instance_id,
grid_size=100,
)
The NumPy implementation is an offline numerical reference used by tests; it cannot be selected as a planner backend.
First-order gradients#
Waypoint tensors and tensor velocity/acceleration requests retain their graphs
through ToppraPlanOptions copying and the timing solve on CPU or CUDA. For
example, with a valid joint goal in the direct-planning setup above:
goal = goal_qpos.detach().requires_grad_()
result = planner.plan(
[PlanState.from_qpos(start_qpos), PlanState.from_qpos(goal)],
ToppraPlanOptions(
control_part="arm",
constraints={"velocity": 0.2, "acceleration": 0.5},
sample_method=TrajectorySampleMethod.QUANTITY,
sample_interval=101,
),
)
assert result.is_all_success()
result.duration.sum().backward()
assert goal.grad is not None
Gradients are local to fixed waypoint filtering, timing-grid topology, output
counts and active constraints. Higher-order derivatives are unsupported.
Failed rows contribute zero gradients; stationary rows differentiate only
their retained position. Robot speed-cap saturation stops gradients through
the capped request. Supply trainable boundary states through PlanState.qpos.
Retiming an existing path#
retime_joint_paths() exposes the same Warp timing solve for a (B, N, DOF)
tensor without constructing a planner. Provide explicit velocity and
acceleration limits; optional path_lengths exclude per-row tail padding
from spline fitting. Outputs preserve float32/float64 input precision, while
other input dtypes are promoted to float32. Recompute poses from the returned
positions because the helper resamples along the fitted spline.