AdrianLlopart commited on
Commit
9722d7d
·
verified ·
1 Parent(s): 0871542

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

Browse files
Files changed (2) hide show
  1. README.md +225 -0
  2. rskill.yaml +133 -0
README.md ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-eef-pose
13
+
14
+ > **OpenRAL rSkill** — wraps the upstream `moveit_msgs/action/MoveGroup`
15
+ > action server so the Reasoner can dispatch a collision-free Cartesian
16
+ > end-effector pose goal (position + orientation) through the same
17
+ > `ExecuteRskill` path used by VLA skills. No model weights — the manifest
18
+ > is the entire artefact. New under
19
+ > [ADR-0052](../../docs/adr/0052-moveit-goal-builder-library.md)'s
20
+ > `rskill-moveit-*` family.
21
+
22
+ This package uses `kind: ros_action` (see
23
+ [ADR-0024](../../docs/adr/0024-ros-wrapped-rskills.md)) — a discriminator
24
+ on `RSkillManifest.kind` that selects the
25
+ [`ROSActionRskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)
26
+ engine at resolve time, with `ros_integration.goal_builder: pose` selecting
27
+ the [`PoseGoalRskill`](../../python/rskill/src/openral_rskill/pose_goal_rskill.py)
28
+ goal-lowering adapter (ADR-0052). It is the generic Cartesian sibling of
29
+ [`rskill-moveit-look-at`](../rskill-moveit-look-at/) (gaze is a computed-pose
30
+ specialisation) and of [`rskill-moveit-joints`](../rskill-moveit-joints/)
31
+ (joint-space). The engine constructs an `rclpy.action.ActionClient` on the
32
+ host `RskillRunnerNode`, sends one goal built from the lowered `pose` block,
33
+ awaits the result, and replays the returned `trajectory_msgs/JointTrajectory`
34
+ one waypoint per `step()` call onto `/openral/candidate_action` so the safety
35
+ supervisor still applies its per-joint envelope check to every commanded
36
+ position.
37
+
38
+ ## What this skill does
39
+
40
+ Plans and executes a collision-free motion that brings a chosen end-effector
41
+ link to a target 6-DOF Cartesian pose (position + orientation) via MoveIt's
42
+ `MoveGroup` action. The goal is authored as a `pose` block — target `position`
43
+ ([x, y, z] metres in `pose.frame_id`) plus a quaternion `orientation` in
44
+ `pose.quaternion_order` (default `xyzw`) — which `PoseGoalRskill` lowers into
45
+ MoveGroup position + orientation constraints. The default goal targets the
46
+ Franka demo (`panda_arm` group, `panda_hand` link in `panda_link0`). Use it
47
+ when you have a Cartesian end-effector target such as a pre-grasp pose.
48
+
49
+ | Field | Value |
50
+ | --- | --- |
51
+ | Actions | `reach` |
52
+ | Objects | _none_ — no per-object specialisation |
53
+ | Scenes | _none_ — the wrapped planner does its own collision check against the live `/planning_scene` |
54
+ | Embodiment | `franka_panda` (default goal); other arm tags listed in the manifest for capability filtering |
55
+
56
+ ## How it works
57
+
58
+ `ROSActionRskill` is a thin `rSkillBase` shim around an `ActionClient`, and
59
+ `goal_builder: pose` lowers the LLM-facing `pose` block into the MoveGroup
60
+ goal:
61
+
62
+ 1. `_configure_impl` lazy-imports `moveit_msgs.action.MoveGroup`, opens
63
+ an `ActionClient` on `/move_action` from the
64
+ `RskillRunnerNode`-supplied node handle, and parses
65
+ `ros_integration.default_goal_json` once.
66
+ 2. `PoseGoalRskill` pops the `pose` block and lowers it into
67
+ `request.goal_constraints[0]` — a `position_constraints` entry (a small
68
+ bounding region of `pose.position_tolerance_m` around the target point) and
69
+ an `orientation_constraints` entry (the target quaternion with
70
+ `pose.orientation_tolerance_rad`), both attached to `pose.link_name` in
71
+ `pose.frame_id`. The orientation quaternion is interpreted in
72
+ `pose.quaternion_order` (default `xyzw`). If `pose.tool_frame` is set, the
73
+ adapter looks up the `link_name ← tool_frame` offset over TF2 (the only
74
+ source of frames) and composes it so the target is expressed for the TCP /
75
+ tool frame; omit it to constrain `pose.link_name` directly.
76
+ 3. On the first `_step_impl(world_state)` call the engine builds the
77
+ `MoveGroup.Goal` from the lowered dict, sends it, polls the goal-accept +
78
+ result futures, extracts
79
+ `result.planned_trajectory.joint_trajectory`, reorders its `joint_names`
80
+ into the host `RobotDescription.joints` order, and returns waypoint 0 as a
81
+ 1-row `Action(JOINT_POSITION, …)`.
82
+ 4. Each subsequent `_step_impl` returns the next cached waypoint; after the
83
+ last one it raises `ROSRskillGoalSatisfied`, which the runner catches to
84
+ close the `ExecuteRskill` goal with `success=True`.
85
+
86
+ The LLM overrides the `pose` block's `position` + `orientation`; planner
87
+ settings are inherited from `default_goal_json`. `plan_only: true` so MoveGroup
88
+ never drives its own controllers — OpenRAL's per-waypoint replay is the only
89
+ actuation path.
90
+
91
+ ### Observation → action contract
92
+
93
+ Input is the ADR-0026 `goal_params_json` `pose` block; output is a joint
94
+ trajectory replayed one waypoint per `step()` as a 1-row `JOINT_POSITION`
95
+ `Action` chunk.
96
+
97
+ ```json
98
+ {"pose": {"position": [0.4, 0.0, 0.5], "orientation": [0.0, 0.0, 0.0, 1.0]}}
99
+ ```
100
+
101
+ | Direction | Key | Shape | Notes |
102
+ | --- | --- | --- | --- |
103
+ | in | `pose.position` | `[x, y, z]` metres in `pose.frame_id` | LLM-overridable |
104
+ | in | `pose.orientation` | unit quaternion in `pose.quaternion_order` (default `xyzw`) | LLM-overridable |
105
+ | out | per-waypoint `Action` | `joint_targets=[[n_dof floats]]`, `horizon=1`, `is_terminal=False` | One chunk per `step()` until completion is signalled by exception |
106
+
107
+ Why one row per chunk (`chunk_size: 1` is schema-enforced for
108
+ `kind: ros_action`): the OpenRAL safety supervisor only validates row 0
109
+ of every `ActionChunk` today
110
+ ([`supervisor_node.py`](../../packages/openral_safety/openral_safety/supervisor_node.py)).
111
+ Packing the full trajectory as one chunk with `horizon=N` would let
112
+ waypoints 1..N actuate unchecked.
113
+
114
+ ## How it was trained / Upstream provenance
115
+
116
+ Nothing is trained — this rSkill wraps the upstream MoveIt motion planner and
117
+ lowers the target pose into constraints analytically.
118
+
119
+ | Field | Value |
120
+ | --- | --- |
121
+ | Upstream | [`moveit_msgs/action/MoveGroup`](https://github.com/moveit/moveit_msgs/blob/master/action/MoveGroup.action) (BSD-3-Clause) |
122
+ | Planner library | [MoveIt 2](https://moveit.picknik.ai/) (BSD-3-Clause) |
123
+ | Collision check | FCL via MoveIt's `PlanningScene` (run during planning) |
124
+ | Wrapped artefact | rSkill manifest + README — no weights, no preprocessor JSONs |
125
+
126
+ ## Supported robots / embodiments
127
+
128
+ | Robot | Embodiment tag | Status | Notes |
129
+ | --- | --- | --- | --- |
130
+ | Franka Panda | `franka_panda` | validated | default goal targets `panda_arm`, `panda_hand` link in `panda_link0` |
131
+ | Universal Robots UR5e | `ur5e` | experimental | needs a `pose.link_name` / `request.group_name` override (`"manipulator"`, `tool0`) |
132
+ | Universal Robots UR10e | `ur10e` | experimental | same as UR5e |
133
+ | SO-100 follower | `so100_follower` | experimental | needs a MoveIt config and link/group override |
134
+ | OpenArm | `openarm` | experimental | bi-manual — choose `left_arm` or `right_arm` group |
135
+ | Flexiv Rizon4 | `rizon4` | experimental | upstream MoveIt config exists; manifest override needed |
136
+ | Rethink Sawyer | `sawyer` | experimental | upstream MoveIt config exists |
137
+ | Trossen WidowX | `widowx` | experimental | upstream MoveIt config exists |
138
+
139
+ Listed `embodiment_tags` only gate which robots see this skill in the
140
+ Reasoner's tool palette; actual resolution depends on `move_group` being up for
141
+ that robot with a `pose` block targeting a valid link in its planning group.
142
+
143
+ ## Sensors required / Observation contract
144
+
145
+ This skill consumes nothing through OpenRAL's sensor pipeline. MoveIt's own
146
+ subscriptions handle planning; when `pose.tool_frame` is set the adapter also
147
+ reads the `link_name ← tool_frame` offset from TF:
148
+
149
+ | Source | Topic / contract | Why it's needed |
150
+ | --- | --- | --- |
151
+ | Joint state | `/joint_states` | Plan from the live start state |
152
+ | Planning scene | `/planning_scene` (or `/monitored_planning_scene`) | Self- and environment-collision check |
153
+ | TF | `/tf`, `/tf_static` | Resolve goal pose / link frames; look up `pose.tool_frame` offset |
154
+
155
+ ## Manifest summary
156
+
157
+ | Field | Value |
158
+ | --- | --- |
159
+ | `name` | `OpenRAL/rskill-moveit-eef-pose` |
160
+ | `version` | `0.1.0` |
161
+ | `license` | `apache-2.0` |
162
+ | `kind` | `ros_action` |
163
+ | `role` | `s1` |
164
+ | `actions` | `[reach]` |
165
+ | `chunk_size` | `1` (schema-enforced for `kind: ros_action`) |
166
+ | `latency_budget.per_chunk_ms` | `2000` (planning latency) |
167
+ | `ros_integration.package` | `moveit_msgs` |
168
+ | `ros_integration.interface_type` | `MoveGroup` |
169
+ | `ros_integration.interface_name` | `/move_action` |
170
+ | `ros_integration.goal_builder` | `pose` (selects `PoseGoalRskill`) |
171
+ | `ros_integration.result_trajectory_field` | `planned_trajectory.joint_trajectory` |
172
+ | `commercial_use_allowed` | `true` (apache-2.0) |
173
+
174
+ Full schema:
175
+ [`openral_core.schemas.RSkillManifest`](../../python/core/src/openral_core/schemas.py).
176
+
177
+ ## Quick start
178
+
179
+ ```python
180
+ from openral_rskill.loader import rSkill
181
+ pkg = rSkill.from_yaml("rskills/rskill-moveit-eef-pose/rskill.yaml")
182
+ print(pkg.manifest.name, pkg.manifest.ros_integration.goal_builder)
183
+ ```
184
+
185
+ End-to-end, with a real MoveIt launch up:
186
+
187
+ ```bash
188
+ # 1. Bring up MoveIt for your robot (example: Panda)
189
+ ros2 launch moveit_resources_panda_moveit_config demo.launch.py
190
+
191
+ # 2. Dispatch a Cartesian end-effector pose goal:
192
+ ros2 action send_goal /openral/execute_rskill openral_msgs/action/ExecuteRskill \
193
+ "{rskill_id: 'OpenRAL/rskill-moveit-eef-pose', deadline_s: 30.0, prompt: 'move to pre-grasp',
194
+ goal_params_json: '{\"pose\": {\"position\": [0.4, 0.0, 0.5], \"orientation\": [0.0, 0.0, 0.0, 1.0]}}'}"
195
+ ```
196
+
197
+ ## Limitations / Roadmap
198
+
199
+ - **Reachability is the planner's call.** A pose outside the arm's dexterous
200
+ workspace simply fails to plan — there is no base-repositioning fallback
201
+ here (that is the navigate rung of the ladder, ADR-0044 Phase 4).
202
+ - **OpenRAL safety supervisor does not do collision checking.** We trust
203
+ MoveIt's internal FCL pass; the per-joint envelope check still runs per
204
+ waypoint. Kernel-side collision checking is a separate ADR + multi-PR effort.
205
+ - **No velocity / jerk bound at the supervisor.** Same posture as
206
+ `rskill-moveit-joints`: the per-joint position envelope runs per
207
+ waypoint; richer bounds are tracked separately.
208
+
209
+ ## License
210
+
211
+ The rSkill package itself (this manifest + README) is **Apache-2.0**. The
212
+ wrapped MoveIt code (`moveit_msgs` IDL, `moveit2` planners) is **BSD-3-Clause**
213
+ and lives outside this repository — installed via `ros-${ROS_DISTRO}-moveit`.
214
+ Per [ADR-0012](../../docs/adr/0012-open-core-licensing.md) both postures are
215
+ commercial-use-permissive.
216
+
217
+ ## See also
218
+
219
+ - [ADR-0052 — MoveIt goal-builder library + rskill-moveit-* rename](../../docs/adr/0052-moveit-goal-builder-library.md)
220
+ - [ADR-0024 — ROS-wrapped rSkills](../../docs/adr/0024-ros-wrapped-rskills.md)
221
+ - [`openral_rskill.ros_action_rskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py) — engine source
222
+ - [`openral_rskill.pose_goal_rskill`](../../python/rskill/src/openral_rskill/pose_goal_rskill.py) — goal-lowering adapter
223
+ - [`rskills/rskill-moveit-joints/`](../rskill-moveit-joints/) — sibling joint-space MoveIt wrapper
224
+ - [`rskills/rskill-moveit-look-at/`](../rskill-moveit-look-at/) — sibling camera-aiming wrapper
225
+ - [CLAUDE.md §3 — Architecture Discipline](../../CLAUDE.md)
rskill.yaml ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rSkill manifest — moveit-eef-pose: plan to a Cartesian end-effector pose (ADR-0052)
2
+ #
3
+ # Wraps `moveit_msgs/action/MoveGroup`. `ros_integration.goal_builder: pose`
4
+ # selects the `PoseGoalRskill` adapter, which consumes a `pose` block (target
5
+ # position + quaternion orientation for a constrained link) and lowers it into
6
+ # MoveGroup position + orientation constraints. The generic Cartesian sibling
7
+ # of rskill-moveit-look-at (gaze is a computed-pose specialisation); new under
8
+ # ADR-0052's rskill-moveit-* family.
9
+ #
10
+ # Collision posture: MoveIt's internal FCL planner does self + planning-scene
11
+ # (world) collision checking at plan time. `plan_only: true` so OpenRAL's
12
+ # per-waypoint replay through /openral/candidate_action stays the only
13
+ # actuation path (the safety kernel checks every step). `chunk_size: 1` so the
14
+ # supervisor sees every waypoint.
15
+
16
+ schema_version: "0.1"
17
+ name: "OpenRAL/rskill-moveit-eef-pose"
18
+ version: "0.1.0"
19
+ license: "apache-2.0"
20
+ role: "s1"
21
+ kind: "ros_action"
22
+
23
+ # Any arm embodiment with a MoveIt configuration. The default goal targets the
24
+ # Franka demo (panda_arm group, panda_hand link); other embodiments override
25
+ # `request.group_name` + `pose.link_name` / `pose.frame_id`.
26
+ embodiment_tags:
27
+ - "franka_panda"
28
+ - "ur5e"
29
+ - "ur10e"
30
+ - "so100_follower"
31
+ - "openarm"
32
+ - "rizon4"
33
+ - "sawyer"
34
+ - "widowx"
35
+
36
+ actuators_required:
37
+ - kind: "joint_position"
38
+ control_mode_semantics:
39
+ mode: "absolute"
40
+
41
+ # REQUIRED for kind: ros_action — one waypoint per Action chunk so the safety
42
+ # supervisor's per-row envelope check sees every commanded position.
43
+ chunk_size: 1
44
+
45
+ latency_budget:
46
+ per_chunk_ms: 2000.0
47
+
48
+ # ADR-0022 — surfaced to the Reasoner LLM tool palette.
49
+ description: >
50
+ Plan and execute a collision-free motion that brings the end-effector link to
51
+ a target 6-DOF Cartesian pose (position + orientation) via MoveIt's MoveGroup
52
+ (self + planning-scene collision checked). Use when you have a Cartesian
53
+ end-effector goal (e.g. a pre-grasp pose); for a joint-space goal use
54
+ rskill-moveit-joints, and to aim a camera at a point use rskill-moveit-look-at.
55
+ actions:
56
+ - "reach"
57
+ objects: []
58
+ scenes: []
59
+
60
+ # Provenance — ROS-wrapped rSkill; paper_url carries the upstream link.
61
+ paper_url: "https://moveit.picknik.ai/"
62
+
63
+ ros_integration:
64
+ package: "moveit_msgs"
65
+ interface_type: "MoveGroup"
66
+ interface_name: "/move_action"
67
+ result_trajectory_field: "planned_trajectory.joint_trajectory"
68
+ # ADR-0052 — selects the PoseGoalRskill goal-lowering adapter: the `pose`
69
+ # block below is lowered into `request.goal_constraints[0]` (position +
70
+ # orientation constraints) on the first step. Set `pose.tool_frame` to express
71
+ # the target for a TCP/tool frame (TF-looked-up link←tool offset); omit it to
72
+ # constrain `pose.link_name` directly.
73
+ goal_builder: "pose"
74
+ default_goal_json: |
75
+ {
76
+ "pose": {
77
+ "frame_id": "panda_link0",
78
+ "link_name": "panda_hand",
79
+ "position": [0.4, 0.0, 0.5],
80
+ "orientation": [0.0, 0.0, 0.0, 1.0],
81
+ "quaternion_order": "xyzw",
82
+ "position_tolerance_m": 0.01,
83
+ "orientation_tolerance_rad": 0.05
84
+ },
85
+ "request": {
86
+ "group_name": "panda_arm",
87
+ "num_planning_attempts": 5,
88
+ "allowed_planning_time": 5.0,
89
+ "max_velocity_scaling_factor": 0.3,
90
+ "max_acceleration_scaling_factor": 0.3,
91
+ "goal_constraints": []
92
+ },
93
+ "planning_options": {
94
+ "plan_only": true,
95
+ "replan": false
96
+ }
97
+ }
98
+ ros_dependencies:
99
+ - "ros-${ROS_DISTRO}-moveit"
100
+ - "ros-${ROS_DISTRO}-moveit-resources-panda-moveit-config"
101
+
102
+ # ADR-0026 — per-skill JSON Schema surfaced to the Reasoner LLM tool palette.
103
+ # The LLM overrides the pose block's position/orientation; planner settings are
104
+ # inherited. orientation is a quaternion in pose.quaternion_order (default xyzw).
105
+ goal_params_schema:
106
+ type: object
107
+ description: >
108
+ Target 6-DOF Cartesian pose for the end-effector link. position is
109
+ [x, y, z] metres in pose.frame_id; orientation is a unit quaternion whose
110
+ component order is pose.quaternion_order (default "xyzw").
111
+ properties:
112
+ pose:
113
+ type: object
114
+ properties:
115
+ position:
116
+ type: array
117
+ items:
118
+ type: number
119
+ minItems: 3
120
+ maxItems: 3
121
+ description: "Target [x, y, z] metres in pose.frame_id."
122
+ orientation:
123
+ type: array
124
+ items:
125
+ type: number
126
+ minItems: 4
127
+ maxItems: 4
128
+ description: "Unit quaternion in pose.quaternion_order (default xyzw)."
129
+ required:
130
+ - position
131
+ - orientation
132
+ required:
133
+ - pose