1. 从零开始搭建SLAM学习路线:一个过来人的完整笔记
SLAM这个词,如果你刚接触机器人或者自动驾驶领域,大概率已经被它反复轰炸过了。全称Simultaneous Localization and Mapping,中文叫同步定位与建图。说白了就是一台机器在一个完全陌生的环境里,一边要知道自己在哪,一边还要把周围的地图画出来。听起来像是个鸡生蛋蛋生鸡的问题——不知道地图怎么定位,不知道位置怎么建图。但SLAM就是要把这两个事情同时解决。
我最初接触SLAM的时候,踩的坑可以说是一个接一个。装ROS环境折腾了三天,跑第一个激光雷达建图的时候rviz里一片空白,好不容易出了点地图又飘得不成样子。后来慢慢从2D激光SLAM摸到视觉SLAM,再到现在的3DGS SLAM,走了不少弯路。这篇笔记就是把我这些年积累的学习路径、实操经验和避坑技巧完整梳理出来,适合刚入门的同学建立知识框架,也适合有一定基础的朋友查漏补缺。
不管你是做ROS SLAM建图和自主导航的工程开发,还是研究视觉SLAM算法的学术方向,或者只是想让自己的机器人跑起来别撞墙,下面的内容应该都能帮到你。
2. SLAM学习路线整体设计与思路拆解
2.1 为什么SLAM的学习顺序不能乱
很多人一上来就想搞视觉SLAM,觉得激光雷达太贵,相机便宜。这个想法本身没错,但如果你连基本的坐标系变换、概率论基础、非线性优化都没搞清楚,直接啃视觉SLAM十四讲,大概率是看一页忘一页。我自己就是这么过来的,第一遍看十四讲的时候,李群李代数那章直接把我劝退了。
SLAM的学习有一条比较合理的路径:先建立数学基础,再理解传感器特性,然后从简单的2D激光SLAM入手跑通整个流程,最后再深入到视觉SLAM和3D SLAM。这条路径的核心逻辑是——先见森林,再见树木。你得先知道一个完整的SLAM系统长什么样,各个模块之间怎么交互,再去深挖每个模块的算法细节。
具体来说,我建议的学习顺序是这样的:
- 第一阶段:数学与编程基础。线性代数(矩阵运算、特征值分解)、概率论(贝叶斯滤波、高斯分布)、非线性优化(最小二乘、高斯牛顿法、LM算法)、C++和Python编程能力。
- 第二阶段:ROS基础与2D激光SLAM。学会用ROS做话题通信、TF变换、rviz可视化,然后跑通gmapping或cartographer,理解栅格地图的构建过程。
- 第三阶段:视觉SLAM入门。理解相机模型、特征点提取与匹配、对极几何、PnP求解、光束法平差。
- 第四阶段:现代SLAM系统。ORB-SLAM3、VINS-Mono、LIO-SAM等开源框架的源码阅读与实战。
- 第五阶段:前沿方向。3DGS SLAM、语义SLAM、多传感器融合等。
2.2 激光SLAM和视觉SLAM该怎么选
这是被问得最多的问题之一。我的建议是:如果你做的是室内机器人、扫地机、AGV这类产品,激光雷达SLAM是首选,因为2D激光雷达成本已经很低了,而且建图精度和鲁棒性都很好。如果你做的是无人机、AR/VR、自动驾驶,视觉SLAM或者视觉惯性融合方案更合适,因为相机成本低、信息丰富,而且能提供更稠密的环境描述。
但说实话,现在纯视觉或者纯激光的方案都越来越少了,主流趋势是多传感器融合。激光雷达提供精确的深度信息,相机提供丰富的纹理信息,IMU提供高频的运动先验,三者互补。所以你不用太纠结先学哪个,关键是理解每种传感器的优缺点和适用场景。
| 对比维度 | 激光SLAM | 视觉SLAM |
|---|---|---|
| 传感器成本 | 2D雷达较低,3D雷达较贵 | 相机成本低 |
| 建图精度 | 高,直接测距 | 中等,依赖三角化 |
| 光照鲁棒性 | 不受光照影响 | 对光照变化敏感 |
| 纹理依赖 | 不依赖纹理 | 弱纹理场景容易丢失 |
| 地图类型 | 栅格地图/点云地图 | 稀疏/半稠密/稠密点云 |
| 计算量 | 相对较低 | 特征提取和优化计算量大 |
| 典型框架 | gmapping、cartographer、LIO-SAM | ORB-SLAM3、VINS-Mono、DSO |
2.3 学习SLAM需要什么样的硬件和软件环境
软件环境方面,Ubuntu是标配,ROS Noetic(Ubuntu 20.04)或者ROS2 Humble(Ubuntu 22.04)是目前比较稳定的选择。如果你刚开始学,我建议直接用ROS1 Noetic,资料多、社区活跃、踩坑容易找到答案。ROS2虽然是大势所趋,但学习阶段的资料丰富度还是不如ROS1。
硬件方面,最低配置是一台带独显的笔记本或者台式机,因为SLAM的编译和运行对CPU和内存要求不低。如果你要跑视觉SLAM,最好有一块NVIDIA显卡,方便跑CUDA加速的特征提取。如果要做实机验证,一个便宜的2D激光雷达(比如思岚的RPLIDAR系列)加一个树莓派或者Jetson Nano就能跑起来。
注意:不要在Windows上折腾ROS,WSL虽然能跑但坑很多,尤其是涉及到USB设备直通和图形化显示的时候。老老实实装双系统或者用Ubuntu单系统。
3. 核心细节解析与实操要点
3.1 坐标系变换:SLAM中最容易被忽视的基础
坐标系变换是SLAM的基石,但很多人在学习初期会跳过这部分直接去看算法。结果就是跑代码的时候TF树报错,完全不知道从哪查起。我用一个生活化的类比来解释:你站在房间里,你知道自己相对于房间的位置,房间相对于整栋楼的位置,整栋楼相对于地图的位置。这三个关系就是三层坐标系变换,任何一层断了,你就无法知道自己在全局地图中的位置。
在ROS中,TF树维护了所有这些坐标系之间的变换关系。常见的坐标系包括:
- map:全局地图坐标系,原点固定在地图某个位置
- odom:里程计坐标系,原点在机器人启动位置
- base_link:机器人本体坐标系
- laser_link:激光雷达坐标系
- camera_link:相机坐标系
TF变换的本质是旋转矩阵和平移向量的组合。一个三维空间中的刚体变换可以用一个4x4的变换矩阵表示:
T = [ R t ] [ 0 1 ]其中R是3x3的旋转矩阵,t是3x1的平移向量。旋转矩阵可以用欧拉角、四元数或者旋转向量来表示。在ROS中,四元数是最常用的旋转表示方式,因为它没有万向锁问题,插值也平滑。
实操中最容易出错的地方是TF变换的时间戳。ROS的TF要求每个变换都带时间戳,而且不同坐标系之间的变换需要时间对齐。如果你发现rviz里机器人模型闪烁或者激光数据对不上,八成是TF的时间戳出了问题。
3.2 激光雷达SLAM建图的核心流程
以最经典的gmapping为例,整个建图流程可以拆解为以下几个关键步骤:
第一步:数据预处理。激光雷达输出的原始数据是极坐标下的距离和角度信息,需要转换成笛卡尔坐标系的点云。同时要过滤掉超出量程的无效点和噪声点。这个步骤看起来简单,但实际中激光雷达的噪声特性需要仔细标定,尤其是低成本雷达在强光或者黑色物体表面容易产生噪点。
第二步:扫描匹配。这是激光SLAM的核心。简单来说,就是把当前帧的激光扫描数据和已有地图进行对齐,找到最优的位姿变换。gmapping用的是粒子滤波的方法,每个粒子代表一个可能的机器人位姿,通过观测模型计算每个粒子的权重,然后重采样。cartographer则用了基于图优化的方法,先构建子图,再做全局优化。
第三步:地图更新。根据扫描匹配的结果,把当前帧的激光数据插入到栅格地图中。每个栅格有一个占据概率,被激光击中的栅格概率增加,激光穿过的栅格概率降低。用对数几率(log-odds)来表示可以避免数值溢出:
l(m) = log(p(m) / (1 - p(m)))更新时直接做加法:l_new = l_old + l_measurement,非常高效。
第四步:位姿修正。当机器人重新访问之前到过的区域时,通过回环检测发现这一点,然后对整条轨迹进行优化,消除累积误差。这一步是建图精度的关键,没有回环检测的SLAM系统跑久了地图一定会歪。
3.3 视觉SLAM的特征点法与直接法之争
视觉SLAM有两大流派:特征点法和直接法。特征点法先提取图像中的关键点(如ORB、SIFT、SURF),然后匹配相邻帧的特征点,通过几何关系求解相机运动。直接法则跳过特征提取,直接利用像素灰度值构建光度误差来优化相机位姿。
特征点法的优势在于对光照变化和运动模糊更鲁棒,因为特征描述子本身具有一定的光照不变性。ORB-SLAM系列就是特征点法的集大成者,它的跟踪、局部建图、回环检测三个线程并行运行,精度和鲁棒性都很好。但特征点法的缺点也很明显:特征提取耗时,在弱纹理场景下容易丢失,而且只能构建稀疏点云地图。
直接法的优势在于能利用所有像素信息,可以构建半稠密甚至稠密地图,在纹理丰富的场景下精度更高。DSO、LSD-SLAM是直接法的代表。但直接法对光照变化非常敏感,而且光度误差函数的非凸性更强,优化容易陷入局部极小值。
我的建议是:入门阶段先学特征点法,因为它的理论体系更成熟,调试工具更完善。等你对SLAM的整体框架有了感觉,再去研究直接法。
3.4 3DGS SLAM:SLAM领域的新范式
3D Gaussian Splatting(3DGS)是最近两年非常火的方向,它用一堆三维高斯椭球来表示场景,每个高斯有位置、协方差、不透明度和球谐系数等属性。渲染的时候把这些高斯投影到图像平面,做alpha混合,就能得到高质量的渲染结果。
3DGS SLAM就是把3DGS作为地图表示,替代传统的点云或者体素地图。这样做的好处是地图更紧凑、渲染质量更高、还能做新视角合成。但挑战也很大:3DGS的优化需要大量计算,实时性是个问题;而且3DGS地图的更新和回环修正不像传统地图那么直接。
目前比较有代表性的工作包括SplaTAM、Gaussian-SLAM等。如果你对这个方向感兴趣,建议先跑通原版3DGS的代码,理解它的渲染管线和优化过程,再去研究怎么把它嵌入SLAM框架。
4. 实操过程与核心环节实现
4.1 ROS环境搭建与第一个激光SLAM建图
假设你已经装好了Ubuntu 20.04,下面是从零开始跑通一个2D激光SLAM建图的完整步骤。
安装ROS Noetic:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-full echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrc安装gmapping和导航包:
sudo apt install ros-noetic-gmapping ros-noetic-navigation ros-noetic-teleop-twist-keyboard启动建图节点。假设你有一个激光雷达发布在/scan话题上,里程计发布在/odom话题上:
rosrun gmapping slam_gmapping scan:=/scan然后在rviz中添加LaserScan和Map显示,用键盘遥控机器人慢慢走一圈,你就能看到地图逐渐构建出来。
保存地图:
rosrun map_server map_saver -f ~/my_map这会生成my_map.pgm和my_map.yaml两个文件,前者是地图图像,后者是地图的元数据配置。
4.2 关键参数调优与计算过程
gmapping的参数很多,但真正影响建图效果的就这么几个:
| 参数名 | 默认值 | 作用 | 调优建议 |
|---|---|---|---|
| particles | 30 | 粒子数量 | 场景大且复杂时增加到50-80 |
| delta | 0.05 | 扫描匹配的位姿变化阈值 | 减小到0.02可提高精度但增加计算量 |
| linearUpdate | 1.0 | 机器人移动多少米更新一次 | 减小到0.5可提高地图密度 |
| angularUpdate | 0.5 | 机器人旋转多少弧度更新一次 | 减小到0.2可改善旋转时的建图 |
| maxRange | 5.0 | 激光最大有效距离 | 根据实际雷达量程设置 |
| sigma | 0.05 | 扫描匹配的方差 | 雷达噪声大时适当增大 |
粒子数量的选择有一个经验公式:粒子数应该与地图面积和机器人位姿不确定性成正比。如果地图是100平米左右的室内环境,30个粒子通常够用。但如果环境中有很多相似的结构(比如长走廊),需要增加粒子数来避免粒子退化。
4.3 视觉SLAM十四讲配套代码实操
视觉SLAM十四讲是入门视觉SLAM最好的教材之一,但书中的代码需要自己配置环境。以下是我实际跑通的环境配置:
安装依赖:
sudo apt install cmake g++ git libeigen3-dev libopencv-dev libpcl-dev libceres-dev libg2o-dev安装Sophus(李代数库):
git clone https://github.com/strasdat/Sophus.git cd Sophus mkdir build && cd build cmake .. make -j4 sudo make install编译并运行ch7的PnP求解示例:
cd slambook2/ch7 mkdir build && cd build cmake .. make -j4 ./pose_estimation_3d2d这个示例演示了如何用3D-2D的匹配点对求解相机位姿。核心是构建重投影误差,然后用Ceres或者g2o做非线性优化。如果你跑出来的位姿和真值差距很大,检查一下特征匹配的质量,可以加一个RANSAC剔除误匹配。
4.4 从建图到自主导航的完整链路
建好地图只是第一步,让机器人在地图上自主导航才是最终目标。ROS的navigation包提供了完整的导航框架,包括全局规划、局部规划、代价地图、恢复行为等模块。
配置导航包的核心文件:
costmap_common_params.yaml:代价地图通用参数,包括机器人半径、障碍物膨胀半径、传感器配置global_costmap_params.yaml:全局代价地图参数local_costmap_params.yaml:局部代价地图参数base_local_planner_params.yaml:局部规划器参数,包括最大速度、加速度、路径评分权重
启动导航:
roslaunch my_robot_nav navigation.launch然后在rviz中用2D Pose Estimate设置初始位置,用2D Nav Goal设置目标点,机器人就会自动规划路径并移动过去。
实操中导航最容易出的问题是代价地图膨胀半径设置不当。膨胀半径太小,机器人会贴着墙走甚至撞墙;膨胀半径太大,狭窄通道会被完全堵死,规划不出路径。我的经验是膨胀半径设置为机器人半径的1.5到2倍比较合适。
5. 常见问题与排查技巧实录
5.1 建图过程中地图漂移怎么办
地图漂移是SLAM中最常见的问题,表现为建好的地图出现重影、墙壁变厚、回环处对不齐。原因通常有以下几个:
里程计精度不够。如果轮式里程计的标定不准确,累积误差会很快导致地图漂移。解决方法是重新标定轮子半径和轮距,或者引入IMU做融合。
回环检测失败。如果机器人回到了之前到过的位置但没有识别出来,误差就无法消除。可以尝试调整回环检测的搜索半径和匹配阈值。对于gmapping,可以增大linearUpdate和angularUpdate的灵敏度。
激光雷达安装位置不准确。如果TF树中激光雷达相对于底盘的变换有偏差,扫描匹配就会出错。用卷尺实际测量雷达的安装位置,确保TF配置中的平移和旋转参数准确。
环境特征太少。在长走廊或者空旷大厅中,激光扫描的几何特征不明显,扫描匹配容易退化。这种情况下可以考虑加入视觉信息或者反光板辅助定位。
5.2 视觉SLAM跟踪丢失的排查思路
视觉SLAM跟踪丢失通常发生在快速运动、弱纹理区域或者光照剧烈变化的时候。排查步骤:
- 检查图像质量。在rviz或者image_view中查看相机图像,确认曝光是否正常、是否有运动模糊。
- 检查特征点数量。ORB-SLAM会在图像上绘制提取到的特征点,如果特征点太少(少于100个),说明场景纹理不足。
- 检查IMU数据。如果用了视觉惯性方案,IMU的偏置和噪声参数需要仔细标定。IMU数据异常会导致跟踪发散。
- 降低运动速度。快速旋转是视觉SLAM最容易丢失的运动模式,因为帧间重叠区域太小。放慢速度通常能解决问题。
5.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| rviz中无激光数据显示 | 话题名不匹配 | rostopic list查看实际话题名 | 修改launch文件中的话题映射 |
| TF报错"Lookup would require extrapolation" | 时间戳不同步 | rosrun tf view_frames查看TF树 | 检查传感器时间戳,加ros::Time::waitForTransform |
| 建图时地图抖动 | 扫描匹配不稳定 | 查看odom和laser的TF频率 | 降低机器人速度,增大粒子数 |
| 导航时机器人原地转圈 | 局部规划器陷入局部极小 | 查看局部代价地图 | 调整局部规划器参数,增加恢复行为 |
| 编译ORB-SLAM3报错 | 依赖版本不兼容 | 查看CMake错误信息 | 使用指定版本的OpenCV和Eigen |
| 3DGS训练显存不足 | 高斯数量过多 | nvidia-smi查看显存占用 | 降低图像分辨率,增加稠密化阈值 |
5.4 几个让我印象深刻的踩坑经历
坑一:ROS时间戳问题。有一次跑建图,地图每隔几秒就跳一下。查了两天才发现是激光雷达的驱动节点用了系统时间而不是ROS时间,导致TF变换的时间戳和激光数据对不上。解决方法是在驱动中统一使用ros::Time::now()。
坑二:OpenCV版本冲突。ORB-SLAM3需要OpenCV 3.x,但Ubuntu 20.04默认装的是OpenCV 4.x。直接编译会报一堆API不兼容的错误。我的做法是从源码编译OpenCV 3.4并安装到/usr/local,然后在CMake中指定路径。
坑三:3DGS SLAM的实时性。第一次跑SplaTAM的时候,每帧要优化好几秒,完全没法实时。后来发现是高斯数量增长太快,稠密化策略太激进。调整了稠密化阈值和修剪策略之后,帧率提升到了可接受的范围。
提示:SLAM的调试过程中,可视化工具是你的最好朋友。rviz、Foxglove、PlotJuggler这些工具能帮你快速定位问题。不要只盯着终端里的日志看,把数据画出来往往一目了然。
6. 进阶方向与学习资源推荐
6.1 值得深入研究的开源框架
当你跑通了基础的建图之后,下一步就是阅读优秀开源项目的源码。以下是我认为最值得花时间研究的几个:
- ORB-SLAM3:视觉SLAM的标杆,支持单目、双目、RGB-D和IMU融合,代码结构清晰,适合学习完整的SLAM系统架构。
- LIO-SAM:激光惯性紧耦合方案,基于因子图优化,适合学习多传感器融合的工程实现。
- Cartographer:Google开源的2D和3D建图方案,工程化程度极高,适合学习工业级SLAM系统的设计。
- VINS-Mono:视觉惯性里程计,香港科技大学开源,适合学习VIO的滑动窗口优化。
- SplaTAM:3DGS SLAM的代表工作,适合了解SLAM与神经渲染的结合。
阅读源码的时候,不要试图一次看懂所有文件。我的方法是先跑通demo,然后从main函数开始,沿着数据流一步步跟踪,画出模块之间的调用关系图。遇到不懂的函数就查文档或者看论文,慢慢就能把整个系统串起来。
6.2 数学基础补强建议
SLAM涉及到的数学知识比较多,但不需要全部学完再开始。我建议采用"用到什么学什么"的策略:
- 线性代数:重点掌握矩阵分解、特征值、最小二乘。推荐MIT的18.06课程。
- 概率论:重点掌握贝叶斯公式、高斯分布、马尔可夫链。推荐《概率机器人》前几章。
- 非线性优化:重点掌握高斯牛顿法、LM算法、图优化。推荐《视觉SLAM十四讲》第6章和第9章。
- 李群李代数:这是SLAM中比较抽象的部分,但理解了之后会发现它其实就是描述旋转和平移的数学工具。推荐Barfoot的《State Estimation for Robotics》。
6.3 保持学习动力的几个建议
SLAM的学习曲线确实陡峭,我见过太多人中途放弃了。几个实用的建议:
第一,不要追求一次学透。第一遍看不懂很正常,先跑通代码,有个感性认识,再回头啃理论会容易很多。
第二,找一个具体的项目做驱动。比如让你的机器人自动巡逻、让无人机自主飞行,有了明确的目标,学习效率会高很多。
第三,加入社区多交流。ROS Answers、GitHub Issues、各种SLAM技术群,遇到问题不要自己死磕,问出来往往很快就能解决。
第四,定期复盘和记录。我现在还保持着写学习笔记的习惯,每次解决了一个bug或者理解了一个新概念,就记下来。过一段时间回头看,能明显感觉到自己的进步。
这个方向后续还可以往语义SLAM、神经辐射场SLAM、多机器人协同SLAM等方向扩展。但不管往哪个方向走,基础打牢了,上层的东西都是触类旁通的。