Skip to content

[Feature]: Per-call lock_joints override on a shared multi-arm MotionPlanner #668

Description

@bheijden

Problem / motivation

We build one MotionPlanner for a multi-arm robot and dispatch per-arm goals at call time via goal_tool_poses. Arms without a goal are held by the planner's optimizer, not hard-locked, so trajopt frequently routes them through significant joint-space detours.

We want to hard-lock an arbitrary subset of joints at their current values per plan call, then unlock them on the next call where we plan all arms together. Today this means changing the lock_joints set, which is baked into the kinematics tensor shapes at MotionPlanner construction. Kinematics.update_kinematics_config is shape-preserving and refuses any change to the set. Rebuilding the planner per call costs many seconds (kinematics rebuild plus CUDA graph re-capture plus warmup), so it is not practical for interactive use.

Proposed solution

Per-call `lock_joints` override on `MotionPlanner.plan_pose` and `MotionPlanner.plan_cspace` that hard-locks a chosen subset of joints at supplied values for that one call, with no rebuild and no CUDA graph re-capture:


mp = MotionPlanner(MotionPlannerCfg.create(...))

# Plan only arm A. Hard-lock arm B's joints at their current values.
mp.plan_pose(
    goal_tool_poses={"arm_a_tool": target_a},
    current_state=full_js,
    lock_joints_override={"arm_b_j1": q1, "arm_b_j2": q2, ...},
)

# Same planner. Plan both arms. Override is per-call only.
mp.plan_pose(
    goal_tool_poses={"arm_a_tool": target_a, "arm_b_tool": target_b},
    current_state=full_js,
)


The override should accept an arbitrary subset of joints by name and should not assume the arms share structure or DOF count.

Alternatives considered

  • Custom cspace cost penalizing motion of the joints we want held: weight tuning is brittle and a soft cost is not equivalent to a hard lock. Requires tuning.
  • Pre-built planner pool, one planner per lock-set permutation: workable but each planner pays its own compilation cost at startup and again whenever the scene changes enough to force a recompile, GPU memory scales with the pool size, and the number of subsets grows quickly with the number of arms.
  • Rebuild the MotionPlanner on every switch: latency is too high for an interactive UI.
  • Goal-hold the idle arm by passing its current FK as a target: still a soft objective; we have observed idle-joint drifts from a few degrees up to a full revolution.
  • Post-check that rejects plans whose idle joints drifted: catches the symptom, does not prevent it, and wastes the plan compute.

Additional context

cuRobo version: 0.8.0.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions