Files
hermes-bot/ros/hermes_bringup/test/test_launch.py
T
kyleandClaude Sonnet 5.5 d8db0a48f5 Run the whole robot from one launch file and one boot-time unit
hermes_bringup starts the face, camera, face detector and audio levels from one
launch file with their parameters in robot.yaml, each node its own respawned
process, the face first and the others after start_delay. robot.service runs it
at boot, so everything is ROS and the face is up whenever the robot is powered.
Reboot-tested: all services and nodes return by themselves, the face draws at
about 28 s on the boot clock, the mic gain survives, nothing throttles.

Cut the CPU: the camera runs at 15 fps (20% of a core, vs 40% at 30), and the
detector went from 131% to 40% by pinning OpenCV to one thread (its DNN burns 3-4x
its elapsed time across cores), decoding the JPEG at half size, and re-identifying
a tracked face at most once a second. The audio scope is published only while
someone is speaking or listening.

A standalone face unit that skipped `ros2 launch` saved only 3.7 s and was dropped:
the owner prefers everything in ROS and is fine with up to a minute. trim-boot.sh
disables the unused snapd and ModemManager.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
2026-10-05 00:10:09 -07:00

35 lines
1.5 KiB
Python

import os
import pytest
import yaml
pytest.importorskip('launch_ros')
HERE = os.path.dirname(__file__)
ROOT = os.path.dirname(HERE)
def test_launch_description_has_one_respawning_process_per_node():
import importlib.util
spec = importlib.util.spec_from_file_location('robot_launch', os.path.join(ROOT, 'launch', 'robot.launch.py'))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
from launch.actions import TimerAction
from launch_ros.actions import Node
ld = mod.generate_launch_description()
nodes = [e for e in ld.entities if isinstance(e, Node)]
delayed = [n for t in ld.entities if isinstance(t, TimerAction) for n in t.actions if isinstance(n, Node)]
assert len(nodes) + len(delayed) == len(mod.NODES) == 4
assert len(nodes) == 1 and 'face' in mod.NODES[1] and len(delayed) == 3 # the face starts at once
# respawn lives on a private attribute that differs between ROS releases, so check the source
src = open(os.path.join(ROOT, 'launch', 'robot.launch.py')).read()
assert 'respawn=True' in src and 'respawn_delay' in src
def test_every_node_in_the_launch_file_has_parameters_and_the_face_is_mounted_portrait():
cfg = yaml.safe_load(open(os.path.join(ROOT, 'config', 'robot.yaml')))
for name in ('camera', 'hermes_face', 'hermes_face_detector', 'hermes_audio_state'):
assert 'ros__parameters' in cfg[name], name
assert cfg['hermes_face']['ros__parameters']['rotation'] == 270
assert cfg['camera']['ros__parameters']['FrameDurationLimits'] == [66666, 66666]