MentorPi A1 底盘接入开发实践:让自研Web系统接管机器人底盘
ROS2机器人控制接口设计与接入实践 本文探讨了如何在不重写底盘控制逻辑的前提下,通过ROS2标准化接口实现Web控制端对MentorPi A1机器人底盘的有效接管。文章首先分析了阿克曼底盘的运动特性与控制需求,提出基于ROS2的分层架构设计,将Web控制端与硬件解耦。详细解析了关键ROS2 Topic(如/cmd_vel、/odom)、Service和参数配置,特别说明了底盘控制、里程计、激光雷
基于 ROS2 + 深度相机 + 激光雷达的控制接口设计与接入实践
当机器人平台进入业务落地阶段,真正决定系统可维护性的,往往不是“能不能跑起来”,而是“控制层能不能被标准化接管”。很多团队拿到一台现成的 ROS2 机器人后,第一反应是直接改原有 App 或者把业务逻辑硬塞进车端节点里。短期看能出效果,长期看却会把控制协议、业务流程和设备依赖耦合在一起,最终导致二次开发成本越来越高。
这篇文章聚焦一个更工程化的问题:如果你已经有一套自己的 Web 控制端,如何在不重写底盘控制逻辑、不侵入业务系统的前提下,接管 MentorPi A1 这类 ROS2 机器人底盘。
真实接入效果:
这里只讨论控制层能力:
- 底盘前进、后退、左转、右转、停止
- 速度配置策略
- 激光雷达数据接入与显示
- 视频流获取与显示
- 目标追踪接入与显示
- WebSocket 通信
- 心跳包与在线状态检测
- 自研系统如何接入
不讨论建图、导航、SLAM、自动驾驶等上层算法话题。
一、为什么选择 MentorPi A1
MentorPi A1 的价值,不在于它“功能很多”,而在于它具备一个比较完整的机器人控制闭环:计算平台、底层控制器、相机、雷达、编码器、电机和 ROS2 软件栈是成套交付的。对于做二次开发的工程师来说,这意味着你不需要先花大量时间做设备拼装与驱动打通,而是可以直接把精力集中在接口接入、控制协议设计和业务联动上。
从公开资料看,MentorPi A1 是一套基于 Raspberry Pi 5 的 ROS2 机器人平台,官方资料给出的系统环境为 Ubuntu 22.04 LTS + ROS2 Humble,并配备 Ackermann 阿克曼底盘、TOF 激光雷达、3D 深度相机、高精度编码器电机,以及一套低层控制器与主控协同工作的双控制器架构。主控侧负责 ROS2、视觉、策略和网络通信,低层控制器负责电机、舵机、电池、电设等实时设备控制,这种分层对于业务系统接管非常重要,因为 Web 控制端实际上只需要面对主控层提供的 ROS2 接口,而不是直接操作底层硬件寄存器。
对于底盘形态,A1 采用的是阿克曼转向结构,而不是麦克纳姆轮。阿克曼的特点是运动学更接近真实车辆:可以稳定前进、后退和转向,但不能横向平移。这意味着 Web 控制端在设计方向键、摇杆和速度模型时,不能沿用全向底盘那套“x/y 双平面自由运动”的交互逻辑,而应当围绕“线速度 + 转向角速度”的组合来组织控制协议。
从接入角度看,MentorPi A1 还有三个优点。第一,ROS2 接口清晰,底盘控制、里程计、电池、雷达、图像流都有对应入口;第二,设备已经具备相机与雷达,便于业务系统同时获得运动和感知数据;第三,平台原生支持网络控制,适合通过 WebSocket + HTTP 视频流的方式做远程控制端。
二、系统控制架构设计
如果目标是“让 Web 控制端接管底盘”,最核心的设计原则只有一句话:业务系统不要直接碰硬件,统一通过 ROS2 控制层收发消息。
在这个结构里,Web 控制端只做三件事:
- 通过 WebSocket 发布控制指令
- 通过 WebSocket 订阅状态数据
- 通过 HTTP 获取视频流
底盘控制节点只做两件事:
- 把标准速度指令转换成底盘可执行的电机/舵机动作
- 把里程计、电池、雷达等状态持续发布出来
用 Mermaid 表达,整个链路可以抽象为:
[图片:系统控制架构图]
这个架构的好处是明确的:
- Web 层和底盘硬件解耦
- 控制协议可以被第三方系统复用
- ROS2 节点可以持续演进,而不影响上层业务
- 视频、雷达、底盘控制分离,便于按需替换
对工程团队而言,这种分层还意味着一个重要能力:以后换前端、不换底盘;换业务系统、不换控制协议;换交互方式、不换 ROS2 接口。
三、ROS2控制链路解析
从本地源码看,底盘控制链路的核心非常清晰:Web 侧通过 rosbridge_server 进入 ROS2,总线中的关键接口集中在速度控制、状态反馈、雷达模式和图像流四类。
1. 关键 Topic
| Topic | Type | 用途 |
|---|---|---|
/controller/cmd_vel |
geometry_msgs/msg/Twist |
主底盘控制入口 |
/cmd_vel |
geometry_msgs/msg/Twist |
兼容控制入口,适合作为急停补发零速通道 |
/odom |
nav_msgs/msg/Odometry |
融合后的运动状态 |
/odom_raw |
nav_msgs/msg/Odometry |
控制器原始里程计 |
/ros_robot_controller/battery |
std_msgs/msg/UInt16 |
电池电压原始值,单位毫伏 |
/scan_raw |
sensor_msgs/msg/LaserScan |
激光雷达原始扫描数据 |
/ascamera/camera_publisher/rgb0/image |
sensor_msgs/msg/Image |
RGB 图像流 |
/ascamera/camera_publisher/depth0/image_raw |
sensor_msgs/msg/Image |
深度图像流 |
其中最值得重点解释的是 /controller/cmd_vel 和 /cmd_vel。
从控制器节点实现来看,二者最终都会进入底盘执行逻辑,但角色不同:
/controller/cmd_vel:主控制入口,直接进入底盘控制回调/cmd_vel:兼容入口,源码中带有限幅保护
这意味着在新系统接入时,应当把 /controller/cmd_vel 作为常规遥控链路,把 /cmd_vel 作为安全补发或兼容入口,而不是二者混用。
2. 关键 Service
| Service | Type | 用途 |
|---|---|---|
/lidar_app/enter |
std_srvs/srv/Trigger |
进入雷达控制模块 |
/lidar_app/exit |
std_srvs/srv/Trigger |
退出雷达控制模块 |
/lidar_app/set_running |
interfaces/srv/SetInt64 |
切换雷达模式 |
/lidar_app/set_param |
interfaces/srv/SetFloat64List |
设置雷达阈值、角度、速度 |
/lidar_app/heartbeat |
std_srvs/srv/SetBool |
雷达模块心跳保活 |
3. 关键 Parameter
| Parameter | Type | 默认值 | 用途 |
|---|---|---|---|
pub_odom_topic |
bool |
true |
是否发布 /odom_raw |
base_frame_id |
string |
base_footprint |
底盘坐标系 |
odom_frame_id |
string |
odom |
里程计坐标系 |
linear_correction_factor |
double |
1.00 |
线速度修正 |
angular_correction_factor |
double |
1.00 |
角速度修正 |
machine_type |
string |
环境变量注入 | 区分底盘类型 |
这里要特别说明一点:源码里没有独立的“Web 速度配置服务”。也就是说,线速度、角速度的日常控制上限,当前更适合由 Web 控制端做本地限幅,而不是指望车端存在一个现成的速度参数接口。
四、遥控控制实现
遥控控制是所有二次开发里最基础、也是最容易被设计错的模块。错误通常不在“消息不会发”,而在“运动模型和底盘形态不匹配”。
MentorPi A1 采用阿克曼底盘。阿克曼底盘和麦轮最大的区别是:
- 麦轮可以横移
- 阿克曼不能横移
- 麦轮更像平面机器人
- 阿克曼更像小车
因此,Web 控制端不应暴露 linear.y 的业务语义,而应该围绕 linear.x + angular.z 组织控制:
- 前进:
linear.x > 0 - 后退:
linear.x < 0 - 左转:
angular.z > 0 - 右转:
angular.z < 0 - 停止:全部置零
1. 遥控 JSON 示例
前进
{
"op": "publish",
"topic": "/controller/cmd_vel",
"msg": {
"linear": {
"x": 0.12,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
}
}
后退
{
"op": "publish",
"topic": "/controller/cmd_vel",
"msg": {
"linear": {
"x": -0.12,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
}
}
左转
{
"op": "publish",
"topic": "/controller/cmd_vel",
"msg": {
"linear": {
"x": 0.10,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": 0.30
}
}
}
右转
{
"op": "publish",
"topic": "/controller/cmd_vel",
"msg": {
"linear": {
"x": 0.10,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": -0.30
}
}
}
停止
{
"op": "publish",
"topic": "/controller/cmd_vel",
"msg": {
"linear": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
}
}
安全急停补发
{
"op": "publish",
"topic": "/cmd_vel",
"msg": {
"linear": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
}
}
2. ROS2 控制链路
3. 工程建议
- 常规遥控统一走
/controller/cmd_vel - 停止或断线保护时,同时向
/controller/cmd_vel与/cmd_vel发送零速 - 不要把阿克曼底盘当成全向底盘,不要暴露横移控制
- 左右转最好带少量前进量,手感会比“原地摆头”更接近真实车辆
五、速度配置设计
很多团队在接入机器人底盘时,会把“速度配置”理解成一个必须由车端提供的参数接口。但从工程实践看,未必如此。
对于现有底盘源码,真正已经落在控制器里的速度相关逻辑主要有两类:
/cmd_vel路径内的安全限幅- 控制器参数里的标定修正系数
这意味着“业务系统里给用户一个速度滑块”这件事,最合理的落点其实在 Web 控制端本身。也就是:
- Web 端保存用户的速度设置
- Web 端在发
Twist前做限幅 - ROS2 只负责执行已经成型的运动指令
1. 建议的速度配置表
| 配置项 | 单位 | 建议值 | 说明 |
|---|---|---|---|
| 最大线速度 | m/s |
0.05 ~ 0.20 |
手动遥控推荐范围 |
| 最大角速度 | rad/s |
0.10 ~ 0.50 |
手动转向推荐范围 |
| 加速度 | m/s² |
业务侧决定 | 当前源码未提供独立接口 |
| 减速度 | m/s² |
业务侧决定 | 当前源码未提供独立接口 |
| 急停线速度 | m/s |
0 |
强制停车 |
| 急停角速度 | rad/s |
0 |
强制停车 |
2. 建议的 Web 配置 JSON
{
"module": "speed_config",
"data": {
"max_linear_speed": 0.20,
"max_angular_speed": 0.50,
"acceleration": null,
"deceleration": null
}
}
3. 与源码的映射关系
| 设计项 | 当前源码是否直接支持 | 说明 |
|---|---|---|
| 最大线速度配置接口 | 否 | 应由 Web 控制端本地限幅 |
| 最大角速度配置接口 | 否 | 应由 Web 控制端本地限幅 |
/cmd_vel 安全限幅 |
是 | 车端已做保护 |
| 线速度修正参数 | 是 | 属于标定参数,不是交互级速度配置 |
| 角速度修正参数 | 是 | 属于标定参数,不是交互级速度配置 |
4. 一个很实用的结论
如果你只是做“业务系统接管底盘”,先别着急改车端参数接口。把速度配置做成 Web 层策略,往往更快、更稳、更便于灰度调试。
六、激光雷达接入实践
激光雷达在这类系统里有两个价值:
- 给车端算法使用
- 给 Web 端做二维环境可视化
从本地源码看,雷达控制节点真正使用的是 sensor_msgs/msg/LaserScan,并订阅 /scan_raw。同时,源码虽然保留了 /scan 相关命名,但当前默认链路里滤波节点并未启用,所以业务上更应该以 /scan_raw 为准。
1. LaserScan 是什么
LaserScan 可以理解成“一圈角度上的距离数组”。它的核心字段包括:
angle_min:起始角angle_max:结束角angle_increment:每个采样点的角度步进range_min:最小有效距离range_max:最大有效距离ranges:每个角度对应的距离值
典型消息示例:
{
"op": "publish",
"topic": "/scan_raw",
"msg": {
"angle_min": -3.14159,
"angle_max": 3.14159,
"angle_increment": 0.01745,
"range_min": 0.05,
"range_max": 12.0,
"ranges": [0.62, 0.61, 0.60, 0.59]
}
}
2. 雷达控制接口
雷达模块当前支持 enter -> set_running -> heartbeat 的工作流。
进入雷达模块
{
"op": "call_service",
"id": "srv_lidar_enter",
"service": "/lidar_app/enter",
"type": "std_srvs/srv/Trigger",
"args": {}
}
设置模式
{
"op": "call_service",
"id": "srv_lidar_mode",
"service": "/lidar_app/set_running",
"type": "interfaces/srv/SetInt64",
"args": {
"data": 1
}
}
设置参数
{
"op": "call_service",
"id": "srv_lidar_param",
"service": "/lidar_app/set_param",
"type": "interfaces/srv/SetFloat64List",
"args": {
"data": [0.60, 90.0, 0.20]
}
}
参数数组的含义固定为:
| 序号 | 含义 | 单位 | 范围 |
|---|---|---|---|
| 1 | 阈值 threshold |
m |
0.3 ~ 1.5 |
| 2 | 扫描角 scan_angle |
deg |
0 ~ 90 |
| 3 | 行为速度 speed |
m/s |
> 0 |
3. 如何推送到 Web
有两种常见方式:
- 直接把
LaserScan通过 rosbridge 转给浏览器 - 在中间层转换成前端更友好的二维点集
如果目标是快速接入,建议先走第一种。因为 LaserScan 本身信息足够完整,前端只需要做一次极坐标转笛卡尔坐标:
x = range * cos(angle)
y = range * sin(angle)
这样就能直接在 Canvas 或 WebGL 里画出二维雷达扇形图。
七、视频流接入实践
对 Web 控制端来说,视频永远是控制体验的关键组成部分。没有视频,远程操控几乎不可用;视频方案选错,带宽和延迟又会立刻失控。
常见的视频接入方案有四种:
- MJPEG
- RTSP
- WebRTC
- WebSocket 二进制流
从本地源码实际情况看,当前平台采用的是 web_video_server + HTTP MJPEG / Snapshot`,而不是 RTSP、WebRTC 或 WebSocket 视频流。
这套方案的优点很朴素:
- 与 ROS 图像 Topic 对接简单
- 浏览器接入成本低
- 调试方便
- 对控制台类应用足够实用
缺点也很明确:
- 带宽占用相对更高
- 长时间多路视频时不如 WebRTC 高效
1. 实际视频源
| 图像源 | Topic | 类型 |
|---|---|---|
| RGB 图像 | /ascamera/camera_publisher/rgb0/image |
sensor_msgs/msg/Image |
| 深度图像 | /ascamera/camera_publisher/depth0/image_raw |
sensor_msgs/msg/Image |
2. 访问地址示例
RGB 实时流
http://<robot-ip>:8080/stream?topic=/ascamera/camera_publisher/rgb0/image
深度图实时流
http://<robot-ip>:8080/stream?topic=/ascamera/camera_publisher/depth0/image_raw
RGB 单帧快照
http://<robot-ip>:8080/snapshot?topic=/ascamera/camera_publisher/rgb0/image
3. 视频播放链路
[图片:视频流链路图]
4. 工程建议
- 控制台优先用 MJPEG,简单稳定
- 带宽不稳定时,回退为 snapshot 模式
- 如果后续要做大规模多端观看,再评估 WebRTC
- 视频链路最好独立于控制链路,避免 WebSocket 被图像数据拖垮
八、心跳包与在线状态检测
机器人系统和普通后台系统最大的不同之一,是它必须面对“网络断了,但底盘还在动”这个风险。
所以,心跳机制不是锦上添花,而是远程控制系统的基础安全能力。
从本地源码看,当前雷达模块采用的是Service 型心跳,而不是自定义 JSON ping/pong。也就是业务系统按固定周期调用:
/lidar_app/heartbeat
后端收到 data=true 后刷新过期时间;超时则自动退出模块。
1. 心跳请求示例
{
"op": "call_service",
"id": "srv_heartbeat_001",
"service": "/lidar_app/heartbeat",
"type": "std_srvs/srv/SetBool",
"args": {
"data": true
}
}
2. 心跳响应示例
{
"op": "service_response",
"id": "srv_heartbeat_001",
"service": "/lidar_app/heartbeat",
"values": {
"success": true,
"message": "start"
},
"result": true
}
3. 推荐时序
4. 在线状态检测建议
真正可用的在线状态,不应该只看 WebSocket 是否连着,而应至少组合三类信息:
| 检测项 | 说明 |
|---|---|
| WebSocket 连接状态 | rosbridge 是否可达 |
/odom 或 /odom_raw 更新时间 |
底盘状态是否持续刷新 |
/ros_robot_controller/battery 更新时间 |
驱动侧是否仍在线 |
一个很实用的经验是:把“连接在线”和“设备在线”分开显示。前者说明浏览器能连上 ROS;后者说明底盘和驱动还在持续工作。
九、目标追踪接入实践
在很多业务场景里,远程控制并不只是“按按钮让车走”,还需要让机器人围绕一个可见目标持续调整位置。对这种需求,更合理的做法不是把视觉算法搬到浏览器,而是让 Web 控制端接管追踪模块的启停、参数、保活和结果显示,算法本体继续运行在 ROS2 侧。
从本地源码看,目标追踪节点已经暴露出一套相对完整的控制接口,Web 侧并不需要理解图像识别细节,只需要按标准 Service 和图像流接入即可。
1. ROS2 接口清单
| 功能 | ROS接口 | 类型 | 说明 |
|---|---|---|---|
| 进入追踪模块 | /object_tracking/enter |
std_srvs/srv/Trigger |
初始化追踪节点与图像订阅 |
| 退出追踪模块 | /object_tracking/exit |
std_srvs/srv/Trigger |
停止追踪并释放资源 |
| 开启/关闭追踪 | /object_tracking/set_running |
std_srvs/srv/SetBool |
控制追踪逻辑是否执行 |
| 设置追踪阈值 | /object_tracking/set_threshold |
interfaces/srv/SetFloat64 |
设置追踪阈值 |
| 设置目标颜色 | /object_tracking/set_target_color |
interfaces/srv/SetPoint |
通过图像坐标取色 |
| 读取目标颜色 | /object_tracking/get_target_color |
std_srvs/srv/Trigger |
返回当前追踪颜色 |
| 模块保活 | /object_tracking/heartbeat |
std_srvs/srv/SetBool |
心跳保活 |
| 图像输入 | /ascamera/camera_publisher/rgb0/image |
sensor_msgs/msg/Image |
RGB 输入流 |
| 结果图像 | /object_tracking/image_result |
sensor_msgs/msg/Image |
追踪结果预览 |
| 底盘控制输出 | /controller/cmd_vel |
geometry_msgs/msg/Twist |
追踪节点控制底盘 |
2. 目标追踪控制链路
3. 接入流程
推荐的接入顺序如下:
- 调用
/object_tracking/enter - 获取或显示 RGB 视频
- 在画面上选取目标颜色
- 调用
/object_tracking/set_target_color - 调用
/object_tracking/set_threshold - 调用
/object_tracking/set_running - 周期发送
/object_tracking/heartbeat - 显示
/object_tracking/image_result
4. 请求示例
进入追踪模块
{
"op": "call_service",
"id": "srv_track_enter",
"service": "/object_tracking/enter",
"type": "std_srvs/srv/Trigger",
"args": {}
}
开启追踪
{
"op": "call_service",
"id": "srv_track_run",
"service": "/object_tracking/set_running",
"type": "std_srvs/srv/SetBool",
"args": {
"data": true
}
}
设置追踪阈值
{
"op": "call_service",
"id": "srv_track_threshold",
"service": "/object_tracking/set_threshold",
"type": "interfaces/srv/SetFloat64",
"args": {
"data": 0.50
}
}
设置目标颜色
这里不是直接传 RGB 数值,而是传图像中的归一化取色点:
{
"op": "call_service",
"id": "srv_track_color",
"service": "/object_tracking/set_target_color",
"type": "interfaces/srv/SetPoint",
"args": {
"data": {
"x": 0.42,
"y": 0.36,
"z": 0.0
}
}
}
读取当前目标颜色
{
"op": "call_service",
"id": "srv_track_get_color",
"service": "/object_tracking/get_target_color",
"type": "std_srvs/srv/Trigger",
"args": {}
}
返回示例:
{
"op": "service_response",
"id": "srv_track_get_color",
"service": "/object_tracking/get_target_color",
"values": {
"success": true,
"message": "255,120,30"
},
"result": true
}
发送追踪心跳
{
"op": "call_service",
"id": "srv_track_heartbeat",
"service": "/object_tracking/heartbeat",
"type": "std_srvs/srv/SetBool",
"args": {
"data": true
}
}
5. 参数说明
| 参数 | 类型 | 单位 | 建议范围 | 说明 |
|---|---|---|---|---|
threshold |
number |
- | 0.1 ~ 0.8 |
追踪阈值,需现场调参 |
x |
number |
归一化 | 0 ~ 1 |
相对图像宽度的采样点 |
y |
number |
归一化 | 0 ~ 1 |
相对图像高度的采样点 |
| 心跳周期 | number |
s |
2 |
建议与雷达模块一致 |
6. 结果图像显示
目标追踪结果图像可以通过 web_video_server 直接显示:
http://<robot-ip>:8080/stream?topic=/object_tracking/image_result
7. 工程建议
- 目标追踪应当被设计成独立模块,不要和手动遥控写进同一个状态机
- 进入追踪前先确认视频可用,否则用户无法完成取色
set_target_color传的是图像坐标,不是 RGB 文本- 关闭追踪时同步停止心跳,并补发底盘零速
- 业务系统负责启停和显示,视觉算法仍应运行在 ROS2 侧
十、自研系统如何接管底盘
这是整篇文章最重要的部分。
如果你现在要做一个新的 Web 控制端,或者要把第三方业务系统接到这台机器人上,真正需要做的不是“理解全部源码”,而是先把控制面和显示面分离,再把接入清单列出来。
1. 最小可用接入清单
| 功能 | ROS接口 | Web接口 |
|---|---|---|
| 前进/后退/左转/右转/停止 | /controller/cmd_vel |
WebSocket publish |
| 急停保护 | /controller/cmd_vel + /cmd_vel |
WebSocket publish |
| 运动状态显示 | /odom |
WebSocket subscribe |
| 里程计兜底 | /odom_raw |
WebSocket subscribe |
| 电池状态显示 | /ros_robot_controller/battery |
WebSocket subscribe |
| 雷达数据显示 | /scan_raw |
WebSocket subscribe |
| 雷达模式控制 | /lidar_app/set_running |
WebSocket call_service |
| 雷达参数设置 | /lidar_app/set_param |
WebSocket call_service |
| 雷达保活 | /lidar_app/heartbeat |
WebSocket call_service |
| 目标追踪启停 | /object_tracking/enter /object_tracking/set_running |
WebSocket call_service |
| 目标追踪参数 | /object_tracking/set_threshold /object_tracking/set_target_color |
WebSocket call_service |
| 目标追踪保活 | /object_tracking/heartbeat |
WebSocket call_service |
| 目标追踪结果显示 | /object_tracking/image_result |
HTTP /stream |
| RGB 视频显示 | /ascamera/camera_publisher/rgb0/image |
HTTP /stream |
| 深度视频显示 | /ascamera/camera_publisher/depth0/image_raw |
HTTP /stream |
2. 推荐接管步骤
第一步:先接控制,不接业务
先做一个最小控制台,只包含:
- 连接状态
- 视频画面
- 四方向控制
- 急停按钮
- 电池和速度显示
这样可以先验证整个控制链路是否通。
第二步:再接状态
至少订阅:
/odom/odom_raw/ros_robot_controller/battery/scan_raw
状态面板越早接上,后续联调成本越低。
第三步:最后接业务动作
例如巡检、远程值守、半自动流程等,都应该建立在控制面已经稳定的基础上,而不是一开始就把业务流程和底盘动作绑死。
3. 一个推荐的职责边界
| 层级 | 职责 |
|---|---|
| Web控制端 | 指令输入、状态显示、速度策略、断线处理 |
| ROS2控制层 | 标准 Topic/Service 暴露、运动执行、设备数据发布 |
| 业务系统 | 流程编排、业务状态机、任务逻辑 |
| 硬件层 | 电机、舵机、雷达、相机、电池 |
如果这个边界划不清,后面任何一个业务需求都会反复侵入底层。
十一、补充:扫描建图与导航实测问题
虽然本文重点讨论的是控制层接管,但在实际项目推进中,控制系统最终往往还是要和建图、定位、导航链路协同工作。基于现场测试,这个平台在“能跑通”和“跑得稳定”之间,仍然存在一些需要二次开发优化的地方。这里单独列出来,方便做系统评估时提前有预期。
1. 扫描建图的实测现象
在实际扫描过程中,如果机器人对同一区域进行两遍扫描,地图中容易出现较明显的重影和错位。直观表现是:
- 墙体轮廓出现双边
- 直线边界变厚
- 局部障碍物位置出现轻微偏移
- 同一结构在地图上叠出两层轮廓
这类问题通常意味着里程计、姿态估计和激光数据在回环不足或局部对齐不稳时存在累计误差。对业务层的影响是:地图“看起来能用”,但用于后续定位或路径规划时,误差会被进一步放大。

单遍扫描地图效果

双遍扫描地图重影效果
2. 导航旋转 90 度场景问题
现场测试中,一个非常典型的问题是:当机器人需要执行接近 90° 的原地转向或大角度姿态调整时,失败概率明显偏高,几乎可以视为高风险动作。
这类问题对阿克曼底盘尤其敏感,原因通常不只是“导航参数没调好”,还可能叠加以下因素:
- 阿克曼底盘本身不适合严格原地旋转
- 里程计角度估计误差被放大
- 转向控制与速度控制耦合较强
- 定位与局部控制器对姿态收敛要求过高
从工程视角看,这说明“导航动作模型”和“底盘实际运动模型”之间仍然存在不完全匹配。业务系统如果需要较强的姿态控制能力,不能默认平台出厂参数就能稳定支撑。
3. 实际距离误差
在实际测试中,约 4.8 m 的真实移动距离,测得存在约 0.6 m 的误差。这不是一个可以忽略的小偏差,因为它会直接影响:
- 导航到点精度
- 停靠位置精度
- 地图与真实场景的一致性
- 重复任务的稳定性
按这个量级估算,误差比例已经足以对较窄通道、门口停靠、工位接近等场景造成实际影响。
4. 官方反馈与工程含义
根据官方技术支持反馈,这类误差问题需要通过二次开发与参数优化来进一步减小,而不是单纯依赖默认配置。
这句话背后的工程含义很明确:
- 平台具备二次开发基础
- 但默认参数并不代表已经针对你的场景完成标定
- 如果项目要求的是“演示可用”,默认配置可能够用
- 如果项目要求的是“稳定交付”,就必须做控制与定位精度优化
5. 建议的优化方向
如果后续要继续推进建图与导航稳定性,建议从以下几个方向入手:
| 优化方向 | 说明 |
|---|---|
| 线速度与角速度重新标定 | 校正运动学误差,减小累计漂移 |
| 编码器与里程计修正 | 提高距离估计准确度 |
| 转向控制参数优化 | 降低阿克曼底盘大角度转向失败率 |
| 激光与底盘外参复核 | 避免传感器安装误差放大地图偏差 |
| 地图构建流程规范化 | 降低“重复扫两遍”带来的重影问题 |
| 场景分级验收 | 区分演示环境与业务环境,不混用评价标准 |
6. 对业务系统接入的启示
即使当前阶段的主要目标是“让 Web 控制端接管底盘”,也建议在系统设计里预留下面几项能力:
- 地图效果对比展示区
- 单遍/双遍地图样例展示
- 定位误差记录区
- 实测距离与理论距离对照表
- 导航失败动作日志
这些内容平时看起来像“附加项”,但一旦项目进入联调或交付阶段,它们会直接决定你能否高效定位问题。
十二、最佳实践与避坑经验
1. 控制频率不要过低
遥控发布不是发一次就完事。建议持续发布速度指令,控制频率至少保持在 5~10Hz 以上;工程上 8~20Hz 更稳妥。
2. 不要把视频流塞进控制通道
WebSocket 负责控制和状态,视频单独走 HTTP。否则一旦网络波动,控制和视频会同时抖。
3. 雷达原始数据优先用 /scan_raw
当前源码主链路使用的是 /scan_raw。如果你默认订阅 /scan,很可能在现场发现根本没有数据。
4. 速度限制一定放在业务侧
即使车端有兼容限幅,Web 控制端也应自行限速。特别是在公网、弱网、浏览器多标签页等复杂环境下,本地限幅可以显著降低误操作风险。
5. 断线保护必须有“双零速”
不要只发一个 topic 的停止命令。更稳妥的方式是同时向:
/controller/cmd_vel/cmd_vel
补发零速,确保不同控制链都能收到停车指令。
6. 在线判断不要只看连接灯
真正的在线判断至少应包含:
- WebSocket 是否连接
/odom是否刷新- 电池状态是否刷新
否则你会遇到一种很危险的假象:页面显示“已连接”,但车端其实已经卡死。
7. 视频带宽要预留冗余
MJPEG 的接入简单,但带宽占用不低。现场使用时,如果网络条件一般,建议:
- 默认只开一路主视频
- 深度图按需打开
- 弱网环境优先 snapshot
8. 不要把底盘控制写成业务回调
控制层应该独立,业务逻辑应该订阅控制层,而不是反过来。把业务流程直接写进底盘控制节点,后续几乎一定会变成维护灾难。
十三、总结
MentorPi A1 这类平台真正适合做二次开发的原因,不是它“功能丰富”,而是它已经具备了比较标准的 ROS2 控制骨架:底盘运动、驱动状态、雷达数据、相机图像、网络接入都已经成型。对于工程团队来说,这样的平台最适合采用“控制层标准化,业务层独立演进”的方式接入。
从这次接管实践可以看到,Web 控制端要想真正稳定接管底盘,并不需要深入改写底层节点,而是要把以下几件事做好:
- 用标准 Topic 接管运动控制
- 用标准 Service 接管雷达模式
- 用独立视频链路承载图像显示
- 用心跳与状态订阅保障在线检测
- 用速度策略和急停机制保证安全
更重要的是,控制层一旦被协议化,业务系统就不再与具体前端形态绑定。今天可以是 Web 控制端,明天也可以是大屏系统、调度平台、远程值守终端,甚至其他第三方系统。只要接口不变,系统就能持续演进。
这也是机器人软件工程里一个很值得坚持的原则:业务会变,交互会变,算法会变,但控制协议最好尽量稳定。
如果你准备让自己的系统接管一台 ROS2 机器人,先把控制层理顺,往往比一开始追求复杂功能更重要。
参考资料
- Hiwonder 官方 MentorPi A1 产品页:https://www.hiwonder.com/products/mentorpi-a1
- Hiwonder 官方 MentorPi A1 单目版产品页:https://www.hiwonder.com/products/mentorpi-a1-monocular-camera-version
- Hiwonder 官方 MentorPi 文档总入口:https://docs.hiwonder.com/projects/MentorPi/en/latest/
- Hiwonder 官方 MentorPi Getting Ready:https://docs.hiwonder.com/projects/MentorPi/en/latest/docs/1.getting_ready.html
- Hiwonder 官方 MentorPi Lidar Lesson:https://docs.hiwonder.com/projects/MentorPi/en/2024-versions/docs/5.lidar_lesson.html
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)