【文献分享】NASA JPL团队CoSTAR一大力作:直接激光雷达里程计:利用密集点云快速定位

论文题目:Direct LiDAR Odometry: Fast Localization With Dense Point Clouds

中文题目:直接激光雷达里程计:利用密集点云快速定位

作者:Kenny Chen, Brett T.Lopez, Ali-akbar Agha-mohammadi

论文链接:https://arxiv.org/pdf/2110.00605.pdf

论文开源代码:https://github.com/vectr-ucla/direct_lidar_odometry

0 笔者个人体会

这篇文章的整体算法的大结构还是与LOAM类似,分成两个部分:scan-to-scan(对应于LOAM的laserOdometry部分),scan-to-map(对应于LOAM的laserMapping部分),但是对于其中非常多的细节进行了创新,我认为这些创新都很有可取之处。比如文中提到的在判断是否添加关键帧时使用的自适应阈值,根据场景大小调整平移距离阈值,这个就可以用到我们的研究开发中;还有那个数据结构重复利用,节省了数据创建释放过程的时间,这个思想看似小改变,但是对于嵌入式设备中的实时性却是有很重要的意义。

个人认为有一个地方还可以进行小改进,注意到两个部分中都是使用了作者的GICP算法进行点云配准,没有进行解耦,scan-to-map阶段提高精度的作用主要体现在匹配点的数量增加,如果使用了两种不同的配准算法,可能会有更精确的效果。

1 效果展示

2 引言

在大型、具有感知挑战性的环境中,准确的状态估计和映射已经成为自主移动机器人的关键能力。基于激光雷达的SLAM性能的瓶颈之一,就是每次扫描的大量数据点很快就会压倒计算有限的处理器(比如嵌入式),这可能导致帧下降,最终导致姿态估计不佳。更具体地说,scan-to-scan的对齐需要两个云之间对应点的配准,但这个过程通常涉及最近邻搜索,随着每次扫描点的数量呈指数增长。基于特征的方法试图通过只使用最突出的点来缓解这种情况,但这些方法通常采用计算密集型的特征提取步骤,可能会意外丢弃数据,否则可能有助于提高下游配准的质量。

本文主要贡献:

  • 一种自适应关键帧系统,该系统通过一种新颖的空间度量来有效地捕获重要的环境信息。
  • 提出了一种基于关键帧的快速子映射方法,该方法通过凸优化生成允许的局部子映射来进行全局姿态优化。
  • NanoGICP,这时一个定制迭代的壁橱点求解器,用于轻量级点云扫描匹配,数据结构回收,消除冗余计算。

3 算法框架

算法框架如下图所示,主要部分就是预处理、scan-to-scan、scan-to-map、地图关键帧管理几个部分。

3.1 预处理

为了最大限度地减少原始传感器数据的信息损失,在预处理过程中只使用了两个滤波器:

  • 首先,我们通过原点周围大小为1 m³的盒子滤波器去除可能来自机器人本身的所有点返回。
  • 然后将生成的云通过分辨率为0.25 m的3D体素网格滤波器发送,以便对后续任务的数据进行轻微采样,同时在周围环境中保持主导结构。

在这项工作中,没有校正运动失真,因为非刚性变换可能会带来计算负担,而是直接使用密集点云,而不是像大多数工作那样提取特征。经过预处理后,每个云平均包含1万个点

3.2 通过Generalized-ICP进行扫描匹配

这个过程通常分两个阶段进行:

  1. 首先提供最佳的瞬时猜测,也就是scan-to-scan的过程
  2. 随后将其改进为与之前的关键帧位置更加全局一致,也就是scan-to-map的过程

1.scan-to-scan

在雷达里程计L中,计算相邻帧的相对变换 X ^ k L \hat X^L_k X^kL:
X ^ k L = a r g m i n ϵ ( X k L P k s , P k t ) \hat X^L_k = argmin \epsilon (X^L_kP^s_k,P^t_k) X^kL=argminϵ(XkLPks,Pkt)

