扫地机器人这几年从"智商税"变成了"真香家电",但真正让我着迷的不是买一台成品,而是自己攒一台。原因很简单:市面上的成品机把SLAM、导航、路径规划这些最核心的东西全封装在黑盒里,你永远不知道它为什么在这个角落卡住、为什么漏扫那块地毯。自己动手做一台,从底盘运动控制到激光建图再到自主导航,整条链路全部打通,那种"它真的自己跑起来了"的成就感,是买十台成品都换不来的。
这篇文章面向三类人:一是想入门ROS2机器人开发但不知道从哪下手的学生和转行者;二是玩过STM32、想往上层算法延伸的嵌入式工程师;三是单纯想搞明白扫地机器人到底怎么工作的技术爱好者。我会把攒一台扫地机器人的三条技术路线讲清楚,再给出一张从零到能跑的完整路线图,包括底盘选型、传感器配置、ROS2环境搭建、SLAM建图、Nav2导航这些关键环节,以及我在实操中踩过的那些坑。
1. 先想清楚你要走哪条路线
攒扫地机器人这件事,最容易犯的错是一上来就买零件。我见过太多人兴冲冲下单了激光雷达和电机,结果发现底盘和雷达的通信协议对不上,或者算力板根本跑不动SLAM,最后零件全吃灰。所以在动手之前,先确定路线,路线决定了你的预算、时间投入和技术栈。
1.1 三条路线的本质区别
我把攒机路线分成三条,核心区别在于"你愿意自己造多少东西"。
路线一:成品底盘 + 上层开发。买一个带电机、编码器、驱动板的成品移动底盘(比如两轮差速底盘),你只负责在上面装雷达、装算力板、跑ROS2。这条路线把最脏最累的底层运动控制外包了,你专注在SLAM和导航上。适合想快速看到机器人跑起来、主要兴趣在上层算法的人。预算大概在1500到3000元。
路线二:STM32自研底盘 + ROS2上层。底盘完全自己搭:STM32做主控,驱动直流减速电机或步进电机,读编码器做里程计,通过串口和上位机通信。上位机跑ROS2做SLAM和导航。这条路线软硬通吃,是嵌入式工程师最舒服的路线,也是最能学到东西的路线。预算大概在1000到2500元,但时间投入是路线一的两三倍。
路线三:全栈从零,连雷达数据都自己处理。用STM32或树莓派直接对接激光雷达,自己写建图算法,不依赖ROS2的现成包。这条路线我不推荐新手走,除非你的目标就是深入理解SLAM算法本身。因为你要自己实现栅格地图、扫描匹配、位姿图优化,工作量巨大,而且很容易在调试阶段失去信心。
三条路线的对比如下:
| 维度 | 路线一:成品底盘 | 路线二:STM32自研底盘 | 路线三:全栈自研 |
|---|---|---|---|
| 底层运动控制 | 厂商搞定 | 自己写 | 自己写 |
| SLAM/导航 | ROS2现成包 | ROS2现成包 | 自己实现 |
| 技术门槛 | 中 | 中高 | 极高 |
| 时间投入 | 1-2周 | 1-2个月 | 半年以上 |
| 预算 | 1500-3000 | 1000-2500 | 2000+ |
| 学习收益 | 上层算法 | 软硬全链路 | 算法底层 |
| 适合人群 | 算法入门 | 嵌入式工程师 | 算法研究者 |
1.2 为什么我推荐大多数人走路线二
路线一看似省事,但有个隐藏问题:成品底盘的里程计精度和通信协议往往是黑盒,当你后面想调优导航参数时,会发现底盘的响应延迟、里程计漂移这些底层问题你根本改不了。而路线二虽然累,但每一个环节你都清楚,出了问题能定位到具体是哪一层。
路线二的关键在于STM32和上位机的分工要清晰。我的做法是:STM32只负责实时性要求高的活——电机PID控制、编码器读取、里程计积分、超声波避障,然后以固定频率(比如50Hz)通过串口把里程计数据打包发给上位机,同时接收上位机下发的速度指令。上位机(树莓派或迷你主机)负责跑ROS2、激光雷达驱动、SLAM、Nav2。这个分工的核心逻辑是:实时控制放在MCU,重计算放在CPU,各干各擅长的。
注意:串口通信的波特率别低于115200,否则50Hz的里程计数据加上速度指令会丢包。我一开始用9600,里程计数据延迟肉眼可见,机器人走起来像喝醉了。
1.3 底盘机械结构的几个硬指标
不管你走哪条路线,底盘机械结构有几个参数直接决定后面SLAM和导航好不好用。
轮距和轴距:两轮差速底盘,两个驱动轮的中心距叫轮距,这个值要准确测量,因为里程计解算要用。轮距测不准,机器人转90度实际只转了80度,建出来的地图就是歪的。
驱动轮直径:同样影响里程计。而且轮子直径要一致,我见过有人用两个不同批次的轮子,直径差了2mm,跑直线跑着跑着就偏了。
万向轮位置:万向轮要放在底盘重心附近,否则驱动轮抓地力不足,容易打滑。打滑是里程计最大的敌人,一打滑里程计就飘,SLAM就崩。
雷达安装高度:激光雷达要装在底盘最高处,且水平。雷达倾斜哪怕2度,扫出来的墙面就是斜的,建图会有重影。我建议用带水平泡的支架,装完拿手机水平仪App校准一下。
2. STM32底盘固件:从电机转动到里程计输出
这一章是路线二的核心,也是整个项目里最容易翻车的地方。很多人STM32玩得很溜,但一到"把电机转动变成ROS2能用的里程计"这一步就卡住了。我把它拆成几个关键环节讲。
2.1 电机选型与驱动:直流减速电机还是步进电机
扫地机器人底盘常用两种电机:直流减速电机(带编码器)和步进电机。
直流减速电机的优势是扭矩大、转速高、驱动简单,配合霍尔编码器能测速和测方向。缺点是低速时控制精度一般,需要PID闭环。我推荐用带AB相霍尔编码器的直流减速电机,减速比选1:30到1:50之间,轮径65mm左右,这样线速度大概能到0.3到0.5m/s,适合室内扫地场景。
步进电机的优势是开环也能精确定位,低速扭矩好。缺点是高速扭矩衰减快,而且容易丢步,丢步了里程计就错了。如果你用步进电机,建议用带闭环的驱动器,或者至少加编码器做校验。
驱动芯片方面,直流电机常用TB6612或DRV8833,步进电机常用DRV8825或TMC2209。这里有个坑:TB6612的峰值电流只有3.2A,如果你电机堵转电流超过这个值,驱动会烧。选驱动前先看电机的堵转电流参数。
2.2 编码器读取与里程计解算
编码器读取有两种方式:定时器编码器模式和外部中断。STM32的定时器自带编码器模式,直接读CNT寄存器就能得到计数值,效率最高。我建议用TIM2和TIM3分别接左右轮的AB相。
里程计解算的核心公式:
每脉冲行驶距离 = (轮子周长) / (编码器线数 × 减速比 × 4) 左轮行驶距离 = 左轮脉冲数 × 每脉冲行驶距离 右轮行驶距离 = 右轮脉冲数 × 每脉冲行驶距离 机器人前进距离 = (左轮行驶距离 + 右轮行驶距离) / 2 机器人转角 = (右轮行驶距离 - 左轮行驶距离) / 轮距这里的"×4"是因为AB相编码器四倍频。如果你用的是单相编码器,就不乘4。这个公式看着简单,但每个参数都要实测校准。轮子周长别用理论值,拿卷尺量实际滚动一圈的距离。轮距也别用卡尺量轮子中心距,让机器人原地转10圈,看实际转了多少度,反推轮距。
提示:里程计校准有个土办法——让机器人直线走3米,看里程计报了多少,如果报2.9米,就把每脉冲行驶距离乘以3/2.9。反复几次就能校准到1%以内。
2.3 串口通信协议设计
STM32和上位机的通信协议要简单可靠。我用的帧格式是:
帧头(0xAA 0x55) + 数据长度 + 数据类型 + 数据载荷 + 校验和数据类型分两种:上行(STM32发给上位机)是里程计数据,下行(上位机发给STM32)是速度指令。里程计数据包含x、y、theta和线速度、角速度,用float或int16打包。速度指令包含目标线速度和目标角速度。
校验和用简单的累加和就行,别用CRC16,增加计算量还容易写错。关键是帧头要选不容易出现在数据里的值,0xAA 0x55这种交替的比较好。
STM32这边用串口空闲中断加DMA接收,这样不占用CPU。发送用DMA,避免阻塞主循环。主循环里跑PID和里程计积分,频率控制在50Hz到100Hz。
2.4 PID调参:让机器人走直线
两轮差速机器人走直线靠的是两个轮子速度一致,但电机特性有差异,必须用PID闭环。我的做法是:外环是速度环,输入是目标线速度和角速度,输出是左右轮目标转速;内环是电流环或直接PWM。
调参顺序是先调单轮速度环。给左轮一个固定目标转速,看实际转速,调P和I直到响应快且不震荡。然后两个轮子都调好,再跑直线测试。如果走直线偏,说明两个轮子的PID参数不一致,或者机械阻力不同,微调其中一个的I参数。
这里有个经验:积分项I别给太大,否则启动时会积分饱和,机器人会猛地冲一下。我一般给I限幅,或者用积分分离——误差大的时候不积分。
3. ROS2环境搭建与激光雷达接入
底盘能动了,接下来是让上位机跑起来。ROS2的版本选择很关键,我推荐Humble,因为它是LTS版本,社区包最全,Nav2和SLAM Toolbox都支持得好。Ubuntu 22.04配Humble是当前最稳的组合。
3.1 ROS2安装与工作空间初始化
安装ROS2 Humble用apt最省事,但要注意源和密钥别搞错。装完之后先跑个小乌龟测试,确认环境没问题。然后创建工作空间:
mkdir -p ~/robot_ws/src cd ~/robot_ws colcon build source install/setup.bash工作空间的目录结构要规划好,我建议按功能分包:robot_bringup放launch文件,robot_description放URDF,robot_driver放底盘和雷达驱动,robot_navigation放Nav2配置。这样后面维护清晰。
注意:每次打开新终端都要source一次setup.bash,嫌麻烦就写进.bashrc。但别把工作空间的setup写进.bashrc,否则多个工作空间会冲突。
3.2 激光雷达选型与驱动
激光雷达是扫地机器人的眼睛。常见的有单线雷达和固态雷达。单线雷达(比如RPLIDAR系列)便宜、驱动成熟,但只能扫一个平面。固态雷达体积小、寿命长,但价格高。
对于攒机,我推荐单线雷达,扫描半径8到12米足够室内用。接入ROS2需要装对应的驱动包,然后配置串口权限。这里有个经典坑:雷达的串口设备名会变,今天叫ttyUSB0,明天可能叫ttyUSB1。解决办法是用udev规则绑定设备名,根据雷达的USB VID/PID创建固定软链接。
雷达驱动发布的话题是sensor_msgs/LaserScan,在RViz2里能看到扫描点。如果点云是歪的,检查雷达安装是否水平;如果扫描范围不对,检查雷达的angle_min和angle_max参数。
3.3 URDF建模:让ROS2认识你的机器人
URDF是ROS2里描述机器人几何和运动关系的文件。你需要定义底盘、两个驱动轮、万向轮、雷达的link和joint。关键是joint的类型:驱动轮是continuous(连续旋转),万向轮是fixed或continuous,雷达是fixed。
URDF里最容易错的是坐标系方向。ROS2约定:x向前,y向左,z向上。雷达的坐标系要和底盘坐标系对齐,否则建图会错位。我建议在URDF里给每个link加一个简单的立方体或圆柱体visual,方便在RViz2里检查。
写完URDF后,用robot_state_publisher发布TF树,用joint_state_publisher发布关节状态。在RViz2里能看到机器人模型和TF坐标系,确认没问题再往下走。
3.4 底盘驱动节点:把串口数据变成ROS2话题
底盘驱动节点是连接STM32和ROS2的桥梁。它要做两件事:一是订阅/cmd_vel话题,把速度指令通过串口发给STM32;二是从串口读里程计数据,发布/odom话题和TF变换。
/odom话题的类型是nav_msgs/Odometry,包含位姿和速度。TF变换是odom到base_link的变换,这个变换由里程计提供。注意:里程计会漂移,所以odom坐标系是局部的,后面SLAM会提供map到odom的修正。
写这个节点用Python或C++都行。Python开发快,C++性能好。我建议先用Python跑通,后面如果需要高频再换C++。节点里要注意串口读取的线程安全,别在主线程里阻塞读。
4. SLAM建图:让机器人认识环境
SLAM是扫地机器人的灵魂。简单说,就是机器人一边走一边画地图,同时知道自己在地图的哪个位置。ROS2里最常用的SLAM包是slam_toolbox,支持在线建图和保存地图。
4.1 slam_toolbox配置与启动
slam_toolbox的配置参数很多,但新手只需要关注几个:max_laser_range设成雷达实际量程,resolution设成0.05米(5cm一个栅格),map_update_interval设成1秒。模式选mapping。
启动顺序很重要:先启动底盘驱动和雷达驱动,确认/odom和/scan话题都有数据,再启动slam_toolbox。启动后在RViz2里添加Map显示,应该能看到地图逐渐长出来。
建图时用手动遥控机器人走一圈,速度别太快,0.2m/s左右。走太快雷达数据匹配不上,地图会糊。转弯时慢一点,让雷达多扫几帧。我一般让机器人沿着墙走一圈,再走几个房间,地图就基本完整了。
4.2 建图质量差的几个原因
建图糊、重影、墙壁歪,是新手最常遇到的问题。我总结了几条排查链路:
第一,里程计不准。这是最常见的原因。如果里程计报的转角和实际转角差得多,SLAM的扫描匹配就会失败。回去校准里程计,走直线和原地转圈测试。
第二,雷达安装不水平。雷达倾斜会导致扫描平面不是水平面,扫到的墙是斜线。用水平仪校准。
第三,雷达数据时间戳不对。如果雷达驱动的时间戳用的是系统时间而不是雷达内部时间,会导致TF变换错位。检查雷达驱动的参数。
第四,环境特征太少。如果房间全是白墙,雷达扫不到特征,SLAM会迷失。这种情况可以贴一些标记物,或者用带反光板的雷达。
第五,机器人速度太快。雷达扫描频率是固定的,走太快两帧之间位移太大,匹配不上。降速。
4.3 地图保存与加载
建好图后,用nav2_map_server的map_saver保存地图,会生成.pgm和.yaml两个文件。.pgm是栅格图,.yaml是元数据(分辨率、原点、阈值)。
保存的地图后面导航时要用。加载地图用map_server节点,发布/map话题。注意地图的origin参数要和建图时的起点一致,否则导航时机器人初始位置会偏。
提示:建图时机器人起点最好选一个特征明显的位置,比如门口或墙角。这样后面导航时,用AMCL定位能快速收敛。
5. Nav2导航:从能建图到能自己跑
建图只是第一步,让机器人自己规划路径、避开障碍、到达目标点,才是扫地机器人的完整形态。Nav2是ROS2的导航框架,功能强大但配置复杂。
5.1 Nav2的核心组件与数据流
Nav2由多个节点组成,核心的有:bt_navigator(行为树导航)、planner_server(全局规划)、controller_server(局部控制)、recoveries_server(恢复行为)、amcl(定位)、map_server(地图)。
数据流是这样的:你给一个目标点,bt_navigator启动行为树,调用planner_server算全局路径,然后controller_server跟踪路径并发布/cmd_vel,同时amcl根据雷达和地图修正机器人位置。如果卡住了,recoveries_server会执行恢复行为,比如后退、旋转。
5.2 代价地图配置:让机器人知道哪里能走
代价地图是Nav2的核心概念,分全局代价地图和局部代价地图。全局代价地图基于静态地图,局部代价地图基于实时雷达数据。
配置的关键参数:inflation_radius是膨胀半径,决定机器人离障碍物多远。设太小会撞,设太大会卡在窄道。我一般设成机器人半径加5cm。cost_scaling_factor是代价衰减系数,影响路径贴墙的程度。
局部代价地图的obstacle_range和raytrace_range要根据雷达量程设。obstacle_range是标记障碍的距离,raytrace_range是清除障碍的距离。设反了会导致障碍物标记不消失。
5.3 行为树与恢复行为
Nav2用行为树组织导航逻辑。默认的行为树是:规划路径→跟踪路径→如果失败→清除代价地图→重新规划→如果还失败→旋转→如果还失败→后退。
行为树可以自定义,但新手先用默认的。恢复行为里,spin和backup最常用。spin是原地旋转找路,backup是后退。这两个行为能解决大部分卡死情况。
如果机器人经常卡在同一个地方,说明代价地图配置有问题,或者雷达有盲区。检查雷达安装位置,确保没有遮挡。
5.4 定位:AMCL与里程计的配合
AMCL是自适应蒙特卡洛定位,用粒子滤波根据雷达和地图估计机器人位置。它发布map到odom的TF变换,修正里程计漂移。
AMCL的参数里,min_particles和max_particles控制粒子数,laser_model_type选likelihood_field。初始位置要手动给,在RViz2里用2D Pose Estimate点一下。
定位不准的常见原因:地图和实际环境不一致(比如家具挪了位置)、雷达数据噪声大、粒子数太少。我建议建图后别挪大件家具,否则要重新建图。
6. 攒机路上那些没人告诉你的坑
前面讲的都是"应该怎么做",这一章讲"实际做的时候会怎么翻车"。这些是我和身边朋友踩过的坑,文档里不会写。
6.1 电源系统的隐形杀手
扫地机器人是移动的,电源系统比想象中复杂。STM32要5V或3.3V,电机要12V,雷达要5V,上位机要5V。如果用一个电池加多个降压模块,要注意共地。我见过有人忘了共地,串口通信时好时坏,查了一周才发现。
电池选锂电还是铅酸?锂电轻、能量密度高,但需要保护板。铅酸重但便宜。我推荐3S锂电(11.1V),配一个12V转5V的降压模块给雷达和上位机。电机直接接12V。
注意:电机启动瞬间电流很大,会把电压拉低,导致STM32复位。解决办法是电机电源和逻辑电源分开,或者加一个大电容缓冲。
6.2 串口通信突然连不上
STM32的CAN通信突然连不上、串口突然没数据,这类问题我遇到过好几次。排查顺序:先看硬件连接,再看波特率,再看终端电阻,最后看软件。
CAN通信需要120欧姆终端电阻,少了通信不稳定。串口通信要注意TX和RX交叉接,很多人接成TX对TX,当然没数据。还有,STM32的串口引脚要配置成复用推挽输出,别配成普通IO。
如果用的是USB转串口模块,注意模块的芯片。CH340便宜但驱动偶尔抽风,CP2102稳定但贵一点。我建议用CP2102。
6.3 雷达数据在RViz2里不显示
雷达驱动跑起来了,但RViz2里看不到点云。排查:先看话题有没有数据(ros2 topic hz /scan),再看frame_id对不对,最后看RViz2的Fixed Frame设对没有。
最常见的原因是frame_id和URDF里的雷达link名字不一致。比如雷达驱动发布的是laser_frame,URDF里叫laser_link,TF树就断了。改成一致就行。
还有一个坑:雷达的扫描角度范围。有些雷达默认只扫180度,你以为它坏了,其实是配置问题。看驱动的angle_min和angle_max参数。
6.4 Nav2规划失败:机器人原地转圈
Nav2启动后,给目标点,机器人原地转圈不走路。这通常是定位没收敛或代价地图有问题。
先看AMCL的粒子云,如果粒子散得到处都是,说明定位没收敛。检查初始位置给对没有,地图和实际环境是否一致。如果粒子收敛了但机器人还是不动,看全局路径有没有规划出来。如果路径是空的,说明目标点在障碍物里,或者代价地图把目标点标记成障碍了。
局部规划失败的话,看controller_server的日志,通常是局部代价地图里没有可行路径。调小inflation_radius试试。
6.5 建图时机器人"跟随焦点随意移动"
这个现象我遇到过:建图时机器人明明没动,地图却在漂移。原因是雷达数据里有动态障碍物(比如走动的人),SLAM把这些动态点当成了环境特征。
解决办法:建图时清场,别让人在雷达范围内走动。如果无法避免,可以在slam_toolbox里调大minimum_travel_distance和minimum_travel_heading,让SLAM不那么敏感。
还有一个原因是里程计漂移。如果机器人静止时里程计还在累积,说明编码器有噪声或者PID在震荡。检查电机是否真的停了,编码器读数是否稳定。
7. 从能跑到好用:进阶优化方向
机器人能自己导航了,但离"好用"还有距离。这一章讲几个进阶优化方向,让你的扫地机器人从"能跑"变成"跑得好"。
7.1 覆盖路径规划:让扫地不漏扫
Nav2默认是点到点导航,但扫地需要覆盖整个区域。覆盖路径规划(Coverage Path Planning)是专门解决这个问题的。常见算法有牛耕式(Boustrophedon)和螺旋式。
牛耕式就是来回扫,像耕地一样。实现思路是:把地图分成若干单元格,规划一条经过所有单元格的路径。ROS2里有nav2_coverage相关的包,但成熟度一般,可能需要自己写。
我的做法是简化版:把房间分成几个矩形区域,每个区域用牛耕式路径,区域之间用Nav2导航连接。这样实现简单,效果也够用。
7.2 多传感器融合:超声波和IMU
单靠雷达,机器人对低矮障碍物(比如拖鞋、电线)检测不到。加超声波传感器能补盲。STM32上接几个超声波模块,检测到障碍就通过串口上报,ROS2这边转成/ultrasonic话题,在代价地图里标记。
IMU能提供角速度和加速度,和里程计融合能提高定位精度。ROS2里有robot_localization包做EKF融合。IMU选MPU6050或ICM20602,通过I2C接STM32,数据打包进里程计帧里。
7.3 回充与自主充电
扫地机器人没电了要自己回去充电。这需要:充电桩有红外或视觉标记,机器人能识别并对接。实现上,充电桩发红外信号,机器人上装红外接收器,靠近时根据信号强度调整方向。
ROS2这边,可以设一个充电桩位置为特殊目标点,电量低时自动导航过去。对接阶段用红外引导,精度要求高,需要单独写控制逻辑。
7.4 远程监控与数据记录
想在外面看机器人状态,可以用ROS2的ros2 bag记录数据,或者用WebSocket把话题转发到网页。rosbridge能把ROS2话题转成WebSocket,前端用roslibjs订阅,就能在浏览器里看地图和机器人位置。
数据记录方面,ros2 bag record能录所有话题,后面回放分析。我建议建图和导航时都录bag,出问题了能复现。
8. 一张攒机路线图:从零到能跑的完整清单
最后给一张实操路线图,按顺序做,每步都有验收标准。
第一阶段:底盘能动(1-2周)
- 采购:STM32开发板、直流减速电机带编码器、电机驱动、电池、轮子、万向轮、底盘板
- 完成:STM32能通过PID控制电机转速,编码器能读数
- 验收:给固定PWM,轮子转速稳定;给目标转速,PID能闭环
第二阶段:里程计输出(3-5天)
- 完成:STM32解算里程计,通过串口按协议发送
- 验收:上位机串口助手能收到正确格式的里程计数据,手动转轮子数据变化正确
第三阶段:ROS2接入(1周)
- 采购:树莓派或迷你主机、激光雷达
- 完成:ROS2环境搭好,底盘驱动节点跑通,雷达驱动跑通
- 验收:RViz2里能看到
/odom和/scan,TF树完整
第四阶段:SLAM建图(3-5天)
- 完成:slam_toolbox配置好,能建图
- 验收:遥控机器人走一圈,地图完整,墙壁直,无重影
第五阶段:Nav2导航(1-2周)
- 完成:Nav2配置好,能点到点导航
- 验收:给目标点,机器人能规划路径、避障、到达
第六阶段:优化(持续)
- 覆盖路径规划、多传感器融合、回充、远程监控
这张路线图的关键是每阶段都有验收标准,别急着往下走。我见过太多人底盘还没调稳就去搞SLAM,结果建图一塌糊涂,回头返工更费时间。
提示:每个阶段都录bag,出问题了回放分析。这是排查问题最有效的手段,比盯着屏幕猜强一百倍。
整个项目做下来,我最大的体会是:扫地机器人是一个典型的"木桶"系统,底盘、里程计、雷达、SLAM、导航,任何一环短板都会让整体体验崩掉。但反过来说,每修好一个环节,你都能明显感觉到机器人变聪明了一点。这种即时反馈,是纯软件项目给不了的。如果你也想攒一台,别想太多,先从让轮子转起来开始。