AdrianLlopart commited on
Commit
b520531
Β·
verified Β·
1 Parent(s): db31f54

chore: publish rSkill OpenRAL/rskill-nav2-navigate-to-pose v0.1.0

Browse files
Files changed (2) hide show
  1. README.md +182 -0
  2. rskill.yaml +195 -0
README.md ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - OpenRAL
4
+ - rskill
5
+ - ros2
6
+ - nav2
7
+ license: apache-2.0
8
+ language:
9
+ - en
10
+ ---
11
+
12
+ # rskill-nav2-navigate-to-pose
13
+
14
+ > **OpenRAL rSkill** β€” wraps the upstream `nav2_msgs/action/NavigateToPose`
15
+ > action server as an OpenRAL rSkill so the Reasoner can dispatch
16
+ > mobile-base navigation through the same `ExecuteSkill` path used by
17
+ > VLA skills. **Result-only mode** β€” Nav2's behaviour tree publishes
18
+ > `/cmd_vel` directly to the base controller; no `Action` chunk flows
19
+ > through OpenRAL's safety supervisor.
20
+
21
+ This package uses `kind: ros_action` (see
22
+ [ADR-0024](../../docs/adr/0024-ros-wrapped-rskills.md)) with
23
+ `ros_integration.result_trajectory_field: null` to put the
24
+ [`ROSActionRskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py)
25
+ adapter into result-only mode: it sends the goal, awaits the action
26
+ result, and raises `ROSRskillGoalSatisfied` on success. Compare with
27
+ the sibling [`rskill-moveit-joints`](../rskill-moveit-joints/)
28
+ skill, which sets `result_trajectory_field` and replays a joint
29
+ trajectory one waypoint at a time.
30
+
31
+ ## What this skill does
32
+
33
+ Navigates the mobile base to a fixed `geometry_msgs/PoseStamped` target
34
+ in the `map` frame via Nav2's `NavigateToPose` action. The default goal
35
+ points at the map origin; override `ros_integration.default_goal_json`
36
+ in a per-deployment copy for real targets.
37
+
38
+ | Field | Value |
39
+ | --- | --- |
40
+ | Actions | `navigate` |
41
+ | Objects | _none_ |
42
+ | Scenes | `indoor` |
43
+ | Embodiment | mobile-manipulator and similar mobile-base embodiments |
44
+
45
+ ## How it works
46
+
47
+ `ROSActionRskill` is a thin `rSkillBase` shim around an `ActionClient`:
48
+
49
+ 1. `_configure_impl` lazy-imports `nav2_msgs.action.NavigateToPose`,
50
+ opens an `ActionClient` on `/navigate_to_pose` from the
51
+ `RskillRunnerNode`-supplied node handle, and parses
52
+ `ros_integration.default_goal_json` once.
53
+ 2. On the first `_step_impl(world_state)` call the adapter sends the
54
+ goal, polls the goal-accept + result futures while the host node's
55
+ main rclpy spin services callbacks, and β€” because
56
+ `result_trajectory_field is None` β€” raises
57
+ `ROSRskillGoalSatisfied` immediately on success. The runner catches
58
+ it specifically and closes the `ExecuteSkill` goal with
59
+ `success=True`.
60
+
61
+ ### Observation β†’ action contract (result-only mode)
62
+
63
+ | Direction | Key | Shape | Notes |
64
+ | --- | --- | --- | --- |
65
+ | in | `world_state.joint_state` | unused | Nav2 consumes its own sensor topics (laser, odom, camera) |
66
+ | out | _none via OpenRAL_ | β€” | Nav2's behaviour tree publishes `/cmd_vel` directly to the base controller |
67
+
68
+ **Safety implication.** No `ActionChunk` is published on
69
+ `/openral/candidate_action`, so the OpenRAL safety supervisor does NOT
70
+ see Nav2's velocity commands. Collision avoidance relies entirely on
71
+ Nav2's costmap + behaviour tree. The follow-up that brings velocity
72
+ streams under the supervisor's envelope is tracked in ADR-0024's
73
+ Β§Out-of-scope and depends on (a) a mobile-base HAL declaring
74
+ `body_twist` in `supported_control_modes` (none exist in-tree today),
75
+ and (b) a velocity / jerk envelope landing in the supervisor (it
76
+ currently checks per-joint position only).
77
+
78
+ ## How it was trained / Upstream provenance
79
+
80
+ Nothing is trained β€” this rSkill wraps the upstream Nav2 stack.
81
+
82
+ | Field | Value |
83
+ | --- | --- |
84
+ | Upstream | [`nav2_msgs/action/NavigateToPose`](https://github.com/ros-navigation/navigation2/blob/main/nav2_msgs/action/NavigateToPose.action) (Apache-2.0) |
85
+ | Planner library | [Nav2](https://docs.nav2.org/) (Apache-2.0) |
86
+ | Costmap / behaviour tree | Nav2's own subsystems β€” see Nav2 docs |
87
+ | Wrapped artefact | rSkill manifest + README β€” no weights |
88
+
89
+ ## Supported robots / embodiments
90
+
91
+ | Robot | Embodiment tag | Status | Notes |
92
+ | --- | --- | --- | --- |
93
+ | Panda Mobile | `panda_mobile` | experimental | no HAL accepting `body_twist` ships in-tree yet; resolution depends on the deployment wiring its own mobile-base HAL or running Nav2's controllers against a sim |
94
+ | Generic mobile-manipulator | `mobile_manipulator` | experimental | union tag β€” palette filter only |
95
+
96
+ A real mobile-base HAL (Turtlebot 4, Clearpath Jackal, etc.) is the
97
+ prerequisite for full end-to-end execution β€” tracked as a separate
98
+ issue.
99
+
100
+ ## Sensors required / Observation contract
101
+
102
+ This skill consumes nothing through OpenRAL's sensor pipeline. Nav2's
103
+ own subscriptions handle:
104
+
105
+ | Source | Topic | Why Nav2 needs it |
106
+ | --- | --- | --- |
107
+ | Laser scan | `/scan` (or per-deployment remap) | Costmap obstacle layer |
108
+ | Odometry | `/odom` | Localisation + behaviour-tree feedback |
109
+ | TF | `/tf`, `/tf_static` | Resolve goal pose in the `map` frame |
110
+ | Map | `/map` (or AMCL initial pose) | Global planner |
111
+
112
+ If your deployment uses a non-default topic remap, surface it on the
113
+ Nav2 launch β€” the wrapped action's contract is intact.
114
+
115
+ ## Manifest summary
116
+
117
+ | Field | Value |
118
+ | --- | --- |
119
+ | `name` | `OpenRAL/rskill-nav2-navigate-to-pose` |
120
+ | `version` | `0.1.0` |
121
+ | `license` | `apache-2.0` |
122
+ | `kind` | `ros_action` |
123
+ | `role` | `s1` |
124
+ | `actions` | `[navigate]` |
125
+ | `chunk_size` | `1` (schema-enforced for `kind: ros_action`) |
126
+ | `latency_budget.per_chunk_ms` | `60000` (navigation is long-horizon; the adapter waits Γ—5 of this on the action result) |
127
+ | `ros_integration.package` | `nav2_msgs` |
128
+ | `ros_integration.interface_type` | `NavigateToPose` |
129
+ | `ros_integration.interface_name` | `/navigate_to_pose` |
130
+ | `ros_integration.result_trajectory_field` | _omitted β†’ result-only mode_ |
131
+ | `commercial_use_allowed` | `true` (apache-2.0) |
132
+
133
+ Full schema:
134
+ [`openral_core.schemas.RSkillManifest`](../../python/core/src/openral_core/schemas.py).
135
+
136
+ ## Quick start
137
+
138
+ ```python
139
+ from openral_rskill.loader import rSkill
140
+ pkg = rSkill.from_yaml("rskills/rskill-nav2-navigate-to-pose/rskill.yaml")
141
+ print(pkg.manifest.name, pkg.manifest.kind, pkg.manifest.ros_integration.interface_name)
142
+ ```
143
+
144
+ End-to-end, with a real Nav2 launch up (e.g. against a Gazebo /
145
+ Turtlebot4 sim):
146
+
147
+ ```bash
148
+ # 1. Bring up Nav2 for your mobile-base sim or robot
149
+ ros2 launch nav2_bringup tb4_simulation_launch.py
150
+
151
+ # 2. Bring up the OpenRAL runner against that embodiment
152
+ ros2 launch openral_rskill_ros skill_runner.launch.py robot:=panda_mobile
153
+
154
+ # 3. From the Reasoner (or by hand), dispatch the goal:
155
+ ros2 action send_goal /openral/execute_skill openral_msgs/action/ExecuteSkill \
156
+ "{rskill_id: 'OpenRAL/rskill-nav2-navigate-to-pose', deadline_s: 120.0, prompt: 'go to map origin'}"
157
+ ```
158
+
159
+ ## Limitations / Roadmap
160
+
161
+ - **Velocity stream bypasses the OpenRAL safety supervisor.** Nav2
162
+ publishes `/cmd_vel` directly. See ADR-0024 Β§Out-of-scope.
163
+ - **Goal hard-coded in the manifest.** v1 ships one goal per
164
+ manifest; structured-prompt support is the next ADR.
165
+ - **No mobile-base HAL in-tree.** Until one lands, the skill resolves
166
+ only inside deployments that ship their own mobile-base wiring.
167
+
168
+ ## License
169
+
170
+ The rSkill package itself (this manifest + README) is **Apache-2.0**.
171
+ The wrapped Nav2 code (`nav2_msgs` IDL, `navigation2` planners) is
172
+ **Apache-2.0** and lives outside this repository β€” installed via
173
+ `ros-${ROS_DISTRO}-nav2-bringup`. Per
174
+ [ADR-0012](../../docs/adr/0012-open-core-licensing.md) both postures
175
+ are commercial-use-permissive.
176
+
177
+ ## See also
178
+
179
+ - [ADR-0024 β€” ROS-wrapped rSkills](../../docs/adr/0024-ros-wrapped-rskills.md)
180
+ - [`openral_rskill.ros_action_rskill`](../../python/rskill/src/openral_rskill/ros_action_rskill.py) β€” adapter source
181
+ - [`rskills/rskill-moveit-joints/`](../rskill-moveit-joints/) β€” sibling MoveIt wrapper (trajectory mode)
182
+ - [CLAUDE.md Β§3 β€” Architecture Discipline](../../CLAUDE.md)
rskill.yaml ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rSkill manifest β€” Nav2 NavigateToPose (kind: ros_action, result-only)
2
+ #
3
+ # Wraps the upstream `nav2_msgs/action/NavigateToPose` action server as
4
+ # an OpenRAL rSkill. See `docs/adr/0024-ros-wrapped-rskills.md`.
5
+ #
6
+ # RESULT-ONLY MODE
7
+ # ----------------
8
+ # `ros_integration.result_trajectory_field` is omitted, which puts the
9
+ # adapter into result-only mode: it sends the goal, awaits the action
10
+ # result, and raises `ROSRskillGoalSatisfied` on success. NO `Action`
11
+ # chunk is emitted to `/openral/candidate_action`. Nav2's own behaviour
12
+ # tree publishes `cmd_vel` directly to the base controller, BYPASSING
13
+ # the OpenRAL safety supervisor for those velocity commands. Collision
14
+ # avoidance relies entirely on Nav2's costmap. The Out-of-scope section
15
+ # of ADR-0024 tracks the follow-up that brings velocity-stream safety
16
+ # under the supervisor's check (no in-tree HAL exposes `body_twist` in
17
+ # `supported_control_modes` today, so this is also blocked on a
18
+ # mobile-base HAL landing β€” issue tracked separately).
19
+ #
20
+ # ADR-0026 β€” per-dispatch structured parameters. The LLM tool palette
21
+ # surfaces this schema to the provider; the reasoner attaches the
22
+ # generated payload as ``goal_params_json`` on the ExecuteSkill goal;
23
+ # the wrapped-action adapter deep-merges it over the
24
+ # ``default_goal_json`` below at configure-time. Omit fields you want
25
+ # from the manifest default.
26
+
27
+ schema_version: "0.1"
28
+ name: "OpenRAL/rskill-nav2-navigate-to-pose"
29
+ version: "0.1.0"
30
+ license: "apache-2.0"
31
+ role: "s1"
32
+ kind: "ros_action"
33
+
34
+ # `mobile_base` is the canonical CLASS tag for any robot with a planar
35
+ # base + ``body_twist`` actuator (defined in ``openral_core.schemas``
36
+ # alongside the robot-specific tags). A robot must (a) declare this tag
37
+ # in its ``robots/<id>/robot.yaml`` and (b) be reachable by a running
38
+ # Nav2 stack on this deployment for the skill to actually resolve.
39
+ # Robot-specific tags (``panda_mobile`` etc.) intentionally do NOT
40
+ # appear here β€” Nav2's behaviour is base-agnostic, so the manifest
41
+ # stays generic and any mobile_base robot can target it.
42
+ embodiment_tags:
43
+ - "mobile_base"
44
+
45
+ # Body twist is what Nav2's behaviour-tree controllers ultimately
46
+ # publish on `/cmd_vel`. Declaring it here is the honest description of
47
+ # what the wrapped server commands; the OpenRAL ActionChunk path is NOT
48
+ # used (see result-only mode above).
49
+ actuators_required:
50
+ - kind: "body_twist"
51
+ control_mode_semantics:
52
+ mode: "absolute"
53
+
54
+ # `chunk_size: 1` is REQUIRED for kind: ros_action even in result-only
55
+ # mode (the schema validator enforces it uniformly across both modes).
56
+ chunk_size: 1
57
+
58
+ # Navigation is long-horizon β€” a full run can take 30+ seconds. The
59
+ # adapter waits Γ—5 of this as the action-result deadline, so allow
60
+ # enough headroom (60 s here β†’ 300 s wait ceiling).
61
+ latency_budget:
62
+ per_chunk_ms: 60000.0
63
+
64
+ # ADR-0022 β€” surfaced to the Reasoner LLM tool palette.
65
+ description: >
66
+ Navigate the mobile base to a target pose via Nav2's NavigateToPose.
67
+ Supports BOTH absolute and relative goals via pose.header.frame_id:
68
+ "map" = absolute world coordinates (drive to (3.5, 2.1)); "base_link"
69
+ = relative to current pose, used for turns / forward / back-up
70
+ (Nav2 transforms via tf2 on goal accept β€” the LLM does NOT need to
71
+ compose quaternions against the live pose). Result-only mode: Nav2
72
+ publishes cmd_vel directly; collision avoidance relies on its costmap.
73
+ actions:
74
+ - "navigate"
75
+ objects: []
76
+ scenes:
77
+ - "indoor"
78
+
79
+ # Provenance β€” required by the rSkill doc validator's publish gate.
80
+ # See sibling moveit-plan-arm manifest for the rationale on using
81
+ # `paper_url` rather than `source_repo` for ROS-wrapped rSkills.
82
+ paper_url: "https://docs.nav2.org/"
83
+
84
+ ros_integration:
85
+ package: "nav2_msgs"
86
+ interface_type: "NavigateToPose"
87
+ interface_name: "/navigate_to_pose"
88
+ # OMITTED β†’ result-only mode (the adapter awaits the result and
89
+ # raises ROSRskillGoalSatisfied without emitting any Action chunk).
90
+ result_trajectory_field: null
91
+ # Default = "stay where you are" (identity offset in base_link).
92
+ # Picked so a misconfigured dispatch is a no-op (Nav2 reports
93
+ # "Reached the goal" without moving) rather than a wild drive to
94
+ # the map origin. The reasoner MUST override at least one of
95
+ # ``pose.position.{x,y}`` or ``pose.orientation.{z,w}`` to actually
96
+ # command motion.
97
+ default_goal_json: |
98
+ {
99
+ "pose": {
100
+ "header": {
101
+ "frame_id": "base_link"
102
+ },
103
+ "pose": {
104
+ "position": {"x": 0.0, "y": 0.0, "z": 0.0},
105
+ "orientation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0}
106
+ }
107
+ },
108
+ "behavior_tree": ""
109
+ }
110
+ ros_dependencies:
111
+ - "ros-${ROS_DISTRO}-nav2-bringup"
112
+ - "ros-${ROS_DISTRO}-nav2-msgs"
113
+
114
+ # ADR-0026 β€” per-skill JSON Schema surfaced to the Reasoner LLM tool
115
+ # palette. The LLM produces a JSON object matching this schema; the
116
+ # adapter deep-merges over ``default_goal_json`` at configure-time.
117
+ #
118
+ # The schema unifies absolute and relative goals: pick ``frame_id``
119
+ # to choose, fill in position + orientation as offsets in that frame.
120
+ # Nav2's bt_navigator transforms the goal to the planning frame on
121
+ # accept via tf2, so the LLM does not need to compose quaternions
122
+ # against the live robot pose β€” declare the offset in ``base_link``
123
+ # and Nav2 does the math.
124
+ goal_params_schema:
125
+ type: object
126
+ description: >
127
+ Target pose for Nav2's NavigateToPose action. Position +
128
+ orientation are interpreted in ``pose.header.frame_id`` (Nav2
129
+ transforms to its planning frame on accept).
130
+ properties:
131
+ pose:
132
+ type: object
133
+ properties:
134
+ header:
135
+ type: object
136
+ properties:
137
+ frame_id:
138
+ type: string
139
+ enum: ["map", "odom", "base_link"]
140
+ description: >
141
+ Frame the position + orientation are expressed in.
142
+ ``map`` = absolute world goal (use when you know the
143
+ world coordinate, e.g. "drive to (3.5, 2.1)").
144
+ ``base_link`` = relative to current pose (use for
145
+ turn / forward / back-up commands β€” position is the
146
+ offset in metres ahead/left/up of the robot, and
147
+ orientation is the rotation from current heading).
148
+ ``odom`` = relative-ish (drifts over time, prefer
149
+ ``base_link``).
150
+ required: ["frame_id"]
151
+ pose:
152
+ type: object
153
+ properties:
154
+ position:
155
+ type: object
156
+ properties:
157
+ x:
158
+ type: number
159
+ description: >
160
+ Target x (m). In ``base_link`` frame this is
161
+ forward distance (positive = ahead, negative =
162
+ backward). In ``map`` frame this is the absolute
163
+ world x coordinate.
164
+ y:
165
+ type: number
166
+ description: >
167
+ Target y (m). In ``base_link`` frame this is
168
+ leftward distance (positive = left). In ``map``
169
+ frame this is the absolute world y coordinate.
170
+ z:
171
+ type: number
172
+ description: "Target z (m); ground robots use 0."
173
+ required: ["x", "y"]
174
+ orientation:
175
+ type: object
176
+ description: >
177
+ Target orientation as a quaternion. For ground robots
178
+ the only non-zero components are ``z = sin(yaw/2)``
179
+ and ``w = cos(yaw/2)`` (yaw rotation around vertical
180
+ axis). In ``base_link`` frame the quaternion is the
181
+ rotation FROM current heading; in ``map`` frame it
182
+ is the absolute world heading.
183
+ Common values for ``base_link`` turns:
184
+ - identity (no rotation): z=0, w=1
185
+ - +90Β° (counter-clockwise): z=0.7071, w=0.7071
186
+ - -90Β° (clockwise): z=-0.7071, w=0.7071
187
+ - 180Β°: z=1.0, w=0
188
+ properties:
189
+ x: { type: number, description: "Quaternion x component." }
190
+ y: { type: number, description: "Quaternion y component." }
191
+ z: { type: number, description: "Quaternion z component (sin(yaw/2) for a pure yaw rotation)." }
192
+ w: { type: number, description: "Quaternion w component (cos(yaw/2) for a pure yaw rotation)." }
193
+ required: ["position", "orientation"]
194
+ required: ["pose"]
195
+ required: ["pose"]