不一样的静态初始化——OpenVins-程序员宅基地

技术标签: slam  算法  经验分享  面试  c++  VIO  

今天讲一下黄国权老师实验室开源的OpenVins工程中的IMU初始化,一般VIO初始化分为两种,一种是静态初始化,一种是动态初始化,而OpenVins则利用加速度的方差差异将运动分为两种状态,一种是静止的,一种是刚开始运动的。个人比较符合应用情况,而且我觉得这个想法挺不错,很有意思。

VIO初始化就要任务一般是估计重力方向,速度,位置,IMU的偏置,尺度。而而在openvins中,只估计重力方向,IMU的偏置。

其实在代码里主要写了三个初始化模式,但是动态初始化好像没写出来,不知道现在有没有出来,大家可以去看看

1) GroundTruth初始化

2 )静止初始化

3 )动态初始化

而我们用到的是静态初始化,该初始化不同于smsckf开源代码中的初始化。

首先,将IMU数据分为两个部分,一个是静止的,一个是跳跃的。所谓“跳跃”是指相机从静止到运动的过程,如下图的w3阶段

 在代码中是通过时间的方式划分w2和w3。那么如何判断w3就是跳跃的窗口?openvins采用加速度的方差进行判断,如果方差大于一个阈值,则认为是跳跃窗口w3,如果方差小于阈值,则认为是静止的。然后通过静止的窗口w2进行初始化。这里个人觉得跳跃窗口的信息不应就这么不用了。

现在展示一下OpenVins初始化的大致流程:

 算法中用到了在估计重力坐标系的时候用到了施密特正交化,原理如下:

 代码中是这一样操作的,静止情况下,我们可以确定加速度在世界坐标系下为(0,0,g),将窗口w2的am2_avg除以其模长,得到平均加速度的方向(单位向量),即为世界系z轴的方向在imu坐标系上的投影:

,通过z轴我们可以利用正交化确定一个坐标系。

 这样我们又可以确定x轴,最后通过x与z叉乘,得到y轴。这样就可以确定初始旋转量了。

静止时刻的陀螺仪理想测量值(角速度)应该为0,因此陀螺仪的bias即窗口w2陀螺仪数据的平均值;静止时的加速度计bias为加速度平均值与实际重力加速度的差。

相关代码如下:

bool InertialInitializer::initialize_with_imu(double &time0, Eigen::Matrix<double,4,1> &q_GtoI0, Eigen::Matrix<double,3,1> &b_w0,
Eigen::Matrix<double,3,1> &v_I0inG, Eigen::Matrix<double,3,1> &b_a0, Eigen::Matrix<double,3,1> &p_I0inG, bool wait_for_jerk) {

// Return if we don't have any measurements
// 如果没有IMU数据,则返回
if(imu_data.empty()) {
return false;
}

// Newest imu timestamp
//最新的IMU时间戳
double newesttime = imu_data.at(imu_data.size()-1).timestamp;

// First lets collect a window of IMU readings from the newest measurement to the oldest
// 这里分为两个窗口,一个是从静态跳到动态,一个是就静态
std::vector<IMUDATA> window_newest, window_secondnew;
for(IMUDATA data : imu_data) {
if(data.timestamp > newesttime-1*_window_length && data.timestamp <= newesttime-0*_window_length) {
window_newest.push_back(data);
}
if(data.timestamp > newesttime-2*_window_length && data.timestamp <= newesttime-1*_window_length) {
window_secondnew.push_back(data);
}
}

// Return if both of these failed
if(window_newest.empty() || window_secondnew.empty()) {
//printf(YELLOW "InertialInitializer::initialize_with_imu(): unable to select window of IMU readings, not enough readings\n" RESET);
return false;
}

// Calculate the sample variance for the newest one
// a_avg平均加速度
Eigen::Matrix<double,3,1> a_avg = Eigen::Matrix<double,3,1>::Zero();
for(IMUDATA data : window_newest) {
a_avg += data.am;
}
a_avg /= (int)window_newest.size();
// a_var方差,通过方差可以判断运动的激励够不够
double a_var = 0;
for(IMUDATA data : window_newest) {
a_var += (data.am-a_avg).dot(data.am-a_avg);
}
a_var = std::sqrt(a_var/((int)window_newest.size()-1));

// If it is below the threshold and we want to wait till we detect a jerk
// 如果运动激励不足,说明是静止的,还没运动,那就不初始化了
if(a_var < _imu_excite_threshold && wait_for_jerk) {
printf(YELLOW "InertialInitializer::initialize_with_imu(): no IMU excitation, below threshold %.4f < %.4f\n" RESET,a_var,_imu_excite_threshold);
return false;
}

// Return if we don't have any measurements
//if(imu_data.size() < 200) {
// return false;
//}

// Sum up our current accelerations and velocities
// 静止窗口中的平均加速度和平均角速度,然后计算方差,判断激励够不够
Eigen::Vector3d linsum = Eigen::Vector3d::Zero();
Eigen::Vector3d angsum = Eigen::Vector3d::Zero();
for(size_t i=0; i<window_secondnew.size(); i++) {
linsum += window_secondnew.at(i).am;
angsum += window_secondnew.at(i).wm;
}

// Calculate the mean of the linear acceleration and angular velocity
Eigen::Vector3d linavg = Eigen::Vector3d::Zero();
Eigen::Vector3d angavg = Eigen::Vector3d::Zero();
linavg = linsum/window_secondnew.size();
angavg = angsum/window_secondnew.size();

// Calculate variance of the
double a_var2 = 0;
for(IMUDATA data : window_secondnew) {
a_var2 += (data.am-linavg).dot(data.am-linavg);
}
a_var2 = std::sqrt(a_var2/((int)window_secondnew.size()-1));

// If it is above the threshold and we are not waiting for a jerk
// Then we are not stationary (i.e. moving) so we should wait till we are
//静止的窗口运动激励要小于阈值才能继续初始化
if((a_var > _imu_excite_threshold || a_var2 > _imu_excite_threshold) && !wait_for_jerk) {
printf(YELLOW "InertialInitializer::initialize_with_imu(): to much IMU excitation, above threshold %.4f,%.4f > %.4f\n" RESET,a_var,a_var2,_imu_excite_threshold);
return false;
}

// Get z axis, which alines with -g (z_in_G=0,0,1)
// 静止时加速度计测量值为g
// 确定z轴方向后,我们利用施密特正交化构建单位坐标系。施密特正交化过程为
// v1 = x1 
// v2 = x2-(x2*v1)/(v1*v1)*v1
// 还剩下一个轴可以利用两个轴的叉乘来确定
Eigen::Vector3d z_axis = linavg/linavg.norm();

// Create an x_axis
// x轴
Eigen::Vector3d e_1(1,0,0);

// Make x_axis perpendicular to z
//根据施密特正交化可得
Eigen::Vector3d x_axis = e_1-z_axis*z_axis.transpose()*e_1;
x_axis= x_axis/x_axis.norm();

// Get z from the cross product of these two
// 通过叉乘确定y轴
Eigen::Matrix<double,3,1> y_axis = skew_x(z_axis)*x_axis;

// From these axes get rotation
//确定初始的旋转
Eigen::Matrix<double,3,3> Ro;
Ro.block(0,0,3,1) = x_axis;
Ro.block(0,1,3,1) = y_axis;
Ro.block(0,2,3,1) = z_axis;

// Create our state variables
Eigen::Matrix<double,4,1> q_GtoI = rot_2_quat(Ro);

// Set our biases equal to our noise (subtract our gravity from accelerometer bias)
//静止的时候,角速度应该就是bias了
Eigen::Matrix<double,3,1> bg = angavg;
//则加速度bias如下,因为静止加速度就是重力和bias了
Eigen::Matrix<double,3,1> ba = linavg - quat_2_Rot(q_GtoI)*_gravity;

// Set our state variables
time0 = window_secondnew.at(window_secondnew.size()-1).timestamp;
q_GtoI0 = q_GtoI;
b_w0 = bg;
v_I0inG = Eigen::Matrix<double,3,1>::Zero();
b_a0 = ba;
p_I0inG = Eigen::Matrix<double,3,1>::Zero();

// Done!!!
return true;

}

个人觉得这个想法在静态初始化中很不错,可以和动态初始化结合起来,更好的评估VIO联合初始化,不然那个动态窗口信息不是浪费了?

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37874102/article/details/114968754

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法