当前位置: 首页 > news >正文

A-LOAM 与 LeGO-LOAM 特征提取前处理差异分析

A-LOAM 与 LeGO-LOAM 特征提取前处理差异分析

A-LOAM 和 LeGO-LOAM 在前端特征提取时,最终目标都是从一帧 LiDAR 点云中提取两类特征:边缘点 edge/corner feature平面点 surface/planar feature。二者的基本依据都是局部曲率:曲率大的点通常表示周围几何变化剧烈,更可能是边缘点;曲率小的点通常表示周围比较平滑,更可能是平面点。但是在真正进行曲率排序和特征点选择之前,A-LOAM 和 LeGO-LOAM 对点云的预处理方式差异很明显。

简单来说,A-LOAM 更偏向于点级别的局部筛选,它主要在 scan line 上根据前后邻近点的距离变化,剔除遮挡点、距离突变点和不稳定点;而LeGO-LOAM 更偏向于区域级别的结构筛选,它会先将点云投影成 range image,再进行地面检测和非地面连通域分割,先保留稳定区域,再从稳定点云中提取边缘点和平面点。


1. 二者共同点:最终都依赖曲率选特征

无论是 A-LOAM 还是 LeGO-LOAM,后续选边缘点和平面点时,都需要计算点的局部曲率。常见做法是取当前点前后若干个邻近点,比较当前点和邻域点之间的坐标变化。如果当前点相对于周围点变化很大,说明它可能处在边缘、角点、物体轮廓或者遮挡边界附近;如果变化很小,则说明它可能位于平滑平面上。

典型曲率计算逻辑可以简化理解为:

float diffX = cloud[i - 5].x + cloud[i - 4].x + cloud[i - 3].x + cloud[i - 2].x + cloud[i - 1].x - 10 * cloud[i].x + cloud[i + 1].x + cloud[i + 2].x + cloud[i + 3].x + cloud[i + 4].x + cloud[i + 5].x; float diffY = cloud[i - 5].y + cloud[i - 4].y + cloud[i - 3].y + cloud[i - 2].y + cloud[i - 1].y - 10 * cloud[i].y + cloud[i + 1].y + cloud[i + 2].y + cloud[i + 3].y + cloud[i + 4].y + cloud[i + 5].y; float diffZ = cloud[i - 5].z + cloud[i - 4].z + cloud[i - 3].z + cloud[i - 2].z + cloud[i - 1].z - 10 * cloud[i].z + cloud[i + 1].z + cloud[i + 2].z + cloud[i + 3].z + cloud[i + 4].z + cloud[i + 5].z; cloudCurvature[i] = diffX * diffX + diffY * diffY + diffZ * diffZ;

这里的核心思想是:如果当前点和前后邻域点差异很大,cloudCurvature[i]就会比较大,后续更可能被选为边缘点;如果当前点和周围点变化平滑,曲率就比较小,后续更可能被选为平面点。

但是问题在于:曲率大不一定真的代表稳定边缘,也可能是遮挡、噪声、离群点造成的假边缘。所以在曲率排序之前,必须先做一定的坏点过滤。A-LOAM 和 LeGO-LOAM 的差异主要就体现在这里。


2. A-LOAM:点级别局部筛选

A-LOAM 的处理方式比较直接。它会先按照 scan line 整理点云,然后计算曲率,再在选特征点之前标记一些不可靠点。典型的不可靠点包括:遮挡边界点、距离突变点、与前后邻居差异过大的点。

例如两个点虽然在点云数组中相邻,但一个点打在近处柱子上,另一个点打在远处墙面上,那么它们在 scan line 上是邻近点,但在真实三维空间中并不连续。如果这种点不提前过滤,它们会因为距离突变导致曲率很大,从而被误选为边缘点。

A-LOAM 中常见的遮挡点标记逻辑可以概括为:

