Skip to content

Commit 08b0c21

Browse files
committed
feat(demos/ota): add SOVD manifest with logical functions
The previous config disabled function auto-generation but the result was zero Functions - which made the tree's Functions section empty rather than meaningful. Add a manifest defining areas, components, apps, and five logical functions: - Autonomous Navigation (bt_navigator + planner + controller + smoother + behaviors + waypoint + velocity smoother + collision monitor + docking + costmaps + lifecycle manager) - Localization (amcl + map server + lifecycle manager) - Perception (scan_sensor_node + ros_gz_bridge - the OTA target) - Fleet Diagnostics (gateway + fault manager) - Live Telemetry (foxglove bridge + robot state publisher) These are the capabilities the demo narrative pivots on: an operator viewing the tree sees "Perception is broken" or "Autonomous Navigation is degraded" rather than scrolling through 27 individual nodes. Switches discovery to hybrid mode so manifest entities + runtime discovery cooperate. unmanifested_nodes: warn + manifest_strict_validation: false tolerate the OTA-driven runtime changes (broken_lidar_legacy disappears on uninstall, obstacle_classifier_v2 appears on install) without manifest reconciliation churn. create_synthetic_components and create_functions_from_namespaces both stay off - the manifest is the source of truth.
1 parent 5067ff9 commit 08b0c21

3 files changed

Lines changed: 335 additions & 8 deletions

File tree

demos/ota_nav2_sensor_fix/Dockerfile.gateway

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
9393

9494
COPY --from=builder /ws/install /ws/install
9595
COPY gateway_config.yaml /etc/ros2_medkit/gateway_config.yaml
96+
COPY manifest.yaml /etc/ros2_medkit/manifest.yaml
9697
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
9798
RUN chmod +x /usr/local/bin/entrypoint.sh
9899

demos/ota_nav2_sensor_fix/gateway_config.yaml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,18 @@ ros2_medkit_gateway:
2525
max_age_seconds: 86400
2626

2727
discovery:
28-
mode: "runtime_only"
28+
# Hybrid: manifest defines areas/components/apps/functions, runtime
29+
# fills in topics/services/params and surfaces OTA-installed nodes
30+
# (e.g. obstacle_classifier_v2 after trigger-install.sh) without
31+
# needing a manifest entry for them.
32+
mode: "hybrid"
33+
manifest_path: "/etc/ros2_medkit/manifest.yaml"
34+
manifest_strict_validation: false
2935
runtime:
30-
# The demo's nav2 nodes don't share a meaningful namespace - they
31-
# all live at root with a few `/global_costmap`, `/local_costmap`
32-
# exceptions. Letting the gateway auto-synthesize Functions from
33-
# those namespaces produces single-host "global_costmap" /
34-
# "local_costmap" / "root" Functions that don't represent any
35-
# logical capability. Disable the auto-gen here; without a
36-
# manifest the Functions tree section just stays empty.
36+
# Manifest defines components, no need for synthetic ones.
37+
create_synthetic_components: false
38+
# Manifest defines functions; the auto-gen-from-namespaces path
39+
# produces single-host noise because the nav2 stack lives at /.
3740
create_functions_from_namespaces: false
3841

