cuRobo vs OMPL: GPU-Accelerated Motion Planning and Collision-Free Trajectory Optimization

cuRobo vs OMPL: GPU-Accelerated Motion Planning and Collision-Free Trajectory Optimization

cuRobo vs OMPL: GPU-Accelerated Motion Planning and Collision-Free Trajectory Optimization

A robot arm that stops for three seconds to think before every move is not a robot cell, it is a demo. For two decades the default answer to “how does a manipulator get from A to B without hitting anything” has been a sampling-based planner from the Open Motion Planning Library, run on a CPU, followed by a smoothing pass to hide the zig-zags the sampler left behind. It works, it is probabilistically complete, and it is often slow and visually ugly. GPU motion planning changes the economics: instead of sampling one path at a time and cleaning it up, you launch thousands of trajectory optimizations at once and keep the best one.

That shift matters now because NVIDIA has moved its cuRobo library from research artifact to a supported backend, released a rewrite under Apache 2.0 in 2026, and wired it into MoveIt 2 through cuMotion. Teams choosing a planner for a new cell face a real architectural fork, not a tuning decision.

This article compares the two families from first principles, walks through how each one actually computes a collision-free trajectory, runs a toy RRT-Connect in Python, sketches the cuRobo API, and ends with a decision matrix and the failure modes nobody puts in a brochure.

What this covers: sampling versus optimization, why asymptotically optimal planners are still slow and jerky, parallel seeds and L-BFGS on a GPU, collision representations from spheres to signed distance fields, time parameterization, no-solution detection, cited benchmark claims with caveats, and integration with MoveIt.

Context and Background

Motion planning for a manipulator means finding a continuous path through configuration space, the space of joint angles, that stays clear of obstacles and respects joint limits. For a six-axis arm that space is six-dimensional, and the obstacles are not simple shapes there: a box on a table maps to a twisted, curved region in joint space that has no closed-form description. That is why planners do not reason about obstacles geometrically in joint space. They ask a collision-checking oracle “is this configuration free?” and build everything else on top of that yes-or-no answer.

The Open Motion Planning Library (OMPL), developed at the Kavraki Lab at Rice University, is the canonical collection of sampling-based planners. Its own documentation is explicit about scope: OMPL “does not contain any code related to, e.g., collision checking or visualization”, which is by design, so it can be wrapped by any framework. It is also MoveIt’s default planning library; the OMPL site recommends using it with ROS through MoveIt. At the time of writing the OMPL homepage lists release 2.0.2 (August 2026). If you run a stock MoveIt 2 install, the planner you are using is almost certainly OMPL, with FCL or Bullet answering the collision queries. Our MoveIt 2 versus Tesseract comparison covers that framework layer in detail.

cuRobo comes from NVIDIA Research. The paper, “cuRobo: Parallelized Collision-Free Minimum-Jerk Robot Motion Generation” (Sundaralingam et al., arXiv 2310.17274), frames collision-free motion generation as a global optimization problem and attacks it with many parallel seeds on a GPU. The abstract reports solving difficult problems in about 50 ms on average and describes this as roughly 60 times faster than state-of-the-art trajectory optimization methods. The Isaac Sim documentation describes cuRobo as a “high-performance, GPU-accelerated robotics motion generation library for robot manipulators” and cuMotion as the production package built on it, with a MoveIt 2 plugin for collision-free planning. The cuRobo project site now labels its v0.7.6 documentation as legacy and states that cuRoboV2, released under Apache 2.0 in April 2026 as v0.8.0, is the version to use for commercial work. The GitHub README tells users who depend on the v1 API to pin to the v0.7.8 tag, because V2 changes the public API.

Two other lineages matter for context. Optimization-based planners predate the GPU: CHOMP (Covariant Hamiltonian Optimization for Motion Planning), STOMP (Stochastic Trajectory Optimization for Motion Planning) and TrajOpt (sequential convex optimization with collision penalties) all treat the trajectory itself as the decision variable. They are shipped in MoveIt and Tesseract, and they are fast when the initial guess is good. cuRobo’s contribution is not a new idea about optimization; it is the observation that if you can run thousands of seeds simultaneously, the weakness of local optimizers, sensitivity to the starting point, mostly stops mattering. We will take that claim apart carefully, because it has limits.

