CoolFace
Modelpublic

OpenRAL/rskill-moveit-multi-joints

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
1likes
Model Card

rskill-moveit-multi-joints

OpenRAL rSkill — wraps the upstream moveit_msgs/action/MoveGroup action server as an OpenRAL rSkill so the Reasoner can dispatch collision-free joint-space motion planning through the same ExecuteRskill path used by VLA skills. No model weights — the manifest is the entire artefact. Renamed from openral/rskill-moveit-plan-arm as part of the rskill-moveit-* rename.

This package uses kind: ros_action — a discriminator on RSkillManifest.kind that selects the `ROSActionRskill` engine at resolve time, with ros_integration.goal_builder: joint selecting the `JointGoalRskill` goal-lowering adapter. The adapter constructs an rclpy.action.ActionClient on the host RskillRunnerNode, sends one goal built from ros_integration.default_goal_json, awaits the result, and replays the returned trajectory_msgs/JointTrajectory one waypoint per step() call onto /openral/candidate_action so the safety supervisor still applies its per-joint envelope check to every commanded position.

What this skill does

Plans and executes a collision-free joint-space motion to a target joint configuration via MoveIt's MoveGroup action. The goal is authored as a joint block (joint_names + positions) — the clean, LLM-facing form (target angles, not hand-written constraint dicts) — which JointGoalRskill lowers into MoveGroup joint_constraints at configure time. The default goal targets the Franka Panda home pose (panda_arm planning group). Other arm embodiments need their own manifest copy with the correct planning-group name and joint names.

FieldValue
Actionsreach
Objectsnone — no per-object specialisation
Scenesnone — the wrapped planner does its own collision check against the live /planning_scene
Embodimentfranka_panda (default goal); other arm tags listed in the manifest for capability filtering

How it works

ROSActionRskill is a thin rSkillBase shim around an ActionClient, and goal_builder: joint lowers the LLM-facing joint block into the MoveGroup goal:

  1. 1._configure_impl lazy-imports moveit_msgs.action.MoveGroup, opens an ActionClient on /move_action from the RskillRunnerNode-supplied node handle, and parses ros_integration.default_goal_json once. JointGoalRskill pops the joint block (joint_names + positions + tolerance) and lowers it into request.goal_constraints[0].joint_constraints — one moveit_msgs/JointConstraint per joint.
  2. 2.On the first _step_impl(world_state) call the adapter:
  3. 3.builds the MoveGroup.Goal from the lowered dict (via rosidl_runtime_py.set_message_fields),
  4. 4.sends it and polls the goal-accept + result futures while the host node's main rclpy spin continues to service callbacks (same pattern as rskill_runner_node._maybe_reset_hal_to_starting_pose),
  5. 5.extracts result.planned_trajectory.joint_trajectory, reorders its joint_names into the host RobotDescription.joints order (see `build_joint_permutation_from_names`),
  6. 6.returns waypoint 0 as a 1-row Action(JOINT_POSITION, …).
  7. 7.Each subsequent _step_impl returns the next cached waypoint.
  8. 8.After the last waypoint, the adapter raises ROSRskillGoalSatisfied — the runner catches it specifically and closes the ExecuteRskill goal with success=True.

