AdrianLlopart commited on
Commit
2e9e257
·
verified ·
1 Parent(s): 1f60d74

chore: publish rSkill OpenRAL/rskill-moveit-joints v0.1.0

Browse files
Files changed (2) hide show
  1. README.md +221 -0
  2. rskill.yaml +128 -0
README.md ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - OpenRAL
4
+ - rskill
5
+ - ros2
6
+ - moveit
7
+ license: apache-2.0
8
+ language:
9
+ - en
10
+ ---
11
+
12
+ # rskill-moveit-joints
13
+
14
+ > **OpenRAL rSkill** — wraps the upstream `moveit_msgs/action/MoveGroup`
15
+ > action server as an OpenRAL rSkill so the Reasoner can dispatch
16
+ > collision-free joint-space motion planning through the same
17
+ > `ExecuteRskill` path used by VLA skills. No model weights — the manifest
18
+ > is the entire artefact. Renamed from `openral/rskill-moveit-plan-arm`
19
+ > under [ADR-0052](../../docs/adr/0052-moveit-goal-builder-library.md).
20
+
21
+ This package uses `kind: ros_action` (see
22
+ [ADR-0024](../../docs/adr/0024-ros-wrapped-rskills.md)) — a discriminator
23
+ on `RSkillManifest.kind` that selects the
24
+ [`ROSActionRskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)
25
+ engine at resolve time, with `ros_integration.goal_builder: joint` selecting
26
+ the [`JointGoalRskill`](../../python/rskill/src/openral_rskill/joint_goal_rskill.py)
27
+ goal-lowering adapter (ADR-0052). The adapter constructs an
28
+ `rclpy.action.ActionClient` on the host `RskillRunnerNode`, sends one goal
29
+ built from `ros_integration.default_goal_json`, awaits the result, and replays
30
+ the returned `trajectory_msgs/JointTrajectory` one waypoint per `step()` call
31
+ onto `/openral/candidate_action` so the safety supervisor still applies its
32
+ per-joint envelope check to every commanded position.
33
+
34
+ ## What this skill does
35
+
36
+ Plans and executes a collision-free joint-space motion to a target joint
37
+ configuration via MoveIt's `MoveGroup` action. The goal is authored as a
38
+ `joint` block (`joint_names` + `positions`) — the clean, LLM-facing form
39
+ (target angles, not hand-written constraint dicts) — which `JointGoalRskill`
40
+ lowers into MoveGroup `joint_constraints` at configure time. The default goal
41
+ targets the Franka Panda home pose (`panda_arm` planning group). Other arm
42
+ embodiments need their own manifest copy with the correct planning-group name
43
+ and joint names.
44
+
45
+ | Field | Value |
46
+ | --- | --- |
47
+ | Actions | `reach` |
48
+ | Objects | _none_ — no per-object specialisation |
49
+ | Scenes | _none_ — the wrapped planner does its own collision check against the live `/planning_scene` |
50
+ | Embodiment | `franka_panda` (default goal); other arm tags listed in the manifest for capability filtering |
51
+
52
+ ## How it works
53
+
54
+ `ROSActionRskill` is a thin `rSkillBase` shim around an `ActionClient`, and
55
+ `goal_builder: joint` lowers the LLM-facing `joint` block into the MoveGroup
56
+ goal:
57
+
58
+ 1. `_configure_impl` lazy-imports `moveit_msgs.action.MoveGroup`, opens
59
+ an `ActionClient` on `/move_action` from the
60
+ `RskillRunnerNode`-supplied node handle, and parses
61
+ `ros_integration.default_goal_json` once. `JointGoalRskill` pops the
62
+ `joint` block (`joint_names` + `positions` + tolerance) and lowers it into
63
+ `request.goal_constraints[0].joint_constraints` — one
64
+ `moveit_msgs/JointConstraint` per joint.
65
+ 2. On the first `_step_impl(world_state)` call the adapter:
66
+ - builds the `MoveGroup.Goal` from the lowered dict (via
67
+ `rosidl_runtime_py.set_message_fields`),
68
+ - sends it and polls the goal-accept + result futures while the
69
+ host node's main rclpy spin continues to service callbacks (same
70
+ pattern as
71
+ `rskill_runner_node._maybe_reset_hal_to_starting_pose`),
72
+ - extracts `result.planned_trajectory.joint_trajectory`, reorders
73
+ its `joint_names` into the host `RobotDescription.joints` order
74
+ (see
75
+ [`build_joint_permutation_from_names`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)),
76
+ - returns waypoint 0 as a 1-row `Action(JOINT_POSITION, …)`.
77
+ 3. Each subsequent `_step_impl` returns the next cached waypoint.
78
+ 4. After the last waypoint, the adapter raises
79
+ `ROSRskillGoalSatisfied` — the runner catches it specifically and
80
+ closes the `ExecuteRskill` goal with `success=True`.
81
+
82
+ The LLM overrides only `joint.positions` (one entry per planning-group joint,
83
+ in the manifest's `joint_names` order); planner and tolerance defaults are
84
+ inherited from `default_goal_json`. `plan_only: true` so MoveGroup never drives
85
+ its own controllers — OpenRAL's per-waypoint replay is the only actuation path.
86
+
87
+ ### Observation → action contract
88
+
89
+ | Direction | Key | Shape | Notes |
90
+ | --- | --- | --- | --- |
91
+ | in | `world_state.joint_state` | `(n_dof,)` float | Read for logging only; the wrapped server consumes its own `/joint_states` subscription |
92
+ | out | per-waypoint `Action` | `joint_targets=[[n_dof floats]]`, `horizon=1`, `is_terminal=False` | One chunk per `step()` until completion is signalled by exception |
93
+
94
+ Why one row per chunk (`chunk_size: 1` is schema-enforced for
95
+ `kind: ros_action`): the OpenRAL safety supervisor only validates row 0
96
+ of every `ActionChunk` today
97
+ ([`supervisor_node.py`](../../packages/openral_safety/openral_safety/supervisor_node.py)).
98
+ Packing the full trajectory as one chunk with `horizon=N` would let
99
+ waypoints 1..N actuate unchecked — unacceptable for a planner whose
100
+ job is to thread between joint-limit walls.
101
+
102
+ ## How it was trained / Upstream provenance
103
+
104
+ Nothing is trained — this rSkill wraps the upstream MoveIt motion
105
+ planner.
106
+
107
+ | Field | Value |
108
+ | --- | --- |
109
+ | Upstream | [`moveit_msgs/action/MoveGroup`](https://github.com/moveit/moveit_msgs/blob/master/action/MoveGroup.action) (BSD-3-Clause) |
110
+ | Planner library | [MoveIt 2](https://moveit.picknik.ai/) (BSD-3-Clause) |
111
+ | Collision check | FCL via MoveIt's `PlanningScene` (run during planning) |
112
+ | Wrapped artefact | rSkill manifest + README — no weights, no preprocessor JSONs |
113
+
114
+ ## Supported robots / embodiments
115
+
116
+ | Robot | Embodiment tag | Status | Notes |
117
+ | --- | --- | --- | --- |
118
+ | Franka Panda | `franka_panda` | validated | default goal targets `panda_arm` home pose |
119
+ | Universal Robots UR5e | `ur5e` | experimental | needs a `joint` / `request.group_name` override (`"manipulator"`, UR joint names) |
120
+ | Universal Robots UR10e | `ur10e` | experimental | same as UR5e |
121
+ | SO-100 follower | `so100_follower` | experimental | needs a MoveIt config and joint-name override |
122
+ | OpenArm | `openarm` | experimental | bi-manual — choose `left_arm` or `right_arm` group |
123
+ | Flexiv Rizon4 | `rizon4` | experimental | upstream MoveIt config exists; manifest override needed |
124
+ | Rethink Sawyer | `sawyer` | experimental | upstream MoveIt config exists |
125
+ | Trossen WidowX | `widowx` | experimental | upstream MoveIt config exists |
126
+
127
+ Listed `embodiment_tags` only gate which robots see this skill in the
128
+ Reasoner's tool palette; actual resolution depends on `move_group` being
129
+ up for that robot with a `joint` block matching its planning group.
130
+
131
+ ## Sensors required / Observation contract
132
+
133
+ This skill consumes nothing through OpenRAL's sensor pipeline. MoveIt's
134
+ own subscriptions handle:
135
+
136
+ | Source | Topic | Why MoveIt needs it |
137
+ | --- | --- | --- |
138
+ | Joint state | `/joint_states` | Plan from the live start state |
139
+ | Planning scene | `/planning_scene` (or `/monitored_planning_scene`) | Self- and environment-collision check |
140
+ | TF | `/tf`, `/tf_static` | Resolve goal pose / link frames |
141
+
142
+ If your deployment uses a non-default topic remap, surface it on the
143
+ MoveIt node's launch — the wrapped action's contract is intact.
144
+
145
+ ## Manifest summary
146
+
147
+ | Field | Value |
148
+ | --- | --- |
149
+ | `name` | `OpenRAL/rskill-moveit-joints` |
150
+ | `version` | `0.1.0` |
151
+ | `license` | `apache-2.0` |
152
+ | `kind` | `ros_action` |
153
+ | `role` | `s1` |
154
+ | `actions` | `[reach]` |
155
+ | `chunk_size` | `1` (schema-enforced for `kind: ros_action`) |
156
+ | `latency_budget.per_chunk_ms` | `2000` (planning latency; the adapter waits ×5 of this on the action result) |
157
+ | `ros_integration.package` | `moveit_msgs` |
158
+ | `ros_integration.interface_type` | `MoveGroup` |
159
+ | `ros_integration.interface_name` | `/move_action` |
160
+ | `ros_integration.goal_builder` | `joint` (selects `JointGoalRskill`) |
161
+ | `ros_integration.result_trajectory_field` | `planned_trajectory.joint_trajectory` |
162
+ | `commercial_use_allowed` | `true` (apache-2.0) |
163
+
164
+ Full schema:
165
+ [`openral_core.schemas.RSkillManifest`](../../python/core/src/openral_core/schemas.py).
166
+
167
+ ## Quick start
168
+
169
+ ```python
170
+ from openral_rskill.loader import rSkill
171
+ pkg = rSkill.from_yaml("rskills/rskill-moveit-joints/rskill.yaml")
172
+ print(pkg.manifest.name, pkg.manifest.kind, pkg.manifest.ros_integration.goal_builder)
173
+ ```
174
+
175
+ End-to-end, with a real MoveIt launch up:
176
+
177
+ ```bash
178
+ # 1. Bring up MoveIt for your robot (example: Panda)
179
+ ros2 launch moveit_resources_panda_moveit_config demo.launch.py
180
+
181
+ # 2. Bring up the OpenRAL runner against the same robot
182
+ ros2 launch openral_rskill_ros skill_runner.launch.py robot:=franka_panda
183
+
184
+ # 3. From the Reasoner (or by hand via the action CLI), dispatch the goal:
185
+ ros2 action send_goal /openral/execute_rskill openral_msgs/action/ExecuteRskill \
186
+ "{rskill_id: 'OpenRAL/rskill-moveit-joints', deadline_s: 30.0, prompt: 'move to home'}"
187
+ ```
188
+
189
+ ## Limitations / Roadmap
190
+
191
+ - **Goal defaults live in the manifest.** The LLM overrides `joint.positions`
192
+ via `goal_params_json`; planner settings and joint names are inherited from
193
+ `default_goal_json`. Cross-embodiment retargeting is a manifest copy with the
194
+ correct `joint.joint_names` + `request.group_name`.
195
+ - **OpenRAL safety supervisor does not do collision checking.** We
196
+ trust MoveIt's internal FCL pass. The per-joint envelope check still
197
+ runs per waypoint. Collision checking inside the OpenRAL kernel is a
198
+ separate ADR + multi-PR effort.
199
+ - **No velocity / jerk bound at the supervisor.** A planner emitting a
200
+ rough trajectory would actuate today; the existing supervisor only
201
+ checks per-joint position envelope. Tracked separately.
202
+
203
+ ## License
204
+
205
+ The rSkill package itself (this manifest + README) is **Apache-2.0**.
206
+ The wrapped MoveIt code (`moveit_msgs` IDL, `moveit2` planners) is
207
+ **BSD-3-Clause** and lives outside this repository — installed via
208
+ `ros-${ROS_DISTRO}-moveit`. Per
209
+ [ADR-0012](../../docs/adr/0012-open-core-licensing.md) both postures
210
+ are commercial-use-permissive.
211
+
212
+ ## See also
213
+
214
+ - [ADR-0052 — MoveIt goal-builder library + rskill-moveit-* rename](../../docs/adr/0052-moveit-goal-builder-library.md)
215
+ - [ADR-0024 — ROS-wrapped rSkills](../../docs/adr/0024-ros-wrapped-rskills.md)
216
+ - [`openral_rskill.ros_action_rskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py) — engine source
217
+ - [`openral_rskill.joint_goal_rskill`](../../python/rskill/src/openral_rskill/joint_goal_rskill.py) — goal-lowering adapter
218
+ - [`rskills/rskill-moveit-eef-pose/`](../rskill-moveit-eef-pose/) — sibling Cartesian end-effector pose wrapper
219
+ - [`rskills/rskill-moveit-look-at/`](../rskill-moveit-look-at/) — sibling camera-aiming wrapper
220
+ - [`rskills/rskill-nav2-navigate-to-pose/`](../rskill-nav2-navigate-to-pose/) — sibling Nav2 wrapper (result-only mode)
221
+ - [CLAUDE.md §3 — Architecture Discipline](../../CLAUDE.md)
rskill.yaml ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rSkill manifest — moveit-joints: plan to a joint configuration (ADR-0024/0052)
2
+ #
3
+ # Wraps `moveit_msgs/action/MoveGroup`. `ros_integration.goal_builder: joint`
4
+ # selects the `JointGoalRskill` adapter, which consumes a `joint` block
5
+ # (`joint_names` + `positions`) and lowers it into a MoveGroup
6
+ # `joint_constraints` goal — the clean, LLM-facing form (positions, not
7
+ # hand-written constraint dicts). Renamed from `openral/rskill-moveit-plan-arm`
8
+ # under ADR-0052 (the `rskill-moveit-*` family: -joints / -eef-pose / -look-at
9
+ # over one ROSActionRskill engine).
10
+ #
11
+ # Collision posture: MoveIt's internal FCL planner does self + planning-scene
12
+ # (world) collision checking at plan time. `plan_only: true` so OpenRAL's
13
+ # per-waypoint replay through /openral/candidate_action stays the only
14
+ # actuation path (the safety kernel checks every step); move_group never drives
15
+ # its own controllers. `chunk_size: 1` so the supervisor sees every waypoint.
16
+
17
+ schema_version: "0.1"
18
+ name: "OpenRAL/rskill-moveit-joints"
19
+ version: "0.1.0"
20
+ license: "apache-2.0"
21
+ role: "s1"
22
+ kind: "ros_action"
23
+
24
+ # Every arm embodiment OpenRAL ships a robot.yaml for. Whether a deployment can
25
+ # resolve this skill depends on whether a matching MoveIt config is running and
26
+ # whether the `joint` block below is correct for that robot's planning group.
27
+ # The Franka `panda_arm` group is the canonical example; for other embodiments
28
+ # copy this manifest and edit `joint.joint_names` + `request.group_name`.
29
+ embodiment_tags:
30
+ - "franka_panda"
31
+ - "ur5e"
32
+ - "ur10e"
33
+ - "so100_follower"
34
+ - "openarm"
35
+ - "rizon4"
36
+ - "sawyer"
37
+ - "widowx"
38
+
39
+ actuators_required:
40
+ - kind: "joint_position"
41
+ control_mode_semantics:
42
+ mode: "absolute"
43
+
44
+ # REQUIRED for kind: ros_action — one waypoint per Action chunk so the safety
45
+ # supervisor's per-row envelope check sees every commanded position.
46
+ chunk_size: 1
47
+
48
+ # Planning is slow on the first call (MoveIt typically 0.5–2 s on a clean
49
+ # scene); subsequent step() calls just dequeue cached waypoints.
50
+ latency_budget:
51
+ per_chunk_ms: 2000.0
52
+
53
+ # ADR-0022 — surfaced to the Reasoner LLM tool palette.
54
+ description: >
55
+ Plan and execute a collision-free motion to a target JOINT configuration via
56
+ MoveIt's MoveGroup (self + planning-scene collision checked). Provide one
57
+ target angle per planning-group joint. Defaults target the Franka Panda home
58
+ pose; override the manifest to retarget. Use when you have a joint-space goal
59
+ (e.g. a policy's in-distribution starting pose); for a Cartesian end-effector
60
+ target use rskill-moveit-eef-pose instead.
61
+ actions:
62
+ - "reach"
63
+ objects: []
64
+ scenes: []
65
+
66
+ # Provenance — ROS-wrapped rSkill; paper_url carries the upstream link.
67
+ paper_url: "https://moveit.picknik.ai/"
68
+
69
+ ros_integration:
70
+ package: "moveit_msgs"
71
+ interface_type: "MoveGroup"
72
+ interface_name: "/move_action"
73
+ # `MoveGroup.Result.planned_trajectory.joint_trajectory` is reordered into the
74
+ # host RobotDescription.joints order before per-waypoint chunk emission.
75
+ result_trajectory_field: "planned_trajectory.joint_trajectory"
76
+ # ADR-0052 — selects the JointGoalRskill goal-lowering adapter: the `joint`
77
+ # block below (joint_names + positions) is lowered into
78
+ # `request.goal_constraints[0].joint_constraints` at configure time.
79
+ goal_builder: "joint"
80
+ # Franka Panda home pose (panda_arm group). For other embodiments override
81
+ # `joint.joint_names` + `request.group_name`.
82
+ default_goal_json: |
83
+ {
84
+ "joint": {
85
+ "joint_names": ["panda_joint1", "panda_joint2", "panda_joint3", "panda_joint4", "panda_joint5", "panda_joint6", "panda_joint7"],
86
+ "positions": [0.0, -0.785, 0.0, -2.356, 0.0, 1.571, 0.785],
87
+ "position_tolerance_rad": 0.001
88
+ },
89
+ "request": {
90
+ "group_name": "panda_arm",
91
+ "num_planning_attempts": 5,
92
+ "allowed_planning_time": 5.0,
93
+ "max_velocity_scaling_factor": 0.3,
94
+ "max_acceleration_scaling_factor": 0.3,
95
+ "goal_constraints": []
96
+ },
97
+ "planning_options": {
98
+ "plan_only": true,
99
+ "replan": false
100
+ }
101
+ }
102
+ ros_dependencies:
103
+ - "ros-${ROS_DISTRO}-moveit"
104
+ - "ros-${ROS_DISTRO}-moveit-resources-panda-moveit-config"
105
+
106
+ # ADR-0026 — per-skill JSON Schema surfaced to the Reasoner LLM tool palette.
107
+ # The LLM overrides only `joint.positions` (one per planning-group joint, in
108
+ # the manifest's joint_names order); planner / tolerance defaults are inherited.
109
+ goal_params_schema:
110
+ type: object
111
+ description: >
112
+ Target joint positions for MoveIt's MoveGroup against the planning group.
113
+ Provide one entry per joint in the manifest's joint_names order.
114
+ properties:
115
+ joint:
116
+ type: object
117
+ properties:
118
+ positions:
119
+ type: array
120
+ items:
121
+ type: number
122
+ minItems: 7
123
+ maxItems: 7
124
+ description: "Target joint angle (rad) per planning-group joint."
125
+ required:
126
+ - positions
127
+ required:
128
+ - joint