Integrating with Your ROS 2 System

This tutorial shows how to integrate ros2_medkit with your existing ROS 2 application.

Overview

ros2_medkit works with any ROS 2 system out of the box through automatic discovery. This tutorial covers:

  • Basic integration (zero configuration)

  • Organizing nodes with namespaces

  • Reporting custom faults

  • Best practices for production

Zero-Configuration Integration

The simplest integration requires no changes to your existing code. Just run the gateway alongside your nodes:

# Terminal 1: Your existing ROS 2 application
ros2 launch my_robot robot.launch.py

# Terminal 2: ros2_medkit gateway
ros2 launch ros2_medkit_gateway gateway.launch.py

The gateway will automatically discover:

  • All running nodes → Apps

  • Top-level namespaces → Areas

  • Namespace groupings → Synthetic Components

  • Topics, services, actions → Data, Operations

  • Parameters → Configurations

Organizing with Namespaces

ros2_medkit maps ROS 2 namespaces to Areas. To create a logical hierarchy, use namespaces in your launch files:

# my_robot.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        # Perception nodes → /perception area
        Node(
            package='camera_driver',
            executable='camera_node',
            namespace='/perception/cameras',
            name='front_camera'
        ),
        Node(
            package='lidar_driver',
            executable='lidar_node',
            namespace='/perception/lidar',
            name='velodyne'
        ),

        # Navigation nodes → /navigation area
        Node(
            package='nav2_controller',
            executable='controller_server',
            namespace='/navigation',
            name='controller'
        ),

        # Manipulation nodes → /manipulation area
        Node(
            package='moveit2',
            executable='move_group',
            namespace='/manipulation',
            name='move_group'
        ),
    ])

This creates the hierarchy:

Areas:
├── perception
│   ├── Component: front_camera
│   └── Component: velodyne
├── navigation
│   └── Component: controller
└── manipulation
    └── Component: move_group

Reporting Custom Faults

To report faults from your nodes, use the ros2_medkit_fault_reporter package:

Add dependency to package.xml:

<depend>ros2_medkit_fault_reporter</depend>

Report faults in your node:

#include "ros2_medkit_fault_reporter/fault_reporter.hpp"
#include "ros2_medkit_msgs/msg/fault.hpp"

using ros2_medkit_fault_reporter::FaultReporter;
using ros2_medkit_msgs::msg::Fault;

class MyNode : public rclcpp::Node
{
public:
  MyNode() : Node("my_node")
  {
    // FaultReporter requires shared_from_this() and a source identifier
    fault_reporter_ = std::make_shared<FaultReporter>(
      this->shared_from_this(),
      this->get_fully_qualified_name());
  }

  void check_sensor()
  {
    if (!sensor_ok_) {
      fault_reporter_->report(
        "SENSOR_DISCONNECTED",
        Fault::SEVERITY_ERROR,
        "Front camera not responding"
      );
    }
  }

private:
  std::shared_ptr<FaultReporter> fault_reporter_;
};

Note

Faults are cleared through the FaultManager service, not the reporter. Use ros2 service call /fault_manager/clear_fault or the REST API DELETE /api/v1/components/{id}/faults/{fault_code}.

Fault severity levels:

  • Fault::SEVERITY_INFO - Informational, no action required

  • Fault::SEVERITY_WARN - Warning, may need attention

  • Fault::SEVERITY_ERROR - Error, requires attention

  • Fault::SEVERITY_CRITICAL - Critical, immediate action required

Exposing Custom Data

Any topic under your node’s namespace is automatically exposed via the Data API:

// These topics will be accessible via:
// GET /api/v1/apps/{app_id}/data
// (or /api/v1/components/{component_id}/data in synthetic mode)

pub_status_ = create_publisher<MyStatus>("status", 10);
pub_diagnostics_ = create_publisher<DiagnosticArray>("diagnostics", 10);

Best practices for data exposure:

  1. Use meaningful topic names

  2. Publish regularly for real-time monitoring

  3. Include timestamps in messages

  4. Use standard message types when possible

Exposing Services and Actions

Services and actions under your namespace become Operations:

// Service: POST /api/v1/apps/{app_id}/operations/reset/executions
srv_reset_ = create_service<std_srvs::srv::Trigger>(
  "reset",
  std::bind(&MyNode::handle_reset, this, _1, _2));

// Action: POST /api/v1/apps/{app_id}/operations/calibrate/executions
action_calibrate_ = rclcpp_action::create_server<Calibrate>(
  this, "calibrate",
  std::bind(&MyNode::handle_goal, this, _1, _2),
  std::bind(&MyNode::handle_cancel, this, _1),
  std::bind(&MyNode::handle_accepted, this, _1));

Exposing Parameters

ROS 2 parameters become Configurations automatically:

// These parameters will be accessible via:
// GET/PUT /api/v1/apps/{app_id}/configurations/{param_name}

declare_parameter("update_rate", 10.0);
declare_parameter("enabled", true);
declare_parameter("mode", "auto");

Make parameters dynamically reconfigurable:

auto param_callback = [this](const std::vector<rclcpp::Parameter>& params) {
  for (const auto& param : params) {
    if (param.get_name() == "update_rate") {
      update_rate_ = param.as_double();
    }
  }
  return rcl_interfaces::msg::SetParametersResult{.successful = true};
};
param_callback_handle_ = add_on_set_parameters_callback(param_callback);

Launch File Integration

Example launch file combining your robot with ros2_medkit:

# robot_with_diagnostics.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    medkit_share = get_package_share_directory('ros2_medkit_gateway')

    return LaunchDescription([
        # Your robot launch
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([
                get_package_share_directory('my_robot'),
                '/launch/robot.launch.py'
            ])
        ),

        # Fault manager for fault tracking
        Node(
            package='ros2_medkit_fault_manager',
            executable='fault_manager_node',
            name='fault_manager'
        ),

        # Gateway with custom config
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([
                medkit_share, '/launch/gateway.launch.py'
            ]),
            launch_arguments={
                'server_host': '0.0.0.0',
                'server_port': '8080'
            }.items()
        ),
    ])

Production Checklist

Before deploying to production:

Enable TLS (Configuring HTTPS/TLS)

Configure authentication (Configuring Authentication)

Set appropriate CORS origins

Use namespaces for logical organization

Implement fault reporting for critical components

Test with expected load

Set up monitoring and alerting

Document your Area/Component hierarchy

See Also