One more piece of vocabulary. A planner returns a path, an ordered list of configurations. A robot needs a trajectory, which is a path plus a timing law that respects velocity, acceleration and jerk limits. Sampling planners solve the first problem and leave the second to a separate time-parameterization step. cuRobo’s formulation folds both into one optimization. That difference in problem statement, more than raw speed, is what separates the two stacks.

The Core Argument: Two Different Answers to the Same Question

Direct answer: OMPL finds a feasible path by randomly sampling joint space and connecting collision-free samples, then relies on smoothing and time parameterization afterwards. cuRobo optimizes whole trajectories directly, running many seeds in parallel on a GPU with collision cost evaluated in batch, so the result is already smooth, timed and near-minimum-jerk when it returns.

GPU motion planning architecture comparing the OMPL sampling pipeline with the cuRobo parallel optimization pipeline

Figure 1: Two pipelines. OMPL samples a path, then post-processes it. cuRobo runs IK, a geometric seed and parallel trajectory optimization in one GPU-resident loop.

Figure 1 puts the two pipelines side by side. The left pipeline is the classic MoveIt flow: a request arrives with a start state and a goal, the planner draws random configurations, a collision checker validates them and the edges between them, a raw path emerges, a shortcutting or smoothing step shortens it, and a time-parameterization algorithm assigns velocities. The right pipeline is cuRobo’s: solve inverse kinematics for many goal candidates in parallel, optionally run a parallel geometric planner for a rough guide, then run a batch of trajectory optimizations from many seeds, keeping the lowest-cost collision-free result. Everything stays in GPU memory between stages.

Sampling-based planning: probabilistic completeness and its price

The core guarantee of a sampling planner is probabilistic completeness: if a solution exists, the probability that the planner finds it approaches one as the number of samples grows. It says nothing about how long that takes, and it says nothing about the quality of what is found. RRT (Rapidly-exploring Random Tree) grows a tree from the start by repeatedly sampling a random configuration, finding the nearest tree node, and extending toward the sample by a bounded step if the segment is collision-free. RRT-Connect grows two trees, from start and goal, and tries to connect them greedily, which is why it is the workhorse for fast feasibility in MoveIt.

The price is that the path is a random walk with a bias toward unexplored space. It wanders. Consecutive segments have arbitrary direction changes at the nodes, which means the raw path has discontinuous velocity direction and, once timed, high jerk. Practitioners therefore always smooth: shortcutting picks two points on the path, tries the straight segment, and replaces the detour if it is free. This is cheap and effective at reducing length, but it only makes the path locally shorter, not smooth in the dynamical sense.

Asymptotic optimality is real, and not what you want in a cell

OMPL also ships asymptotically optimal planners. RRT rewires the tree so that the cost of the best path converges to the optimum as samples go to infinity; PRM does the same on a roadmap; BIT, AIT and EIT order their search with heuristics so convergence is faster. The OMPL planner page lists a long family of these, including RRT#, RRTX, Informed RRT, FMT* and a meta-planner, CForest, that runs several optimal planners in parallel. BiTRRT and T-RRT, the transition-based variants, bias sampling toward low-cost regions of a cost map.

Asymptotic optimality is a statement about a limit. In a six or seven dimensional joint space, the number of samples needed to approach the optimum with useful precision is large, and every sample costs a collision check. Under a 100 ms control-cell budget you stop long before convergence, and you get the best path found so far, which is typically feasible but lumpy. The honest summary is that sampling planners are excellent at “is there a way through this narrow passage” and weak at “give me the best smooth motion within 50 ms”.

Optimization-based planning: smooth by construction, local by nature

Optimization planners start from the opposite end. They parameterize a trajectory, for example as waypoints or B-spline control points, define a cost that combines smoothness (acceleration or jerk), collision penalties and goal constraints, and follow the gradient. CHOMP uses a signed distance field of the environment and its gradient to push the trajectory away from obstacles. TrajOpt convexifies collision constraints around the current iterate and solves a sequence of convex problems. STOMP avoids gradients entirely and perturbs the trajectory with noise, which is useful when the cost is not differentiable.