残差项是由GICP计算得到:
ϵ ( X k L P k s , P k t ) = ∑ i N d i T ( C k , i t + X k L C k , i s X k L T ) − 1 d i \epsilon (X^L_kP^s_k,P^t_k)=\sum^N_i d_i^T(C^t_{k,i} + X^L_kC^s_{k,i}X_k^{L^T})^{-1}d_i ϵ(XkLPks,Pkt)=iNdiT(Ck,it+XkLCk,isXkLT)1di
C k , i t C^t_{k,i} Ck,it C k , i s C^s_{k,i} Ck,is分别是第k-1时刻和k时刻点云的协方差矩阵, d t = p i t − X k L p i s d_t=p^t_i-X^L_k p^s_i dt=pitXkLpis表示对应点的距离残差。

如果有IMU,则使用IMU预积分提供一个先验初始猜测,如果没有IMU则设置先验猜测为单位矩阵I,以减少收敛到次优局部最小值的可能,文章中有一些关于IMU预积分的说明,这里就不多说了。

2.scan-to-map

这部分的目标不是计算两个瞬时点云之间的相对变换,而是通过与局部子图匹配的方式,进一步改进前一步的运动估计,使其更加全局一致。
换句话说,这里的任务是计算当前源云 P k s P^s_k Pks和某些子图 S k S_k Sk之间的最优变换 X k W X^W_k XkW,使得:

X ^ k W = a r g m i n ϵ ( X k W P k s , S k ) \hat X^W_k = argmin \epsilon (X^W_k P^s_k,S_k) X^kW=argminϵ(XkWPks,Sk)
残差项是与scan-to-scan类似:
X k W = a r g m i n ∑ j M d j T ( C k , j S + X k W C k , j s X k W T ) − 1 d j X^W_k=argmin \sum^M_j d_j^T(C^S_{k,j} + X^W_k C^s_{k,j}X_k^{W^T})^{-1}d_j XkW=argminjMdjT(Ck,jS+XkWCk,jsXkWT)1dj
这个公式使用前一节从L到W的scan-to-scan的结果进行初始化,即: X k W = X k − 1 W ∗ X k L X^W_k = X^W_{k−1} * X^L_k XkW=Xk1WXkL,以便该先验运动可以与历史地图数据进行比较,以获得全局一致性。

3.基于关键帧的快速子图

这项工作的一个关键创新在于如何管理地图信息,并在scan-to-map匹配中快速生成局部子地图,以进行全局自运动细化。作者没有直接使用点云并将点存储到典型的八叉树数据结构中,而是保留了要在其中搜索的关键帧的历史记录,其中每个关键帧都以键值对的形式链接到相应的点云帧。然后通过连接关键帧子集的相应点云来生成用于scna-to-map匹配的局部子地图,而不是直接在机器人当前位置的某个半径内检索局部点。

这种设计选择的含义是双重的:首先,通过在“关键帧空间”而不是“点云空间”中搜索,得到了一个计算上更容易处理的问题。

此外,与基于范围的方法相比,基于关键帧的方法构建了更宽松的子地图。也就是说,由于从关键帧点云导出的子图的大小完全依赖于激光雷达传感器的距离,而不是预定的距离,派生的子图可以与当前扫描有更大的重叠。

固定半径r = 20 m的子图(图a)与当前扫描没有充分重叠,并且由于只包含空间附近的点,可能会随着时间的推移而引入漂移;然而,基于关键帧的方法(图b)涵盖了当前扫描的大部分内容,这有助于更好地实现扫描到映射的对齐。

1)通过kNN和凸包选择关键帧:设 K k K_k Kk为所有关键帧点云的集合,如下:将子地图 S k S_k Sk定义为K个最近邻关键帧扫描 Q k Q_k Qk和L个最近邻凸包扫描 H k H_k Hk的连接,使得 S k = Q k ⊕ H k S_k = Q_k⊕H_k Sk=QkHk

