启动系统

相关源文件

以下文件用于生成此文档页面:

启动系统是根据配置动态生成并启动机器人操作所需所有 ROS2 节点的编排层。它加载 robot_config YAML 文件并调用专门的构建器模块来创建感知、控制、执行和其他子系统的节点。系统支持多种控制模式,并根据运行时参数有条件地生成节点。

有关 robot_config YAML 结构本身的信息,请参阅 机器人配置文件。有关契约定义详情,请参阅 契约定义。有关外设配置,请参阅 外设配置


架构概述

启动系统遵循 构建器模式,其中中央编排器(robot.launch.py:123-360)加载配置并将节点生成委托给专门的构建器模块。每个构建器负责特定子系统(如相机、控制器、推理)并返回启动动作列表。

启动系统组件

        graph TB
    subgraph "入口点"
        MAIN["robot.launch.py<br/>generate_launch_description()"]
        SETUP["launch_setup()<br/>OpaqueFunction"]
    end

    subgraph "配置加载"
        LOAD["load_robot_config()<br/>lines 87-120"]
        YAML["robot_config YAML<br/>so101_single_arm.yaml"]
        CONFIG["RobotConfig 对象<br/>来自 loader.py"]
    end

    subgraph "启动构建器"
        CONTROL["control.py<br/>generate_ros2_control_nodes()"]
        PERCEPTION["perception.py<br/>generate_camera_nodes()<br/>generate_tf_nodes()"]
        SIMULATION["simulation.py<br/>generate_gazebo_nodes()"]
        EXECUTION["execution.py<br/>generate_inference_node()<br/>generate_action_dispatcher_node()"]
        TELEOP["teleop.py<br/>generate_teleop_nodes()"]
        RECORDING["recording.py<br/>generate_recording_nodes()"]
    end

    subgraph "生成的节点"
        CTRL_NODES["ros2_control_node<br/>controller_spawner"]
        CAM_NODES["usb_cam/realsense_node<br/>static_transform_publisher"]
        SIM_NODES["gzserver<br/>gzclient<br/>spawn_entity"]
        EXEC_NODES["lerobot_policy_node<br/>action_dispatcher_node"]
        TELE_NODES["robot_teleop_node"]
        REC_NODES["episode_recorder<br/>或 ros2 bag record"]
    end

    MAIN --> SETUP
    SETUP --> LOAD
    LOAD --> YAML
    YAML --> CONFIG

    SETUP --> CONTROL
    SETUP --> PERCEPTION
    SETUP --> SIMULATION
    SETUP --> EXECUTION
    SETUP --> TELEOP
    SETUP --> RECORDING

    CONTROL --> CTRL_NODES
    PERCEPTION --> CAM_NODES
    SIMULATION --> SIM_NODES
    EXECUTION --> EXEC_NODES
    TELEOP --> TELE_NODES
    RECORDING --> REC_NODES
    

源码: src/robot_config/launch/robot.launch.py:1-360, src/robot_config/robot_config/launch_builders/control.py, src/robot_config/robot_config/launch_builders/perception.py, src/robot_config/robot_config/launch_builders/execution.py


主启动文件:robot.launch.py

主启动文件 robot.launch.py 位于 src/robot_config/launch/robot.launch.py,作为启动整个机器人系统的单一入口点。

启动参数

参数

默认值

描述

robot_config

test_cam

机器人配置名称 (从 co nfig/robots/<name>.yaml 加载)

config_path

''

配置文件的可选完整路径 (覆盖 robot_config)

use_sim

false

启用仿真模式 (Gazebo)

auto_ start_controllers

true

自动启动控制器 (设为 false 用于调试)

control_mode

''

从 YAML 覆盖控制模式 (teleopmodel_inferencemoveit_planning)

with_inference

''

启用推理管道 (为空时自动检测)

with_moveit

''

启用 MoveIt 规划 (为空时自动检测)

moveit_display

true

为 MoveIt 启动 RViz (仅当 MoveIt 启用时)

record

false

启用 rosbag 录制

record_mode

continuous

录制模式: continuousepisodic

源码: src/robot_config/launch/robot.launch.py:51-62

启动流程

        graph TB
    START["generate_launch_description()"]
    ARGS["声明启动参数<br/>robot_config, use_sim, control_mode 等"]
    OPAQUE["OpaqueFunction(launch_setup)"]

    SETUP_START["launch_setup(context)"]

    PARSE["解析启动参数<br/>lines 140-155"]
    NORMALIZE["规范化布尔值<br/>use_sim, auto_start_controllers"]

    LOAD["load_robot_config()<br/>lines 157-164"]
    PATH_INJECT["将 '_config_path' 注入 robot_config<br/>lines 167-174"]

    MODE_OVERRIDE["应用 control_mode 覆盖<br/>lines 177-181"]
    MODE_SELECT["选择活动控制模式<br/>line 180"]

    INFER_DETECT["确定 with_inference 标志<br/>lines 183-196"]

    BUILD_CONTROL["生成控制节点<br/>lines 199-206"]
    BUILD_SIM{"use_sim?<br/>lines 209-217"}
    BUILD_PERCEPTION["生成感知节点<br/>lines 220-240"]
    BUILD_TELEOP{"control_mode == 'teleop'?<br/>lines 243-262"}
    BUILD_EXECUTION{"with_inference?<br/>lines 265-275"}
    BUILD_MOVEIT{"with_moveit?<br/>lines 278-308"}
    BUILD_RECORDING{"record?<br/>lines 311-323"}

    RETURN["返回动作列表"]

    START --> ARGS
    ARGS --> OPAQUE
    OPAQUE --> SETUP_START

    SETUP_START --> PARSE
    PARSE --> NORMALIZE
    NORMALIZE --> LOAD
    LOAD --> PATH_INJECT

    PATH_INJECT --> MODE_OVERRIDE
    MODE_OVERRIDE --> MODE_SELECT
    MODE_SELECT --> INFER_DETECT

    INFER_DETECT --> BUILD_CONTROL
    BUILD_CONTROL --> BUILD_SIM

    BUILD_SIM -->|是| SIM_NODES["Gazebo 节点"]
    BUILD_SIM -->|否| BUILD_PERCEPTION
    SIM_NODES --> BUILD_PERCEPTION

    BUILD_PERCEPTION --> BUILD_TELEOP

    BUILD_TELEOP -->|是| TELEOP_NODES["遥控节点"]
    BUILD_TELEOP -->|否| BUILD_EXECUTION
    TELEOP_NODES --> BUILD_EXECUTION

    BUILD_EXECUTION -->|是| EXEC_NODES["推理 + 分发器"]
    BUILD_EXECUTION -->|否| BUILD_MOVEIT
    EXEC_NODES --> BUILD_MOVEIT

    BUILD_MOVEIT -->|是| MOVEIT_NODES["MoveIt 节点"]
    BUILD_MOVEIT -->|否| BUILD_RECORDING
    MOVEIT_NODES --> BUILD_RECORDING

    BUILD_RECORDING -->|是| RECORD_NODES["录制节点"]
    BUILD_RECORDING -->|否| RETURN
    RECORD_NODES --> RETURN
    

源码: src/robot_config/launch/robot.launch.py:123-360

配置路径注入

启动系统将 _config_path 键注入 robot_config 字典,使下游节点能够访问原始 YAML 文件:

# lines 167-174
robot_config['_config_path'] = str(Path(robot_config_share) / "config" / "robots" / f"{robot_config_name}.yaml")

这对于以下组件至关重要:- episode_recorder:需要路径来加载契约(episode_recorder.py:195-206)- inference_service:需要路径来加载观测规格(lerobot_policy_node.py:158-159)- action_dispatcher:需要路径来加载动作规格(action_dispatcher_node.py:106-117

源码: src/robot_config/launch/robot.launch.py:167-174


启动构建器

启动构建器是为特定子系统生成节点的模块化函数。每个构建器位于 src/robot_config/robot_config/launch_builders/

构建器模块架构

        graph LR
    subgraph "launch_builders/"
        CONTROL["control.py<br/>ros2_control + 控制器"]
        PERCEPTION["perception.py<br/>相机 + TF"]
        SIMULATION["simulation.py<br/>Gazebo"]
        EXECUTION["execution.py<br/>推理 + 分发"]
        TELEOP["teleop.py<br/>遥控操作"]
        RECORDING["recording.py<br/>数据录制"]
    end

    subgraph "输入"
        CONFIG["robot_config dict"]
        MODE["control_mode str"]
        SIM["use_sim bool"]
    end

    subgraph "输出"
        NODES["List[Node | ExecuteProcess]"]
    end

    CONFIG --> CONTROL
    CONFIG --> PERCEPTION
    CONFIG --> SIMULATION
    CONFIG --> EXECUTION
    CONFIG --> TELEOP
    CONFIG --> RECORDING

    MODE --> CONTROL
    MODE --> EXECUTION
    MODE --> TELEOP
    MODE --> RECORDING

    SIM --> CONTROL
    SIM --> PERCEPTION
    SIM --> SIMULATION
    SIM --> EXECUTION

    CONTROL --> NODES
    PERCEPTION --> NODES
    SIMULATION --> NODES
    EXECUTION --> NODES
    TELEOP --> NODES
    RECORDING --> NODES
    

源码: src/robot_config/robot_config/launch_builders/

控制构建器 (control.py)

生成 ros2_control 节点和控制器启动器。

关键函数: - generate_ros2_control_nodes(robot_config, use_sim, auto_start_controllers) - 主入口点 - 返回:(nodes_list, spawners_dict),其中 spawners_dict 将控制器名称映射到启动器节点

逻辑: 1. 从 robot_config 确定活动控制模式 2. 从 control_modes[mode].controllers 提取控制器列表 3. 仿真模式:启动器等待 Gazebo 的 controller_manager 4. 硬件模式:先启动 ros2_control_node,然后启动器 5. 使用 RegisterEventHandler(OnProcessExit) 进行顺序启动

示例:

control_modes:
  model_inference:
    controllers:
      - joint_state_broadcaster
      - arm_position_controller
      - gripper_position_controller

生成 3 个启动器节点来激活这些控制器。

源码: src/robot_config/robot_config/launch_builders/control.py

感知构建器 (perception.py)

生成相机驱动节点和静态 TF 发布器。

关键函数: - generate_camera_nodes(robot_config, use_sim) - 创建相机驱动节点 - generate_tf_nodes(robot_config) - 创建静态变换发布器 - generate_virtual_camera_relays(robot_config) - 为虚拟相机创建主题中继节点

相机驱动:

驱动

ROS2 包

配置字段

opencv

usb_cam

indexwidthheightfpspixel_format

realsense

realsense2_camera

serial_numberwidthheightfpsenable_depth

TF 发布: 对于每个带有 transform 字段的相机,生成一个 static_transform_publisher 节点,发布相机坐标系相对于其父坐标系的变换。

源码: src/robot_config/robot_config/launch_builders/perception.py

执行构建器 (execution.py)

生成推理服务和动作分发器节点。

关键函数: - generate_inference_node(robot_config, control_mode, use_sim) - 路由到单体或分布式 - generate_monolithic_inference_node() - 单进程推理 - generate_distributed_inference_nodes() - 边缘 + 云端推理 - generate_action_dispatcher_node(robot_config, control_mode, use_sim) - 动作分发器

执行模式:

        graph TB
    INFER_CHECK{"inference.enabled?"}

    EXEC_MODE{"execution_mode"}

    MONO["generate_monolithic_inference_node()<br/>单个 lerobot_policy_node"]

    DIST["generate_distributed_inference_nodes()<br/>边缘: lerobot_policy_node<br/>云端: pure_inference_node"]

    DISPATCH["generate_action_dispatcher_node()<br/>action_dispatcher_node"]

    INFER_CHECK -->|否| NONE["None"]
    INFER_CHECK -->|是| EXEC_MODE

    EXEC_MODE -->|monolithic| MONO
    EXEC_MODE -->|distributed| DIST

    MONO --> DISPATCH
    DIST --> DISPATCH
    

单体节点参数:

{
    "checkpoint": model_config["path"],
    "robot_config_path": str(robot_config_path),
    "device": "auto",
    "execution_mode": "monolithic",
    "node_name": "act_inference_node"
}

分布式节点参数: - 边缘节点: 与单体相同但 execution_mode: "distributed" - 云端节点:

{
    "policy_path": policy_path,
    "input_topic": "/preprocessed/batch",
    "output_topic": "/inference/action",
    "device": "auto"
}

源码: src/robot_config/robot_config/launch_builders/execution.py:1-360

遥控构建器 (teleop.py)

生成人工控制设备的遥控操作节点。

条件激活: 仅当 control_mode == 'teleop'teleoperation.enabled == true 时生成节点。

设备类型: - leader_arm:用于双边遥控操作的 SO-101 主臂 - gamepad:Xbox/PlayStation 控制器 - vr:VR 控制器输入 - imu:基于 IMU 的控制

源码: src/robot_config/robot_config/launch_builders/teleop.py

录制构建器 (recording.py)

生成数据录制节点。

录制模式:

模式

描述

生成的节点

con tinuous

传统 ros2 bag record (一体化文件)

ExecuteProcess (['ros2', 'bag', 'record'])

e pisodic

通过 Action Server 逐 episode 触发

Node('dataset_ tools', 'episode_recorder')

连续模式:

# lines 58-99
recording_action = ExecuteProcess(
    cmd=['ros2', 'bag', 'record', '-o', output_file] + topics,
    output='screen'
)

从 robot_config 自动发现主题(关节、相机、控制器)。

Episodic 模式:

# lines 102-168
episode_recorder_node = Node(
    package='dataset_tools',
    executable='episode_recorder',
    parameters=[
        {'robot_config_path': robot_config_path},
        {'bag_base_dir': bag_base_dir}
    ]
)

需要在单独的终端中手动运行 ros2 run dataset_tools record_cli 来触发录制。

源码: src/robot_config/robot_config/launch_builders/recording.py:1-226


控制模式系统

启动系统根据活动控制模式调整节点生成,控制模式决定机器人的操作行为。

控制模式确定

        graph TD
    START["启动参数: control_mode"]

    CHECK_OVERRIDE{"control_mode != ''?"}

    USE_OVERRIDE["robot_config['default_control_mode'] = control_mode<br/>line 178"]

    USE_DEFAULT["active_control_mode = robot_config.get('default_control_mode', 'model_inference')<br/>line 180"]

    VALIDATE{"模式存在于<br/>control_modes?"}

    ERROR["抛出错误:<br/>无效控制模式"]

    PROCEED["继续使用 active_control_mode"]

    START --> CHECK_OVERRIDE

    CHECK_OVERRIDE -->|是| USE_OVERRIDE
    CHECK_OVERRIDE -->|否| USE_DEFAULT

    USE_OVERRIDE --> USE_DEFAULT

    USE_DEFAULT --> VALIDATE

    VALIDATE -->|否| ERROR
    VALIDATE -->|是| PROCEED
    

源码: src/robot_config/launch/robot.launch.py:177-181

控制模式对节点生成的影响

每个控制模式定义:1. 要启动的控制器 - 在 control_modes[mode].controllers 中列出 2. 推理启用 - control_modes[mode].inference.enabled 3. 执行器配置 - control_modes[mode].executor

示例模式:

control_modes:
  teleop:
    controllers:
      - joint_state_broadcaster
      - arm_position_controller
      - gripper_position_controller
    inference:
      enabled: false
      force_disable: true
    executor:
      type: topic
      mode: teleop

  model_inference:
    controllers:
      - joint_state_broadcaster
      - arm_position_controller
      - gripper_position_controller
    inference:
      enabled: true
      model: so101_act
    executor:
      type: topic
      mode: model_inference

  moveit_planning:
    controllers:
      - joint_state_broadcaster
      - arm_trajectory_controller
      - gripper_trajectory_controller
    inference:
      enabled: false
    executor:
      type: action
      mode: moveit_planning

源码: src/robot_config/config/robots/so101_single_arm.yaml:46-103


条件节点生成

启动系统使用条件逻辑根据运行时标志生成适当的节点。

推理管道启用

        graph TD
    WITH_INFERENCE_ARG["启动参数: with_inference"]

    CHECK_ARG{"with_inference != ''?"}

    USE_ARG["with_inference = parse_bool(with_inference_str)<br/>line 186"]

    AUTO_DETECT["with_inference = control_modes[mode].inference.enabled<br/>line 189"]

    TELEOP_CHECK{"control_mode == 'teleop'<br/>&& with_inference_str == ''?"}

    FORCE_FALSE["with_inference = False<br/>line 193"]

    GENERATE{"with_inference?"}

    GEN_INFERENCE["generate_inference_node()<br/>generate_action_dispatcher_node()"]

    SKIP["跳过推理节点"]

    WITH_INFERENCE_ARG --> CHECK_ARG

    CHECK_ARG -->|是| USE_ARG
    CHECK_ARG -->|否| AUTO_DETECT

    USE_ARG --> TELEOP_CHECK
    AUTO_DETECT --> TELEOP_CHECK

    TELEOP_CHECK -->|是| FORCE_FALSE
    TELEOP_CHECK -->|否| GENERATE

    FORCE_FALSE --> GENERATE

    GENERATE -->|True| GEN_INFERENCE
    GENERATE -->|False| SKIP
    

逻辑: 1. 如果显式设置 --with_inference,使用该值 2. 否则,从 control_modes[mode].inference.enabled 自动检测 3. 在 teleop 模式下强制禁用(除非显式覆盖)

源码: src/robot_config/launch/robot.launch.py:183-196

仿真与硬件模式

        graph TD
    USE_SIM["启动参数: use_sim"]

    PARSE["parse_bool(use_sim_str, default=False)"]

    SIM_CHECK{"use_sim == True?"}

    SIM_PATH["仿真路径"]
    HW_PATH["硬件路径"]

    SIM_GAZEBO["生成 Gazebo 节点<br/>gzserver, gzclient, spawn_entity"]
    SIM_CTRL["控制器等待 Gazebo 的<br/>controller_manager"]
    SIM_CAM["跳过物理相机驱动<br/>使用 Gazebo 相机插件"]

    HW_CTRL["启动 ros2_control_node<br/>然后启动控制器"]
    HW_CAM["生成相机驱动节点<br/>usb_cam, realsense2_camera"]
    HW_HARDWARE["加载 so101_hardware 插件"]

    USE_SIM --> PARSE
    PARSE --> SIM_CHECK

    SIM_CHECK -->|是| SIM_PATH
    SIM_CHECK -->|否| HW_PATH

    SIM_PATH --> SIM_GAZEBO
    SIM_GAZEBO --> SIM_CTRL
    SIM_CTRL --> SIM_CAM

    HW_PATH --> HW_CTRL
    HW_CTRL --> HW_CAM
    HW_CAM --> HW_HARDWARE
    

源码: src/robot_config/launch/robot.launch.py:142-217


启动示例

示例 1:带模型推理的仿真

ros2 launch robot_config robot.launch.py \
  robot_config:=so101_single_arm \
  use_sim:=true \
  control_mode:=model_inference

生成的节点: 1. Gazebo 服务器/客户端 2. spawn_entity(URDF 机器人) 3. joint_state_broadcaster 4. arm_position_controller 5. gripper_position_controller 6. Gazebo 相机插件 7. static_transform_publisher(用于相机) 8. lerobot_policy_node 9. action_dispatcher_node

源码: src/robot_config/launch/robot.launch.py:22-26

示例 2:带遥控和录制的真实硬件

ros2 launch robot_config robot.launch.py \
  robot_config:=so101_single_arm \
  control_mode:=teleop \
  record:=true \
  record_mode:=episodic

生成的节点: 1. ros2_control_node (so101_hardware) 2. joint_state_broadcaster 3. arm_position_controller 4. gripper_position_controller 5. usb_cam 节点(顶部、腕部相机) 6. realsense_node(前置相机) 7. static_transform_publisher(相机坐标系) 8. robot_teleop_node 9. episode_recorder(后台服务)

需要手动步骤:

# 在单独终端中
ros2 run dataset_tools record_cli

源码: src/robot_config/launch/robot.launch.py:28-32, src/robot_config/robot_config/launch_builders/recording.py:102-168

示例 3:MoveIt 规划模式

ros2 launch robot_config robot.launch.py \
  robot_config:=so101_single_arm \
  control_mode:=moveit_planning \
  use_sim:=true \
  moveit_display:=true

生成的节点: 1. Gazebo 服务器/客户端 2. spawn_entity 3. joint_state_broadcaster 4. arm_trajectory_controller 5. gripper_trajectory_controller 6. move_group (MoveIt) 7. moveit_gateway_node 8. rviz2(带 MoveIt 配置)

源码: src/robot_config/launch/robot.launch.py:34-38


参数传播

启动系统通过多种机制将配置参数传播到下游节点:

1. ROS 参数(节点特定)

# 示例: action_dispatcher_node
parameters=[{
    "robot_config_path": str(robot_config_path),
    "control_frequency": 100.0,
    "queue_size": 100,
    "watermark_threshold": 20
}]

源码: src/robot_config/robot_config/launch_builders/execution.py:270-305

2. 环境变量

# 为 LeRobot 环境注入 PYTHONPATH
env = prepare_lerobot_env()
# env['PYTHONPATH'] = '/path/to/lerobot:...'

Node(
    package='inference_service',
    executable='lerobot_policy_node',
    env=env,  # LeRobot 环境
    parameters=[...]
)

源码: src/robot_config/robot_config/launch_builders/execution.py:99-123

3. 直接文件路径

# 传递 robot_config_path 用于契约加载
robot_config_path = robot_config.get('_config_path', '')
parameters=[{'robot_config_path': robot_config_path}]

节点直接从 YAML 加载契约:

# 在 episode_recorder.py 中
from robot_config.loader import load_robot_config
robot_config = load_robot_config(robot_config_path)
contract = robot_config.to_contract()

源码: src/dataset_tools/dataset_tools/episode_recorder.py:194-206, src/inference_service/inference_service/lerobot_policy_node.py:158-159


调试启动问题

启用调试输出

ros2 launch robot_config robot.launch.py \
  robot_config:=so101_single_arm \
  use_sim:=true \
  --log-level debug

禁用自动启动以便手动检查

ros2 launch robot_config robot.launch.py \
  robot_config:=so101_single_arm \
  auto_start_controllers:=false

这会启动 ros2_control_node 但不启动控制器。手动启动它们进行调试:

ros2 control load_controller joint_state_broadcaster
ros2 control set_controller_state joint_state_broadcaster active

常见问题

问题

原因

解决方案

“Controller already loaded”

之前的 ros2_control_node 仍在运行

运行 p kill -9 ros2_control_node./scripts/cleanup_ros.sh

“Config file not found”

无效的 robot_config 名称

检查 config/robots/ 目录中的可用配置

“Invalid control mode”

control_mode 不在 YAML 中

验证 robot_config YAML 中的 control_modes 部分

推理节点失败

缺少模型 检查点

验证 robot_config 中 models[name].path 存在

相机无法打开

权限被拒绝

运行 sudo chmod 666 /dev/video* 或将用户添加到 video

源码: src/robot_config/launch/robot.launch.py:46-49, src/robot_config/README.en.md:510-516


总结

启动系统提供 配置驱动、模块化的编排层 用于机器人启动:

  1. 单一入口点: robot.launch.py 加载配置并编排所有构建器

  2. 构建器模式: 每个子系统有专门的构建器模块

  3. 条件生成: 节点根据控制模式、use_sim 和功能标志生成

  4. 参数传播: 配置通过 ROS 参数、环境变量和文件路径流向节点

  5. 模式灵活性: 支持具有不同控制器/执行器配置的 teleop、model_inference 和 moveit_planning

关键文件: - src/robot_config/launch/robot.launch.py - 主编排器 - src/robot_config/robot_config/launch_builders/ - 构建器模块 - src/robot_config/config/robots/so101_single_arm.yaml - 配置源

源码: src/robot_config/launch/robot.launch.py:1-360, src/robot_config/robot_config/launch_builders/