首先通过vins_estimator mode监听几个Topic(频率2000Hz),将imu数据,feature数据,raw_image数据(用于回环检测)通过各自的回调函数封装起来

ros::Subscriber sub_imu = n.subscribe(IMU_TOPIC, , imu_callback, ros::TransportHints().tcpNoDelay());
ros::Subscriber sub_image = n.subscribe("/feature_tracker/feature", , feature_callback);
ros::Subscriber sub_raw_image = n.subscribe(IMAGE_TOPIC, , raw_image_callback);
imu_buf.push(imu_msg);
feature_buf.push(feature_msg);
image_buf.push(make_pair(img_ptr->image, img_msg->header.stamp.toSec()));

然后开启处理measurement的线程

std::thread measurement_process{process};

process()函数中,首先将获取的传感器数据imu_buf feature_buf对齐,注意这里只保证了相邻的feature数据之间有完整的imu数据,并不能保证imu和feature数据的精确对齐

// multiple IMU measurements and only one vision(features) measurements
std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>>
getMeasurements()
{
std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>> measurements; while (true)
{
if (imu_buf.empty() || feature_buf.empty())
return measurements; // synchronize, if strictly synchronize, should change to ">="
// end up with : imu_buf.front()->header.stamp < feature_buf.front()->header.stamp // 1. should have overlap
if (!(imu_buf.back()->header.stamp > feature_buf.front()->header.stamp))
{
ROS_WARN("wait for imu, only should happen at the beginning");
sum_of_wait++;
return measurements;
} // 2. should have complete imu measurements between two feature_buf msg
if (!(imu_buf.front()->header.stamp < feature_buf.front()->header.stamp))
{
ROS_WARN("throw img, only should happen at the beginning");
feature_buf.pop();
continue;
} sensor_msgs::PointCloudConstPtr img_msg = feature_buf.front();
feature_buf.pop(); std::vector<sensor_msgs::ImuConstPtr> IMUs;
while (imu_buf.front()->header.stamp <= img_msg->header.stamp)
{
IMUs.emplace_back(imu_buf.front());
imu_buf.pop();
} measurements.emplace_back(IMUs, img_msg);
}
return measurements;
}

接下来进入对measurements数据的处理:

处理imu数据的接口函数是processIMU()

处理vision数据的借口函数是processImage()

(一)IMU

1. 核心API:

midPointIntegration(_dt, acc_0, gyr_0, _acc_1, _gyr_1, delta_p, delta_q, delta_v,
linearized_ba, linearized_bg,
result_delta_p, result_delta_q, result_delta_v,
result_linearized_ba, result_linearized_bg, );

其中,0代表上次测量值,1代表当前测量值,delta_p,delta_q,delta_v代表相对预积分初始参考系的位移,旋转四元数,以及速度(例如,从k帧预积分到k+1帧,则参考系是k帧的imu坐标系)

对应实现的是公式:

相应的离散实现使用Euler,Mid-point,或者龙格库塔(RK4)数值积分方法。

Euler方法如下:

2. 求状态向量对bias的Jacobian,当bias变化较小时,使用Jacobian去更新状态;否则需要以当前imu为参考系,重新预积分,对应repropagation()。同时,需要计算error state model中误差传播方程的系数矩阵F和V:

    // pre-integration