2)自适应关键帧:关键帧节点添加通常使用固定阈值(例如,每1米或10◦的平移或旋转变化),但最佳位置可能高度依赖于周围环境的结构。更具体地说,在大规模环境中,点云扫描捕捉到的特征更加突出,可以在更长的时间内依赖。相反,对于狭窄或小规模的环境,需要更小的阈值来持续捕获子地图中的小规模特征(即紧角),以获得更好的定位。因此,需要根据瞬时点云扫描中的“空间”来缩放新关键帧的平移阈值,定义为 m k = α m k − 1 + β M k m_k = \alpha m_{k−1} + \beta M_k mk=αmk1+βMk,其中 M k M_k Mk为预处理点云中原点到每个点的中值欧式距离,α = 0.95, β = 0.05, m k m_k mk为用于缩放关键帧阈值 t h k th_k thk的平滑信号,在k时刻使:

t h k = { 10 m ,      i f   m k > 20 m 5 m ,        i f   m k > 10 m & m k ≤ 20 m 1 m ,        i f   m k > 5 m & m k ≤ 10 m 0.5 m ,     i f   m k ≤ 5 m th_k =\left\{ \begin{array}{l} 10m,\ \ \ \ if\ m_k>20m\\ 5m, \ \ \ \ \ \ if\ m_k>10m\&m_k≤20m\\ 1m, \ \ \ \ \ \ if \ m_k>5m\&m_k≤10m\\ 0.5m, \ \ \ if \ m_k≤5m\\ \end{array} \right. thk= 10m,    if mk>20m5m,      if mk>10m&mk20m1m,      if mk>5m&mk10m0.5m,   if mk5m

旋转阈值固定在30°。下图展示了关键帧添加以及自适应关键帧的效果,这个操作有助于增强对环境维度变化的鲁棒性。

3.3 算法优化

1.扫描拼接子地图法线

广义icp涉及最小化两个云之间的平面距离,其中这些平面通过扫描中每个点的计算协方差来建模。文章不是在每次迭代中计算子地图中每个点的法线,而是假设子地图协方差 C k S C^S_k CkS可以通过将构成子地图的n个关键帧中的法线 C k , n S C^S_{k,n} Ck,nS拼接起来来近似,即 C k S ≈ ∑ n N C k , n S C^S_k≈\sum^N_n C^S_{k,n} CkSnNCk,nS。因此,不需要显式地计算每个子地图的法线集,而只需将先前计算的法线集拼接在一起即可重建。

2数据结构回收利用

在上述基础上,当前LiDAR里程计的几个算法步骤可以从数据结构共享和重用中受益,通过消除不必要和冗余的操作,大大降低了整体系统开销。如下表所示,该系统总共需要8个元素才能成功执行scna-to-scan和scna-to-map的匹配。

在四个必需的kdtree数据结构中,只有两个需要显式构建。也就是说,源(输入)云 T k s o u r c e T^{source}_k Tksource的树可以在每次扫描采集时只构建一次,并在两个模块之间共享(因为两个源都使用相同的扫描)。对于scan-to-scan的目标树 T k t a r g e t T^{target}_k Tktarget,这仅仅是前一次迭代的扫描到扫描的源树 T k − 1 s o u r c e T^{source}_{k-1} Tk1source,因此可以传播。scna-to-map的目标树需要显式地构建,但由于子地图是从一组关键帧派生的,因此只有当通过我们的kNN和凸包策略选择的关键帧集从一次迭代到下一次迭代发生变化时,才需要执行此构建,例如 S k ≉ = S k − 1 S_k≉= S_{k−1} Sk=Sk1。否则,可以再次重用数据结构以节省额外的计算量。另一方面,GICP所需的点协方差 C k C_k Ck只需在每次扫描采集中计算一次,其数据可以直接在其他三个实例中共享。

3.双NanoGICP

为了促进扫描匹配模块之间的交叉对话,作者开发了NanoGICP,这是一种自定义迭代最近点求解器,它结合了FastGICP和NanoFLANN开源软件包,并对数据结构共享进行了额外的修改。
NanoGICP使用NanoFLANN高效构建KD-tree,用FastGICP进行点云对应匹配。

4 实验与结果

4.1 组件评估

1.基于关键帧的子图创建

比较了三种子映射方案的绝对姿态误差(APE)、处理时间和CPU负载,包括:基于半径(r = 10 m)、基于1m静态阈值的关键帧,以及基于自适应阈值的关键帧。

在图中可以看出:关键帧空间中的子地图可以通过考虑更远的点来显著减少位置误差,否则这些点将超出基于半径的方法的范围。处理时间和CPU负载显示出类似的趋势:基于半径的处理每次扫描明显慢于每次扫描74.2 ms,平均CPU负载为37.5%,而静态和自适应方案分别为21.6 ms / 10.2%和19.1 ms / 9.1%。

2.数据结构回收利用

测量和比较不同回收方案之间的处理时间和CPU使用率通过箱线图和下降的百分比扫描数据集。

回收kdtrees而不回收协方差可以略微改善处理时间和CPU百分比,而回收协方差而不回收kdtrees可以提供更显著的性能提升;这是合理的,因为文中的协方差回收方案比kdtree重用更具主动性。最后,使用表中详细说明的完整方案显着降低了这两个指标,平均处理时间为21.9 ms, CPU负载为9.5%,这可以防止任何LiDAR帧丢失。

3.NanoGICP

该基准测试测量了在100次运行中对齐两个LiDAR扫描的平均收敛时间,并与PCL的GICP实现以及FastGICP的多线程实现进行了比较。可以观察到,与FastGICP (72.88 ms)和PCL的GICP (178.24 ms)相比,NanoGICP的平均收敛速度(42.53 ms)更快。

4.2 基准测试结果

为DLO的测程精度和CPU负载与几种LiDAR和LiDAR-imu测程方法(包括BLAM, Cartographer,LIO-Mapping,LOAM和LOCUS)进行了比较,使用来自地下挑战赛城市电路的Alpha和Beta课程数据集,结果如下表所示。

结果所示:提出的方法的CPU负载被测量为远低于任何其他算法,平均和峰值使用不到一个核心。除了内部数据结构的广泛重用之外,这可能是系统生成子地图的结果。这一观察结果也可以解释DLO的绝对姿势误差(APE)和平均误差(ME)要低得多,相对姿势误差也有类似的趋势。有了这个更快的处理时间,文中的方法在Alpha和Beta过程中都优于所有其他方法,即使没有运动失真校正,在Beta过程中对于max, mean和standard deviation的精度也是两倍以上。除了更宽松的子地图方法之外,所提出的方法比其他方法更不可能掉帧,并且有处理资本以更高的分辨率匹配密集的点云。

4.3 田野实验

使用Ouster OS1将DLO集成到飞行器(上图a)上,使用Velodyne VLP-16将DLO集成到Boston Dynamics Spot(上图b)上,在两个具有感知挑战性的环境中进行了手动和自动遍历:在肯塔基州列克星敦的地下石灰岩洞穴和加利福尼亚州洛杉矶的废弃地铁中(下图)。

这两个地点都包含经常挑战感知系统的环境属性,包括光线条件差,没有特色的走廊,以及灰尘或雾等微粒的存在。尽管在废弃的地铁中穿越了超过850米的三个不同的楼层,文中的系统仅报告了10厘米的端到端漂移,这主要归功于DLO强大的关键帧方案,该方案适用于大小空间。在地下矿井的试验表明:虽然洞穴深处的环境没有任何外部照明,但DLO仍然可以在348米的自主飞行中可靠地跟踪飞行器。这些结果证明了文中的方法在现实世界中的可靠性。

5 总结

这项工作提出了Direct LiDAR Odometry (DLO),这是一种轻量级和精确的前端定位解决方案,在极端环境下长期遍历的计算开销最小。一个关键创新是我们如何有效地使用关键帧-点云对数据库派生出用于全局姿态优化的局部子地图。这反过来又允许大量的求解器数据结构在系统模块之间共享和重用,所有这些都可以使用我们定制的NanoGICP云注册包来实现。

未来将会将研究重点放在与IMU的紧耦合上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/118211.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在 CelebA 数据集上训练的 PyTorch 中的基本变分自动编码器

摩西西珀博士 一、说明 我最近发现自己需要一种方法将图像编码到潜在嵌入中,调整嵌入,然后生成新图像。有一些强大的方法可以创建嵌入或从嵌入生成。如果你想同时做到这两点,一种自然且相当简单的方法是使用变分自动编码器。 这样的深度网络不…

学习LevelDB架构的检索技术

目录 一、LevelDB介绍 二、LevelDB优化检索系统关键点分析 三、读写分离设计和内存数据管理 (一)内存数据管理 跳表代替B树 内存数据分为两块:MemTable(可读可写) Immutable MemTable(只读&#xff0…

力扣370周赛 -- 第三题(树形DP)

该题的方法,也有点背包的意思,如果一些不懂的朋友,可以从背包的角度去理解该树形DP 问题 题解主要在注释里 //该题是背包问题树形dp问题的结合版,在树上解决背包问题 //背包问题就是选或不选当前物品 //本题求的是最大分数 //先转…

京东商品详情API接口(PC端和APP端),京东详情页,商品属性接口,商品信息查询

京东开放平台提供了API接口来访问京东商品详情。通过这个接口,您可以获取到商品的详细信息,如商品名称、价格、库存量、描述等。 以下是使用京东商品详情API接口的一般步骤: 注册并获取API权限:您需要在京东开放平台上注册并获取…

arcgis pro模型构建器

如果你不想部署代码包环境来写arcpy代码,还想实现批量或便携封装的操作工具,那么使用模型构建器是最好的选择。1.简介模型构建器 1.1双击打开模型构建器 1.2简单模型构建步骤 先梳理整个操作流程,在纸上绘制在工具箱中找到所需工具拖进来把…

LangChain+LLM实战---实用Prompt工程讲解

原文:Practical Prompt Engineering 注:本文中,提示和prompt几乎是等效的。 这是一篇非常全面介绍Prompt的文章,包括prompt作用于大模型的一些内在机制,和prompt可以如何对大模型进行“微调”。讲清楚了我们常常听到的…

搭建二维码系统,轻松实现固定资产的一物一码管理

固定资产管理中普遍存在盘点难、家底不清、账实不一致、权责不清晰等问题,可以在草料上搭建固定资产管理系统,通过组合功能模块实现资产信息展示、领用登记、出入库管理、故障报修等功能,对固定资产进行一物一码规范化管理。 比如张掖公路事业…

创建基于多任务的并发服务器

有几个请求服务的客户端&#xff0c;我们就创建几个子进程。 这个过程有以下三个阶段&#xff1a; 这里父进程传递的套接字文件描述符&#xff0c;实际上不需要传递&#xff0c;因为子进程会复制父进程拥有的所有资源。 #include <stdio.h> #include <stdlib.h>…

票务营销数字化:景区增收利器

身处数字化时代&#xff0c;景区门票销售早已插上数字化翅膀&#xff0c;通过一站式的票务管理、精准的营销策略等为景区带来了数字化增长。票务营销系统如何帮助景区增收&#xff1f; l 提高工作效率&#xff1a;传统的景区售票方式往往需要大量的人工操作&#xff0c;不仅耗时…

微信小程序overflow-x超出部分样式不渲染

把display:flex改成display:inline-flex&#xff0c; 将对象作为内联块级弹性伸缩盒显示&#xff0c; 类似与是子元素将父元素撑开&#xff0c;样式就显示出来了

纺织布料行业小程序开发

随着互联网的发展&#xff0c;越来越多的消费者通过线上渠道购买纺织布料产品。为了满足市场需求&#xff0c;越来越多的纺织布料企业选择开发小程序&#xff0c;以提高销售效率、拓宽销售渠道和提升用户体验。下面是开发纺织布料行业小程序的具体步骤&#xff1a; 1. 登录乔拓…

Flume从入门到精通一站式学习笔记

文章目录 什么是FlumeFlume的特性Flume高级应用场景Flume的三大核心组件Source&#xff1a;数据源channelsink Flume安装部署Flume的使用案例&#xff1a;采集文件内容上传至HDFS案例&#xff1a;采集网站日志上传至HDFS 各种自定义组件例如&#xff1a;自定义source例如&#…

Python的切片操作详细用法解析

在利用Python解决各种实际问题的过程中&#xff0c;经常会遇到从某个对象中抽取部分值的情况&#xff0c;切片操作正是专门用于完成这一操作的有力武器。理论上而言&#xff0c;只要条件表达式得当&#xff0c;可以通过单次或多次切片操作实现任意切取目标值。切片操作的基本语…

开源 Wiki 软件 wiki.js

wiki.js简介 最强大、 可扩展的开源Wiki 软件。使用 Wiki.js 美观直观的界面让编写文档成为一种乐趣&#xff01;根据 AGPL-v3 许可证发布。 官方网站&#xff1a;https://js.wiki/ 项目地址&#xff1a;https://github.com/requarks/wiki 主要特性&#xff1a; 随处安装&a…

机器学习笔记 - 感知器的数学表达

一、假设前提 感知机(或称感知器,Perceptron)是Frank Rosenblatt在1957年就职于Cornell航空实验室(Cornell Aeronautical Laboratory)时所发明的一种人工神经网络。 它可以被视为一种最简单形式的前馈神经网络,是一种二元线性分类模型,其输入为实例的特征向量,输出为实…

绕开网站反爬虫原理及实战

1.摘要 在本文中,我首先对网站常用的反爬虫和反自动化技术做了一个梳理, 并对可能能够绕过这些反爬技术的开源库chromedp所使用的技术分拆做一个介绍, 最后利用chromedp库对一个测试网站做了爬虫测试, 并利用chromedp库绕开了爬虫限制,成功通过程序自动获取到信息。在测试过程…

如何发布自己的golang库

如何发布自己的golang库 1、在 github/gitee 上创建一个 public 仓库&#xff0c;仓库名与 go 库名一致&#xff0c;然后将该仓库 clone 到本地。 本文这里使用 gitee。 $ git clone https://gitee.com/zsx242030/goutil.git2、进入项目文件夹&#xff0c;进行初始化。 $ go…

Webpack介绍大全

Webpack 一 、什么是webpack WebPack是一个现代JS应用程序的静态模块打包器&#xff08;module bundler&#xff09; 模块&#xff08;模块化开发&#xff0c;可以提高开发效率&#xff0c;避免重复造轮子&#xff09; 打包&#xff08;将各个模块&#xff0c;按照一定的规则…

新版onenet平台安全鉴权的确定与使用

根据onenet官方更新的文档&#xff1a;平台提供开放的API接口&#xff0c;用户可以通过HTTP/HTTPS调用&#xff0c;进行设备管理&#xff0c;数据查询&#xff0c;设备命令交互等操作&#xff0c;在API的基础上&#xff0c;根据自己的个性化需求搭建上层应用。 为提高API访问安…

JavaScript作用域实战

● 首先&#xff0c;我们先创建一个函数&#xff0c;和以前一样&#xff0c;计算一个年龄的 function calcAge(birthYear) {const age 2037 - birthYear;return age; }● 然后我们创建一个全局变量&#xff0c;并调用这个函数 const firstName "IT知识一享"; cal…