从 OpenClaw 语音控制到实验室巡逻路线:一次 ROS 2 导航闭环验证
从语音控制、地图保存到 Nav2 纯规划验证,记录一条实验室巡逻路线的安全闭环。
1. 记录目的
今天的工作不是单独验证某个 ROS 2 节点,而是把语音控制、地图、定位、路线标定和路径规划串成一条完整链路。
这次主要解决五个问题:
- OpenClaw 能否通过语音链路实际调用小车底盘?
- 当前实验室地图能否正确保存并重新加载?
- 小车在地图上的定位偏移究竟来自哪里?
- 人工采集的巡逻点是否位于可通行区域?
- 在不让车辆运动的情况下,能否证明整条路线可以被 Nav2 规划?
今天继续采用系统化排错方法:
记录现象
-> 检查节点、话题和 TF
-> 提取日志与数值证据
-> 区分事实、推测和显示误解
-> 选择影响范围最小的处理方式
-> 进行静态验证、规划验证和退出验证
2. 今天处理的系统链路
今天涉及的功能可以分为四层:
语音输入
-> ASR
-> OpenClaw 决策
-> ROS 2 动作服务
-> 底盘控制
双雷达
-> LaserScan 合并与过滤
-> Cartographer / AMCL
-> map 与 TF
人工驾驶
-> 采集 map -> base_footprint
-> 生成巡逻点
-> 保存路线配置
路线配置
-> Nav2 planner_server
-> ComputePathToPose
-> 纯规划验证
这种分层很重要。语音有回复不代表动作执行成功,地图能显示不代表定位准确,目标点位于白色区域也不代表两点之间一定能生成路径,博主建议每次执行操作结束时,去使用AI拉取后台日志,看看有没有板块报错,因为学习途中很多部分是无法完全可视化完成的,介于win转linux的同学,我建议还是采用我的方式去进行检验。
3. OpenClaw 语音控制验证
3.1 工作流程
当前语音控制链路可以概括为:
麦克风录音
-> VAD 判断语音结束
-> ASR 转写文字
-> OpenClaw 生成回复和动作列表
-> action_service 执行动作
-> /cmd_vel 控制底盘
-> TTS 播放回复
3.2 实际验证
测试“前进一米”后,车辆确实完成了约一米的前进动作。这说明 OpenClaw 不只是返回文本,它已经能够经过动作服务调用底盘。
但“能动”只是最低层验证。当前方案仍存在以下缺口:
- 语音交互主要是单轮指令模式;
- 缺少稳定的唤醒、工作和休眠状态机;
- 缺少连续对话窗口;
- 缺少单独的中途取消词;
- 动作接口还没有严格限制为安全白名单。
因此,今天只证明了链路可用,没有把 OpenClaw 直接接入巡逻路线自动执行。
3.3 对休眠计时的认识
后续设计不能简单地从接收到语音后固定倒计时 30 秒。正确逻辑应当是:
唤醒
-> 接收命令
-> 执行任务
-> 反馈结果
-> 进入空闲状态
-> 从空闲状态开始计算 30 秒
任务执行期间不应进入休眠倒计时。新命令、中途取消或任务反馈都应刷新会话状态。
4. 地图保存与版本验证
4.1 保存结果
本次地图保存到:
/home/jetson/.config/m3pro_openclaw/maps/20260805_203638/
├── patrol_map.yaml
└── patrol_map.pgm
地图参数:
尺寸:231 x 270
分辨率:0.05 m/pixel
原点:[-3.4, -2.66, 0]
地图文件版本标识:
YAML SHA-256:d3e2d7770b8bb5bf9fced298ba2120e5e0f0ea256bc6cd900e7996b58d0a8034
PGM SHA-256:4e77c90fa1449e57e68ca8abcc148ffd03c0358aa7c2524815ffe93a180df16c
4.2 为什么不能只看文件存在
地图保存完成后进行了以下验证:
map_saver_cli返回成功状态;- YAML 和 PGM 均存在且大小非零;
- YAML 包含图像路径、分辨率、原点和占用阈值;
- PGM 可以被识别为有效 Netpbm 图像;
- 使用临时
nav2_map_server重新加载 YAML; - 地图服务器能够进入
active; - 重新发布的尺寸和分辨率与原地图一致。
这次验证说明:文件存在只是第一层,能够重新加载并发布一致的 /map,才算地图真正可用。
5. 实验室巡逻路线标定
5.1 标定方法
小车从 home 出发,使用手柄依次到达三个巡逻停靠点,车辆停稳后采样:
map -> base_footprint
每个点连续读取多帧,使用稳定区间的中位值,而不是只记录单帧数据。
5.2 最终坐标
home
x = -0.007 m
y = -0.002 m
yaw = -3.11 deg
lab_point_1
x = 1.646 m
y = -0.106 m
yaw = 0.893 deg
lab_point_2
x = 1.613 m
y = 0.729 m
yaw = 88.539 deg
lab_point_3
x = 0.142 m
y = 0.905 m
yaw = 175.212 deg
最终路线为:
home -> lab_point_1 -> lab_point_2 -> lab_point_3 -> home
第四次停车位置经过确认就是返回后的 home,因此没有把它保存成重复的第四个巡逻点。
6. 一次典型的定位偏移排查
这是今天最值得记录的排错过程。
6.1 现象
车辆到达第一个点后,RViz 中看起来出现了偏移。画面中还能看到小车和一条很长的黄色连线,容易误以为小车模型、雷达或地图发生了错位。(当时博主在第一次位移发生偏移的时候,使用了很多方法去验证是否是控制器或者雷达问题,建议大家都在标定结束后复盘时利用AI去分析原因并做对比,使用控制变量法去排错是最便捷的,排错不要心急,很多时候可能是外部变化引起的暂时性问题)
6.2 运行状态
后台检查结果:
/scan正常发布,频率约7.15 Hz;- 雷达坐标系和外参正常;
map -> base_footprint连续存在;- AMCL、地图服务器和 RViz 均处于运行状态;
/cmd_vel当时只有joy_ctrl一个发布者。
车辆从暂定 home 到第一个点的地图位移约为:
前进约 1.60 m
横向变化约 3.2 cm
这说明车辆并没有在地图坐标中产生明显横移。
6.3 日志与数值证据
底层里程计和 AMCL 之间存在明显差异:
odom 认为车辆偏航约 +12.3 deg
map -> odom 使用约 -12.2 deg 修正
AMCL 的位置协方差也一度升高,说明定位结果不够集中。
6.4 根因判断
问题包含两个部分:
第一,RViz 中的长黄线是 TF 显示的 odom -> base_footprint 父子坐标关系。车辆离开里程计原点后,连线自然变长。它不是第二个车辆位置,也不是雷达漂移。
第二,车辆返回实体 home 后,AMCL 最初给出的坐标与起点相差约 15 cm / 16.6 deg。车辆实际位置和朝向没有变化,因此不能把误差归因于人工摆放,而应继续检查定位是否及时更新。
6.5 处理决定
没有重新点击 2D Pose Estimate,也没有立刻修改厂商 AMCL 源码,而是调用静止更新服务:
ros2 service call /request_nomotion_update std_srvs/srv/Empty '{}'
这一操作不会驱动车辆,只要求 AMCL 在车辆静止时重新使用当前雷达数据更新粒子分布。
6.6 验证结果
静止更新后,定位从:
x = -0.089 m
y = 0.009 m
yaw = -14.4 deg
逐步收敛到:
x = -0.007 m
y = -0.002 m
yaw = -3.11 deg
这证明主要问题是 AMCL 更新滞后和里程计航向误差累积,而不是车辆实际返回位置错误。
这个案例再次说明:看到界面偏移后,不能直接拖动车模或重设初始位置。应该先比较 map、odom、base_footprint 和雷达数据之间的关系。
7. 路线配置与静态安全检查
路线保存到:
/home/jetson/.config/m3pro_openclaw/routes/lab_patrol_route.yaml
本地保留:
D:\AutomataCar\lab_patrol_route.yaml
路线 SHA-256:
b2ee71deedfeb53d9592e78df172f9469f0a40e287a78ef55097704faba12dac
配置中明确设置:
require_confirmation: true
autonomous_execution_enabled: false
随后对地图栅格进行了静态检查:
home最近障碍距离约0.87 m;lab_point_1最近障碍距离约0.74 m;lab_point_2最近障碍距离约0.46 m;lab_point_3最近障碍距离约0.86 m;- 四段直线路径均未穿过占用或未知栅格;
- 整条路线最小静态余量约
0.47 m。
点位安全和路径可规划是两个不同问题,因此还需要下一层 Nav2 验证。
8. Nav2 纯规划验证
8.1 为什么先做 dry-run
这里的 dry-run 指只启动路径规划器并计算路径,不启动控制器,不让车辆运动。
本次只启动:
planner_server
global_costmap
没有启动:
controller_server
bt_navigator
waypoint_follower
因此规划过程中不会出现新的 /cmd_vel 发布者。
8.2 参数问题
检查厂商参数时发现:
global_costmap:
global_costmap:
ros__parameters:
use_sim_time: True
当前是实机环境,没有 /clock。直接使用该值可能导致代价地图时间不更新。
处理方式不是修改厂商 YAML,而是新增独立参数文件:
D:\AutomataCar\nav2_planner_dry_run.yaml
其中强制使用实机时间,并且只加载静态地图层和膨胀层。
8.3 规划结果
四段 ComputePathToPose 请求全部返回 SUCCEEDED:
| 路段 | 路径长度 | 路径点数量 | 规划耗时 |
|---|---|---|---|
home -> point_1 |
1.666 m |
66 | 2.222 ms |
point_1 -> point_2 |
0.854 m |
33 | 1.112 ms |
point_2 -> point_3 |
1.498 m |
59 | 0.878 ms |
point_3 -> home |
0.940 m |
37 | 0.739 ms |
最大首尾坐标误差约 1.7 cm,规划日志中没有 WARN 或 ERROR。
验证过程中反复检查 /cmd_vel,始终只有:
joy_ctrl
纯规划结束后,临时规划器按以下顺序退出:
deactivate -> cleanup -> SIGTERM 专属进程组
退出后确认没有残留 planner_server 或 global_costmap 节点。
9. 断电前的安全收尾
车辆电量不足后,没有继续执行实车测试,而是停止本次标定启动的功能:
- AMCL;
- 地图服务器;
- RViz;
- EKF 和 TF;
- 雷达合并与预处理;
- 临时 Nav2 规划器。
停止时使用已经确认过的独立进程组,没有使用范围过大的 pkill,也没有误伤厂商底盘常驻节点。
这次再次验证了一个重要原则:机器人测试的结束条件不是“功能已经跑通”,而是“运动输出已经停止、临时节点已经退出、配置已经保存”。
10. 今天形成的认识
10.1 语音回复不等于动作链路完成
必须同时确认 ASR 文本、OpenClaw 动作列表、动作服务结果、/cmd_vel 发布者和车辆实际表现。
10.2 地图显示正常不等于定位可靠
地图、雷达点、RobotModel、TF 连线和 AMCL 粒子分别表示不同信息。必须先理解显示对象,再判断是否真的偏移。
10.3 物理事实优先于单次软件估计
车辆实体回到相同位置且朝向一致,而 AMCL 坐标不同,说明应该检查定位更新和里程计累积,不能先怀疑用户摆放错误。
10.4 先验证规划,再允许控制
目标点自由、线段自由和 Nav2 规划成功是逐层增强的证据。只有这些验证完成后,才适合进入低速实车测试。
10.5 尽量使用独立覆盖配置
发现厂商 use_sim_time 配置不适合当前实机时,使用独立 dry-run 参数文件,而不是直接修改厂商源码。这样更容易回滚,也更方便比较差异。
10.6 安全状态必须能被证明
“我没有发送运动命令”不够,还应检查 /cmd_vel 的实际发布者,并在退出后确认相关进程和节点消失。
11. 阶段结论
今天最大的进展不是让小车多运行了一个功能,而是建立了从语音意图到物理运动、从地图文件到路线规划的验证闭环。
这条链路可以概括为:
语音可识别
!= 动作可安全执行
地图可显示
!= 地图可重新加载
点位在自由区域
!= 路线一定可规划
路线可规划
!= 可以直接开放自动驾驶
目前已经证明:地图有效、定位问题可以通过数据链路解释、三个巡逻点可以组成闭环、Nav2 可以为四段路线生成路径,并且整个验证过程没有产生自主速度输出。
下一步才是低速实车验收,而不是直接让 OpenClaw 自动巡逻。