// time interval of two imu; last and current imu measurements; p,q,v relate to local frame; ba and bg; propagated p,q,v,ba,bg;
// whether to update Jacobian and calculate F,V
void midPointIntegration(double _dt,
const Eigen::Vector3d &_acc_0, const Eigen::Vector3d &_gyr_0,
const Eigen::Vector3d &_acc_1, const Eigen::Vector3d &_gyr_1,
const Eigen::Vector3d &delta_p, const Eigen::Quaterniond &delta_q, const Eigen::Vector3d &delta_v,
const Eigen::Vector3d &linearized_ba, const Eigen::Vector3d &linearized_bg,
Eigen::Vector3d &result_delta_p, Eigen::Quaterniond &result_delta_q, Eigen::Vector3d &result_delta_v,
Eigen::Vector3d &result_linearized_ba, Eigen::Vector3d &result_linearized_bg, bool update_jacobian)
{
//ROS_INFO("midpoint integration");
// mid-point integration with bias = 0
Vector3d un_acc_0 = delta_q * (_acc_0 - linearized_ba);
Vector3d un_gyr = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
result_delta_q = delta_q * Quaterniond(, un_gyr() * _dt / , un_gyr() * _dt / , un_gyr() * _dt / );
Vector3d un_acc_1 = result_delta_q * (_acc_1 - linearized_ba);
Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1);
result_delta_p = delta_p + delta_v * _dt + 0.5 * un_acc * _dt * _dt;
result_delta_v = delta_v + un_acc * _dt;
// ba and bg donot change
result_linearized_ba = linearized_ba;
result_linearized_bg = linearized_bg; // jacobian to bias, used when the bias changes slightly and no need of repropagation
if(update_jacobian)
{
// same as un_gyr, gyrometer reference to the local frame bk
Vector3d w_x = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg; // last acceleration measurement
Vector3d a_0_x = _acc_0 - linearized_ba;
// current acceleration measurement
Vector3d a_1_x = _acc_1 - linearized_ba; // used for cross-product
// pay attention to derivation of matrix product
Matrix3d R_w_x, R_a_0_x, R_a_1_x; R_w_x<<, -w_x(), w_x(),
w_x(), , -w_x(),
-w_x(), w_x(), ;
R_a_0_x<<, -a_0_x(), a_0_x(),
a_0_x(), , -a_0_x(),
-a_0_x(), a_0_x(), ;
R_a_1_x<<, -a_1_x(), a_1_x(),
a_1_x(), , -a_1_x(),
-a_1_x(), a_1_x(), ; // error state model
// should use discrete format and mid-point approximation
MatrixXd F = MatrixXd::Zero(, );
F.block<, >(, ) = Matrix3d::Identity();
F.block<, >(, ) = -0.25 * delta_q.toRotationMatrix() * R_a_0_x * _dt * _dt +
-0.25 * result_delta_q.toRotationMatrix() * R_a_1_x * (Matrix3d::Identity() - R_w_x * _dt) * _dt * _dt;
F.block<, >(, ) = MatrixXd::Identity(,) * _dt;
F.block<, >(, ) = -0.25 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt * _dt;
F.block<, >(, ) = -0.25 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * _dt * -_dt;
F.block<, >(, ) = Matrix3d::Identity() - R_w_x * _dt;
F.block<, >(, ) = -1.0 * MatrixXd::Identity(,) * _dt;
F.block<, >(, ) = -0.5 * delta_q.toRotationMatrix() * R_a_0_x * _dt +
-0.5 * result_delta_q.toRotationMatrix() * R_a_1_x * (Matrix3d::Identity() - R_w_x * _dt) * _dt;
F.block<, >(, ) = Matrix3d::Identity();
F.block<, >(, ) = -0.5 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt;
F.block<, >(, ) = -0.5 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * -_dt;
F.block<, >(, ) = Matrix3d::Identity();
F.block<, >(, ) = Matrix3d::Identity(); MatrixXd V = MatrixXd::Zero(,);
V.block<, >(, ) = 0.25 * delta_q.toRotationMatrix() * _dt * _dt;
V.block<, >(, ) = 0.25 * -result_delta_q.toRotationMatrix() * R_a_1_x * _dt * _dt * 0.5 * _dt;
V.block<, >(, ) = 0.25 * result_delta_q.toRotationMatrix() * _dt * _dt;
V.block<, >(, ) = V.block<, >(, );
V.block<, >(, ) = 0.5 * MatrixXd::Identity(,) * _dt;
V.block<, >(, ) = 0.5 * MatrixXd::Identity(,) * _dt;
V.block<, >(, ) = 0.5 * delta_q.toRotationMatrix() * _dt;
V.block<, >(, ) = 0.5 * -result_delta_q.toRotationMatrix() * R_a_1_x * _dt * 0.5 * _dt;
V.block<, >(, ) = 0.5 * result_delta_q.toRotationMatrix() * _dt;
V.block<, >(, ) = V.block<, >(, );
V.block<, >(, ) = MatrixXd::Identity(,) * _dt;
V.block<, >(, ) = MatrixXd::Identity(,) * _dt; //step_jacobian = F;
//step_V = V;
jacobian = F * jacobian;
covariance = F * covariance * F.transpose() + V * noise * V.transpose();
}
}

