"""Bring up the robot: camera, face, face detection, audio levels, the MCP bridge and the thermal guard. ros2 launch hermes_bringup robot.launch.py [camera:=false] [face:=false] [vision:=false] [audio:=false] [mcp:=false] [thermal:=false] [start_delay:=6.0] Every node is its own process and is respawned if it exits, so one crash cannot take the others down: the face keeps running when the camera does not. Parameters are in config/robot.yaml. The face starts at once; the camera, vision, audio and MCP nodes wait start_delay seconds, so the four Python processes do not fight over the CPU while the face imports and draws its first frame. """ import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, TimerAction from launch.conditions import IfCondition from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node # (launch argument, package, executable, node name in robot.yaml) NODES = [ ('camera', 'camera_ros', 'camera_node', 'camera'), ('face', 'hermes_face', 'face_node', 'hermes_face'), ('vision', 'hermes_vision', 'face_detector', 'hermes_face_detector'), ('audio', 'hermes_audio', 'audio_state', 'hermes_audio_state'), ('mcp', 'hermes_mcp', 'mcp_server', 'hermes_mcp'), ('thermal', 'hermes_thermal', 'thermal_guard', 'hermes_thermal'), ] DELAYED = {'camera', 'vision', 'audio', 'mcp', 'thermal'} def generate_launch_description(): params = os.path.join(get_package_share_directory('hermes_bringup'), 'config', 'robot.yaml') actions = [DeclareLaunchArgument(arg, default_value='true', description=f'run the {name} node') for arg, _, _, name in NODES] actions.append(DeclareLaunchArgument('start_delay', default_value='6.0', description='seconds the other nodes wait for the face')) for arg, package, executable, name in NODES: node = Node(package=package, executable=executable, name=name, output='screen', parameters=[params], respawn=True, respawn_delay=2.0, condition=IfCondition(LaunchConfiguration(arg))) actions.append(TimerAction(period=LaunchConfiguration('start_delay'), actions=[node]) if arg in DELAYED else node) return LaunchDescription(actions)