ORB-SLAM3:从特征提取到稠密点云建图

本文通过ORB-SLAM3中的两个核心模块------ORB特征提取器和稠密点云建图,来深入探索视觉SLAM背后的技术原理。本文将以源代码解析的方式,带你走进SLAM的奇妙世界。

一、ORB-SLAM3:从稀疏到稠密的演进

ORB-SLAM3是一个支持视觉、视觉加惯导、混合地图的SLAM系统,可以在单目、双目和RGB-D相机上利用针孔或者鱼眼模型运行。它是第一个基于特征的紧耦合VIO系统,仅依赖于最大后验估计(包括IMU在初始化时)。

然而,ORB-SLAM3生成的是稀疏点云地图,只能定位,无法直接用于导航任务。稀疏地图虽然能够满足定位需求,但在需要与真实环境进行物理交互的场景中(如机器人导航、避障、抓取等),我们需要更密集的环境表示------这就是稠密点云建图的意义所在。

为了实现稠密建图,基于ORB-SLAM3的架构,额外添加了一个稠密建图线程。这个线程在Tracking过程中,将关键帧的RGB图像和深度图像加入到稠密点云线程中,利用RGB-D图像和相机姿态数据,实现了3D场景的密集点云重建。其实高翔的开源项目(ORBSLAM2_with_pointcloud_map)已经有针对ORBSLAM2的稠密建图功能。现在从高博的源码略加修改,实现OBRslam3的稠密建图功能。

二、ORB特征提取器:让机器"看见"世界的关键

ORB(Oriented FAST and Rotated BRIEF)特征是一种兼具速度和旋转不变性的局部特征。ORB-SLAM3中的ORBextractor.cc文件实现了这一核心模块。

2.1 构建图像金字塔 ------ 解决尺度不变性

为了让机器在不同距离下都能认出同一个物体,系统会构建图像金字塔:将原图像逐层缩小,形成一系列不同尺度的图像。这样,远处的小物体在金字塔底层也能变成"大物体"被检测到。

// 逐层计算缩放系数

mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;

代码中,每一层的缩放因子累乘得到,使得算法具有了尺度不变性。

2.2 FAST角点检测 ------ 快速找到"兴趣点"

FAST角点检测算法通过比较像素与其周围圆周上像素的灰度差异,快速筛选出潜在的角点。ORBextractor中采用了自适应阈值:先用较严格的阈值iniThFAST检测,如果角点数量不足,则改用更宽松的minThFAST。

2.3 四叉树均匀化 ------ 让特征点分布更均匀

直接提取的FAST角点往往会扎堆在纹理丰富的区域,导致地图构建不均衡。ORBextractor使用四叉树算法,将图像不断分割成小方块,并保证每个子区域内只保留响应值最大的一个特征点。

2.4 计算特征点方向 ------ 旋转不变性

ORB特征的一大创新是加入了灰度质心法计算方向。简单来说,把特征点周围的图像块看作一个"刚体",计算其灰度重心,从几何中心指向重心的方向就是该特征点的主方向。

static float IC_Angle(const Mat& image, Point2f pt, const vector<int> & u_max)

{

// 计算图像矩 m10, m01

// 使用 fastAtan2 得到角度

return fastAtan2((float)m_01, (float)m_10);

}

有了这个方向,后续的BRIEF描述子就可以通过旋转采样点来获得旋转不变性。

2.5 生成BRIEF描述子 ------ 给特征点"身份证"

BRIEF描述子通过比较特征点周围随机点对的灰度值,生成一个二进制串(256位)。ORB在BRIEF的基础上,利用前面计算的方向对随机点进行旋转,从而得到Steered BRIEF,使其具备旋转不变性。

最终每个特征点都有一个256位的独特"指纹",可用于快速匹配。

三、稠密点云建图:让世界从"点"变成"面"

ORB-SLAM3原始版本只输出稀疏的特征点地图。而许多应用(如机器人导航、三维重建)需要更密集的环境表示。pointcloudmapping.cc文件实现了一个增强模块,利用RGB-D相机(或双目)构建稠密点云地图。

3.1 从关键帧生成点云

每当系统插入一个新的关键帧,insertKeyFrame函数会接收对应的彩色图和深度图。在单独的线程中,generatePointCloud函数将每个像素反投影到三维空间:

p.z = d;