(二)Vision

首先判断该帧是否关键帧:

    if (f_manager.addFeatureCheckParallax(frame_count, image))
marginalization_flag = MARGIN_OLD;
else
marginalization_flag = MARGIN_SECOND_NEW;

关键帧的判断依据是rotation-compensated过后的parallax足够大,并且tracking上的feature足够多;关键帧会保留在当前Sliding Window中,marginalize掉Sliding Window中最旧的状态,如果是非关键帧则优先marginalize掉。

1. 标定外参旋转矩阵

initial_ex_rotation.CalibrationExRotation(corres, pre_integrations[frame_count]->delta_q, calib_ric)

其中

pre_integrations[frame_count]->delta_q

是使用imu pre-integration获取的旋转矩阵,会和视觉跟踪求解fundamentalMatrix分解后获得的旋转矩阵构建约束方程,从而标定出外参旋转矩阵。

2. 线性初始化

    if (solver_flag == INITIAL)
{
if (frame_count == WINDOW_SIZE)
{
bool result = false;
if( ESTIMATE_EXTRINSIC != && (header.stamp.toSec() - initial_timestamp) > 0.1)
{
result = initialStructure();
initial_timestamp = header.stamp.toSec();
}
if(result)
{
solver_flag = NON_LINEAR;
solveOdometry();
slideWindow();
f_manager.removeFailures();
ROS_INFO("Initialization finish!");
last_R = Rs[WINDOW_SIZE];
last_P = Ps[WINDOW_SIZE];
last_R0 = Rs[];
last_P0 = Ps[]; }
else
slideWindow();
}
else
frame_count++;
}

3. 非线性优化

    else
{
TicToc t_solve;
solveOdometry();
ROS_DEBUG("solver costs: %fms", t_solve.toc()); if (failureDetection())
{
ROS_WARN("failure detection!");
failure_occur = ;
clearState();
setParameter();
ROS_WARN("system reboot!");
return;
} TicToc t_margin;
slideWindow();
f_manager.removeFailures();
ROS_DEBUG("marginalization costs: %fms", t_margin.toc());
// prepare output of VINS
key_poses.clear();
for (int i = ; i <= WINDOW_SIZE; i++)
key_poses.push_back(Ps[i]); last_R = Rs[WINDOW_SIZE];
last_P = Ps[WINDOW_SIZE];
last_R0 = Rs[];
last_P0 = Ps[];
}

主要的初始化,非线性优化的api均在这里,因此放在后面去说明。

VINS(七)estimator_node 数据对齐 imu预积分 vision的更多相关文章

  1. 转载泡泡机器人——IMU预积分总结与公式推导1

    IMU预积分技术最早由T Lupton于12年提出[1],C Forster于15年[2][3][4]将其进一步拓展到李代数上,形成了一套优雅的理论体系.Forster将IMU预积分在开源因子图优化库 ...

  2. 转载泡泡机器人——IMU预积分总结与公式推导2

    本文为IMU预积分总结与公式推导系列技术报告的第二篇. 承接第一篇的内容,本篇将推导IMU预积分的测量值,并分析其测量误差的分布形式. 传统捷联惯性导航的递推算法,以初始状态为基础,利用IMU测量得到 ...

  3. IMU 预积分推导

    给 StereoDSO 加 IMU,想直接用 OKVIS 的代码,但是有点看不懂.知乎上郑帆写的文章<四元数矩阵与 so(3) 左右雅可比>提到 OKVIS 的预积分是使用四元数,而预积分 ...

  4. VINS(三)IMU预积分

    IMU的数据频率一般远高于视觉,在视觉两帧k,k+1之间通常会有>10组IMU数据.IMU的数据通过积分,可以获取当前位姿(p位置,q四元数表达的姿态).瞬时速度等参数. 在VIO中,如果参考世 ...

  5. IMU预积分

    https://www.sohu.com/a/242760307_715754 http://www.sohu.com/a/243155537_715754 https://www.sohu.com/ ...

  6. SLAM+语音机器人DIY系列:(三)感知与大脑——2.带自校准九轴数据融合IMU惯性传感器

    摘要 在我的想象中机器人首先应该能自由的走来走去,然后应该能流利的与主人对话.朝着这个理想,我准备设计一个能自由行走,并且可以与人语音对话的机器人.实现的关键是让机器人能通过传感器感知周围环境,并通过 ...

  7. 结构体的数据对齐 #pragma浅谈

    之前若是有人拿个结构体或者联合体问我这个结构占用了多少字节的内存,我一定觉得这个人有点low, 直到某某公司的一个实习招聘模拟题的出现,让我不得不重新审视这个问题, 该问题大致如下: typedef ...

  8. 数据对齐 posix_memalign 函数详解

    对齐 数 据的对齐(alignment)是指数据的地址和由硬件条件决定的内存块大小之间的关系.一个变量的地址是它大小的倍数的时候,这就叫做自然对齐 (naturally aligned).例如,对于一 ...

  9. 从零开始一起学习SLAM | 用四元数插值来对齐IMU和图像帧

    视觉 Vs. IMU 小白:师兄,好久没见到你了啊,我最近在看IMU(Inertial Measurement Unit,惯性导航单元)相关的东西,正好有问题求助啊 师兄:又遇到啥问题啦? 小白:是这 ...

