ABOT-M1-ATK激光雷达建图与ROS导航全流程避坑
简介:面向参加中国机器人及人工智能大赛、全球校园精英人工智能算法大赛等智能车巡航赛项的选手与指导教师,这份文档围绕 ABOT-M1-ATK 的配置与快速上手展开,可作为从开箱取机到各模块功能验证的实操参考。内容按启动流程、向日葵远程桌面接入、激光雷达建图、机器人导航、射击模块、视觉识别与语音交互逐项排布,并附 Gmapping、Hector、Cartographer 三种 SLAM 方案的启动命令与地图保存路径,同时把 abot_vision、abot_voice、lidar_ws、robot_ws 等功能包按用途归位说明,还列出取机、充电与拆解方面的安全注意事项。压缩包仅含 1 个 PDF 文件,大小 2.3MB,便于随车携带离线查阅。已有 576 人学习,适合 ROS 与无人驾驶方向的中初级读者对照硬件动手复现,也可作为赛前调试与排错的速查手册。
1. 拿到 ABOT-M1-ATK 先别急着敲启动脚本
第一次给这台智能车上电的人,八成会卡在同一个画面:无线连上了、远程桌面也通了,敲下./1-gmapping.sh,终端里红字滚了一屏,/scan话题却是空的。ABOT-M1-ATK 是一台用 ROS 串起来的差速/全向底盘小车,激光雷达负责环境感知与建图,相机做视觉识别,语音模块和射击模块挂在同一套 bringup 体系下,面向的是机器人导航、无人驾驶巡航这一类赛项的训练与实操。它能解决的核心问题不是「让车动起来」,而是把建图、定位、路径规划、外设联调这条链路完整跑通一遍。适合已经有一点 Linux 和 ROS 基础、要带学生或者自己要打巡航赛项的人,纯零基础上来会有点吃力。
2. 上电、配网与远程接管:把「人在电脑前、车在场地里」跑通
2.1 从箱子里拿出来到主控启动,动作顺序不能乱
这一步看着最没技术含量,实际是最容易造成硬件损伤的地方。车体前后轮胎之间是受力结构,激光雷达的支架只是固定件,托着它抬车,轻则雷达歪几度,重则内部电机轴承受损,后面建图会一直出现墙面重影,你会以为是算法参数问题,查半天查不到根子上。
我一般的动作顺序是这样:
- 双手从前后轮之间插入,托住底板,平抬平放,落地后再松手;
- 检查雷达上表面有没有放东西(说明书、螺丝刀、扎带),雷达是 360 度扫描,上方遮挡会直接吃掉一片视场;
- HDMI 线接 10 寸显示器,显示器单独供电;
- 按车身电源开关,等 5 秒,让底盘供电稳定;
- 再按主控制器开关,等系统完全起来(屏幕上出现桌面,不是出现 logo 就算完)。
主控起来之后,先做一次最小自检,不要急着跑功能脚本:
# 确认系统与 ROS 环境正常 rosversion -d # 打印 ROS 发行版,确认环境变量已 source ls ~/ # 看四个工作空间是否都在:robot_ws / lidar_ws / abot_vision / abot_voice printenv | grep ROS_MASTER # 确认没有残留的跨机 ROS_MASTER_URI参数说明:rosversion -d输出的是发行版代号,不同批次的车预装系统可能不一样,后面所有roslaunch的包路径都以这台车实际环境为准,不要照抄别人机器上的路径。printenv | grep ROS_MASTER这一步经常被跳过,但如果之前有人在这台车上配过跨机通信,ROS_MASTER_URI会指向另一台机器,本机启动的节点会全部连不上 master,现象就是「roscore 明明起来了,rostopic list 是空的」。
2.2 无线接管:为什么第一次一定要接显示器
很多人会问,既然有远程桌面,为什么还要拖一根 HDMI。原因是配网这一步本身需要交互界面:车机要先连上一个 Wi-Fi(用手机热点最省事,场地里没有可用 AP 的时候尤其方便),才能被你的笔记本访问到。这一步没有屏幕就只能靠改配置文件,对新手不友好,出错也不好回滚。
配网和接管的完整链路:
# 车机端:确认无线已经拿到地址 ip addr show wlan0 | grep "inet " # 笔记本端:先确认能 ping 通,再开远控客户端 ping -c 4 <车机IP>车机上预装的远控客户端在桌面左下角图标里能找到,打开后会显示本机的识别码和验证码,这两个码每台设备不同,以车机屏幕上显示的为准,不要用别人的。笔记本端装同一个客户端、保证也在同一网络下,输入识别码和验证码就能接管桌面。
这里有个取舍要讲清楚:远程桌面传的是整块屏幕图像,带宽占用比 SSH 高一个量级,好处是 RViz 能直接看。建图和导航调试离不开 RViz,所以现场用远控是合理的;但如果只是改个参数、看个日志,SSH 更省事也更快。我的习惯是两条链路都留着,远控用来看图,SSH 用来敲长命令。
提示:远控客户端断开重连后,车机上正在跑的终端不会中断,但如果远控进程本身崩了,桌面会话可能被回收。跑长任务(比如建图)之前先把脚本放到
tmux或者nohup里,比裸跑在桌面终端里稳。
2.3 电源管理与长时间存放的维护节奏
这台车用的是需要定期维护的电池组,充电器规格是 DC12.6V/2.0A,充电时充电器背部亮红灯,充满转绿灯。三条经验值得写下来:
第一,电量显示剩一格的时候就去充,不要等它自己关机。锂电池深度放电一次,容量掉一截是回不来的,而且主控在低电量下突然掉电,最容易把正在写的地图文件写坏——.pgm只写了一半,下次map_server加载直接报错。
第二,充电时不要跑大电流负载。射击模块、底盘电机同时工作的时候电流很大,边充边跑会让充电器一直处在恒流阶段,电池发热,长期下来循环寿命明显缩短。
第三,半个月以上不用,必须补一次电。电池自放电加上保护板静态功耗,放两个月再拿出来经常已经过放了。我的做法是每个月固定一天,把所有不用的车统一充到指示灯的中间状态再收起来。
注意:不要私自拆机。车身上的螺丝有些是结构件预紧的,拆过之后底盘平面度变了,里程计和 IMU 的安装角度也跟着变,建图漂移会变得很难查。真出问题走返修,比自己拧一遍划算。
3. 激光雷达建图:Gmapping、Hector、Cartographer 三套方案的选型与实跑
3.1 三种 SLAM 的适用边界,先选对再动手
同样一颗 2D 激光雷达,挂三个不同的 SLAM 包,出来的图质量差别可以很大。选型不看哪个「先进」,看你的场景和硬件条件。
| 方案 | 是否依赖轮式里程计 | 是否依赖 IMU | 场地适配 | 典型问题 |
|---|---|---|---|---|
| Gmapping | 强依赖 | 不强制 | 中小场地、走廊、房间 | 快速转向时粒子退化,图会「糊」 |
| Hector | 不依赖 | 不强制 | 静止或慢速、雷达帧率高的场景 | 快速运动时容易整体漂移,回环差 |
| Cartographer | 弱依赖 | 建议有 | 大场地、有回环的赛道 | 参数多,外参标定不准时子图拼接会错层 |
Gmapping 是粒子滤波那一套,靠里程计做运动预测、靠雷达做观测更新,轮子打滑或者里程计标定不准,它会把误差直接吃进粒子分布里,表现就是地图边缘出现毛刺、同一面墙画出两条线。Hector 走的是扫描匹配,不信任里程计,所以对雷达的角分辨率和帧率要求高,机器人运动要平缓,一猛打方向就容易整体偏。Cartographer 用子图加回环检测,跑大场地优势明显,代价是要做 IMU 和雷达的外参标定,参数没调好会出现「楼层错位」那种感觉。
第一次上车,我建议先用 Gmapping 跑通全流程,把键盘遥控、TF、地图保存这几件事理顺,再去试另外两个。上来就啃 Cartographer,出问题你分不清是标定错了还是参数错了。
3.2 Gmapping 全流程与键盘遥控的按键映射
车机上已经把这套流程包成了一个脚本,右键桌面打开终端:
# 启动建图:脚本内部会依次拉起 roscore、底盘 bringup、gmapping、rviz 和遥控终端 ./1-gmapping.sh这个脚本会开多个终端窗口,其中第五个是遥控终端。切到那个窗口,把输入焦点点进去,再按键才有效——这一点是新手最高频的翻车点,键按了半天车不动,其实是焦点还在 RViz 上。
按键映射按脚本里打印的提示来,大致是:
| 按键 | 动作 | 大写锁定开启时 |
|---|---|---|
| U / I / O | 左前 / 正前 / 右前 | U、O 变为左前、右前平移 |
| J / L | 左转 / 右转 | 变为左右平移 |
| M / , / . | 左后 / 正后 / 右后 | , 和 . 变为左后、右后平移 |
不同批次的脚本提示文案可能略有出入,以终端里实际打印的为准。开始推图之前,先做两件事:把车放在场地里的起始位置,在地上贴个胶带做记号,导航阶段必须回到同一个点;然后确认雷达数据是活的:
# 新开一个终端,确认雷达话题有数据、频率正常 rostopic hz /scan # 确认 TF 链路完整:odom -> base_link -> laser rosrun tf tf_echo odom base_link rosrun tf view_frames && evince frames.pdf参数说明:rostopic hz /scan正常应该稳定在一个固定频率上,如果长期低于雷达标称值,先查 USB 带宽和供电,不是算法问题。tf_echo返回的平移量应该随车移动而变化,如果一直是 0,说明里程计没起来,Gmapping 会直接拒绝工作。view_frames生成的那张 PDF 是排查 TF 的黑匣子,一眼就能看出哪一段断了。
推图的手法比参数重要:速度放慢,转弯前先减速,沿着墙走一圈再往里绕,避免原地高速旋转。走廊场景尽量走中线,让两侧墙同时进视场,匹配会更稳。一个场地推完,不要急着保存,先在 RViz 里看一圈有没有明显错层,有的话重推一遍比后期修图省事得多。
3.3 Hector 与 Cartographer 的换跑方式
脚本封装版跑通之后,建议手动起一次,把每个节点是干什么的弄清楚。Hector 的启动序列:
# 终端 1:底盘 + IMU 的 bringup roslaunch abot_bringup robot_with_imu.launch # 终端 2:Hector 建图 roslaunch abot_slam hector_mapping.launch # 终端 3:RViz 可视化 roslaunch abot_slam view_mapping.launch # 终端 4:键盘遥控 rosrun teleop_twist_keyboard teleop_twist_keyboard.pyCartographer 只是把第二个终端换成:
roslaunch abot_slam cartographer.launch手动起的时候注意两点。第一,RViz 的 Fixed Frame 要设成map,不是odom也不是base_link,否则你会看到地图在动、车不动,误以为算法有问题。第二,四个终端里的 bringup 只能起一次,重复启动会出现两个base_link的发布者,TF 树直接打架,报错信息是「TF_REPEATED_DATA」,这个错误一旦出现只能把多余的节点杀掉重来。
对比验证的方法很简单:同一块场地、同一条路线,三个算法各跑一遍,把生成的.pgm叠在一起看墙角是否重合。Gmapping 的图通常在近处贴合好、远处有累积误差;Hector 在慢速直线段很干净,急转弯处容易整体偏;Cartographer 在大回环场地上闭合得最好。这个对比做完,你对「什么场地该用哪个」就有直觉了,不用再背结论。
3.4 地图落盘:save_map 的路径约定与手动兜底
建图过程中不要关终端,直接新开一个窗口保存:
roslaunch abot_slam save_map.launch这个 launch 内部调的是map_server的map_saver,输出一对文件:.pgm是灰度栅格图,.yaml是元数据(分辨率、原点、occupied/free 阈值)。默认落盘路径看 launch 里的参数,不同批次可能指向不同目录,第一次用的时候先cat一下 launch 文件确认。
如果 launch 出问题,手动兜底:
# -f 指定输出前缀,map:= 指定要保存的话题名 rosrun map_server map_saver -f ~/maps/field_01 map:=/map参数说明:-f后面是路径前缀,会生成field_01.pgm和field_01.yaml;分辨率写在 yaml 里,常见是 0.05 米/像素,这个值直接决定导航时栅格的精细程度,改小会让代价地图计算量上升,改大则窄通道过不去。存完立刻cat一下 yaml,确认origin和resolution正常,空文件或者 0 字节的 pgm 基本都是保存时主控掉电或者磁盘满了导致的,这种图导航阶段加载会直接崩。
提示:每次保存都用「场地名 + 日期」命名,别用
map、map1这种。导航阶段加载错地图,现象是车按建图时的旧环境规划路径,你会以为定位崩了,实际只是文件名搞混了。
4. 导航闭环:从 map 到 2D Nav Goal 的启动顺序与参数
4.1 启动顺序错了,后面全是无效排查
导航依赖 TF 树里map -> odom -> base_link这条链完整,其中map -> odom是 AMCL 发布的,odom -> base_link是底盘发布的。顺序反了,AMCL 起来的时候找不到里程计,会卡在初始化阶段,RViz 里车模型是灰的。
脚本版一条命令搞定:
# 前提:车已经放回建图时的起始位置,雷达视野内没有临时障碍物 ./2-navigation.sh手动版三连:
roslaunch abot_bringup robot_with_imu.launch roslaunch robot_slam navigation.launch roslaunch robot_slam view_nav.launch起来之后不要立刻点目标点,先验证三件事:
# 1. TF 链路是否通,Translation 应该有正常数值而不是全 0 rosrun tf tf_echo map base_link # 2. 地图服务是否真的加载了 rosservice call /static_map "{}" | head -n 5 # 3. 定位是否收敛,看粒子云是否聚成一团 rostopic echo /amcl/particlecloud -n 1 | head -n 3参数说明:tf_echo里如果map -> base_link超时,八成是 AMCL 没起来或者初始位姿给得太离谱;/static_map返回空说明地图没加载,检查 yaml 路径;粒子云如果散成一大片,说明初始位置给错了,把车挪回建图起点,或者在 RViz 里用2D Pose Estimate重新给一个朝向。
4.2 代价地图与膨胀半径,最值得动手的两个参数
导航「能不能走」很大程度取决于代价地图参数,这几个值我一般会打开看一眼:
# costmap_common_params.yaml 片段 robot_radius: 0.22 # 圆形底盘等效半径,实测车体最宽处的一半 inflation_radius: 0.45 # 膨胀半径,必须大于 robot_radius cost_scaling_factor: 3.0 # 代价衰减速度,越大衰减越快,越贴近障碍物inflation_radius设得太小,规划出来的路径会贴着墙角走,实车因为控制误差蹭上去;设得太大,窄通道直接被判成不可通行,规划器报「no path found」。经验值是车体半径的 1.8 到 2.2 倍,然后在实际场地上跑一圈微调。cost_scaling_factor控制的是「离障碍物多远开始变得不愿意走」,调大之后路径会更愿意走中间,但绕障会变得犹豫。
另外两个参数值得关注:全局代价地图的update_frequency和局部代价地图的publish_frequency。局部图更新太慢,动态障碍物(比如走动的队友)来不及反映,车会直直撞上去;更新太快,主控 CPU 吃满,/cmd_vel发布抖动,车走起来一顿一顿。默认值先跑,出问题再动。
4.3 点下 2D Nav Goal 没反应,按这四类查
第一类,目标点本身不合法。点在障碍物膨胀区里、点在地图边界外、点在未知区域(灰色),规划器都会直接放弃。把目标点放到白色可通行区域,方向箭头拖出一个合理朝向再松手。
第二类,RViz 的 Fixed Frame 不对。设成base_link的时候,你点下去的目标点会跟着车一起动,看着像是没反应。固定成map。
第三类,全局规划器插件没加载。navigation.launch里如果move_base的base_global_planner参数写错,节点能起来但规划服务是空的,rostopic echo /move_base/status看不到任何输出。查 launch 里的插件名和实际安装的包是否对得上。
第四类,TF 超时。日志里出现「Timed out waiting for transform from base_link to map」,说明 AMCL 掉线或者时间戳不同步。多机部署的时候最容易出现这个,原因是两台机器的系统时间差了几秒,tf直接判定数据过期。用date对一下时间,必要时上chrony。
注意:导航过程中不要用手去拽车。底盘在闭环控制,你拽一下它会把这段位移当成里程计误差吃进去,定位瞬间偏掉,后面整条路径都歪。要停就按遥控终端上的急停,或者发零速度。
5. 现场避坑:五条血泪记录,从雷达到地图文件
5.1 抬车托了雷达支架,建图一直有重影
现象:建图时同一面墙画出两条平行线,间距稳定在 3 到 5 厘米,怎么调 Gmapping 参数都不消。
原因:抬车的时候手托了雷达支架,支架和底板的连接面被压出微小形变,雷达安装平面不再水平,扫描平面相对地面有个固定倾角,远处墙面自然就被「切」成两条。
解决:把车放平,用水平仪贴在雷达底座上量,肉眼看不出来但仪器能测到。轻微形变可以通过重新紧固支架螺丝恢复,明显的变形只能换支架。之后再抬车,一律托前后轮之间的底板。
5.2 遥控按键没反应,车一动不动
现象:遥控终端里按键,屏幕上没有任何输出,车也不动。
原因:三种可能——终端没有输入焦点;输入法处在中文状态吃掉了字母键;大写锁定状态和你以为的相反,导致平移和旋转的映射反了。
解决:先在遥控终端里点一下再按键;把输入法切到英文;看一眼终端的指示灯。这个坑我见过太多次,尤其用远程桌面的时候,鼠标在 RViz 窗口里点过,焦点就不在终端上了。
5.3 建图越跑越歪,回不到起点
现象:绕场一圈回到起点,地图上起点和终点的墙面错开半米以上,回环完全闭合不了。
原因:轮式里程计标定不准(轮径参数和实际不符)、地面太滑导致打滑、推图速度太快。Gmapping 强依赖里程计,这三条里任何一条都会让误差累积。
解决:先把推图速度降下来,转弯前减速;地面如果是抛光瓷砖,铺一层地毯或者防滑垫;里程计标定用直线走 3 米、原地转 10 圈的方式反推轮径和轮距参数。做完这三步再跑,误差通常会小一个量级。
5.4 导航启动即报 TF 超时
现象:navigation.launch起来后,move_base日志刷「Timed out waiting for transform from base_link to map」,RViz 里车模型不动。
原因:启动顺序错了(先起导航后起底盘),或者 AMCL 的初始位姿和实际位置差太远,粒子全都落在无效区域。
解决:严格按底盘、导航、RViz 的顺序起;车放回建图起点后,用2D Pose Estimate手动给一个大致位姿,等粒子云收敛成一团再下目标点。
5.5 电量见底还在跑,地图文件写坏了
现象:保存地图之后,map_server加载报错,yaml 能读但 pgm 打不开,文件大小明显偏小。
原因:保存的瞬间主控掉电,.pgm只写入了文件头。低电量下这种概率很高,尤其是地图文件写在大容量 SD 卡上的时候。
解决:看到电量剩一格就停手充电,别赌最后几分钟。已经写坏的地图没法修,只能重推。养成习惯:保存之后再ls -l看一眼文件大小,正常的 pgm 至少几十 KB 级别,几百字节的一定是坏的。
6. 赛前十五分钟自检:把相机、语音、射击串成一条验收链
到了场地,留十五分钟做一遍全链路自检,比临时改参数有用得多。我一般把下面这个脚本存成precheck.sh放在家目录,每次开机跑一遍:
#!/usr/bin/env bash # 赛前自检:话题、TF、地图、外设逐项过一遍 set -u echo "== ROS 环境 ==" rosversion -d echo "== 雷达 ==" timeout 5 rostopic hz /scan 2>/dev/null | tail -n 2 || echo "雷达未启动" echo "== 相机 ==" rostopic list | grep -q '^/usb_cam/image_raw$' && echo "相机话题在线" || echo "相机未启动" echo "== TF 链路 ==" timeout 5 rosrun tf tf_echo map base_link 2>/dev/null | grep -m1 Translation \ || echo "map -> base_link 断开" echo "== 地图服务 ==" timeout 5 rosservice call /static_map "{}" >/dev/null 2>&1 && echo "地图已加载" \ || echo "地图未加载" echo "== 速度指令 ==" rostopic info /cmd_vel | grep -m1 "Publishers" || echo "无 cmd_vel 发布者"脚本里每一项的判据都很直白:timeout 5防止某个话题不存在时卡死;grep -q只判存在不刷屏;tf_echo抓到第一行Translation就够,说明链路通。哪一项报红,对应回去查那一章,不要跳着改别的参数。
外设单独验的时候,顺序是相机、语音、射击。相机两条命令,能看到实时画面就是正常:
roslaunch usb_cam usb_cam_test.launch rosrun rqt_image_view rqt_image_view语音要先把蓝牙耳机连上(车机右上角设置里配对),然后:
# 终端 1 roscore # 终端 2 rosrun robot_voice tts_subscribe # 终端 3:发一条文本,耳机里应该能听到播报 rostopic pub /voiceWords std_msgs/String "data: '1234'" -1-1表示只发一次然后退出,不加的话会一直循环发,耳机里会连续播报,测试的时候容易被吓一跳。
射击模块放在最后,而且一定要确认枪口方向无人、无贵重物品。水弹必须用矿泉水泡够 8 小时以上,泡不够会卡弹,卡弹之后强行继续发指令容易烧电机:
# 终端 1 roslaunch abot_bringup shoot.launch # 终端 2:触发一次发射 rostopic pub /shoot std_msgs/String "data: ''" -1从那以后我每次把车从箱子里拿出来,都会先用手背在雷达底座上摸一下平不平,再开机;保存完地图一定ls -l看一眼文件大小。这两个动作加起来不到十秒,省下的返工时间是以小时计的。希望帮到你。
本文还有配套的精品资源,点击获取