The weakness is well known and structural: these are local methods on a non-convex problem. Start the optimizer on the wrong side of an obstacle and it converges to a local minimum that is not collision-free, or never converges at all. A single CPU optimizer therefore needs a good initial trajectory, often the output of a sampling planner, which is exactly why hybrid pipelines (OMPL seed, then CHOMP or STOMP refinement) exist in MoveIt. The refinement works, but it serializes two planners and inherits the sampler’s latency.

The cuRobo bet: replace one good seed with thousands of cheap ones

cuRobo’s central design decision is to attack the local-minimum problem with brute-force parallelism rather than cleverness. The paper describes combining relatively simple optimizers with many parallel seeds, where seeds are different initial trajectories, including ones that go around obstacles in different ways. If even a few of a few thousand seeds fall into the basin of a good collision-free solution, the batch returns it. The optimizers named in the documentation are gradient descent, L-BFGS and MPPI (Model Predictive Path Integral), the last being a sampling-based stochastic optimizer that is gradient-free and naturally parallel.

This is not magic, and it is worth stating what it buys. It converts a latency problem into a throughput problem. A GPU has thousands of arithmetic units that sit idle if you run one trajectory at a time; running 32 or 4,096 trajectories costs little more wall-clock time than running one, up to the point where memory or compute saturates. The CPU equivalent would multiply latency linearly. That asymmetry is the whole story, and it is why the approach emerged only after GPUs and differentiable, batched kinematics became cheap.

Deeper Analysis: How Each Stack Computes a Collision-Free Trajectory

Understanding why the GPU version is fast requires looking at the three inner loops that dominate runtime: kinematics, collision checking and the optimizer step. Each has a CPU implementation that is inherently sequential and a GPU implementation that is batched.

Kinematics and collision queries on the GPU

Every candidate configuration requires forward kinematics, the chain of homogeneous transforms from base to each link. On a CPU this is a tight loop over links per configuration. On a GPU it is a batched operation over configurations: one thread block per configuration, links processed in sequence within the block, thousands of configurations at once. cuRobo implements forward kinematics and its gradient as custom CUDA kernels, because the optimizer needs the Jacobian of the cost with respect to joint angles. The V2 README adds topology-aware kinematics, differentiable inverse dynamics and map-reduce self-collision checking aimed at high degree-of-freedom robots such as humanoids.

Collision checking is where representation choices dominate. The common options trade accuracy against query cost:

  • Meshes are exact but expensive: distance queries against triangle soups involve bounding volume hierarchies and branching, which GPUs handle poorly.
  • Primitives such as cuboids, capsules and spheres have closed-form distance functions and no branching. cuRobo’s documentation lists cuboids, meshes and depth images as world representations.
  • Sphere decomposition of the robot approximates each link with a set of overlapping spheres, so robot-versus-world distance becomes a sum of cheap sphere-to-field queries. It is conservative if the spheres bound the link, and it is the representation that makes batched GPU evaluation practical.
  • Signed distance fields store, at each voxel, the distance to the nearest obstacle (negative inside). A query is a trilinear interpolation of voxel values, and the gradient comes almost for free. This is the format optimizers want.

Figure 2 shows how these pieces fit together for one query.

Collision checking pipeline for GPU motion planning with robot spheres queried against a signed distance field built from depth images

Figure 2: Collision checking in a GPU planner. Robot links become spheres, the world becomes a signed distance field, and one batched lookup yields both distance and gradient.

The ESDF (Euclidean signed distance field) is the key data structure for perception-driven planning. nvblox, NVIDIA’s GPU mapping library, integrates depth images into a truncated signed distance field and derives an ESDF from it. The cuRobo documentation added an ESDF voxel-grid collision checker in v0.7.0 and includes nvblox examples where obstacles come from a RealSense depth camera, and the Isaac Sim tutorial notes there are known issues with the nvblox examples and that the tutorial is unsupported on aarch64. The cuRoboV2 README claims GPU-native ESDF construction from depth images that is up to 10 times faster than the state of the art; I cite that as the project’s claim and have not reproduced it.

For a sphere with radius r at position p, the collision cost is simply max(0, r + margin minus sdf(p)), summed over spheres and timesteps. Because the SDF is a grid in GPU memory, the full trajectory cost for 4,000 seeds by 30 timesteps by 60 spheres is a single batched gather, around seven million lookups, with no branching.