The LLM overrides only joint.positions (one entry per planning-group joint, in the manifest's joint_names order); planner and tolerance defaults are inherited from default_goal_json. plan_only: true so MoveGroup never drives its own controllers — OpenRAL's per-waypoint replay is the only actuation path.

Observation → action contract

DirectionKeyShapeNotes
inworld_state.joint_state(n_dof,) floatRead for logging only; the wrapped server consumes its own /joint_states subscription
outper-waypoint Actionjoint_targets=[[n_dof floats]], horizon=1, is_terminal=FalseOne chunk per step() until completion is signalled by exception

Why one row per chunk (chunk_size: 1 is schema-enforced for kind: ros_action): the OpenRAL safety supervisor only validates row 0 of every ActionChunk today (`supervisor_node.py`). Packing the full trajectory as one chunk with horizon=N would let waypoints 1..N actuate unchecked — unacceptable for a planner whose job is to thread between joint-limit walls.

GPU-accelerated planning (cuMotion)

On a host that clears the cuMotion GPU floor (RobotCapabilities.supports_cumotion() — Ampere+, CUDA ≥ 13, ~8 GB VRAM), the runner sets MotionPlanRequest.pipeline_id = "isaac_ros_cumotion" so MoveIt plans with NVIDIA's CUDA-accelerated cuMotion pipeline; otherwise it falls back to OMPL. Transparent — same skill, no manifest change — and it never bypasses the safety kernel: the planned trajectory still replays through /openral/candidate_action and is validated waypoint-by-waypoint. Install: see `docs/contributing/toolchain.md` → "GPU motion planning — cuMotion".

How it was trained / Upstream provenance

Nothing is trained — this rSkill wraps the upstream MoveIt motion planner.

FieldValue
Upstream`moveit_msgs/action/MoveGroup` (BSD-3-Clause)
Planner libraryMoveIt 2 (BSD-3-Clause)
Collision checkFCL via MoveIt's PlanningScene (run during planning)
Wrapped artefactrSkill manifest + README — no weights, no preprocessor JSONs

Supported robots / embodiments

RobotEmbodiment tagStatusNotes
Franka Pandafranka_pandavalidateddefault goal targets panda_arm home pose
Universal Robots UR5eur5eexperimentalneeds a joint / request.group_name override ("manipulator", UR joint names)
Universal Robots UR10eur10eexperimentalsame as UR5e
SO-100 followerso100_followerexperimentalneeds a MoveIt config and joint-name override
OpenArmopenarmexperimentalbi-manual — choose left_arm or right_arm group
Flexiv Rizon4rizon4experimentalupstream MoveIt config exists; manifest override needed
Rethink Sawyersawyerexperimentalupstream MoveIt config exists
Trossen WidowXwidowxexperimentalupstream MoveIt config exists

Listed embodiment_tags only gate which robots see this skill in the Reasoner's tool palette; actual resolution depends on move_group being up for that robot with a joint block matching its planning group.

Sensors required / Observation contract

This skill consumes nothing through OpenRAL's sensor pipeline. MoveIt's own subscriptions handle:

SourceTopicWhy MoveIt needs it
Joint state/joint_statesPlan from the live start state
Planning scene/planning_scene (or /monitored_planning_scene)Self- and environment-collision check
TF/tf, /tf_staticResolve goal pose / link frames

If your deployment uses a non-default topic remap, surface it on the MoveIt node's launch — the wrapped action's contract is intact.

Manifest summary

FieldValue
nameOpenRAL/rskill-moveit-multi-joints
version0.1.0
licenseapache-2.0
kindros_action
roles1
actions[reach]
chunk_size1 (schema-enforced for kind: ros_action)
latency_budget.per_chunk_ms2000 (planning latency; the adapter waits ×5 of this on the action result)
ros_integration.packagemoveit_msgs
ros_integration.interface_typeMoveGroup
ros_integration.interface_name/move_action
ros_integration.goal_builderjoint (selects JointGoalRskill)
ros_integration.result_trajectory_fieldplanned_trajectory.joint_trajectory
commercial_use_allowedtrue (apache-2.0)

Full schema: `openral_core.schemas.RSkillManifest`.

Quick start

python
from openral_rskill.loader import rSkill
pkg = rSkill.from_yaml("rskills/rskill-moveit-joints/rskill.yaml")
print(pkg.manifest.name, pkg.manifest.kind, pkg.manifest.ros_integration.goal_builder)

End-to-end, with a real MoveIt launch up:

bash
# 1. Bring up MoveIt for your robot (example: Panda)
ros2 launch moveit_resources_panda_moveit_config demo.launch.py

# 2. Bring up the OpenRAL runner against the same robot
ros2 launch openral_rskill_ros skill_runner.launch.py robot:=franka_panda

# 3. From the Reasoner (or by hand via the action CLI), dispatch the goal:
ros2 action send_goal /openral/execute_rskill openral_msgs/action/ExecuteRskill \
    "{rskill_id: 'OpenRAL/rskill-moveit-multi-joints', deadline_s: 30.0, prompt: 'move to home'}"

Limitations / Roadmap

  • —Goal defaults live in the manifest. The LLM overrides joint.positions via goal_params_json; planner settings and joint names are inherited from default_goal_json. Cross-embodiment retargeting is a manifest copy with the correct joint.joint_names + request.group_name.
  • —OpenRAL safety supervisor does not do collision checking. We trust MoveIt's internal FCL pass. The per-joint envelope check still runs per waypoint. Collision checking inside the OpenRAL kernel is a separate ADR + multi-PR effort.
  • —No velocity / jerk bound at the supervisor. A planner emitting a rough trajectory would actuate today; the existing supervisor only checks per-joint position envelope. Tracked separately.

License

The rSkill package itself (this manifest + README) is Apache-2.0. The wrapped MoveIt code (moveit_msgs IDL, moveit2 planners) is BSD-3-Clause and lives outside this repository — installed via ros-${ROS_DISTRO}-moveit. Both postures are commercial-use-permissive.

See also

  • —MoveIt goal-builder library + rskill-moveit-* rename
  • —ROS-wrapped rSkills
  • —`openral_rskill.ros_action_rskill` — engine source
  • —`openral_rskill.joint_goal_rskill` — goal-lowering adapter
  • —`rskills/rskill-moveit-eef-pose/` — sibling Cartesian end-effector pose wrapper
  • —`rskills/rskill-moveit-look-at/` — sibling camera-aiming wrapper
  • —`rskills/rskill-nav2-navigate-to-pose/` — sibling Nav2 wrapper (result-only mode)
  • —CLAUDE.md §3 — Architecture Discipline