第4章:环境感知与特征提取

大家好,我是你们的讲师。今天我们来聊聊SLAM系统里最基础也最关键的一环——环境感知与特征提取。说白了,就是让机器人“看懂”周围的世界。

你想想看,机器人拿到的原始数据,不管是激光雷达的点云,还是相机的图像,都像是一堆没加工的原材料。我们要做的,就是从这些原材料里提炼出“精华”——也就是那些稳定、可重复、有区分度的特征。没有这一步,后面的建图和定位都无从谈起。

4.1 点云滤波与降采样

先说说激光雷达的数据。我刚开始做SLAM那会儿,拿到一帧64线激光雷达的数据,好家伙,几十万个点。直接拿去算?算力根本扛不住。而且原始点云里还有不少噪声,比如飘在空中的灰尘、传感器自身的测量误差。

所以,第一步就是滤波和降采样。我的个人习惯是,先做滤波,再做降采样。顺序别搞反了。

4.1.1 常用滤波方法

  • 体素滤波(Voxel Filter):这是最常用的降采样方法。把空间划分成一个个小立方体(体素),每个体素里只保留一个重心点。体素大小决定了降采样的程度。我一般设0.1m到0.2m,具体看场景。
  • 统计滤波(Statistical Outlier Removal):用来去除离群点。计算每个点与邻近k个点的平均距离,如果距离超过阈值,就认为是噪声。我在室内场景里,k取20,阈值倍数设1.0,效果不错。
  • 半径滤波(Radius Outlier Removal):设定一个半径,如果某个点周围邻居数量少于阈值,就干掉它。适合去除孤立的噪点。

核心经验:体素滤波的体素大小,直接影响后续特征提取的质量。太小了,点太多算不动;太大了,细节全丢了。我建议你先用0.2m试跑一遍,看看效果再调。

// PCL中的体素滤波示例
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;
voxel_filter.setInputCloud(cloud_in);
voxel_filter.setLeafSize(0.2f, 0.2f, 0.2f); // 体素边长0.2米
voxel_filter.filter(*cloud_out);

4.2 平面与边缘特征提取

滤波之后,点云干净多了。接下来要提取特征。在激光SLAM里,最经典的特征就是平面和边缘。

为什么会是这两个?因为结构化环境里,墙面是平面,墙角、柱子边缘是边缘。这些特征稳定、容易匹配。我在做仓储机器人项目时,就是靠提取货架边缘和地面平面来做定位的。

4.2.1 平面特征提取

常用的方法是RANSAC(随机采样一致性)。它的思路很简单:随机选三个点构成一个平面,然后看有多少点在这个平面附近。如果支持的点够多,就认为找到了一个平面。

嗯,这里要注意:RANSAC对参数敏感。距离阈值设太大,会把弯曲墙面也当成平面;设太小,平面又不够完整。我一般设0.05m到0.1m。

// PCL中RANSAC提取平面
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.05); // 5cm阈值
seg.setInputCloud(cloud_filtered);
seg.segment(*inliers, *coefficients);

4.2.2 边缘特征提取

边缘特征,我习惯用曲率来提取。计算每个点与其邻域点构成的协方差矩阵,特征值之间的关系能反映曲率大小。曲率大的点,大概率是边缘点。

LOAM算法里就是这么干的。它把点云按曲率排序,曲率最大的那些点作为边缘特征,曲率最小的作为平面特征。这个方法简单有效,我在实际项目中一直在用。

避坑指南:我曾经在室外场景里吃过亏——草地上的点云曲率也很大,被误识别成边缘。后来我加了高度阈值,只提取地面以上0.3m到2.0m范围内的点,问题就解决了。

4.3 视觉特征提取(ORB / SIFT)

说完激光,再聊聊视觉。视觉SLAM里,特征点提取是核心中的核心。ORB和SIFT是两大代表。

4.3.1 ORB特征

ORB(Oriented FAST and Rotated BRIEF)是我个人最常用的。为什么?因为它快!在嵌入式设备上也能跑实时。它结合了FAST角点检测和BRIEF描述子,还加了旋转不变性。

说白了,ORB就是“又快又够用”。在室内场景、纹理丰富的环境下,ORB的表现非常稳定。我做过对比,同样一张图,ORB提取1000个点只要10ms,SIFT要100ms以上。

// OpenCV中ORB特征提取
cv::Ptr<cv::ORB> orb = cv::ORB::create(500); // 提取500个特征点
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
orb->detectAndCompute(image, cv::Mat(), keypoints, descriptors);

4.3.2 SIFT特征

SIFT(Scale-Invariant Feature Transform)是经典中的经典。它的优势是尺度不变性和旋转不变性都很好,适合大尺度变化的场景。比如无人机从高空飞到低空,SIFT依然能匹配上。

但代价就是慢。而且SIFT是有专利的,商用要注意。我一般在学术研究或者对精度要求极高的场景才用SIFT。

我的选择建议:

  • 嵌入式设备、实时性要求高 → ORB
  • 大尺度变化、精度优先 → SIFT
  • 纹理稀疏场景 → 考虑用线特征或直接法

4.4 本章知识体系

为了让大家更直观地理解本章的知识结构,我画了一张图。你可以看到,环境感知分为激光和视觉两条线,最终都汇聚到特征提取这个核心任务上。

第4章 环境感知与特征提取 知识体系 环境感知 激光雷达点云 视觉图像 点云滤波与降采样 平面特征提取 边缘特征提取 ORB特征提取 SIFT特征提取 特征匹配 特征数据输出

4.5 实践中的坑与经验

最后,分享几个我在项目中踩过的坑,希望能帮你少走弯路。

坑1:点云滤波参数不是通用的。 室内0.1m体素效果很好,到了室外空旷场景,0.2m都不够。我建议你写个参数配置文件,不同场景加载不同参数。

坑2:视觉特征提取要关注光照变化。 有一次我在傍晚测试,光线变暗后ORB提取的特征点数量直接掉了一半。后来我加了自适应阈值,才稳定下来。

小技巧: 如果你做的是激光+视觉融合,建议让激光提取平面特征,视觉提取角点特征。两者互补,效果比单一传感器好很多。

好了,这一章的内容就到这里。环境感知和特征提取是SLAM的基石,基础打牢了,后面的建图和路径规划才能稳。希望你能动手把代码跑一遍,亲自感受一下不同参数带来的变化。

可可精品资料库