The optimizer loop: why parallel seeds change the geometry of the problem

The cost cuRobo minimizes is a weighted sum: a smoothness term penalizing jerk (hence “minimum-jerk” in the paper title), joint position, velocity, acceleration and jerk limit terms, a collision term as above, self-collision, and a goal-pose term. The dominant solver is typically L-BFGS with a line search, applied to all seeds in the batch at once, optionally preceded by a gradient-descent or MPPI stage that gets seeds near a basin. Seeds that converge to infeasible local minima are discarded and the best feasible one is selected.

Trajectory optimization with parallel seeds on a GPU showing seed generation, batched L-BFGS iterations and selection of the best collision-free trajectory

Figure 3: Parallel seeds. Many initial trajectories run through batched cost evaluation and L-BFGS steps, and the best feasible one is returned.

Figure 3 shows the loop. Seeds come from several sources: linear interpolations in joint space between start and several IK solutions, perturbed versions of those, and, in the full motion generation pipeline, the output of a parallel geometric planner. Cost evaluation and gradient computation are batched across seeds. After each iteration a feasibility mask marks seeds that already satisfy all constraints, and the loop terminates when enough seeds converge or the iteration budget ends.

A design point deserves emphasis: because the trajectory is a decision variable with timing included, the output is directly executable. cuRobo’s examples call a result accessor that returns an interpolated plan at a fixed time step, and the library documents re-timing support in later 0.7 releases. In an OMPL pipeline the equivalent is a separate step: MoveIt’s time parameterization plugins, such as Iterative Parabolic or TOTG (Time-Optimal Trajectory Generation), take the geometric path and assign a velocity profile. TOTG produces time-optimal profiles for a given path but cannot change the path’s geometry, so a jerky path stays jerky, just fast.

What the published numbers say, and how to read them

The cuRobo paper’s headline numbers are impressive, and they are also benchmark claims from the authors on a particular problem set. As reported in the paper text: against Tesseract motion generation, cuRobo is about 60 times faster on average on an RTX 4090 desktop (2.95 s versus 50 ms), against TrajOpt about 87 times faster on trajectory optimization, against Tesseract’s geometric planner about 101 times faster, and against TracIK about 23 times faster for inverse kinematics at batch size 1000. On Jetson AGX Orin the paper reports about 28 times at the MAXN power mode and 21 times at 15 W. The success rate on its 2,600 benchmark problems is reported as 99.8 percent for cuRobo versus 98.53 percent for Tesseract, with C-space path length about 53 percent shorter than Tesseract’s geometric planner and mean motion time about 1.23 times lower. The project site separately states motion generation within 30 ms for global motions and UR10 motions within 100 ms on a Jetson Orin.

Read these with the following caveats, which apply to any planner benchmark. The baselines are other optimization-based planners in a particular framework (Tesseract), not MoveIt with OMPL RRT-Connect, so the numbers do not tell you the speed ratio against the OMPL stack; they are not interchangeable. The comparison is GPU versus CPU, so it reflects hardware as well as algorithm. The problem set and success definition matter: a benchmark of tabletop manipulator tasks says little about a cluttered bin or a high-DoF mobile manipulator. And single-run latency on a tuned workstation is different from tail latency inside a ROS 2 system with message serialization. I treat these as evidence that the approach is sound, not as a number to plan a project around. Measure on your robot and your scenes.

Decision flow for choosing between OMPL and cuRobo based on GPU availability, scene complexity and trajectory quality requirements

Figure 4: A practical decision flow. GPU availability, narrow passages and the need for smooth timed output decide the planner.

Walk-through: A Runnable RRT-Connect and a cuRobo Sketch

Theory only goes so far. The code below is a deliberately small RRT-Connect in pure Python, written in the style of OMPL’s planner but not using OMPL itself, so it runs anywhere with no dependencies. The workspace is a two-dimensional unit square with three circular obstacles, standing in for a configuration space. The point is to make the behaviour described above visible: a feasible but wandering path, a shortcut pass, and the cost of every collision query.

import math, random

# Workspace: unit square with circular obstacles (cx, cy, r)
OBST = [(0.5, 0.5, 0.2), (0.25, 0.75, 0.1), (0.75, 0.25, 0.1)]