float depth1 = sqrt(cloud[i].x * cloud[i].x + cloud[i].y * cloud[i].y + cloud[i].z * cloud[i].z); float depth2 = sqrt(cloud[i + 1].x * cloud[i + 1].x + cloud[i + 1].y * cloud[i + 1].y + cloud[i + 1].z * cloud[i + 1].z); int columnDiff = std::abs(int(cloud[i + 1].intensity) - int(cloud[i].intensity)); if (columnDiff < 10) { if (depth1 - depth2 > 0.3) { cloudNeighborPicked[i - 5] = 1; cloudNeighborPicked[i - 4] = 1; cloudNeighborPicked[i - 3] = 1; cloudNeighborPicked[i - 2] = 1; cloudNeighborPicked[i - 1] = 1; cloudNeighborPicked[i] = 1; } else if (depth2 - depth1 > 0.3) { cloudNeighborPicked[i + 1] = 1; cloudNeighborPicked[i + 2] = 1; cloudNeighborPicked[i + 3] = 1; cloudNeighborPicked[i + 4] = 1; cloudNeighborPicked[i + 5] = 1; cloudNeighborPicked[i + 6] = 1; } }

这段逻辑的作用是:如果两个相邻点的深度差突然变大,就说明这里可能是遮挡边界。A-LOAM 会把当前点附近的一小段邻域标记为cloudNeighborPicked = 1,表示这些点后续不适合参与特征提取。

A-LOAM 还会检查当前点与前后点的距离差,如果当前点和两侧邻居都不连续,也会将其标记为不可靠点:

float diff1 = (cloud[i].x - cloud[i - 1].x) * (cloud[i].x - cloud[i - 1].x) + (cloud[i].y - cloud[i - 1].y) * (cloud[i].y - cloud[i - 1].y) + (cloud[i].z - cloud[i - 1].z) * (cloud[i].z - cloud[i - 1].z); float diff2 = (cloud[i].x - cloud[i + 1].x) * (cloud[i].x - cloud[i + 1].x) + (cloud[i].y - cloud[i + 1].y) * (cloud[i].y - cloud[i + 1].y) + (cloud[i].z - cloud[i + 1].z) * (cloud[i].z - cloud[i + 1].z); float dis = cloud[i].x * cloud[i].x + cloud[i].y * cloud[i].y + cloud[i].z * cloud[i].z; if (diff1 > 0.0002 * dis && diff2 > 0.0002 * dis) { cloudNeighborPicked[i] = 1; }

这说明 A-LOAM 并不是完全直接从原始点云中选特征,它也会提前过滤明显不稳定的点。但它的处理粒度主要是点级别局部邻域级别。它关注的是“这个点附近是否有明显距离突变”“这个点是否处在遮挡边界附近”,而不是判断一整片点云区域是不是稳定结构。


3. A-LOAM 后续如何选边缘点和平面点

在标记完不可靠点之后,A-LOAM 会按 scan line 分段,每段内部根据曲率排序。曲率大的点会被选为边缘点,曲率小的点会被选为平面点。

简化逻辑如下:

for (int i = 0; i < N_SCANS; i++) { for (int j = 0; j < 6; j++) { int sp = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * j / 6; int ep = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * (j + 1) / 6 - 1; std::sort(cloudSmoothness.begin() + sp, cloudSmoothness.begin() + ep, by_value()); } }

每根 scan line 分成 6 段的目的,是让特征点分布更加均匀。否则如果直接对整帧点云排序,边缘点和平面点可能集中在某一个局部区域,导致后续匹配约束分布不均。

边缘点选择逻辑可以概括为:

int largestPickedNum = 0; for (int k = ep; k >= sp; k--) { int ind = cloudSmoothness[k].ind; if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] > edgeThreshold) { largestPickedNum++; if (largestPickedNum <= 2) { cloudLabel[ind] = 2; // sharp corner cornerPointsSharp.push_back(cloud[ind]); cornerPointsLessSharp.push_back(cloud[ind]); } else if (largestPickedNum <= 20) { cloudLabel[ind] = 1; // less sharp corner cornerPointsLessSharp.push_back(cloud[ind]); } else { break; } cloudNeighborPicked[ind] = 1; } }

平面点选择逻辑可以概括为:

int smallestPickedNum = 0; for (int k = sp; k <= ep; k++) { int ind = cloudSmoothness[k].ind; if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] < surfThreshold) { cloudLabel[ind] = -1; // flat surface surfPointsFlat.push_back(cloud[ind]); smallestPickedNum++; if (smallestPickedNum >= 4) break; cloudNeighborPicked[ind] = 1; } }

因此,A-LOAM 的整体逻辑可以总结为:先在 scan line 上剔除遮挡点和局部不稳定点,再把每根线束分成若干段,在每段内按曲率选择边缘点和平面点。


4. LeGO-LOAM:区域级结构筛选

LeGO-LOAM 的处理更加结构化。它不是直接在 scan line 上做局部坏点剔除,而是先将点云投影成二维 range image。这个 range image 中,行表示激光线束编号,列表示水平角索引。

range image: col 0 col 1 col 2 col 3 ... row 0 p p p p row 1 p p p p row 2 p p p p ... row N p p p p

有了这个二维结构之后,LeGO-LOAM 可以做两件 A-LOAM 中没有明确做的事情:

第一,基于低线束之间的几何关系检测地面点;
第二,对非地面点做连通域分割,判断哪些点属于稳定连续结构。

地面检测逻辑大致如下:

for (int j = 0; j < Horizon_SCAN; ++j) { for (int i = 0; i < groundScanInd; ++i) { lowerInd = j + i * Horizon_SCAN; upperInd = j + (i + 1) * Horizon_SCAN; lowerPoint = fullCloud->points[lowerInd]; upperPoint = fullCloud->points[upperInd]; diffX = upperPoint.x - lowerPoint.x; diffY = upperPoint.y - lowerPoint.y; diffZ = upperPoint.z - lowerPoint.z; angle = atan2(diffZ, sqrt(diffX * diffX + diffY * diffY)) * 180 / M_PI; if (abs(angle - sensorMountAngle) <= 10) { groundMat.at<int8_t>(i, j) = 1; groundMat.at<int8_t>(i + 1, j) = 1; } } }

这一步的核心是:在同一列中比较上下相邻线束的点,如果它们之间的连线角度接近雷达安装角,就认为这两个点可能位于同一个地面平面上。地面点会被标记出来,后续不参与普通非地面连通域分割,但仍然可能作为平面特征参与优化。

接着,LeGO-LOAM 会对非地面点做连通域分割。它会从labelMat = 0的未处理点开始,检查上下左右邻居,如果邻居点和当前点在几何上连续,就把它们归为同一个 segment。

连续性判断的核心逻辑可以概括为:

float d1 = std::max(rangeMat.at<float>(fromIndX, fromIndY), rangeMat.at<float>(thisIndX, thisIndY)); float d2 = std::min(rangeMat.at<float>(fromIndX, fromIndY), rangeMat.at<float>(thisIndX, thisIndY)); float alpha = segmentAlphaX; // 左右邻居使用水平角分辨率 // float alpha = segmentAlphaY; // 上下邻居使用垂直角分辨率 float angle = atan2(d2 * sin(alpha), d1 - d2 * cos(alpha)); if (angle > segmentTheta) { labelMat.at<int>(thisIndX, thisIndY) = labelCount; queueIndX[queueSize] = thisIndX; queueIndY[queueSize] = thisIndY; queueSize++; }

这段代码的含义是:如果两个相邻点的距离变化比较平滑,计算出来的angle会比较大,说明它们更可能属于同一个连续表面,于是被放入同一个 segment;如果距离突变明显,说明它们可能位于遮挡边界或不同物体上,就不把它们连在一起。

LeGO-LOAM 在完成一个 segment 的生长后,还会判断这个 segment 是否有效。典型判断包括:点数是否足够多,是否跨越足够多的扫描线。如果一个 segment 点数太少,或者只出现在很局部的位置,就可能被认为是小簇噪声或离群结构。

bool feasibleSegment = false; if (allPushedIndSize >= 30) { feasibleSegment = true; } else if (allPushedIndSize >= segmentValidPointNum) { if (lineCount >= segmentValidLineNum) feasibleSegment = true; } if (feasibleSegment == true) { labelCount++; } else { for (size_t i = 0; i < allPushedIndSize; ++i) { labelMat.at<int>(allPushedIndX[i], allPushedIndY[i]) = 999999; } }

这一步就是 LeGO-LOAM 和 A-LOAM 的一个关键区别:
A-LOAM 主要判断某个点附近是否不稳定;LeGO-LOAM 则判断一整片点云区域是否是可靠结构。


5. LeGO-LOAM 后续如何选边缘点和平面点

经过地面检测和连通域分割之后,LeGO-LOAM 会生成segmentedCloud。这个点云不是原始点云,而是经过筛选后的点云,里面主要包含有效非地面 segment 点和部分地面点,不可靠小簇和离群点已经被过滤掉。

后续 LeGO-LOAM 也会按照 scan line 分成若干小段,例如每根线束分成 6 段,然后在每一段中按曲率排序,选择边缘点和平面点。

for (int i = 0; i < N_SCAN; i++) { for (int j = 0; j < 6; j++) { int sp = segmentedCloudScanStartInd[i] + (segmentedCloudScanEndInd[i] - segmentedCloudScanStartInd[i]) * j / 6; int ep = segmentedCloudScanStartInd[i] + (segmentedCloudScanEndInd[i] - segmentedCloudScanStartInd[i]) * (j + 1) / 6 - 1; std::sort(cloudSmoothness.begin() + sp, cloudSmoothness.begin() + ep, by_value()); } }

这里需要注意:LeGO-LOAM 的“6 段”不是把某个平面分成 6 块,而是把每根 scan line 在水平方向上的点序列按索引范围分成 6 个子区间。然后在每个子区间中,从segmentedCloud的稳定有效点里根据曲率大小选择特征。

边缘点通常要求曲率大,并且一般不是地面点:

if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] > edgeThreshold && segInfo.segmentedCloudGroundFlag[ind] == false) { cloudLabel[ind] = 1; cornerPointsSharp.push_back(segmentedCloud->points[ind]); }

