激光雷达原理与点云处理:建图的数据基石
说实话,很多新手一上来就调 SLAM 参数,结果地图建得乱七八糟。为什么?因为连传感器怎么工作的都没搞明白。激光雷达就是 SLAM 的眼睛,你连眼睛的脾气都不了解,怎么可能建出好地图?
这一章,我们就来聊聊激光雷达的工作原理,以及怎么处理它吐出来的原始点云数据。嗯,这部分做好了,后面的建图才能顺风顺水。
激光雷达工作原理:它到底是怎么「看」世界的?
激光雷达,说白了就是「激光 + 雷达」。它发射一束激光,打到物体上反射回来,通过计算时间差来测距。这个原理其实很简单,跟蝙蝠用超声波差不多,只不过激光雷达用的是光速级别的激光。
我当年第一次拆开一个 16 线激光雷达时,看到里面旋转的机械结构,才真正理解它的工作方式。每个激光发射器对应一个「线」,16 线就是 16 个激光器同时工作,每转一圈就能扫出 16 圈的点云。
核心公式:距离 = (光速 × 飞行时间) / 2
光速 ≈ 3×10⁸ m/s,所以时间测量精度直接决定了测距精度。1 纳秒的误差,对应约 15 厘米的距离偏差。
常见的激光雷达类型,我整理了一个表格,方便你对比:
| 类型 | 工作原理 | 典型代表 | 优缺点 |
|---|---|---|---|
| 机械旋转式 | 激光器整体旋转扫描 | Velodyne VLP-16 | 视野广(360°),但有机械磨损 |
| 固态(MEMS) | 微振镜反射激光 | Livox Horizon | 寿命长,但视场角较小 |
| 固态(Flash) | 一次发射覆盖整个视场 | LeddarTech | 无运动部件,但探测距离有限 |
| 混合固态 | 旋转棱镜 + 固定激光器 | RoboSense RS-LiDAR | 平衡了寿命和视场角 |
我的经验:做室内机器人建图,机械旋转式还是最靠谱的选择。虽然它转起来有点吵,但 360° 的视野在 SLAM 里太重要了。固态雷达视野窄,容易丢特征,建图容易崩。
点云滤波:为什么原始数据不能直接用?
激光雷达吐出来的原始点云,其实挺「脏」的。你想想看,激光打到玻璃上会穿透,打到镜子上会反射到别处,下雨天还会被雨滴反射回来。这些异常点如果不处理,建图时就会出现各种鬼影和噪点。
我曾经在一个玻璃幕墙的办公楼里做测试,结果地图上凭空多出了一堵「墙」。后来才发现,那是玻璃反射造成的虚像点。从那以后,我每次建图前都会先做点云滤波。
直通滤波:快速裁剪无用区域
直通滤波,名字听着高大上,其实就是「切一刀」。你设定一个范围,在这个范围之外的点全部扔掉。
举个例子:你的机器人高度是 1 米,那地面以下和天花板以上的点云基本没用。直接切掉,数据量能减少 30% 以上。
// PCL 直通滤波示例
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud_in);
pass.setFilterFieldName("z"); // 沿 Z 轴裁剪
pass.setFilterLimits(0.1, 1.5); // 保留 0.1m ~ 1.5m 范围
pass.filter(*cloud_filtered);
注意:直通滤波的阈值不能设得太死。我见过有人把 Z 轴下限设成 0.2m,结果机器人爬个小坡,地面点全被切掉了,建图直接失败。留点余量,比如设成 0.05m。
体素滤波:降采样但不丢结构
体素滤波是点云处理里最常用的降采样方法。它的思路很简单:把空间划分成一个个小立方体(体素),每个体素里只保留一个点(通常是重心点)。
这样做的好处是:点云密度均匀了,数据量大幅减少,但物体的几何结构基本保留。我一般会把体素大小设在 0.05m ~ 0.1m 之间,具体看场景。
// PCL 体素滤波示例
pcl::VoxelGrid<pcl::PointXYZ> voxel;
voxel.setInputCloud(cloud_in);
voxel.setLeafSize(0.05f, 0.05f, 0.05f); // 5cm 体素
voxel.filter(*cloud_filtered);
体素大小怎么选?我总结了一个经验表:
| 场景 | 推荐体素大小 | 说明 |
|---|---|---|
| 室内小房间 | 0.02m ~ 0.05m | 细节多,需要保留特征 |
| 室内大空间 | 0.05m ~ 0.10m | 平衡精度和速度 |
| 室外空旷 | 0.10m ~ 0.20m | 点云稀疏,降采样力度可以大 |
| 实时建图 | 0.08m ~ 0.15m | 优先保证帧率 |
我的习惯:先设一个较大的体素(比如 0.1m)跑一遍,看看建图效果。如果特征匹配不够,再逐步减小。别一上来就用 0.01m,那跟没滤波一样,CPU 直接拉满。
点云处理流程:从原始数据到可用数据
在实际项目中,我不会只用一种滤波。通常是一个组合拳:先直通滤波去掉无用区域,再体素滤波降采样,最后可能再加一个统计滤波去掉离群点。
下面这张图,是我自己总结的点云预处理流程:
统计滤波:揪出那些「不靠谱」的点
统计滤波的原理很有意思:它假设点云中每个点的邻域距离服从高斯分布。如果一个点离它的邻居太远(超过均值 + 若干倍标准差),就判定为离群点,直接扔掉。
我习惯把标准差倍数设成 1.0 ~ 2.0 之间。设得太小,会把正常点也误删;设得太大,离群点又滤不干净。
// PCL 统计滤波示例
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud_in);
sor.setMeanK(50); // 每个点考虑 50 个邻居
sor.setStddevMulThresh(1.0); // 标准差倍数阈值
sor.filter(*cloud_filtered);
避坑指南:我曾经在一个工厂车间里做测试,环境里有很多金属表面,激光反射特别强,导致点云里出现了大量「飞点」。统计滤波设了 2.0 倍标准差都滤不干净。后来我把 meanK 从 50 调到了 100,才把那些飞点干掉。记住:环境越复杂,meanK 就要越大。
滤波顺序有讲究吗?
有,而且很重要。我的建议是:先直通,再体素,最后统计。
为什么?直通滤波先砍掉大量无用点,体素滤波降采样时计算量就小很多。统计滤波放在最后,是因为体素滤波已经让点云均匀了,统计滤波的效果会更好。
你想想看,如果先做统计滤波,点云里还有几百万个点,每个点都要算 50 个邻居的距离,那计算量得多大?
小结
这一章我们聊了激光雷达的工作原理,以及三种最常用的点云滤波方法。直通滤波负责「切」,体素滤波负责「缩」,统计滤波负责「清」。三者配合,就能把原始点云处理成干净、轻量、结构完整的数据。
嗯,数据基础打好了,下一阶段我们才能放心地把它喂给建图算法。记住一句话:点云处理的质量,直接决定了建图的上限。别在这个环节偷懒。