尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于ROS的自主建图漫游车:从硬件搭建到SLAM导航实践

基于ROS的自主建图漫游车:从硬件搭建到SLAM导航实践 我的业余时间大多耗在了机器人和小玩具的折腾上这次的项目标题叫Build an Autonomous Mapping Rover翻译过来就是“打造一辆自主建图漫游车”。听起来挺唬人实际上就是一台能自己走走转转、把周围环境扫描成地图的小车。放在一两年前这种系统还属于实验室专属现在零配件和开源算法都成熟了普通人完全可以在家里搭一套能跑、能出图、能导航的完整样机。这篇文章不谈那些虚的天花乱坠的概念只讲我实际搭建过程中的完整思路、硬件选型、软件配置以及踩过的那些坑。目标是让你看完之后手里有货、心里有数能自己复现一台能建图、能定位、能自主航点导航的漫游车。1. 项目整体设计与思路拆解1.1 自主建图漫游车到底在解决什么问题先说清楚一个概念自主建图漫游车由三个核心能力构成——环境感知、实时定位建图、自主路径规划。很多人一开始只盯着“建图”两个字觉得能扫出一张地图就算成功其实建图只是基础。真正让这台车“自主”的是在建好的地图上实现实时定位即定位和避障导航即规划。换句话说漫游车至少要能在未知环境里走一遍把地图“画”出来然后在这张地图上找到自己在哪里才能规划去某个目标点的路线。这套逻辑在扫地机器人、园区巡检车、仓库自动搬运设备上都能见到本质是同一套技术栈。我的项目目标定得很具体让小车在室内环境比如客厅、走廊、办公室里自主巡航通过雷达扫描生成二维栅格地图然后在地图上指定目标点小车自动规划路径并安全到达。整个系统建图分辨率控制在5厘米可实时在地图上看到小车位置。1.2 为什么选择“轮式底盘2D雷达开源算法”这条技术路线我当初在方案选型上纠结过一段时间主要有三条路线视觉SLAM用深度相机或双目相机、3D激光SLAM用三维激光雷达、2D雷达SLAM用单线激光雷达。视觉SLAM价格最便宜一个深度相机才几百块但弱点非常明显对环境光照敏感走廊强光、暗光、白墙无纹理区域都容易丢特征。3D激光SLAM效果最好但一套雷达动辄几千上万对计算平台要求也高不适合作为入门项目的核心传感器。最终我选择了2D单线雷达配合轮式里程计的组合核心原因是性价比极高且技术生态非常成熟。RPLIDAR A1这一级别的雷达只有三百元左右扫描半径12米刷新率10Hz配合电机编码器算出来的轮式里程计在小范围室内场景下完全够用。算法层面有GMapping、Cartographer这样的开源方案可以直接用社区资料也丰富遇到问题几乎都能找到解决方案。实际跑下来这套方案让我省了不少心但这不是说它没有坑——2D雷达有天然限制它只能扫描一个平面所以对桌腿、椅子腿这类低矮障碍物很敏感但对于低于雷达安装高度的物体比如地上的线缆、低矮台阶它就“看不见”了。所以在搭建时要把雷达安装高度放在20到30厘米左右才能兼顾室内家具的检测范围。这属于硬件层面就需要提前想清楚的约束后面会在安装部分细说。1.3 系统架构模块化设计才是后期不痛苦的根源我把整套系统按功能拆成了几个独立模块每个模块之间通过消息通信底盘控制模块接收速度指令驱动电机同时发布编码器里程计数据。传感器数据模块采集雷达扫描数据并配合IMU惯性测量单元提供姿态参考。建图与定位模块运行SLAM算法实时拼接激光帧生成地图同时输出机器人在地图中的位姿。导航规划模块加载已生成的地图执行定位AMCL并完成全局路径规划与局部避障。上层调度模块负责接收任务指令比如目标点坐标协调导航模块执行。这样设计的好处是坏了一个模块不影响其他模块调试。建图的时候只开雷达、底盘和SLAM算法导航的时候再单独启动导航栈。如果你把代码焊死在一个进程里后面排查问题会非常痛苦。2. 硬件搭建从零拼出一台能跑的漫游车2.1 底盘、电机与驱动板的选型与教训底盘是整个项目的地基要保证承载能力、行驶稳定性还要方便安装雷达和计算板。我用的是一块通用的四轮亚克力底盘尺寸大约30cm×25cm四个直流减速电机带霍尔编码器每个电机额定电压12V减速比1:30最大转速约300RPM。这里有一个关键细节直流减速电机必须带编码器没有编码器就没有轮式里程计后续SLAM定位会非常困难。编码器分为霍尔编码器和光电编码器霍尔编码器抗干扰能力更强室内环境首选这种。我用的是每圈13脉冲、配合减速比1:30后等效每圈390脉冲的型号精度基本够用。电机驱动板用的是经典的双H桥驱动模块型号是TB6612FNG比L298N轻、效率高支持两路电机调速最大持续电流1.2A足够驱动这种小电机。如果你的电机功率更大可以换BTS7960或者大电流的MOS驱动模块。接线时特别注意共地逻辑电源和电机电源必须共地否则PWM信号会因为电压基准不同导致电机抖动甚至失控。这是我第一次接线踩过的坑线后面会细讲。2.2 主控与计算平台怎么选性价比最高漫游车有“轻量控制”和“重计算”两层任务。轻量控制包括读取编码器、输出PWM、采集IMU数据我用了一块STM32F103开发板来做重计算包括雷达数据处理、SLAM建图、路径规划我用了一台运行Ubuntu的迷你主机作为上位机。STM32F103是典型的低成本MCU主频72MHz专门负责底层实时控制。上位机最初我试过树莓派4B跑轻量级建图勉强够用但建图时CPU常年满载后面还要跑导航和可视化明显吃力。我换成了一台带四核J4125处理器的迷你工控机8GB内存跑ROS系统完全没压力整机功耗也只有15W左右用一块12V锂电池就能同时给底盘和电脑供电。如果你预算宽裕也可以用NVIDIA Jetson Orin Nano这类带GPU的板子以后想加视觉模型识别就方便了。但作为纯建图导航项目J4125级别的机器绝对够用。这里的原则是计算平台性能留够30%余量不要刚刚好否则后面加功能必然卡顿。2.3 雷达、IMU与传感器安装的物理要求雷达我选了思岚RPLIDAR A1这是一颗360度扫描的单线激光雷达测量半径12米测距误差在厘米级别采样频率8000次/秒扫描频率可以调到10Hz。它通过串口转USB连接上位机ROS里已经有现成的驱动包插上就能出激光话题。IMU我选的是MPU6050六轴惯性测量单元注意IMU的安装位置要尽量靠近小车的旋转中心并且保持水平。如果不水平陀螺仪和加速度计的零偏会变化后续融合出来的航向角会漂移。安装雷达时雷达中心线要与小车前进方向对齐偏差角度哪怕只有几度建出的地图也会整体旋转影响后续定位。传感器安装顺序建议是这样的先把底盘拼好确认电机转向一致再装IMU最后装雷达。每装一层就测试一层不要一次性全装完再通电否则出了问题很难定位出是哪部分线踩了坑或哪颗螺丝松了。2.4 供电系统最容易忽视又最容易出事的一环供电是整个项目里最容易出问题的地方而且一出问题就是反复重启、电机抖动、雷达丢帧这种疑难杂症。我的经验是动力电源和逻辑电源必须分开。底盘电机启动瞬间电流很大12V电机空载电流可能只有200mA但堵转时冲击到2A以上都有可能。如果计算板、雷达、IMU都跟电机共用同一路电源电机一转电压瞬间跌落计算板就会重启。我的做法是一块12V/5200mAh锂电池直接给电机驱动板供电。电机的正负极从电池端单独引出。再用一块降压模块DC-DC 12V转5V/3A单独给上位机、雷达和MCU供电。给上位机供电的线尽量短且粗降低线路压降。电池我最终选了带保护板的锂电池组过放保护非常重要。有些便宜电池没有保护板把电压耗到6V以下电池就报废了。另外开机顺序也有讲究先接电池供电再开上位机等系统起来后再跑底盘驱动。反过来的话上位机启动瞬间电流大容易把电池电压拉低引发MCU误启动。3. 软件系统ROS环境搭建与核心算法参数3.1 为什么选ROS全家桶而不是自己造轮子软件部分我当然不是从零写SLAM算法ROSRobot Operating System是这个项目的绝对主力框架。ROS不是一个操作系统本身而是一套运行在Linux上的分布式通信中间件它把传感器驱动、算法模块、可视化工具都做成了独立的“节点”节点之间通过话题Topic进行订阅和发布。选择ROS最大的原因是生态成熟雷达驱动、底盘驱动、GMapping建图、AMCL定位、导航栈全部有现成的开源包。我自己只需要写底盘驱动和任务调度的代码其他都靠配置参数来适配。如果你非要从无到有写一套SLAM那已经不是项目而是科研课题了。这里要区分一下ROS1和ROS2。ROS1为Noetic版本支持Ubuntu 20.04资料最多坑最少非常适合学习和小型项目。ROS2在实时性、多机通信上有优势但一些经典包比如GMapping在ROS2下还需要额外适配。我的项目用的是ROS1 Noetic稳定性最高。如果你决定用ROS2也能跑Cartographer 2D建图和Nav2导航栈但调试门槛确实高一截。3.2 机器人模型URDF与TF坐标树地图乱掉的根源多半在这里在ROS里跑SLAM之前必须先定义好机器人模型URDF和TF变换树。TF是坐标变换系统要知道雷达在车身上的哪个位置、底盘中心在哪里、雷达安装高度是多少才能把雷达扫描到的点投影到世界坐标系里。我的坐标树很简单map - odom - base_footprint - base_link - lasermap全局地图坐标系由SLAM算法维护。odom里程计坐标系根据编码器积分得到。base_footprint机器人在地面上的投影点。base_link机器人本体坐标系通常定义在底盘中心。laser雷达坐标系安装位置相对于base_link有一个平移和旋转量。最常犯的错误是雷达安装位置测量不准。雷达安装高度差了1厘米、安装角度偏了2度建图质量就会明显劣化。地图会出现双层边缘、重影、整体扭曲这些问题。我在第一次跑建图时就发现走廊地图是歪的排查了半天最后发现雷达支架垂直度偏了3度重新固定后立即恢复正常。测量方法简单但要有耐心用卷尺量雷达中心到小车前进方向中轴线的左右距离差再量雷达平面到地面的高度。URDF文件里这些数值要精确到毫米级。3.3 底盘驱动节点里程计必须稳否则一切白费底盘驱动节点是上位机和MCU沟通的桥梁它接收/cmd_vel话题上的速度指令转发给下位机同时从下位机读取编码器计数计算并发布里程计数据/odom。里程计的计算公式不复杂。假设差速底盘左右轮编码器单位时间内的脉冲增量分别为delta_left和delta_right每个脉冲对应的车轮行进距离为pulse_to_meter那么左右轮的行程为distance_left delta_left * pulse_to_meter distance_right delta_right * pulse_to_meter两轮间距为wheel_base则小车前行位移和航向角变化为distance_center (distance_left distance_right) / 2 delta_theta (distance_right - distance_left) / wheel_base把这个航向角累加起来再配合初始位置就能推出小车在世界坐标系里的坐标。这就是最经典的差速里程计模型。要注意的是里程计的累积误差是不可避免的所以后面才需要雷达和粒子滤波AMCL来修正它这正是SLAM链条中“定位”的作用。我用串口线连接STM32和上位机协议用的是简化的文本帧格式。比如向上位机发送vx:0.2;vw:0.0;\n下位机按固定周期解析执行下位机向上位机发送l:123;r:456;\n作为编码器脉冲计数。串口波特率我设在115200足够用再高容易出错。3.4 建图算法选型GMapping与Cartographer的对比建图算法我前后试了两种分别是经典的GMapping和Google的Cartographer。两者都能建出不错的二维栅格地图但适用场景略有不同。GMapping基于粒子滤波原理是把机器人可能的位姿用一批粒子来表示每帧激光数据来的时候更新粒子权重最终收敛到最可能的位姿。优点是参数少、调试容易、小场景下效果很好、对CPU要求低。缺点是大场景或者回环较多时粒子容易耗尽位置估计会崩溃也就是俗称的“打滑”“飞轨”。Cartographer是基于图优化Graph SLAM的算法它会维护历史轨迹和约束在后端做闭环检测大场景、回环环境下表现远好于GMapping。但参数多、配置复杂对CPU要求也高。我最终在小场景测试用GMapping大场景跑了一圈后切到了Cartographer。两者各有适用范围不需要纠结建议先从GMapping起步跑通全链路后再尝试Cartographer。下面给出我在GMapping里调得比较顺的一组参数适合30平米左右的室内场景参数名建议值说明maxUrange4.0激光最大有效距离超过此距离的点不参与匹配4米比较合适minimumScore200.0激光帧与地图匹配的最低得分低于此值算法认为匹配失败可适当调低particles30粒子数量室内小场景30就够太大耗CPUlinearUpdate0.5小车平移0.5米时触发一次扫描匹配angularUpdate0.2小车旋转0.2弧度约11.5度时触发扫描匹配lsigma0.05激光噪音标准差数值越小越信任激光ogain3.0地图占用概率增益影响地图明暗对比调参的核心原则是先保证建图时小车移动速度慢线速度0.2m/s角速度0.3rad/s以内再根据地图效果微调参数。如果地图出现重影看看是不是雷达安装松动、移动过快或maxUrange过大纳入了远处噪音点。如果地图出现空洞考虑降低minimumScore或增加particles。4. 自主导航与航点巡航实现4.1 从静态地图到实时定位AMCL怎么知道“我在哪”建图完成后系统要解决的下一个问题是“我怎么知道自己在地图上的哪个位置”。我采用了ROS的AMCLAdaptive Monte Carlo Localization自适应蒙特卡洛定位包处理这个任务。AMCL的原理是维护一堆粒子每个粒子代表一个可能的机器人位姿x、y、yaw。初始阶段粒子分布在整个地图上每次接收到激光帧后算法计算每个粒子位置的激光模拟与实际激光的匹配度匹配度高的粒子保留匹配度低的淘汰。这个过程不断迭代粒子逐渐收敛到机器人真实位置附近。这个场景可以用一个类比理解闭着眼睛在一片大房间里找自己的位置每摸到一次墙就可以筛掉一大批不可能的猜测摸的次数越多位置越精确。在启动AMCL之前需要给定一个初始位置估计。如果初始姿态误差太大粒子可能会收敛到错误的位置也就是“定位绑架”问题。实用做法是先手动遥控小车转一圈让激光扫到周围环境逼着粒子尽快收敛然后再切换到自动导航模式。AMCL参数里有两个非常关键min_particles和max_particles。粒子太少容易被噪音带偏粒子太多吃CPU。我设置为min_particles500、max_particles2000小场景500~1000个粒子就够大场景建议上限到3000以上。另一个参数update_min_d和update_min_a控制粒子更新的频率分别表示小车移动多少距离、旋转多少角度才进行一次重采样。我设置为0.1m和0.1rad能很好地平衡定位精度和实时性。4.2 全局路径规划与局部避障谁来决定小车怎么走导航栈中路径规划分为两层全局路径规划和局部路径规划。全局路径规划器GlobalPlanner会在静态地图上从机器人当前位置到目标点搜索一条可行路径常见算法有Dijkstra和A*。局部路径规划器DWA则负责实时处理动态障碍物它会在小范围内采样多条速度指令评估每条指令能否避开障碍、方向是否偏向目标、速度是否合理然后选最优的一条执行。两者的关系像一个公司里的总监和组长总监定大方向组长解决路上的突发情况。DWA里我最常用也最推荐调的是这几个参数参数名建议值说明max_vel_x0.3最大前进线速度单位m/s室内太快容易扫飞地图min_vel_x0.0最小前进速度设为0允许原地转向max_vel_trans0.3最大平移速度max_vel_theta0.8最大角速度单位rad/smin_in_place_vel_theta0.3原地旋转的最小角速度防止旋转过慢xy_goal_tolerance0.1到达目标点的允许距离误差0.1米比较合适yaw_goal_tolerance0.1到达目标点的允许角度误差约5.7度sim_time2.0轨迹模拟的时间窗口长度秒太短容易急转太长反应迟钝说实话导航调参是最花时间的一步但也是最出成就感的一步。第一次看到小车自己从客厅穿过走廊开到卧室门口心里是真的爽感觉前面几个星期的折腾都值了。4.3 完整巡航流程建图、保存、定位、导航一条龙整个系统的实际使用流程是这样的启动建图启动雷达驱动、底盘驱动、GMapping。roscore roslaunch turn_on_wheeltec_robot lidar.launch roslaunch turn_on_wheeltec_robot base.launch roslaunch turn_on_wheeltec_robot gmapping.launch遥控小车扫图用键盘遥控节点或手柄慢慢推着小车走遍房间每个角落。难点在于要把房间的每个犄角旮旯都扫到地图边缘才不会缺一块。保存地图用map_server包把ROS中的栅格地图保存下来生成map.pgm和map.yaml两个文件。加载地图并启动定位关掉建档启动节点启动map_server提供了地图再启动AMCL定位节点。设置目标点在Rviz中点击“2D Nav Goal”按钮在地图上点击目标位置并设置朝向然后小车就会自主规划路径开过去。加入巡航任务如果要实现多点巡航我写了一个简单的调度节点定时读取坐标队列里的目标点依次发送给导航栈。每个目标点到达后停留3秒再发送下一个点。这套流程看似简单实际执行中有无数细节。最典型的是建图时如果小车转得稍快激光帧之间重叠角度大地图边缘就会出现撕裂感。解决办法只有一个字慢。建图速度宁慢勿快这是所有踩过的坑里最重要的一条经验。5. 常见问题与排查技巧实录5.1 地图重影、错位、乱漂的根因排查建图过程中最常见的问题就是地图重影或漂移。我总结了一套排查思路从高频到低频排序雷达固定松动雷达支架螺丝没拧紧跑起来雷达轻微晃动。排查方法断电后用手轻晃雷达如果能感觉到任何位移就是松动拧紧即可。里程计标定不准左右轮距、轮径参数不对导致里程计积分出来的轨迹和实际轨迹之间有偏差。排查方法让小车直线走2米看里程计反馈值是不是2米差太多就检查轮径参数编码器分辨率。再让小车原地转360度看航向角是否回到0如果偏了就检查左右轮编码器是否一致。激光最大距离过长maxUrange设置过大比如9米以上远处光线弱的墙体被误检测成噪点并参与匹配导致地图整体扭曲。把maxUrange调到4~6米通常能明显改善。移动速度过快建图时让小车跑得太快激光帧重叠不够匹配算法找不到足够的共同特征。减慢到线速度0.2m/s以内即可。地面反光严重某些瓷砖地面在低角度激光照射下产生镜面反射雷达收到的点会拉成一条“假直线”。这时可以调高雷达安装高度让入射角更陡减少反光影响。5.2 导航时小车抖、撞墙、找不到路怎么办导航阶段的问题和建图阶段完全不一样。小车抖左右扭动通常是DWA局部规划器参数里sim_time太短导致轨迹模拟不够远规划器对未来缺乏预见性就会频繁变方向。我建议sim_time至少设为1.5到2.0秒。小车撞墙一般是局部代价地图的膨胀半径inflation_radius设置太小。这个参数控制障碍物周围“虚拟禁区”的范围默认0.1米会让小车贴着墙走稍微有点惯性误差就蹭墙。调到0.2到0.3米能有效避免剐蹭。小车找不到路则要检查是否有动态障碍物挡在可行路径上或者在窄通道中规划失败。排查方法是在Rviz里显示全局规划路径Global Plan和局部规划路径Local Plan看是哪一层没规划出来。如果是全局层没路径说明静态地图里障碍物太密检查膨胀半径如果是局部层没路径说明DWA在当前位置附近找不到可行速度空间可以尝试提高max_vel_theta让小车的更多旋转选项来调整方向。5.3 串口通信异常乱码、断连、数据错乱的排查实录底盘节点和MCU之间的串口通信是项目里最容易出诡异问题的地方。有一次我打开底盘驱动节点里程计数据时而正常时而乱跳用串口助手直接看原始数据发现部分数据帧明显错位甚至出现非法字符。排查下来原因是我用的USB转串口模块在干扰下偶发丢字节导致帧错位而我的驱动代码没有做完整的帧校验。解决思路有三条帧格式设计用起始符比如0xAA 0x55加数据长度再加CRC16校验让驱动节点丢弃不合法帧。提高波特率健壮性对电机的PWM频率做调整换更粗的串口线或者将串口线远离电机线减少电磁干扰。异常重同步驱动节点检测到连续5帧非法数据时自动重新同步帧头而不是停留在错误状态。这类问题最有意思的地方在于它不是某一个硬件坏了而是整个链路里的噪声被放大后的结果。排查价值很高解决之后整个系统的稳定性会有一个质的提升。6. 扩展方向与个人经验总结6.1 这套系统还能往哪些方向扩展自主建图漫游车完成之后它已经是一个可以二次开发的折腾平台而不只是一台会动的玩具。按照我的经验值得延展的方向有三个第一是多传感器融合。在2D雷达的基础上增加深度相机用视觉信息补足低矮障碍物检测弥补2D雷达的扫描盲区。融合方式可以用扩展卡尔曼滤波EKF把IMU、轮式里程计、视觉里程计全部融合成一个高精度里程计这会让长距离建图的精度提升一大截。第二是语义建图。现在雷达建出来的是二维栅格地图只有“有没有障碍”这个信息。如果接入目标检测模型把摄像头画面里的门、桌子、椅子识别出来把语义标签叠加到地图上就能实现“房间里有一张桌子”这种语义级别的地图这对后续的复杂任务调度很有价值。第三是云端地图管理。建好的地图可以上传到云端做多楼层、多区域的地图存储和切换让机器人在大区域环境下自主选择加载对应楼层的建图文件。这个方向更偏工程适合想把项目做成产品的同学。6.2 我踩过的大坑与应该早点知道的事回头看这个项目有不少事情如果一开始就知道能省掉大量返工时间。这里挑几个值得说的第一硬件安装的精度决定了算法效果的上限。这是最痛彻的领悟。一开始我总觉得软件调参能弥补硬件误差后来发现雷达歪了3度、轮距测量差了5毫米地图和导航的精度就永远差了那么一点怎么调参都调不回来。如果你也想做这类项目安装环节把尺子量准绝对是投入产出比最高的一件事。第二不要在同一个地方反复折腾超过1小时先查外围再做深入排查。有一次我搞了一晚上里程计数据乱跳后来发现是USB线接触不良。这类经验说起来简单但人一旦钻进“调算法参数”的坑里就不愿意回头看硬件连线了。现在我的排查顺序永远是供电、接线、通信、传感器标定最后才是算法参数。第三数据可记录性是项目的生命线。有时候某个现象只在特定跑动路径下出现如果当时没有录包ROS的Bag文件复现起来就会非常痛苦。所以我从第一天就开启了rosbag record的习惯每次跑完实验先把数据包录下来再慢慢分析。有了数据包定位重建、算法对比、故障复盘都有了抓手。做一个自主建图漫游车表面上是把一堆硬件和代码组装在一起实际做下来你会发现它把嵌入式控制、传感器原理、概率机器人、路径规划、系统调试这些知识全部串成了一条线。这种项目最大的价值不是你最终得到的那张地图而是你在这个过程里建立起来的系统级直觉——当一个复杂系统出问题时你能不能快速判断问题在哪一层、是什么性质、优先级多高。如果你也想动手做一台我的建议是别纠结选型太久先用手头最便宜的雷达加一块树莓派把建图跑通再一步步升级。真正的坑都埋在实际跑的过程中提前看再多资料都不如自己让小车跑起来撞一次墙来得实在。
返回列表