Repository navigation
Add differentiable Warp TOPPRA planning on CPU and CUDA - #804
Conversation
[High impact] The PR appears safe to merge, with the earlier non-blocking path-length validation issue still open.
|
| path_lengths = path_lengths.to(device=positions.device, dtype=torch.int32) | ||
| if bool(((path_lengths < 2) | (path_lengths > count)).any().item()): | ||
| raise ValueError(f"path_lengths must be between 2 and {count}") |
There was a problem hiding this comment.
Invalid lengths silently shorten paths
Both retime_joint_paths() and _tensor_path_lengths() convert path_lengths to torch.int32 before checking its range. An invalid torch.int64 length such as 4294967298 wraps to 2 and passes the check for a four-waypoint path. The planner then uses only the first two waypoints instead of rejecting the input.
Check the original values against 2..count before converting them, and add an overflow case to the length tests.
Prompt To Fix With AI
This is a comment left during a code review.
Path: embodichain/lab/sim/motion/planners/toppra_planner.py
Line: 318-320
Comment:
**Invalid lengths silently shorten paths**
Both `retime_joint_paths()` and `_tensor_path_lengths()` convert `path_lengths` to `torch.int32` before checking its range. An invalid `torch.int64` length such as `4294967298` wraps to `2` and passes the check for a four-waypoint path. The planner then uses only the first two waypoints instead of rejecting the input.
Check the original values against `2..count` before converting them, and add an overflow case to the length tests.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Description
TOPPRA planning currently detaches joint waypoints into NumPy and dispatches CPU process workers. This PR replaces that path with an in-tree float64 Warp implementation and connects first-order gradients through
ToppraPlanner, typed option copies, andMotionGeneratorresampling. CPU and CUDA use Warp as their only runtime implementation.ToppraPlannerto a single Warp adapter. The offline NumPy oracle remains for numerical tests; runtime validation is shared without importing that oracle.mainbehavior for robot speed caps,control_part, TIME defaults, frozen joints and float32 planner results. Reject raw motion in a frozen joint before waypoint filtering.retime_joint_paths()for existing joint paths, preserving valid prefixes, float32/float64 precision, isolated failures and endpoint padding. Update the tutorial, Task Program fixtures, benchmark adapter, planner/API documentation and motion-planning context.Migration: remove
backend,max_workersandmp_contextfromToppraPlannerCfgconstructors and configuration dictionaries. These fields are no longer accepted. The robot selects the device; the CPU thread executor is shared by the Warp runtime.close()remains an idempotent lifecycle hook.Timing optimizes conservative interval constraints on the existing not-a-knot spline, so durations can differ from the former external solver. Gradients are first-order and local to fixed filtering, sampling topology and active constraints; higher-order derivatives are unsupported. Trainable boundary states use
PlanState.qpos; the genericMotionGenOptions.start_qposdeepcopy behavior is outside this change.This is standalone on current
main(82563fd5), extracted from the TOPPRA work in #712 with the CPU implementation improvements. It does not depend on #712 or include its Atomic Skill benchmark framework, evidence runtime, or NMG retiming feature.Dependencies: declare SciPy directly and remove the external
toppra==0.6.3runtime dependency. Benchmark availability no longer depends on that external package.Type of change
Validation
196 focused checks passed:
MotionGeneratorand interpolation implementations ran unchanged.black .with Black 26.3.1;git diff --check; changed Python modules compile.Coverage includes finite differences, independent HiGHS/analytic projection oracles on CPU/CUDA, cooperative blocks with more than 64 planes, finite infeasible rows, extreme scales, both output precisions, runtime-cap gradients before/after saturation, frozen-joint sub-tolerance motion, failed-row gradients, variable-length output, CUDA streams, repeated backward, CPU concurrency, foreign-tape isolation, failure cleanup and external-fork recovery. New regressions forbid CPU process creation and runtime imports of the offline NumPy solver; removed configuration fields are rejected.
Full native simulation, selected Task Program integration tests and the Sphinx dummy build remain blocked locally by the existing DexSim import error: missing
reconstruct_cable_rings_kernelindexsim.engine.newton_physics.runtime_kernels. They are not reported as passing.Inference performance
Paired A/B against this PR’s previous
c316cab1revision on EPYC with an eight-physical-core affinity budget and RTX PRO 5000 Blackwell. Same 7-DoF paths, 8 waypoints, limits and grid; three warmups and 21 synchronized observations per case. CPU variants share one bounded executor to hold worker placement and allocator state constant. Times are median real.plan()latency, excluding setup/JIT.Across the 2/8/32-waypoint fixed-128-output GPU matrix, speedup is 1.84–5.76x. CPU dense-output cases improve up to 1.52x; small outputs retain the original sampler and show timing variation around baseline. All 162 measured version/device/shape combinations passed sampled endpoint/finite/limit checks, and the first eight rows’ q/v/a/dt outputs match exactly before/after in float32. Internal computation stays float64; training/backward throughput is not part of these claims.
Context was revised for CUDA block-uniform exits and the split inference/recorded paths. Existing public planner documentation remains accurate: signatures, configuration, units, sampling and gradient contracts are unchanged by this optimization.
Screenshots
N/A — numerical planning and API changes.
Checklist
black .command to format the code base.python docs/scripts/check_api_docs.py), if applicable.