File size: 11,569 Bytes
2e9e257 c4568ac 2e9e257 c4568ac 2e9e257 c4568ac 2e9e257 c4568ac 2e9e257 c4568ac 2e9e257 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | ---
language:
- en
license: apache-2.0
pipeline_tag: robotics
tags:
- OpenRAL
- rskill
- ros2
- moveit
- franka_panda
- ur5e
- ur10e
- so100_follower
- openarm
- rizon4
- sawyer
- widowx
inference: false
---
# rskill-moveit-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`
> under [ADR-0054](../../docs/adr/0054-moveit-goal-builder-library.md).
This package uses `kind: ros_action` (see
[ADR-0024](../../docs/adr/0024-ros-wrapped-rskills.md)) β a discriminator
on `RSkillManifest.kind` that selects the
[`ROSActionRskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)
engine at resolve time, with `ros_integration.goal_builder: joint` selecting
the [`JointGoalRskill`](../../python/rskill/src/openral_rskill/joint_goal_rskill.py)
goal-lowering adapter (ADR-0054). 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.
| Field | Value |
| --- | --- |
| Actions | `reach` |
| Objects | _none_ β no per-object specialisation |
| Scenes | _none_ β the wrapped planner does its own collision check against the live `/planning_scene` |
| Embodiment | `franka_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. `_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. On the first `_step_impl(world_state)` call the adapter:
- builds the `MoveGroup.Goal` from the lowered dict (via
`rosidl_runtime_py.set_message_fields`),
- 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`),
- extracts `result.planned_trajectory.joint_trajectory`, reorders
its `joint_names` into the host `RobotDescription.joints` order
(see
[`build_joint_permutation_from_names`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)),
- returns waypoint 0 as a 1-row `Action(JOINT_POSITION, β¦)`.
3. Each subsequent `_step_impl` returns the next cached waypoint.
4. 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
| Direction | Key | Shape | Notes |
| --- | --- | --- | --- |
| in | `world_state.joint_state` | `(n_dof,)` float | Read for logging only; the wrapped server consumes its own `/joint_states` subscription |
| out | per-waypoint `Action` | `joint_targets=[[n_dof floats]]`, `horizon=1`, `is_terminal=False` | One 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`](../../packages/openral_safety/openral_safety/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, ADR-0065)
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`](../../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.
| Field | Value |
| --- | --- |
| Upstream | [`moveit_msgs/action/MoveGroup`](https://github.com/moveit/moveit_msgs/blob/master/action/MoveGroup.action) (BSD-3-Clause) |
| Planner library | [MoveIt 2](https://moveit.picknik.ai/) (BSD-3-Clause) |
| Collision check | FCL via MoveIt's `PlanningScene` (run during planning) |
| Wrapped artefact | rSkill manifest + README β no weights, no preprocessor JSONs |
## Supported robots / embodiments
| Robot | Embodiment tag | Status | Notes |
| --- | --- | --- | --- |
| Franka Panda | `franka_panda` | validated | default goal targets `panda_arm` home pose |
| Universal Robots UR5e | `ur5e` | experimental | needs a `joint` / `request.group_name` override (`"manipulator"`, UR joint names) |
| Universal Robots UR10e | `ur10e` | experimental | same as UR5e |
| SO-100 follower | `so100_follower` | experimental | needs a MoveIt config and joint-name override |
| OpenArm | `openarm` | experimental | bi-manual β choose `left_arm` or `right_arm` group |
| Flexiv Rizon4 | `rizon4` | experimental | upstream MoveIt config exists; manifest override needed |
| Rethink Sawyer | `sawyer` | experimental | upstream MoveIt config exists |
| Trossen WidowX | `widowx` | experimental | upstream 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:
| Source | Topic | Why MoveIt needs it |
| --- | --- | --- |
| Joint state | `/joint_states` | Plan from the live start state |
| Planning scene | `/planning_scene` (or `/monitored_planning_scene`) | Self- and environment-collision check |
| TF | `/tf`, `/tf_static` | Resolve 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
| Field | Value |
| --- | --- |
| `name` | `OpenRAL/rskill-moveit-joints` |
| `version` | `0.1.0` |
| `license` | `apache-2.0` |
| `kind` | `ros_action` |
| `role` | `s1` |
| `actions` | `[reach]` |
| `chunk_size` | `1` (schema-enforced for `kind: ros_action`) |
| `latency_budget.per_chunk_ms` | `2000` (planning latency; the adapter waits Γ5 of this on the action result) |
| `ros_integration.package` | `moveit_msgs` |
| `ros_integration.interface_type` | `MoveGroup` |
| `ros_integration.interface_name` | `/move_action` |
| `ros_integration.goal_builder` | `joint` (selects `JointGoalRskill`) |
| `ros_integration.result_trajectory_field` | `planned_trajectory.joint_trajectory` |
| `commercial_use_allowed` | `true` (apache-2.0) |
Full schema:
[`openral_core.schemas.RSkillManifest`](../../python/core/src/openral_core/schemas.py).
## 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-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`. Per
[ADR-0012](../../docs/adr/0012-open-core-licensing.md) both postures
are commercial-use-permissive.
## See also
- [ADR-0054 β MoveIt goal-builder library + rskill-moveit-* rename](../../docs/adr/0054-moveit-goal-builder-library.md)
- [ADR-0024 β ROS-wrapped rSkills](../../docs/adr/0024-ros-wrapped-rskills.md)
- [`openral_rskill.ros_action_rskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py) β engine source
- [`openral_rskill.joint_goal_rskill`](../../python/rskill/src/openral_rskill/joint_goal_rskill.py) β goal-lowering adapter
- [`rskills/rskill-moveit-eef-pose/`](../rskill-moveit-eef-pose/) β sibling Cartesian end-effector pose wrapper
- [`rskills/rskill-moveit-look-at/`](../rskill-moveit-look-at/) β sibling camera-aiming wrapper
- [`rskills/rskill-nav2-navigate-to-pose/`](../rskill-nav2-navigate-to-pose/) β sibling Nav2 wrapper (result-only mode)
- [CLAUDE.md Β§3 β Architecture Discipline](../../CLAUDE.md)
|