def free(q):
    return all(math.dist(q, (cx, cy)) > r for cx, cy, r in OBST)

def edge_free(a, b, res=0.01):
    n = max(2, int(math.dist(a, b) / res))
    return all(free((a[0] + (b[0]-a[0])*i/n, a[1] + (b[1]-a[1])*i/n))
               for i in range(n + 1))

def steer(a, b, step):
    d = math.dist(a, b)
    if d <= step:
        return b
    t = step / d
    return (a[0] + (b[0]-a[0])*t, a[1] + (b[1]-a[1])*t)

def nearest(tree, q):
    return min(range(len(tree)), key=lambda i: math.dist(tree[i][0], q))

def extend(tree, q, step):
    i = nearest(tree, q)
    new = steer(tree[i][0], q, step)
    if edge_free(tree[i][0], new):
        tree.append((new, i))
        return len(tree) - 1
    return None

def connect(tree, q, step):
    last = None
    while True:
        idx = extend(tree, q, step)
        if idx is None:
            return last, False
        last = idx
        if math.dist(tree[idx][0], q) < 1e-9:
            return idx, True

def path_to_root(tree, i):
    out = []
    while i is not None:
        out.append(tree[i][0]); i = tree[i][1]
    return out

def rrt_connect(start, goal, step=0.05, iters=5000, seed=1):
    random.seed(seed)
    ta, tb, swapped = [(start, None)], [(goal, None)], False
    for _ in range(iters):
        q = (random.random(), random.random())
        ia = extend(ta, q, step)
        if ia is not None:
            ib, ok = connect(tb, ta[ia][0], step)
            if ok:
                pa, pb = path_to_root(ta, ia), path_to_root(tb, ib)
                path = pa[::-1] + pb[1:]
                return path[::-1] if swapped else path
        ta, tb, swapped = tb, ta, not swapped
    return None

def shortcut(path, tries=200, seed=2):
    random.seed(seed)
    p = list(path)
    for _ in range(tries):
        if len(p) < 3: break
        i, j = sorted(random.sample(range(len(p)), 2))
        if j - i > 1 and edge_free(p[i], p[j]):
            p = p[:i+1] + p[j:]
    return p

def length(p):
    return sum(math.dist(p[i], p[i+1]) for i in range(len(p)-1))

if __name__ == "__main__":
    s, g = (0.05, 0.05), (0.95, 0.95)
    raw = rrt_connect(s, g)
    sm = shortcut(raw)
    print("raw", len(raw), round(length(raw), 3), "smoothed", len(sm), round(length(sm), 3))

On my run with seed 1, going from (0.05, 0.05) to (0.95, 0.95), the planner returned a raw path of 34 waypoints with length 1.601, and the shortcut pass reduced it to 4 waypoints with length 1.342. The straight-line distance is about 1.273, so even after smoothing the path is roughly five percent longer than the unobstructed line, which is expected since the central obstacle forces a detour. These numbers are from a single seeded toy run, illustrative only, and change with the seed. The structure is what to notice. Every call to edge_free is a loop of point collision queries at 0.01 resolution, and the planner calls it for every extension and every connect step. Scale this to a seven-joint arm with mesh collision and the validity checker, not the sampling logic, dominates runtime. OMPL itself leaves this oracle to the host framework for exactly that reason.

The shortcut pass also shows why smoothing is incomplete. The result is a polyline: four straight segments with corners. The corners are where velocity direction changes instantaneously, and a time-parameterizer has to slow to nearly zero speed there or accept a violation. A smooth trajectory needs a fitted spline or a separate optimization pass, which is the step a joint optimizer eliminates.

The cuRobo side: what the API looks like

The cuRobo documentation describes a four-step flow for motion generation: build a configuration from a robot description file and a world dictionary, create and warm up a MotionGen object, define start and goal, and call plan_single. The sketch below follows the names shown in the legacy v0.7 Python examples. Because cuRoboV2 (v0.8.0) changed the public API, treat this as the v1 interface that you would pin to the v0.7.8 tag, and check the current documentation before porting. I could not verify the V2 equivalents for this article.

