ROS2 多机器人通用 Driver 层复盘:BaseRobotDriver 到多平台 Mock 切换实现 1. Driver 层的作用在机器人二次开发中不能把所有逻辑都写在 ROS2 节点里。ROS2 节点更适合负责Topic 通信 Service 调用 参数读取 launch 启动 日志输出 任务逻辑组织而机器人底层相关能力应该交给 Driver 层例如连接机器人 读取状态 发送速度 普通停止 急停 模式切换这样设计的核心思想是ROS2 节点负责“通信和调度”Driver 层负责“真正控制机器人”。后续如果从 Mock 机器人换成真实四足机器人、人形机器人或者机械臂只需要替换 Driver不需要大改上层 ROS2 节点。2. BaseRobotDriver 统一接口BaseRobotDriver是所有机器人 Driver 的统一标准。它不直接控制机器人而是规定每一种机器人都应该具备哪些基础能力。推荐接口connect() get_state() send_velocity(linear_x, angular_z) stop() emergency_stop() set_mode(mode)每个接口的含义接口作用connect()连接机器人或初始化模拟机器人get_state()获取机器人状态例如电量、速度、模式、急停状态send_velocity()发送速度命令stop()普通停止emergency_stop()急停进入安全停止状态set_mode()切换机器人模式例如stand/walk/stop/estop3. MockRobotDriver 的意义MockRobotDriver是一个模拟机器人驱动。它不需要真实硬件而是在代码里模拟机器人状态和控制行为。它可以模拟是否连接 connected 当前模式 mode 当前电量 battery 当前线速度 linear_x当前角速度 angular_z 是否急停 emergency_stop示例状态{connected: true,mode: stand,battery: 87.5,linear_x: 0.0,angular_z: 0.0,emergency_stop: false}没有真实机器人时也能先把 ROS2 通信、状态发布、速度控制、安全保护这些上层逻辑跑通。4./robot/state状态发布机器人状态是持续变化的数据所以应该用 ROS2 Topic 发布。推荐话题名/robot/state前期为了简单可以用std_msgs/msg/String 里面放 JSON 字符串。数据流如下MockRobotDriver | | get_state() v state_publisher_node | | publish v /robot/state也就是说state_publisher_node定时调用driver.get_state()Driver 返回当前机器人状态节点把状态发布到/robot/state其他模块比如 FSM、安全模块、可视化模块都可以订阅这个状态验证命令ros2 topic echo /robot/state也可以查看发布频率ros2 topic hz /robot/state5./cmd_vel速度控制移动机器人常用/cmd_vel表示速度控制命令。话题名/cmd_vel消息类型geometry_msgs/msg/Twist常用字段linear.x # 前进 / 后退速度 angular.z # 左转 / 右转角速度控制节点的数据流/cmd_vel | | Twist 消息 v control_node | | 提取 linear.x 和 angular.z v driver.send_velocity()也就是说control_node不直接控制硬件而是把 ROS2 收到的速度命令转交给 Driver 层。/cmd_vel负责 ROS2 通信send_velocity()负责底层控制。6. 速度限幅速度控制不能完全相信上游输入。因为/cmd_vel可能来自键盘控制节点 视觉闭环节点 FSM 状态机 导航模块 测试脚本如果某个节点发出了过大的速度机器人可能发生危险。所以控制节点里要加入速度限幅。例如参数max_linear_speed: 0.5 max_angular_speed: 1.0如果收到linear.x 9.9 angular.z 9.9实际下发给 Driver 的速度应该被限制成linear.x 0.5 angular.z 1.0超过最大速度就裁剪到安全范围内。7. 普通停止和急停机器人停止至少要区分两种普通停止 stop() 急停 emergency_stop()功能含义后续能否继续运动stop()普通停止把速度清零可以emergency_stop()急停进入安全锁定状态不应该直接继续stop()是“停一下”emergency_stop()是“危险情况下立刻停并且锁住运动”。急停后普通速度命令不应该继续生效。例如触发 emergency_stop | v 速度清零 | v emergency_stop True | v 后续 send_velocity() 被拒绝急停可以设计成 Service/robot/emergency_stop调用命令ros2 service call /robot/emergency_stop std_srvs/srv/Trigger {}8. Heartbeat 心跳机制Heartbeat 中文叫心跳机制。它用来判断控制端是否还在线。例如控制端持续发送/robot/heartbeat如果机器人控制节点长时间没有收到心跳就认为控制端可能崩溃或断开连接。这时应该自动调用driver.stop()它主要解决这个危险场景程序崩溃后机器人继续执行最后一次速度命令。例如机器人正在前进控制程序突然退出如果没有心跳超时保护机器人可能还会保持前进状态。心跳逻辑可以理解为如果当前时间 - 上一次心跳时间 heartbeat_timeout 自动停止机器人9. 多平台 Driver 切换为了体现“多机器人平台二次开发”不能只写一个 Mock 机器人。可以设计多个 DriverBaseRobotDriver | ├── MockRobotDriver ├── MockDogDriver └── MockHumanoidDriver它们都实现同一套接口connect() get_state() send_velocity() stop() emergency_stop() set_mode()区别只是底层实现不同。例如 MockDogDriver 模拟四足机器人MockHumanoidDriver 模拟人形机器人MockRobotDriver 通用模拟机器人上层 ROS2 节点不需要知道当前是哪种机器人只需要调用统一接口。这就是接口抽象的价值上层代码不变底层 Driver 可替换。10. 配置文件切换机器人类型为了避免在代码里写死机器人类型可以使用配置文件。例如robot_type: mock_dog max_linear_speed: 0.5 max_angular_speed: 1.0 heartbeat_timeout: 2.0也可以切换成robot_type: mock_humanoid切换机器人平台时只改配置不改代码。11. 推荐项目结构可以采用下面这种结构robot_base/ ├── robot_base/ │ ├── drivers/ │ │ ├── base_driver.py │ │ ├── mock_robot_driver.py │ │ ├── mock_dog_driver.py │ │ └── mock_humanoid_driver.py │ │ │ ├── nodes/ │ │ ├── state_publisher_node.py │ │ └── control_node.py │ │ │ ├── config/ │ │ └── robot_config.yaml │ │ │ └── utils/ │ └── driver_factory.py │ ├── launch/ │ └── robot_base.launch.py ├── package.xml └── setup.py重点是分清职责模块职责drivers/封装机器人底层能力nodes/ROS2 通信节点config/参数和机器人类型配置launch/一键启动driver_factory.py根据配置创建不同 Driver