AdrianLlopart's picture
docs: HF model card β€” best-of-both front-matter derived from manifest
c4568ac verified
|
Raw
History Blame Contribute Delete
11.6 kB
---
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)