import torch
from curobo.types.math import Pose
from curobo.types.robot import JointState
from curobo.wrap.reacher.motion_gen import (
    MotionGen, MotionGenConfig, MotionGenPlanConfig,
)

# World: one table as a cuboid. dims in meters, pose is [x, y, z, qw, qx, qy, qz]
world_config = {
    "cuboid": {
        "table": {"dims": [1.5, 1.5, 0.1],
                  "pose": [0.5, 0.0, -0.05, 1, 0, 0, 0]},
    },
}

config = MotionGenConfig.load_from_robot_config(
    "ur5e.yml", world_config, interpolation_dt=0.01
)
motion_gen = MotionGen(config)
motion_gen.warmup()            # compiles/captures kernels; do this once at startup

goal = Pose.from_list([-0.4, 0.0, 0.4, 1.0, 0.0, 0.0, 0.0])
start = JointState.from_position(
    torch.zeros(1, 6).cuda(),
    joint_names=["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint",
                 "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"],
)

result = motion_gen.plan_single(start, goal, MotionGenPlanConfig(max_attempts=1))
if result.success:
    traj = result.get_interpolated_plan()   # timed, jerk-minimized samples
    print("dt:", result.interpolation_dt)

Three details in this snippet matter operationally. First, warmup() exists because the first call pays a large one-time cost for graph capture and kernel setup. Latency numbers quoted for cuRobo are post-warmup, so a cold-start planner process will miss a deadline on its first request unless you warm it at boot. Second, the goal is a Cartesian pose, not a joint configuration: the library solves collision-free inverse kinematics internally, producing many IK candidates in parallel, which removes the usual “which of the eight IK branches do I hand the planner” problem from application code. Third, the start state is a batch dimension of one; the library also exposes batched planning interfaces, which is how fleets of arms or many goals are served from a single GPU.

Reactive control with the same machinery

The documentation also lists MPPI-based model predictive control as an example. The idea follows from the parallel-seed design: if you can evaluate thousands of rollouts in milliseconds, you can re-plan at control rate against a moving obstacle instead of planning once. The Isaac Sim tutorial presents reactive MPPI control alongside collision-free IK and motion planning. This blurs the line between planner and controller, and it raises a safety question we return to below: a re-planner that is fast is not automatically one that is safe.

Integrating with MoveIt, Isaac Sim and the Rest of the Stack

For most teams the question is not “cuRobo or OMPL” in the abstract; it is “can I get GPU planning inside the stack I already run?” The Isaac Sim documentation answers part of it. cuMotion is described as a production motion generation package for manipulators whose current version uses cuRobo as its backend, and it provides collision-free planning through a plugin for MoveIt 2 along with supporting ROS 2 packages. In practice that means MoveIt keeps its role as the framework: robot description, planning scene, MoveGroup interface, execution through ROS 2 controllers. Only the planning plugin changes, from an OMPL-backed pipeline to a cuMotion-backed one.

This is the integration worth preferring over calling cuRobo directly from application code, because the planning scene, attached objects and trajectory execution semantics already exist in MoveIt. If you built your task logic as a behavior tree around planning actions, the planner swap is invisible to the tree: a “plan to pose” action node calls the same MoveIt interface and gets a trajectory back. That is the sort of boundary that makes a migration low-risk, and it is the argument for keeping planners behind a stable service interface.

A note on what I could and could not confirm. The Isaac Sim 5.1 documentation page states that the cuMotion example with Isaac Sim over the ROS 2 bridge was “somewhat limited in Isaac 3.0” and would be expanded later; I did not verify current Isaac ROS release contents or version compatibility matrices for this article, and they change quickly. The legacy cuRobo site does not itself mention Isaac ROS or MoveIt; that integration lives in the cuMotion documentation. Before committing, read the current cuMotion and Isaac ROS docs for your Isaac Sim, JetPack and ROS 2 distribution, because GPU stacks are strict about CUDA, driver and framework versions.

Where the time-parameterization boundary sits

With OMPL in MoveIt, planning adapters form a chain: the planner produces a geometric path, a shortcut or path-simplification adapter shortens it, and a time-parameterization adapter assigns timestamps under velocity and acceleration limits. With cuRobo the optimizer owns the timing. That is cleaner but has a consequence: limits you want respected must be inside the optimizer’s cost or constraint set, not left to a downstream filter. Jerk limits in particular are rarely in the standard MoveIt chain. If your robot controller enforces its own jerk limiting, a mismatch between the planned and enforced limits shows up as tracking error rather than a planning failure.

