【PLIO学习总结】laserMapping中的时间戳与状态更新逻辑

本文仅用于个人学习总结记录。如有错误,请批评指正。

0、PLIO简要思路

从PLIO的论文中,可以知道,完整的PLIO算法采用IMU和LiDAR数据同时作为“输入”,维护状态变量包括加速度和角速度。

同时,PLIO是一种distortion-free的方法,即不需要进行去畸变。
之所以是不需要去畸变,是因为对每个雷达点都进行状态更新,然后将lidar点投回世界系作为地图点。

有了这个基本概念,我们就开始看laserMappingmain函数中的处理流程。

本文按照“IMU作为output”,即IMU不仅用于预测,也具有观测模型。此时,use_imu_as_input为false,imu_en为true。

1、不同时刻的更新逻辑

对于一个LIO系统,同时存在LiDAR和IMU数据,下图画出了对齐以后的两个时间轴。

在这里插入图片描述
可以看出,一次lidar的scan之间有多个绿色的雷达点,同时也有多个IMU测量。
两个IMU的测量间,可能有多个LiDAR pint;
两个LiDAR point间,也可能有多个IMU(虽然我不知道为啥,但代码的逻辑是这么处理的。实测发现,基本上两个Point之间只会出现“1个”IMU)

在代码的sync_packages时,把LiDAR和IMU数据进行了打包,保证了在后续处理时,永远是一个完整的scan中LiDAR Point
和IMU数据。

那么,在收到一个IMU或一个LiDAR时,理论上都进行状态更新。
但是,在代码实现中,永远是在每个LiDAR Point到来时进行更新。只不过,如果这个point到来时,buffer中下一个IMU数据如果是这个point之前,则需要用IMU的数据进行更新;否则,只更新LiDAR Point这部分。

1.1 无IMU数据

这里我们把这种情况叫做 Case 1,具体的示意图如下:

在这里插入图片描述
即当前时间是一个lidar point对应的时刻,前一次状态更新时刻time_predict_last_const和当前时刻time_current之间没有插入IMU数据。

此时,需要做的事情为:

  • 根据上一次IMU的输入,进行预测操作,预测状态量,和对应的协方差;
  • 根据LiDAR的观测和残差,更新状态量和协方差。

1.2 有IMU的数据

这里我们把这种情况叫做 Case 2,具体的示意图如下:

在这里插入图片描述

这就稍微有一些复杂了,因为上一个“状态完整更新”时刻应该是上一个Point,但当前的Point和前一个Point之间,有了多个IMU数据(一般是1个)。

此时,做的事情包括:

  • 对第1个IMU时刻,计算上一个Point到这个IMU的时间间隔dt,然后预测新的状态,和协方差;
  • 利用第1个IMU的数据,观测模型,计算残差,并进行更新全部状态和协方差;
  • 如果有多个IMU,则重复上面两个步骤。注意,重复时,时间间隔为距离上一个IMU测量的时间间隔,而不是到前一个LiDAR Point的;
  • 完成所有IMU时刻的预测和更新之后,处理新的LiDAR Point的数据,此时再执行1.1里面的两步:计算当前Point到前面最近IMU的时间间隔dt,预测状态和协方差;根据LiDAR的数据进行更新。

1.3 完整的时间戳示意图

在这里插入图片描述

2、代码

捋清楚上面的过程,就可以看代码了。这里,只保留代码的框架。


// IMU作为状态量进行更新
if (!use_imu_as_input){// 对所有(时间压缩后的)雷达点进行更新for (k = 0; k < time_seq.size(); k++)       {PointType &point_body  = feats_down_body->points[idx+time_seq[k]];time_current = point_body.curvature / 1000.0 + pcl_beg_time;    // 获取当前雷达点时间戳if(imu_en){bool imu_comes = time_current > imu_next.header.stamp.toSec();// 是否有了新的IMU数据?如果有,执行下面while,即case2中所说的内容;如果没有,跳过这部分,只是简单的case1while (imu_comes){imu_next = *(imu_deque.front());                // 获得buffer中的IMU,这个IMU的时间戳处于“上一次更新”和“当前雷达点”时间戳之间double dt = imu_last.header.stamp.toSec() - time_predict_last_const;kf_output.predict(dt, Q_output, input_in, true, false);                 // case2中,预测到当前IMU时刻时,状态的预测;double dt_cov = imu_last.header.stamp.toSec() - time_update_last;       // 问题:if (dt_cov > 0.0){kf_output.predict(dt_cov, Q_output, input_in, false, true);         // case2中,预测到当前IMU时刻时,协方差的状态;kf_output.update_iterated_dyn_share_IMU();                          // case2中,IMU进行update}}}double dt = time_current - time_predict_last_const;             kf_output.predict(dt, Q_output, input_in, true, false);         // case1,lidar点到前一个预测时刻,状态的预测time_predict_last_const = time_current;if (!kf_output.update_iterated_dyn_share_modified())            // case1,lidar进行update{idx = idx+time_seq[k];				// 获取下一个 Lidar Point的索引continue;}}
}

