Migration Guide: Runtime to Hybrid Mode
This guide helps you migrate from runtime-only discovery to hybrid mode, enabling stable entity IDs, semantic groupings, and offline detection.
Overview
Runtime-only mode (default) automatically discovers:
ROS 2 nodes → Apps
Namespace groupings → Synthetic Components
Top-level namespaces → Areas
Topics, services, actions → Data, operations
Hybrid mode adds:
Stable, meaningful entity IDs
Apps entity type (software applications)
Functions entity type (high-level capabilities)
Offline detection for apps
Custom metadata and descriptions
The migration process creates a manifest that maps your existing ROS 2 nodes to the SOVD entity model while preserving runtime data access.
Step 1: Audit Your System
First, understand your current ROS 2 system structure.
List all running nodes:
ros2 node list
Example output:
/amcl
/bt_navigator
/controller_server
/ld08_driver
/map_server
/planner_server
/robot_state_publisher
/turtlebot3_node
For each node, get detailed info:
ros2 node info /amcl
Note down:
Node name and namespace
Published/subscribed topics
Provided services and actions
Purpose and category
Create a spreadsheet or document with columns:
Node Name |
Namespace |
Category |
Purpose |
|---|---|---|---|
amcl |
/ |
localization |
Adaptive Monte Carlo Localization |
planner_server |
/ |
navigation |
Global path planning |
controller_server |
/ |
navigation |
Local trajectory tracking |
… |
… |
… |
… |
Step 2: Define Areas
Group your nodes into logical subsystems. Common groupings:
By function: perception, localization, navigation, control
By hardware: sensors, actuators, compute
By namespace: ROS 2 namespace structure
Example area mapping:
areas:
- id: perception
name: "Perception"
category: "sensor-processing"
description: "Sensor data acquisition and processing"
- id: localization
name: "Localization"
category: "state-estimation"
description: "Robot pose estimation"
- id: navigation
name: "Navigation"
category: "motion-planning"
description: "Path planning and execution"
- id: control
name: "Control"
category: "motion-control"
description: "Low-level motor control"
Tip
Start with broad categories and refine later. You can nest areas using
subareas if needed.
Step 3: Define Components
Map physical and virtual hardware. Components represent:
Physical sensors (LiDAR, cameras, IMU)
Actuators (motors, grippers)
Compute units (main computer, edge devices)
Virtual units (containers, processes)
Example component mapping:
components:
- id: lidar-sensor
name: "LiDAR Sensor"
type: "sensor"
area: perception
description: "360° laser range finder"
- id: main-computer
name: "Main Computer"
type: "controller"
area: control
description: "Raspberry Pi 4 running ROS 2"
- id: opencr-board
name: "OpenCR Board"
type: "controller"
area: control
description: "Motor controller board"
Step 4: Define Apps
Create an app entry for each ROS 2 node. Key decisions:
Choose a stable ID: Use lowercase with hyphens (e.g.,
lidar-driver)Set human-readable name: Clear, descriptive name
Configure ros_binding: How to match the ROS 2 node
Set component: Which component hosts this app
Set depends_on: Dependencies on other apps
Example app mapping:
apps:
# Node: /ld08_driver
- id: lidar-driver
name: "LiDAR Driver"
category: "driver"
is_located_on: lidar-sensor
ros_binding:
node_name: ld08_driver
# Node: /amcl
- id: amcl-node
name: "AMCL Localization"
category: "localization"
is_located_on: main-computer
depends_on:
- lidar-driver
ros_binding:
node_name: amcl
# Node: /planner_server
- id: planner-server
name: "Planner Server"
category: "navigation"
is_located_on: main-computer
depends_on:
- amcl-node
ros_binding:
node_name: planner_server
Handling namespaced nodes:
# Node: /robot1/lidar_driver
- id: robot1-lidar-driver
name: "Robot 1 LiDAR Driver"
ros_binding:
node_name: lidar_driver
namespace: /robot1
# Match node in any namespace
- id: generic-teleop
name: "Teleop Controller"
ros_binding:
node_name: teleop_keyboard
namespace: "*"
Step 5: Define Functions
Identify high-level capabilities your robot provides:
What can your robot do as a user?
Which apps work together for each capability?
Example function definitions:
functions:
- id: autonomous-navigation
name: "Autonomous Navigation"
category: "mobility"
description: "Navigate autonomously to goals"
hosted_by:
- amcl-node
- planner-server
- controller-server
- bt-navigator
- id: localization
name: "Localization"
category: "state-estimation"
description: "Determine robot position on map"
hosted_by:
- amcl-node
- map-server
- id: perception
name: "Environment Perception"
category: "sensing"
hosted_by:
- lidar-driver
Step 6: Create the Manifest File
Combine all sections into a complete manifest:
manifest_version: "1.0"
metadata:
name: "my-robot"
version: "1.0.0"
description: "My robot system manifest"
config:
unmanifested_nodes: warn
inherit_runtime_resources: true
areas:
# ... your area definitions
components:
# ... your component definitions
apps:
# ... your app definitions
functions:
# ... your function definitions
Save as system_manifest.yaml in your config directory.
Step 7: Test in Hybrid Mode
Start your ROS 2 system as normal
Start the gateway with manifest:
ros2 run ros2_medkit_gateway gateway_node --ros-args \ -p manifest.enabled:=true \ -p manifest.file_path:=/path/to/system_manifest.yaml \ -p manifest.mode:=hybrid
Check manifest status:
curl http://localhost:8080/api/v1/manifest/status | jq
Verify apps are linked:
curl http://localhost:8080/api/v1/apps | jq '.[] | {id, name, is_online}'
Check for orphan nodes (warnings in gateway logs):
ros2 run ros2_medkit_gateway gateway_node ... 2>&1 | grep -i orphan
Step 8: Iterate and Refine
Based on testing results:
App not linking to node:
Check
ros_binding.node_namespellingVerify namespace matches (use
ros2 node listto confirm)Try wildcard namespace:
namespace: "*"Check gateway logs for matching attempts
Orphan nodes appearing:
Add app entries for missing nodes
Or set
config.unmanifested_nodes: ignoreto hide them
Missing data/operations:
Ensure
config.inherit_runtime_resources: trueCheck that the bound node has the expected topics/services
Refining the manifest:
Add descriptions to improve documentation
Add tags for filtering
Adjust categories for better organization
Define dependencies between apps
Validation Checklist
Before finalizing your manifest:
[ ] All required fields present (manifest_version, entity ids and names)
[ ] All area references are valid (component.area, subarea.parent)
[ ] All component references are valid (app.component)
[ ] All app references are valid (depends_on, function.hosted_by)
[ ] IDs are unique within each entity type
[ ] ros_binding configured for all apps that map to ROS nodes
[ ] Functions have at least one app in hosted_by
Run validation:
# Start gateway with strict validation
ros2 run ros2_medkit_gateway gateway_node --ros-args \
-p manifest.enabled:=true \
-p manifest.file_path:=/path/to/system_manifest.yaml \
-p manifest.strict_validation:=true
Common Issues
“App X not found for function Y”
The hosted_by list references an app ID that doesn’t exist.
# Wrong - typo in app ID
functions:
- id: nav-function
hosted_by:
- amcl_node # Should be "amcl-node"
“Component X not found for app Y”
The component references a component that doesn’t exist.
# Wrong - component doesn't exist
apps:
- id: my-app
component: nonexistent-component
“Duplicate ID: X”
Two entities have the same ID.
# Wrong - duplicate IDs
components:
- id: sensor
name: "LiDAR"
- id: sensor # Duplicate!
name: "Camera"
Node not linking (is_online: false)
Common causes:
Node name mismatch (check exact spelling)
Namespace mismatch (try wildcard)
Node not running when gateway starts
Node in different ROS domain
Debug with:
# List actual nodes
ros2 node list
# Check gateway logs
ros2 run ros2_medkit_gateway gateway_node ... 2>&1 | grep -i link
Best Practices
Start simple: Begin with just apps and expand to functions later
Use consistent naming: Follow a pattern for IDs (e.g.,
{component}-{function})Document as you go: Add descriptions while the context is fresh
Version your manifest: Use semantic versioning in metadata
Keep manifest in source control: Track changes alongside code
Test incrementally: Add a few entities, test, repeat
Use validation: Always run with
strict_validation: trueinitially
Next Steps
Manifest-Based Discovery - Complete user guide
Manifest Schema Reference - Full schema reference