For multi-axis cells where the robot is just one device on a deterministic fieldbus, remember that planning latency is only half of cycle time. The other half is the communication layer that carries the trajectory to the drives; our comparison of EtherCAT, PROFINET and SERCOS motion control covers how interpolation and synchronization are handled at that layer. A 30 ms planner behind a 20 ms non-deterministic path to the drive is not a 30 ms system.

A note on planning beyond robots

The same pattern, many cheap parallel candidates scored by a differentiable or batchable model, shows up outside robotics. Computer-aided synthesis planning searches a tree of candidate reaction steps with learned scoring, a structurally similar search-versus-optimize trade-off that we cover in our post on AI retrosynthesis planning. The analogy is loose, but it helps explain why throughput-oriented hardware keeps displacing latency-oriented sequential search.

Trade-offs, Gotchas, and What Goes Wrong

The strongest case against GPU motion planning is operational, not algorithmic. You need a CUDA-capable NVIDIA GPU on the robot controller or a networked planning server, a matched driver and CUDA toolchain, and a build that the vendor supports on your platform. The Isaac Sim tutorial notes cuRobo is not supported on aarch64 for that tutorial, though cuRobo’s own documentation reports Jetson Orin results, so verify your exact platform. A planner that needs a GPU also adds a failure domain: a driver crash, thermal throttling or memory exhaustion on the same GPU that runs perception becomes a planning outage.

Local minima do not vanish; they get outnumbered. Parallel seeds raise the probability that one lands in a good basin, but a problem with a genuinely narrow passage, a slot the arm must thread through, can defeat every seed if no seed passes near it. This is the classic niche where sampling planners remain superior: probabilistic completeness is a real property, and a seed batch has no such guarantee. A pragmatic response is a geometric planner stage that supplies seeds through the passage. cuRobo includes a parallel geometric planner for that role, and the paper reports it as roughly 101 times faster than Tesseract’s geometric planner, a number to treat with the caveats given earlier.

No-solution detection is weaker than it looks. When all seeds fail, the planner returns failure, but that is not a proof that no solution exists. It means no seed converged to a feasible trajectory within the iteration budget. OMPL has the mirror problem: a timeout does not prove infeasibility either. Neither stack can certify infeasibility cheaply, so production systems need a policy for failure: retry with more seeds, relax the margin, fall back to the other planner, or escalate to a human. Treat the failure branch as a first-class path in your task logic.

Collision representation errors become safety errors. A sphere decomposition that under-covers a gripper, a voxel grid whose resolution is coarser than the clearance you need, or a depth camera that cannot see a thin cable will all produce trajectories the planner is happy with and the physical world is not. The signed distance field is also only as fresh as the last depth frame, so a dynamic obstacle that appeared after the last update is invisible. Margins help, but a safety function that relies on the planner’s world model is not a safety function. Keep a certified safety layer, such as speed and separation monitoring, independent of the planner.

Determinism and reproducibility. Sampling planners are random by construction, and parallel GPU optimizers add floating-point non-determinism across batch orderings. For regulated or validated cells, record the seed, library version and configuration with every executed trajectory. The V1 to V2 API change reinforces the point: pin versions explicitly, and do not let a container rebuild silently move you from v0.7.x to v0.8.x.

Licensing is now simpler but still read it. The GitHub repository states Apache-2.0 for the code, with bundled example robot assets under separate licenses, and the project site directs commercial users to cuRoboV2. Because the project site singles out V2 for commercial use, a team that adopted an older v0.7 release should re-check which license terms apply to the exact version they run. I did not audit the licensing history for this article.

Practical Recommendations

Choose by constraints, not by fashion. If your robot controller has no NVIDIA GPU, if your scenes contain genuinely tight passages, or if you already have a validated MoveIt and OMPL pipeline that meets its cycle time, stay on OMPL and spend your effort on better collision geometry and a decent time-parameterizer. Sampling planners remain the right tool for feasibility in hard topology, and the asymptotically optimal ones (RRT, BIT, AIT*) are excellent for offline planning where you can afford seconds.