平面点通常要求曲率小,地面点也可以作为平面点候选:

if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] < surfThreshold) { cloudLabel[ind] = -1; surfPointsFlat.push_back(segmentedCloud->points[ind]); }

因此,LeGO-LOAM 的特征提取可以理解为:先通过地面检测和区域分割得到更干净的 segmentedCloud,再在 segmentedCloud 中按 scan line 和 6 个子区间进行曲率排序,最后选取边缘点和平面点。


6. 二者核心区别

A-LOAM 和 LeGO-LOAM 在特征提取前的处理可以总结为两种不同思路。

A-LOAM 是“局部点级筛选”。它直接在 scan line 上检查相邻点之间的距离变化,把遮挡边界、深度突变、不稳定邻域点标记掉。它不会显式判断一整片点云区域是不是稳定结构,也不会专门进行地面检测。它的优点是实现简单、通用性强,更接近原始 LOAM 的思路;缺点是在低线束雷达和复杂地面场景下,对小簇噪声、地面结构和非地面结构的区分不如 LeGO-LOAM 细。

LeGO-LOAM 是“区域级结构筛选”。它先将点云投影成 range image,然后利用低线束之间的几何关系检测地面点,再对非地面点进行连通域分割,保留稳定 segment,过滤小簇、噪声和离群点。这样进入曲率排序的segmentedCloud已经比原始点云干净很多,后续选出来的边缘点和平面点通常更稳定。尤其对于地面移动机器人和低线束 LiDAR,LeGO-LOAM 的地面约束和区域分割能有效提高特征提取质量。


7. 总结