3942
# Enable /updates endpoints; provider supplied by ota_update_plugin below.
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
# Copyright 2026 bburda. Apache-2.0.
2+
#
3+
# SOVD manifest for the OTA over SOVD nav2 sensor-fix demo.
4+
#
5+
# The demo runs in hybrid discovery mode: this manifest defines areas /
6+
# components / apps / functions, runtime discovery fills in topic
7+
# bindings, and OTA-installed nodes (e.g. obstacle_classifier_v2 after
8+
# trigger-install.sh) appear automatically without needing a manifest
9+
# entry.
10+
#
11+
# The point of the manifest is to make the Functions tree meaningful:
12+
# without it the gateway either auto-synthesizes one Function per
13+
# namespace (mostly single-host noise on this stack, where everything
14+
# lives at root) or leaves it empty. Here we group nodes by capability
15+
# so an operator viewing the tree sees Navigation / Localization /
16+
# Perception / Diagnostics / Visualization, not opaque hashes.
17+
18+
manifest_version: "1.0"
19+
20+
metadata:
21+
name: "ota-nav2-sensor-fix"
22+
description: "OTA over SOVD demo - TurtleBot3 + Nav2 + headless Gazebo + ros2_medkit gateway"
23+
version: "0.1.0"
24+
25+
config:
26+
# broken_lidar_legacy gets uninstalled by the OTA flow; obstacle_classifier
27+
# gets installed at runtime. Both are unmanifested at boot - tolerate.
28+
unmanifested_nodes: warn
29+
# Pull in topics, services, params from the running graph for entities
30+
# the manifest declares.
31+
inherit_runtime_resources: true
32+
33+
# =============================================================================
34+
# AREAS
35+
# =============================================================================
36+
areas:
37+
- id: robot
38+
name: "Robot"
39+
description: "TurtleBot3 platform, sensors, and TF infrastructure"
40+
namespace: /
41+
42+
- id: navigation
43+
name: "Navigation"
44+
description: "Nav2 motion / planning / control stack"
45+
namespace: /
46+
47+
- id: diagnostics
48+
name: "Diagnostics"
49+
description: "ros2_medkit gateway, fault manager, OTA"
50+
namespace: /
51+
52+
- id: visualization
53+
name: "Visualization"
54+
description: "Foxglove bridge and simulator-ROS bridge"
55+
namespace: /
56+
57+
# =============================================================================
58+
# COMPONENTS - logical groupings the apps live on
59+
# =============================================================================
60+
components:
61+
- id: lidar-sensor
62+
name: "LiDAR Sensor"
63+
type: "sensor"
64+
description: "2D LiDAR (broken_lidar / fixed_lidar after OTA swap)"
65+
area: robot
66+
67+
- id: robot-base
68+
name: "TurtleBot3 Base"
69+
type: "platform"
70+
description: "Robot platform - URDF publisher and gz bridge"
71+
area: robot
72+
73+
- id: nav2-motion
74+
name: "Nav2 Motion"
75+
type: "controller"
76+
description: "BT navigator, planner, controller, behaviors, costmaps"
77+
area: navigation
78+
79+
- id: nav2-localization
80+
name: "Nav2 Localization"
81+
type: "controller"
82+
description: "AMCL + map server + localization lifecycle manager"
83+
area: navigation
84+
85+
- id: medkit-gateway-unit
86+
name: "ros2_medkit Gateway"
87+
type: "controller"
88+
description: "SOVD HTTP gateway with the OTA update plugin loaded"
89+
area: diagnostics
90+
91+
- id: fault-manager-unit
92+
name: "Fault Manager"
93+
type: "controller"
94+
description: "Fault aggregation backend behind /faults"
95+
area: diagnostics
96+
97+
- id: foxglove-unit
98+
name: "Foxglove Bridge"
99+
type: "controller"
100+
description: "WebSocket bridge for Foxglove Studio panels"
101+
area: visualization
102+
103+
# =============================================================================
104+
# APPS - one per ROS 2 node we care about (skip nav2-internal _rclcpp_node
105+
# helpers and transform_listener_impl_* - they're plumbing, not user-facing)
106+
# =============================================================================
107+
apps:
108+
# ── LiDAR / perception ────────────────────────────────────────────
109+
- id: scan-sensor
110+
name: "Scan Sensor"
111+
category: "sensor"
112+
is_located_on: lidar-sensor
113+
description: "LaserScan publisher (broken_lidar pre-OTA, fixed_lidar post-OTA)"
114+
ros_binding: { node_name: scan_sensor_node, namespace: / }
115+
116+
- id: ros-gz-bridge
117+
name: "ROS-Gazebo Bridge"
118+
category: "simulation"
119+
is_located_on: lidar-sensor
120+
description: "Bridges /scan_sim and /clock from gz-sim"
121+
ros_binding: { node_name: ros_gz_bridge, namespace: / }
122+
123+
# ── Robot platform ────────────────────────────────────────────────
124+
- id: robot-state-publisher
125+
name: "Robot State Publisher"
126+
category: "platform"
127+
is_located_on: robot-base
128+
description: "Publishes the robot URDF TF tree"
129+
ros_binding: { node_name: robot_state_publisher, namespace: / }
130+
131+
# ── Nav2 motion ───────────────────────────────────────────────────
132+
- id: bt-navigator
133+
name: "BT Navigator"
134+
category: "navigation"
135+
is_located_on: nav2-motion
136+
description: "Behavior Tree navigator - hosts navigate_to_pose action"
137+
ros_binding: { node_name: bt_navigator, namespace: / }
138+
139+
- id: planner-server
140+
name: "Planner Server"
141+
category: "navigation"
142+
is_located_on: nav2-motion
143+
description: "Global path planner"
144+
ros_binding: { node_name: planner_server, namespace: / }
145+
146+
- id: controller-server
147+
name: "Controller Server"
148+
category: "navigation"
149+
is_located_on: nav2-motion
150+
description: "Local path follower"
151+
ros_binding: { node_name: controller_server, namespace: / }
152+
153+
- id: smoother-server
154+
name: "Smoother Server"
155+
category: "navigation"
156+
is_located_on: nav2-motion
157+
description: "Path smoothing"
158+
ros_binding: { node_name: smoother_server, namespace: / }
159+
160+
- id: route-server
161+
name: "Route Server"
162+
category: "navigation"
163+
is_located_on: nav2-motion
164+
description: "Route planning"
165+
ros_binding: { node_name: route_server, namespace: / }
166+
167+
- id: behavior-server
168+
name: "Behavior Server"
169+
category: "navigation"
170+
is_located_on: nav2-motion
171+
description: "Recovery behaviors"
172+
ros_binding: { node_name: behavior_server, namespace: / }
173+
174+
- id: waypoint-follower
175+
name: "Waypoint Follower"
176+
category: "navigation"
177+
is_located_on: nav2-motion
178+
description: "Sequenced waypoint navigation"
179+
ros_binding: { node_name: waypoint_follower, namespace: / }
180+
181+
- id: velocity-smoother
182+
name: "Velocity Smoother"
183+
category: "navigation"
184+
is_located_on: nav2-motion
185+
description: "/cmd_vel smoothing"
186+
ros_binding: { node_name: velocity_smoother, namespace: / }
187+
188+
- id: collision-monitor
189+
name: "Collision Monitor"
190+
category: "navigation"
191+
is_located_on: nav2-motion
192+
description: "Emergency stop on imminent collision"
193+
ros_binding: { node_name: collision_monitor, namespace: / }
194+
195+
- id: docking-server
196+
name: "Docking Server"
197+
category: "navigation"
198+
is_located_on: nav2-motion
199+
description: "Approach + dock action"
200+
ros_binding: { node_name: docking_server, namespace: / }
201+
202+
- id: global-costmap
203+
name: "Global Costmap"
204+
category: "navigation"
205+
is_located_on: nav2-motion
206+
description: "Static + obstacle costmap for planning"
207+
ros_binding: { node_name: global_costmap, namespace: /global_costmap }
208+
209+
- id: local-costmap
210+
name: "Local Costmap"
211+
category: "navigation"
212+
is_located_on: nav2-motion
213+
description: "Local rolling costmap for control"
214+
ros_binding: { node_name: local_costmap, namespace: /local_costmap }
215+
216+
- id: lifecycle-manager-navigation
217+
name: "Lifecycle Manager (Navigation)"
218+
category: "navigation"
219+
is_located_on: nav2-motion
220+
description: "Nav2 motion lifecycle orchestration"
221+
ros_binding: { node_name: lifecycle_manager_navigation, namespace: / }
222+
223+
# ── Nav2 localization ─────────────────────────────────────────────
224+
- id: amcl
225+
name: "AMCL"
226+
category: "localization"
227+
is_located_on: nav2-localization
228+
description: "Adaptive Monte Carlo Localization"
229+
ros_binding: { node_name: amcl, namespace: / }
230+
231+
- id: map-server
232+
name: "Map Server"
233+
category: "localization"
234+
is_located_on: nav2-localization
235+
description: "Static map publisher"
236+
ros_binding: { node_name: map_server, namespace: / }
237+
238+
- id: lifecycle-manager-localization
239+
name: "Lifecycle Manager (Localization)"
240+
category: "localization"
241+
is_located_on: nav2-localization
242+
description: "Localization lifecycle orchestration"
243+
ros_binding: { node_name: lifecycle_manager_localization, namespace: / }
244+
245+
# ── Diagnostics ───────────────────────────────────────────────────
246+
- id: medkit-gateway
247+
name: "ros2_medkit Gateway"
248+
category: "gateway"
249+
is_located_on: medkit-gateway-unit
250+
description: "SOVD REST gateway, hosts the OTA plugin"
251+
ros_binding: { node_name: ros2_medkit_gateway, namespace: / }
252+
253+
- id: medkit-fault-manager
254+
name: "Fault Manager"
255+
category: "diagnostics"
256+
is_located_on: fault-manager-unit
257+
description: "Fault aggregation + storage"
258+
ros_binding: { node_name: fault_manager, namespace: / }
259+
260+
# ── Visualization ─────────────────────────────────────────────────
261+
- id: foxglove-bridge
262+
name: "Foxglove Bridge"
263+
category: "visualization"
264+
is_located_on: foxglove-unit
265+
description: "WebSocket bridge on :8765"
266+
ros_binding: { node_name: foxglove_bridge, namespace: / }
267+
268+
# =============================================================================
269+
# FUNCTIONS - what the user actually selects in the tree to ask
270+
# "is this capability working?"
271+
# =============================================================================
272+
functions:
273+
- id: autonomous-navigation
274+
name: "Autonomous Navigation"
275+
category: "mobility"
276+
description: "Plan + drive to a goal pose - the headline OTA demo capability"
277+
hosted_by:
278+
- bt-navigator
279+
- planner-server
280+
- controller-server
281+
- smoother-server
282+
- route-server
283+
- behavior-server
284+
- waypoint-follower
285+
- velocity-smoother
286+
- collision-monitor
287+
- docking-server
288+
- global-costmap
289+
- local-costmap
290+
- lifecycle-manager-navigation
291+
292+
- id: localization
293+
name: "Localization"
294+
category: "mobility"
295+
description: "Where is the robot in the map - AMCL + map server"
296+
hosted_by:
297+
- amcl
298+
- map-server
299+
- lifecycle-manager-localization
300+
301+
- id: perception
302+
name: "Perception"
303+
category: "sensing"
304+
description: "LaserScan stream feeding nav2 - the OTA target"
305+
hosted_by:
306+
- scan-sensor
307+
- ros-gz-bridge
308+
309+
- id: fleet-diagnostics
310+
name: "Fleet Diagnostics"
311+
category: "diagnostics"
312+
description: "SOVD REST surface + fault aggregation - this panel"
313+
hosted_by:
314+
- medkit-gateway
315+
- medkit-fault-manager
316+
317+
- id: live-telemetry
318+
name: "Live Telemetry"
319+
category: "observability"
320+
description: "Foxglove bridge + URDF publisher feeding the 3D panel"
321+
hosted_by:
322+
- foxglove-bridge
323+
- robot-state-publisher

0 commit comments

Comments
 (0)