If cycle time is dominated by planning, if you need smooth minimum-jerk output without a separate pass, if you replan against sensed obstacles, or if you plan for many robots or many goals per second, GPU motion planning earns its hardware cost. Start by running cuMotion behind MoveIt on a representative scene set and compare tail latency, not averages. A hybrid is also legitimate: cuRobo as primary with OMPL as the slow-but-complete fallback when the GPU planner reports failure.

A short checklist for a pilot:

  • Define a benchmark set from your own cell: at least 200 start and goal pairs including your worst clutter and your tightest passage.
  • Record p50, p95 and p99 planning latency after warmup, plus success rate and trajectory duration, for both stacks on identical scenes and identical collision geometry.
  • Verify the sphere model of every tool and gripper against a mesh, with a margin you can justify.
  • Pin cuRobo and CUDA versions; warm the planner at process start and include the cold-start cost in your deadline analysis.
  • Test the failure path deliberately with an unreachable goal and a narrow passage, and confirm your task logic handles both.
  • Keep an independent safety layer that does not trust the planner’s world model.
  • Log seed, version and configuration with every executed trajectory.

Figure 4 above summarises the same logic as a flow. And this decision matrix condenses the comparison for the four situations teams ask about most:

Situation OMPL (RRT-Connect, RRT, BIT) cuRobo or cuMotion Better fit
Fast cycle time, open workspace, NVIDIA GPU available Feasible but needs smoothing and timing passes Single pass, timed and smooth output cuRobo
Narrow passage, complex topology Probabilistic completeness helps Depends on seeds reaching the passage OMPL, or hybrid
No GPU on controller, CPU-only edge device Works anywhere MoveIt does Not applicable OMPL
Reactive replanning against sensed obstacles Too slow for control-rate use MPPI and ESDF queries designed for it cuRobo

Frequently Asked Questions

Is cuRobo faster than OMPL?

The published comparisons are against other optimization planners, not OMPL. The cuRobo paper reports roughly 60 times faster motion generation than Tesseract on an RTX 4090 desktop, with about 50 ms average solve time for hard problems. Those numbers come from the authors on their benchmark and hardware. Against OMPL RRT-Connect, results depend on scene complexity, collision checker and whether you count smoothing and time parameterization, so benchmark your own scenes.

What is the difference between sampling-based and optimization-based motion planning?

Sampling-based planners such as RRT-Connect and PRM draw random configurations and connect collision-free ones into a path, with probabilistic completeness but no smoothness guarantee. Optimization-based planners such as CHOMP, TrajOpt, STOMP and cuRobo treat the whole trajectory as variables and minimize a cost combining smoothness and collision penalties. They produce smooth results quickly but are local methods that can get stuck in infeasible minima.

Does cuRobo work with MoveIt?

Yes, through cuMotion. The Isaac Sim documentation describes cuMotion as a production motion generation package that uses cuRobo as its backend and provides collision-free planning through a MoveIt 2 plugin plus supporting ROS 2 packages. Check the current cuMotion and Isaac ROS documentation for supported ROS 2 distributions, Isaac Sim versions and JetPack or driver requirements before planning a deployment.

What license does cuRobo use?

The NVlabs GitHub repository lists Apache-2.0 for the code, with bundled example robot assets under their own licenses described in LICENSE_ASSETS. The legacy documentation site says commercial users should use cuRoboV2, released under Apache 2.0 in April 2026 as v0.8.0. Because earlier versions used different terms, confirm the license of the exact version you deploy rather than assuming.

Why are RRT and PRM slow if they are asymptotically optimal?

Asymptotic optimality means the solution cost converges to the optimum as the number of samples approaches infinity. In a six or seven dimensional joint space, useful precision needs very many samples, each requiring collision checks. With a fixed time budget the planner returns the best path so far, which is feasible but rarely optimal or smooth, so it still needs smoothing and time parameterization.

Can GPU motion planning prove that no collision-free path exists?

No. When all parallel seeds fail, the planner reports failure, which means none converged to a feasible trajectory within its budget, not that no solution exists. Sampling planners have the same limitation when they time out. Production systems need a failure policy: retry with more seeds or a larger margin, fall back to another planner, or ask for human help.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *