🤖 roboto_origin_03 Wiki
首页 / 项目根 / 手柄控制与服务接口

本文档聚焦 Roboto Atom01 在 ROS2 部署阶段的人机交互层,系统阐述推理节点(inference_node)如何通过标准 ROS2 sensor_msgs/Joy 消息接收手柄输入,以及如何通过 std_srvs/Trigger 服务接口向外部暴露电机生命周期与推理状态的控制能力。理解这一层的设计,是连接底层实时控制与上层交互决策的关键枢纽。

Sources: inference_node.hpp


系统架构与数据流

手柄控制与服务接口并非孤立模块,而是嵌入在推理节点内部的一组 ROS2 订阅端与服务端。系统启动时,start_robot.sh 会同时拉起 joy_node(ROS2 官方 joy 包)与 roboto_inference_node。前者通过 Linux input/event 子系统读取物理手柄信号并发布到 /joy;后者订阅该话题,将摇杆与按键映射为速度指令或状态切换事件,同时对外暴露 10 个标准触发服务。

以下 Mermaid 图展示了关键节点、话题与服务的拓扑关系:

graph LR
    A[joy_node<br/>ROS2 joy 包] -->|/joy<br/>sensor_msgs/Joy| B[roboto_inference_node]
    C[外部遥控/导航栈] -->|/cmd_vel<br/>geometry_msgs/Twist| B
    B -->|/action<br/>sensor_msgs/JointState| D[RobotInterface]
    B -->|/imu<br/>sensor_msgs/Imu| E[上位机/可视化]
    B -->|/joint_states<br/>sensor_msgs/JointState| E
    D -->|CAN/Serial| F[电机驱动器]
    D -->|Serial/CAN| G[IMU]
    B -.->|std_srvs/Trigger| H[服务调用方<br/>CLI/SDK]

图中 B 节点是本文的核心对象。它的订阅端采用 KeepLast(1) + Best Effort + Volatile 的 QoS 策略,确保在实时控制场景下总是消费最新的手柄或速度指令,避免因历史消息堆积造成控制延迟。服务端则基于 std_srvs/srv/Trigger,这是一种无参请求-响应模式,适合“执行一次、反馈结果”的离散控制语义。

Sources: inference_node.hpp


手柄控制映射

推理节点在 subs_joy_callback 中完成手柄信号的语义解析。默认情况下,is_joy_control_true,表示手柄拥有最高控制权;通过 Y 键可随时切至 /cmd_vel 外部指令控制。

摇杆与扳机轴映射

手柄的模拟输入通过 sensor_msgs::msg::Joyaxes 数组读取,并经过 clip_cmd 参数限幅后写入共享的 cmd_vel_ 缓冲区。clip_cmd 是一个六元数组 [vx_min, vx_max, vy_min, vy_max, ω_min, ω_max],在默认配置中对应 [-0.4, 0.6, -0.4, 0.4, -0.8, 0.8]

物理输入 Joy 索引 语义 限幅区间
右摇杆左右(X 轴) axes[4] 前后线速度 vx [clip_cmd[0], clip_cmd[1]]
右摇杆上下(Y 轴) axes[3] 左右线速度 vy [clip_cmd[2], clip_cmd[3]]
LT(左扳机) axes[2] 左转角速度 ω [clip_cmd[4], 0]
RT(右扳机) axes[5] 右转角速度 ω [0, clip_cmd[5]]

当 LT 与 RT 同时未按下时,角速度归零;若同时按下,以 LT 逻辑优先(代码中先判断 axes[2] < 0)。

按键映射

按键通过 buttons 数组读取,所有按键均在上升沿触发(当前帧为 1 且上一帧为 0 时生效),避免长按导致重复执行。

按键 Joy 索引 功能 适用模式
X buttons[2] 电机使能 / 失能切换 全局
A buttons[0] 关节复位到默认角度 全局(需电机已初始化)
B buttons[1] 推理启停切换 全局
Y buttons[3] 切换手柄控制 ↔ /cmd_vel 控制 全局
LB buttons[4] 切换策略模式(中断 / 运动策略) BeyondMimic / Interrupt 模式
RB buttons[5] 切换运动序列 BeyondMimic 模式

当按下 X 键时,节点会暂停推理(若正在运行),然后调用 RobotInterface::init_motors()deinit_motors(),实现安全的电机上下电流程。B 键则直接对 is_running_ 原子变量取反,控制 ONNX 推理循环的启停。

Sources: ros_interface.cpp


双模式控制机制

推理节点支持两种互斥的指令输入通道,由 is_joy_control_ 原子标志仲裁:

两种模式共享相同的 cmd_mutex_ 锁与 clip_cmd 限幅逻辑,因此切换时不会引发数据竞争。该设计使得机器人可以在手柄遥控与自主导航栈之间无缝切换——例如,在调试阶段用手柄介入,在运行阶段交由导航算法接管。

Sources: ros_interface.cpp


服务接口详解

推理节点在构造函数中注册了 10 个 std_srvs/srv/Trigger 服务,覆盖电机生命周期管理、状态读取与推理控制。每个服务均在执行前进行严格的前置状态校验,防止在推理运行或电机未初始化时执行危险操作。

服务名 功能描述 前置条件 执行效果
init_motors 初始化电机 电机未初始化 调用 RobotInterface::init_motors(),电机上电
deinit_motors 反初始化电机 电机已初始化 调用 RobotInterface::deinit_motors(),电机下电
reset_joints 复位关节 推理已暂停、电机已初始化 关节回到 joint_default_angle
set_zeros 标定零位 推理已暂停、电机已初始化 设置当前关节位置为零点
clear_errors 清除驱动错误 RobotInterface 已实例化 清除底层电机错误标志
refresh_joints 刷新电机状态 电机已初始化 刷新内部关节状态缓存
read_joints 读取并发布关节状态 电机已初始化 发布 /joint_states
read_imu 读取并发布 IMU 状态 IMU 已初始化 发布 /imu
start_inference 启动 ONNX 推理 推理未运行 is_running_ = true
stop_inference 暂停 ONNX 推理 推理正在运行 is_running_ = false

所有服务均采用 try-catch 包裹底层调用,并将异常信息通过 response->message 返回给调用方。例如,reset_joints 在推理运行时立即拒绝请求,返回 "Inference is running, cannot reset joints.",这种**失效安全(fail-safe)**设计是实机部署的关键保障。

Sources: ros_interface.cpp


启动流程与运行环境

start_robot.sh 是生产环境下的统一入口脚本,它负责完成编译、DDS 配置、会话管理与节点健康检查。其启动顺序如下:

sequenceDiagram
    participant U as 用户
    participant S as start_robot.sh
    participant I as inference_session<br/>(screen)
    participant J as joy_session<br/>(screen)
    participant DDS as Fast DDS<br/>共享内存

    U->>S: 执行脚本
    S->>S: 检查并 source ROS2 环境
    S->>S: 导出 Fast DDS XML 配置
    S->>S: colcon build 编译推理包
    S->>I: 启动 inference_node
    S->>S: ros2 node list 检查
    S->>J: 启动 joy_node
    S->>S: ros2 node list 检查
    S->>S: verify_dds_effectiveness
    S->>U: 输出 screen 会话查看命令

脚本通过 screen 将两个节点分别隔离在独立会话中:inference_session 运行推理主节点,joy_session 运行手柄驱动节点。两者均继承脚本中设置的 RMW_IMPLEMENTATION=rmw_fastrtps_cppFASTRTPS_DEFAULT_PROFILES_FILE 环境变量,确保共享内存传输生效。用户可通过 screen -r inference_sessionscreen -r joy_session 实时查看日志,按 Ctrl+A 再按 D 即可 detach 而不中断进程。

Sources: start_robot.sh


实时线程与安全机制

推理节点内部包含三条具有不同实时优先级的线程:

由于手柄回调运行于 main 线程,而 cmd_vel_ 同时被 inference 线程读取,节点使用 cmd_mutex_ 保护速度指令缓冲区。同理,act_mutex_mode_mutex_perception_mutex_ 等锁分别隔离了动作输出、策略模式切换与感知观测的并发访问,避免在推理循环中途被手柄事件打断而产生不一致状态。

Sources: inference_node.cpp


配置要点与扩展建议

手柄控制的行为可通过 inference.yaml 中的参数调整,最常用的是 clip_cmdjoint_default_angle

若需集成自定义手柄(如 PS5 或 Switch Pro 手柄),只需确保 joy_node 正确映射到 Linux /dev/input/js0,并根据实际 axesbuttons 顺序微调 subs_joy_callback 中的索引即可,无需改动推理循环本身。

Sources: inference.yaml


阅读衔接

本文档聚焦于人机交互与服务暴露层。若需了解推理节点内部 ONNX 运行时的模型加载、观测栈管理与策略切换逻辑,请参阅前序文档 推理节点与ONNX策略部署;若希望通过 Python SDK 以编程方式调用上述服务接口或封装新的交互逻辑,请继续阅读 Python SDK与二次开发