3、遗留问题

现在还遗留了一个问题,就是代码中有两个变量,记录上一次XXX的时间:
time_predict_last_consttime_update_last
从名字上看,前者是“上一次predict的时刻”,后者是“上一次update的时刻”。二者什么区别么?

在代码中,二者的区别体现在

if(!prop_at_freq_of_imu)
{double dt_cov = time_current - time_update_last;if (dt_cov > 0.0){kf_output.predict(dt_cov, Q_output, input_in, false, true);time_update_last = time_current;   							// 这里更新了“上一次更新时间戳”}
}
kf_output.predict(dt, Q_output, input_in, true, false);
time_predict_last_const = time_current;								// 这里更新了“上一次预测时间戳”

即,如果prop_at_freq_of_imu是false的,两者不一样;
但是,目前版本的代码中,prop_at_freq_of_imu一直是true,因此,这里时间戳有一些的混乱。

所以,在作者的代码中,有这么一行:

time_predict_last_const = imu_last.header.stamp.toSec(); // big problem 

作者注释了“大问题”,可能是一些功能增删造成的混乱吧。但应该不影响上面部分的大逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot266基于Web的农产品直卖平台的设计与实现

农产品直卖平台的设计与实现 摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#x…

C#构造函数

C#中的构造函数是一种特殊的方法&#xff0c;用于创建和初始化类的对象。构造函数的名称与类的名称相同&#xff0c;并且没有返回类型。 在C#中&#xff0c;构造函数有以下几种类型&#xff1a; 默认构造函数&#xff1a;如果在类中没有定义构造函数&#xff0c;系统将自动提供…

二、HarmonyOS 操作系统以及相关生态

前言 2019年8月9日&#xff0c;华为技术有限公司在华为开发者大会上正式发布了HarmonyOS 1.0&#xff0c;同时宣布该操作系统源代码开源。 2020年9月10日&#xff0c;HarmonyOs 2.0正式发布。与HarmonyOs 1.0版本相比&#xff0c;HarmonyOs 2.0在分布式软总线、分布式数据管理、…

Java数据结构-二叉树

文章目录 前言一、树型结构1.1概念1.2 知识点1.3 树的表示形式1.4 树的应用 二、二叉树2.1 概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基本操作2.5.1 二叉树的遍历2.5.2 二叉树的基本操作 前言 对学习的二叉树的知识进行总结。 一、树型结构 1.1…

蓝桥杯--日期统计

目录 一、题目 二、解决代码 三、代码分析 ​四、另一种思路 五、关于set文章推荐 一、题目 二、解决代码 #include <bits/stdc.h> using namespace std; int main() {int arr[100] { 5,6,8,6,9,1,6,1,2,4,9,1,9,8,2,3,6,4,7,7,5,9,5,0,3,8,7,5,8,1,5,8,6,1,8,3,0,…

【大模型API调用初尝试二】星火认知大模型 百度千帆大模型

大模型API调用初尝试二 科大讯飞—星火认知大模型单轮会话调用多轮会话调用 百度—千帆大模型获取access_token单轮会话多轮会话 科大讯飞—星火认知大模型 星火认知大模型是科大讯飞开发的&#xff0c;直接使用可以点击星火认知大模型&#xff0c;要调用API的话在讯飞开发平台…

C#版开源免费的Bouncy Castle密码库

前言 今天大姚给大家分享一款C#版开源、免费的Bouncy Castle密码库&#xff1a;BouncyCastle。 项目介绍 BouncyCastle是一款C#版开源、免费的Bouncy Castle密码库&#xff0c;开发人员可以通过该项目在他们的 C# 应用程序中使用 Bouncy Castle 提供的各种密码学功能&#x…

虚幻4 | 制作游戏——学习记录(一)

1. 启动Epic后下载虚幻4&#xff0c;打开虚幻4后新建一个第三人称游戏项目&#xff0c;效果如下&#xff1a; &#xff08;1&#xff09;内容/ThirdPersonBP/Blueprints中的ThirdPersonCharacter&#xff08;左下角人物&#xff09; 这是模板中使用的主要蓝图类&#xff0c;它…

ABAP接口部分-C#调用RFC

目录 ABAP接口部分-C#调用RFC创建表结构创建RFC函数创建C#项目引用SAP .Net Connector包绘制窗口的控件最终布局代码 项目配置报错SAP.Middleware.Connector.RfcDestinationManager报错SAP.Middleware.Connector.RfcLoginexception报错SAP.Middleware.Connector.RfcInvalidStat…

集合实现类研究底层(部分):手撕ArrayList底层源码、手撕LinkedList底层源码、手写单向链表和双向链表

day26上 集合框架图 标绿已经学习底层&#xff0c;深入底层主要是研究实现类底层 继承关系图 手撕ArrayList底层源码 ps:研究添加元素的过程 思路&#xff1a; 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程 提升&#xff1a…

BUGKU-WEB ezbypass

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 代码审计题目发现需要构造一个字符串&#xff0c;使得它不包含字母、数字、特殊字符的PHP代码片段&#xff0c;长度小于105&#xff0c;然后传递给$codepost提交参数&#xff0c;eval&#xff08;&…

数仓基础理论(一)

数据仓库概念 数据库 vs 数据仓库 从数据来源进行区分 数据库&#xff1a;企业中基础核心的业务数据 数据仓库&#xff1a;数据库中的数据 从数据存储进行区分 数据库&#xff1a;核心作用就是查找业务数据&#xff0c;基本上行式存储(带有索引)&#xff0c;基本上无法存…

大模型学习笔记六:Semantic Kernel

文章目录 一、Semantic Kernel介绍和发展1&#xff09;SK 的语言开发进展2&#xff09;SK的生态位3&#xff09;SK基础架构 二、环境搭建1&#xff09;初始化2&#xff09;Semantic Functions&#xff08;不用编写代码&#xff0c;用配置实现回调函数&#xff09;3&#xff09;…

js的异常处理

1、throw抛异常 throw抛出异常信息&#xff0c;程序也会终止执行&#xff1b; throw后面跟的是错误提示信息&#xff1b; new Error() 配个throw使用&#xff0c;能设置更详细的错误信息。 function counter(x,y) {if (!x || !y) {throw new Error(参数不能为空)}retu…

Kotlin 数据解析(Gson)

一、添加依赖 build.gradle.kts(:app) // gson数据解析implementation("com.google.code.gson:gson:2.8.6") 对象类&#xff1a; // 对象类 class Account {var uid:String "00001"var userName:String "Freeman"var password:String &quo…

k8s+wordpress+zabbix+elastic+filebeat+kibana服务搭建以及测试

一&#xff0c;环境&#xff1a;docker&#xff0c;k8s&#xff0c;zabbix&#xff0c;以及搭建worpdress&#xff0c;elasticsearch&#xff0c;filebeat&#xff0c;kibana 二&#xff0c;主机分配&#xff1a; 名称host详述个人博客3192.168.142.133 搭配mysql8.0.36的数据…

DVWA靶场-暴力破解

DVWA是一个适合新手锻炼的靶机&#xff0c;是由PHP/MySQL组成的 Web应用程序&#xff0c;帮助大家了解web应用的攻击手段 DVWA大致能分成以下几个模块&#xff0c;包含了OWASP Top 10大主流漏洞环境。 Brute Force——暴力破解 Command Injection——命令注入 CSRF——跨站请…

SublimeText4 安装

Sublime Text 可以编写html&#xff0c;css&#xff0c;js&#xff0c;php等等&#xff0c;是一个轻量、简洁、高效、跨平台的编辑器。 图1&#xff1a;SublimeText官网 Sublime Text具有漂亮的用户界面和强大的功能&#xff0c;例如代码缩略图&#xff0c;Python的插件&#…

Java的编程之旅41——字符流

目录 1.字符流的简介 2.字符的编码与解码 3.字符流读写操作 1.字符流写入 2.字符流复制文件 4.FileWriter&FileReader 5.缓冲区高效读写 6.序列化与反序列化 1.字符流的简介 在Java中&#xff0c;字符流是用于处理字符数据的输入输出流。它是以字符为单位进行处理&a…

读取txt文件并统计每行最长的单词以及长度

读取txt文件并统计每行最长的单词以及长度 题目 在 D:\\documant.txt 文本中,文件中有若干行英文文本,每行英文文本中有若干个单词&#xff0c;每个单词不会跨行出现每行至多包含100个字符,要求编写一个程序,处理文件,分析各行中的单词,找到每行中的最长单词&#xff0c;分别…