随机推荐

  1. ListView实现下拉刷新(一)建立头布局

    一.效果演示 ListView实现下拉刷新,是很常见的功能.下面是一个模拟的效果,如下图:                                   效果说明:当往下拉ListView的时候 ...

  2. pypy入门:pypy的安装及使用介绍

    在做python开发的人,应该或多或少的听说过一点pypy吧.我猜.所以就不做背景介绍了,有不懂的同学可以看看这里: 1.什么是pypy: http://www.360doc.com/content/ ...

  3. 编程三基:cpu:算法,总线(io):一切皆文件;内存:运行空间

    编程三基:cpu:算法,总线:一切皆文件:内存:数据.运行空间 原文找不到了.

  4. 四·安装mysql-5.7.16-linux-glibc2.5-x86_64.tar.gz(基于Centos7源码安装)

    1.在Linux中创建用户mysql和主目录,并创建密码 2.解压缩tar.gz并拷贝到/usr/local/mysql目录下面 3.把/usr/local/mysql文件夹拥有者改为mysql 进入 ...

  5. 体验了Sublime + Emmet,才体会到原来前端开发可以这么痛快!

    从当初用notepad写出第一个web页面,到现在偶尔使用Editplus做一些HTML5的消遣,不知不觉已经15年了  --! 在这中间,和那些老顽固一样,坚决远离FP.DW那些半自动的前端开发工具 ...

  6. BZOJ3790:神奇项链(Manacher)

    Description 母亲节就要到了,小 H 准备送给她一个特殊的项链.这个项链可以看作一个用小写字 母组成的字符串,每个小写字母表示一种颜色.为了制作这个项链,小 H 购买了两个机器.第一个机器可 ...

  7. MaBatis(5)输入/输出映射

    本次全部学习内容:MyBatisLearning   输入映射: 通过parameType指定输入参数的类型,类型可以是简单类型,hashmap,pojo等     传递pojo的包装对象 需求: 即 ...

  8. 掺合模式(Mixin)

    Mixin是JavaScript中用的最普遍的模式,可以就任意一个对象的全部或部分属性拷贝到另一个对象上.从提供的接口来看,有的是对对象的操作,有的是对类的操作.对类的操作又称为掺元类(Mixin c ...

  9. 【题解】洛谷P1065 [NOIP2006TG] 作业调度方案(模拟+阅读理解)

    次元传送门:洛谷P1065 思路 简单讲一下用到的数组含义 work 第i个工件已经做了几道工序 num 第i个工序的安排顺序 finnish 第i个工件每道工序的结束时间 need 第i个工件第j道 ...

  10. HDU 2031 进制转换(10进制转R进制)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2031 进制转换 Time Limit: 2000/1000 MS (Java/Others)    M ...