A-LOAM 和 LeGO-LOAM 的特征提取最终都依赖曲率排序,但两者在曲率排序之前的处理粒度不同。A-LOAM 更关注局部点的可靠性,它通过深度差、遮挡关系和邻域距离变化,提前标记不适合作为特征的点,然后在 scan line 的分段中选择曲率大的边缘点和曲率小的平面点。它属于典型的点级别过滤方法,结构简洁,适合理解 LOAM 的基本特征提取思想。

LeGO-LOAM 则更强调结构层面的稳定性。它不是直接从 scan line 中选特征,而是先通过 range image 建立二维邻接关系,再进行地面检测和非地面连通域分割。地面点被单独标记,非地面点被分割成一个个 segment,小簇和离群点被过滤,最后再从segmentedCloud中按 scan line 和 6 个子区间提取边缘点和平面点。因此,LeGO-LOAM 的特征候选点经过了更强的结构筛选,尤其适合低线束激光雷达和地面车场景。

一句话概括:A-LOAM 是先剔除明显坏点,再按曲率选特征;LeGO-LOAM 是先找地面和稳定区域,再在稳定点云中按曲率选特征。A-LOAM 的处理粒度是点级别,LeGO-LOAM 的处理粒度是区域级别。

版权声明: 辛苦码字不易,转载请注明原文出处和作者信息,谢谢理解

欢迎分享与交流,但拒绝任何形式的商业转载或洗稿。

http://www.rkmt.cn/news/1528979.html

相关文章:

  • FigmaCN中文汉化插件:3分钟让Figma界面说中文的终极解决方案
  • 2026更新固原市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询
  • HS2-HF_Patch:三分钟搞定游戏汉化与功能增强的终极解决方案
  • PVE网络配置避坑指南:从静态IP切到DHCP,这3个细节不注意小心失联!
  • 北京婚纱照优选推荐|综合实力TOP5,榜首首选北京三川影像 - 江湖评测
  • 2026年林芝工程承包商选型避坑指南:资质、材料、本地化能力全面对标 - 优质企业观察收录
  • 2026更新鄂州市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询
  • Mate Engine:开源虚拟角色引擎与桌面交互系统技术方案
  • DLSS Swapper完整指南:如何一键智能升级游戏性能,彻底告别卡顿
  • 全屋定制不想交品牌税?这家大连本地全屋定制工厂值得放进备选清单 - 资讯纵览
  • 2026更新哈密市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询
  • ComfyUI ControlNet预处理架构解析:从模块化设计到企业级部署的完整技术指南
  • OBS Spout2插件:打破分辨率限制的视频共享终极方案
  • MPC8533E安全引擎:硬件加密通道与密钥管理实战解析
  • 从Jupyter到生产:Triton推理服务实战指南
  • Spek音频频谱分析工具深度解析:技术架构与跨平台部署实战指南
  • 嵌入式缓存实战:拆解PowerPC L2缓存的PLRU、ECC与状态机
  • 不会做微信投票?一文掌握简单高效制作办法 - 投票评选活动
  • 3个简单步骤,让你的Windows任务栏瞬间变透明
  • 美控造纸行业解决方案:从制浆到排放,全流程 测量助力降本增效 - 仪表人老张
  • 终极Windows生产力神器:PowerToys完全指南,让你的工作效率翻倍!
  • 应届生毕业档案存放在哪里?正规档案存放流程详解 - 慧办好
  • 终极指南:使用OpenCore Legacy Patcher让老Mac焕发新生,免费升级最新macOS
  • PDF 拆分怎么弄 | 选页/范围/单页/均分四种模式完整教程
  • 嘉兴市奥克斯空调维修师傅电话|各区金牌师傅,靠谱选欧米到家 - 欧米到家
  • VLC点击暂停插件:终极播放控制体验完全指南
  • 2026更新定西市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询
  • 2026更新福州市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询
  • 【CANdelaStudio-从入门到深入到实战】19 会话切换的安全门禁:27服务与状态机深度联动
  • 深入解析LINFlexD控制器:LIN总线在汽车电子中的核心配置与实战