p.x = ( u - mCx) * p.z / mFx; p.y = ( v - mCy) * p.z / mFy;

p.r = imRGB.ptr<uchar>(v)[u*3];

p.g = imRGB.ptr<uchar>(v)[u*3+1];

p.b = imRGB.ptr<uchar>(v)[u*3+2];

这里利用了相机内参(mFx, mFy, mCx, mCy)和深度值,计算出每个像素在世界坐标系下的坐标及颜色。

3.2 点云滤波与压缩

原始的稠密点云数据量巨大,且包含噪声。模块中使用了两种滤波器:统计滤波(剔除离群点,如深度传感器噪声)和体素滤波(将空间划分为小立方体,每个体素内只保留一个代表点,大幅压缩数据量)。

voxel.setLeafSize( resolution, resolution, resolution); statistical_filter.setMeanK(50);

statistical_filter.setStddevMulThresh(1.0);

3.3 回环检测时的点云更新

回环检测是SLAM中至关重要的一环。当系统检测到回环时,关键帧的位姿会被全局优化调整。相应地,已经生成的点云也需要"搬家"到正确的位置。

updatecloud函数会遍历所有受回环影响的关键帧,重新变换它们对应的局部点云,并合并到全局地图中。代码甚至用"五角星"图案在控制台打印出回环处理的醒目提示,非常有趣:

void PointCloudMapping::Five_pointed_star() { // 打印一个漂亮的大五角星 }

3.4 坐标系转换与发布

为了便于在ROS中可视化,模块将点云从SLAM坐标系转换到机器人常用的坐标系(如/pointCloud),并通过ROS话题发布,方便使用Rviz等工具实时查看建图效果。

Eigen::Matrix4f m;

m<< 0,0,1,0, -1,0,0,0, 0,-1,0,0;

pcl::transformPointCloud (source, out, transform);

四、从ORB-SLAM2到ORB-SLAM3:点云建图模块的移植

pointcloudmapping.cc这个文件,正是高翔博士在其开源项目ORBSLAM2_with_pointcloud_map中的核心工作。高翔博士修改增加了点云模块,在程序中添加了pointcloudmapping的相关程序,使ORB-SLAM在运行数据集时能直接显示点云图。其基本思想是:为每个关键帧构造相应的点云,然后依据从ORB-SLAM2中获取的关键帧位置信息将所有的点云拼接起来,形成一个全局点云地图。下面是一些需要改动的内容介绍。

4.1 核心逻辑:基本一致

  • 点云生成(generatePointCloud)

    :功能逻辑完全一致,都是从深度图反投影生成三维点云,并利用体素滤波和统计滤波进行优化。

  • 回环更新(updatecloud)

    :高翔原版代码并未实现回环检测时的点云更新功能。因此,这部分逻辑是你提供的代码独有的增强功能。

  • 坐标系转换(Cloud_transform)

    :PCL库默认的坐标系(x前,y左,z上)与SLAM系统的相机坐标系(z前,x右,y下)存在差异,因此需要一个变换矩阵来转换坐标轴方向。这个核心的变换逻辑在两个版本中是完全一致的。

4.2 代码适配微调

由于两个版本构建在完全不同的SLAM系统上,为适配新的系统架构,进行以下修改是必然的:

  • 命名空间调整

    :为了适配ORB-SLAM3,代码所在命名空间从ORB_SLAM2变为了ORB_SLAM3。

  • API接口适配

    :为了与ORB-SLAM3新的KeyFrame类和Converter工具类对接,相关的函数调用进行了更新。

4.3 回环更新(updatecloud)

高翔原版的 ORBSLAM2_with_pointcloud_map 虽然能够生成稠密点云,但并没有处理回环检测后地图不一致的问题。也就是说,当 SLAM 系统发现之前走过的路径形成闭环,并对所有关键帧位姿进行全局优化调整后,原有点云依然停留在旧位姿下,导致地图出现"重影"或错位。您这份代码实现了一个完整的回环点云修正流程,下面我们逐段剖析其实现细节。

ORB-SLAM 的后端会不断优化相机轨迹和地图点位置。当检测到回环时,系统会执行全局光束法平差(Global BA),调整所有关键帧的位姿。此时,之前根据旧位姿拼接起来的点云就与真实世界不再对齐。如果不更新点云,最终得到的地图会在回环闭合处出现明显的断裂或双重结构。因此,updatecloud 的核心任务就是:利用回环优化后的新位姿,重新生成受影响的局部点云,并替换掉全局地图中的错误部分。

代码逐段解析

4.3.1 触发条件:lastKeyframeSize == LoopKfId

在 NormalshowPointCloud 主循环中,有一个判断:

if(lastKeyframeSize == LoopKfId) updatecloud();

这里的 LoopKfId 应该是在外部(如 LoopClosing 线程)检测到回环后设置的一个关键帧 ID 阈值,lastKeyframeSize 是当前已处理的关键帧计数。当两者相等时,意味着回环发生且相关关键帧已到位,于是调用 updatecloud() 进行点云修正。

4.3.2 获取受回环影响的关键帧列表

currentvpKFs 是在 insertKeyFrame 中传入的,它保存了经过回环优化后的关键帧集合(通常是回环两侧的所有关键帧)。内层循环遍历已存储的所有点云块(pointcloud 向量),找到 ID 匹配的关键帧对应的原始点云(pcE,即 generatePointCloud 返回的局部点云)。注意:这里保存的是生成时相对于相机坐标系的原始点云,而不是已经变换到世界坐标系的点云。这为后续用新位姿重新变换提供了基础。

4.3.3 用优化后的位姿重新变换点云

currentvpKFs[i]->GetPoseMat() 获取的是回环优化后的相机位姿(世界 → 相机)。由于 pcE 原本是相机坐标系下的点云(由深度图反投影得到,原点在相机光心),要将其变换到世界坐标系,需要乘以 T 的逆(即相机→世界的变换矩阵)。所有重新变换后的点云累加到临时点云 tmp1 中。

4.3.4 体素滤波降采样

重新生成的点云可能非常密集,直接用 tmp1 替换全局地图会导致内存爆炸。因此先用体素滤波器(voxel)进行下采样,生成 tmp2。

4.3.5 安全更新与备份

这里通过判断当前全局点云点数是否足够来确认回环有效性(点数太少说明还未构建足够地图,可能误触发)。确认后,清空旧全局点云,用 swap 高效替换为修正后的点云。

4.4 移植步骤参考

如果你也想在自己的环境中完成类似的移植,可以参考以下步骤:

  • 依赖库安装

    :安装PCL点云库、Eigen、OpenCV等必要依赖。

  • 源代码修改

    :将现在的pointcloudmapping.h和pointcloudmapping.cc添加到ORB-SLAM3的src/目录下。

  • CMakeLists配置

    :在CMakeLists.txt中添加PCL库的链接,并设置相关路径。

  • 编译与测试

    :重新编译项目,使用RGB-D数据集(如TUM数据集)进行测试,验证稠密点云建图效果。

  • 输出结果

    :程序运行结束后,会生成.pcd格式的点云文件,可用PCL自带的pcl_viewer或CloudCompare等工具查看。

五、参考文献与引用链接

以下链接供你进一步了解和复核:

官方与权威资源

高翔点云建图相关

稠密建图改进相关

相关推荐
IT WorryFree16 小时前
肺癌机器人专用技能定制(OpenClaw-Medical-Skills 适配版)
机器人
鲁邦通物联网20 小时前
酒店与园区梯控安装架构设计:非侵入式物理隔离与状态机实现
机器人·巡检机器人·机器人梯控·agv梯控·机器人乘梯·机器人自主乘梯·agv机器人梯控
kobesdu20 小时前
FAST-LIO2 + 蓝海M300激光雷达:从建图到实时栅格图的完整流程
算法·机器人·ros·slam·fast lio
Deepoch21 小时前
VLA边缘认知系统:Deepoc开发板让除草机器人懂农艺会决策
机器人
猿饵块1 天前
机器人--cfg参数
人工智能·机器人
视***间1 天前
智赋机器人,算力启新程——视程空间以全栈算力方案,让智能机器人真正落地千行百业
人工智能·机器人·边缘计算·视程空间·高算力·2070tflops
Deepoch1 天前
Deepoc开发板:VLA架构赋能巡检机器人智能作业新范式
科技·机器人·具身模型·deepoc
出门吃三碗饭1 天前
Robotics从零动手做一个迷你机器人并学会控制它
机器人·twist
穿条秋裤到处跑1 天前
每日一道leetcode(2026.04.07):模拟行走机器人 II
leetcode·机器人