首页 > 其他分享 >VINS-Mono工程笔记(五):IMU预积分

VINS-Mono工程笔记(五):IMU预积分

时间:2024-12-10 18:30:47浏览次数:6  
标签:acc Vector3d Mono IMU result delta VINS dt gyr

 1.processimu()函数分析

/**
 * 处理IMU数据
 * linear_acceleration 线加速度
 * angular_velocity    角速度
 * */
void Estimator::processIMU(double dt, const Vector3d &linear_acceleration, const Vector3d &angular_velocity)
{
    //1.判断是不是第一个imu消息,如果是第一个imu消息,则将当前传入的线加速度和角速度作为初始的加速度和角速度
    if (!first_imu)
    {
        first_imu = true;
        acc_0 = linear_acceleration;//记录线加速度值
        gyr_0 = angular_velocity;//记录角速度值
    }
    /**
     * 2.创建预积分对象
     * 首先,pre_integrations是一个数组,里面存放了(WINDOW_SIZE + 1)个指针,指针指向的类型是IntegrationBase的对象
    */
    if (!pre_integrations[frame_count])
    {
        //创建pre_integrations[frame_count]中的对象
        pre_integrations[frame_count] = new IntegrationBase{acc_0, gyr_0, Bas[frame_count], Bgs[frame_count]};
    }
    //frame_count==0表示此时滑动窗口中还没有图像帧数据,所以先不进行预积分
    if (frame_count != 0)
    {
        //3.进行预计分
        pre_integrations[frame_count]->push_back(dt, linear_acceleration, angular_velocity);
        //if(solver_flag != NON_LINEAR)
            tmp_pre_integration->push_back(dt, linear_acceleration, angular_velocity);
 
        dt_buf[frame_count].push_back(dt);
        //当前的线加速度和角速度存放到先加速度buffer和角速度buffer当中
        linear_acceleration_buf[frame_count].push_back(linear_acceleration);
        angular_velocity_buf[frame_count].push_back(angular_velocity);
 
        int j = frame_count;
        /**
         * 4.更新Rs、Ps、Vs三个向量数组。
         * Rs为旋转向量数组,Ps为位置向量数组,Vs为速度向量数组,数组的大小为WINDOW_SIZE + 1
         * 那么,这三个向量数组中每个元素都对应的是每一个window
        */
        //计算上一时刻的加速度,前边乘一个旋转矩阵Rs[j]的作用是进行坐标系转换
        Vector3d un_acc_0 = Rs[j] * (acc_0 - Bas[j]) - g;
        //根据上一时刻陀螺仪的角速度和当前时刻的角速度求出平均角速度
        Vector3d un_gyr = 0.5 * (gyr_0 + angular_velocity) - Bgs[j];
        //计算当前时刻陀螺仪的姿态(旋转)矩阵。这里其实是在上一时刻的旋转矩阵基础上和当前时刻的旋转增量相乘得到的
        Rs[j] *= Utility::deltaQ(un_gyr * dt).toRotationMatrix();
        //求当前时刻的加速度
        Vector3d un_acc_1 = Rs[j] * (linear_acceleration - Bas[j]) - g;
        //求上一时刻和当前时刻的平均加速度
        Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1);
        //位移(位置)更新,位置是在之前的基础上加上当前的位移量,使用的是位移公式:s = v*t + 1/2*a*t^2
        Ps[j] += dt * Vs[j] + 0.5 * dt * dt * un_acc;
        //速度更新,使用的是速度公式:v = a * t a是加速度,t是时间
        Vs[j] += dt * un_acc;
    }
    //更新acc_0和gyr_0的值,本次的线加速度和角速度作为下一个IMU消息的前一个状态值
    acc_0 = linear_acceleration;
    gyr_0 = angular_velocity;
}

1)判断是否是第一个imu数据的标志,并记录last_imu数据。由于使用的是中值积分,需要用到历史的一个imu。

2)对滑动窗口内每个帧创建预积分对象IntegrationBase对象存入pre_integrations数组当中。

3)当frame_count==0的时候表示滑动窗口中还没有图像帧数据,所以不需要进行预积分,只进行线加速度和角速度初始值的更新。当frame_count!=0的时候,说明滑动窗口中已经有图像帧的数据了,此时就可以对该图像帧对应的imu进行预积分,通过IntegrationBase类型对象的push_back函数实现。

pre_integrations[frame_count]->push_back(dt, linear_acceleration, angular_velocity);
tmp_pre_integration->push_back(dt, linear_acceleration, angular_velocity);

4)更新Rs、Ps和Vs三个数组的值。这三个数组的大小为滑动窗口大小+1,这里按照图像帧的id来计算得到滑动窗口中每个图像帧所对应的旋转矩阵、位置和速度值。这三个值在后边进行窗口滑动和进行图像位姿初始化的时候需要使用。

2.imu预积分代码

/**
     * dt  时间间隔
     * acc 陀螺仪的线加速度
     * gyr 陀螺仪的角速度
    */
    void push_back(double dt, const Eigen::Vector3d &acc, const Eigen::Vector3d &gyr)
    {
        dt_buf.push_back(dt);
        acc_buf.push_back(acc);
        gyr_buf.push_back(gyr);
        //进入预积分阶段
        propagate(dt, acc, gyr);
    }

push_back()内部存储了imu数据,并执行propagate进行前向积分。

   /**
     * 预计分计算
     * _dt    时间间隔
     * _acc_1 线加速度
     * _gyr_1 角速度
    */
    void propagate(double _dt, const Eigen::Vector3d &_acc_1, const Eigen::Vector3d &_gyr_1)
    {
        dt = _dt;
        acc_1 = _acc_1;
        gyr_1 = _gyr_1;
        Vector3d result_delta_p;
        Quaterniond result_delta_q;
        Vector3d result_delta_v;
        Vector3d result_linearized_ba;
        Vector3d result_linearized_bg;
        //中点积分方法
        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, 1);
 
        //checkJacobian(_dt, acc_0, gyr_0, acc_1, gyr_1, delta_p, delta_q, delta_v,
        //                    linearized_ba, linearized_bg);
        //更新PQV
        delta_p = result_delta_p;
        delta_q = result_delta_q;
        delta_v = result_delta_v;
        //更新偏置
        linearized_ba = result_linearized_ba;
        linearized_bg = result_linearized_bg;
        delta_q.normalize();
        //时间进行累加
        sum_dt += dt;
        //预积分完后,更新当前的线加速度和角速度为上一时刻的线加速度和角速度
        acc_0 = acc_1;
        gyr_0 = gyr_1;
     
    }

基于一个初始为0的状态量(p,v,q,ba,bg),通过midPointIntegration()对单个imu进行中值积分,计算一帧图像内所有imu的积分结果。该积分结果是在imu local系下进行不考虑重力和初始状态的影响,使用时只用将重力和初始状态的积分量补偿进来即可得到实际积分值,避免了重复积分计算。在integratebase内部对单个imu积分后,还是会更新当前帧的状态量。

在midPointIntegration()函数内,实现了基于离散采样形式的积分过程,以及运动学方程的传播过程。

 /**
     * 该函数是以中值点的方式进行预积分求解PVQ的,需要注意的是这里使用的是离散形式的预积分公式
     * 参数中_0代表上次测量值,_1代表当前测量值,delta_p,delta_q,delta_v代表相对预积分初始参考系的位移,旋转四元数,以及速度
     * 从k帧预积分到k+1帧,则参考系是k帧的imu坐标系
    */
    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");
        //delta_q为相对预计分参考系的旋转四元数,线加速度的测量值减去偏差然后和旋转四元数相乘表示将线加速度从世界坐标系下转到了body(IMU)坐标系下
        Vector3d un_acc_0 = delta_q * (_acc_0 - linearized_ba);
        //计算平均角速度
        Vector3d un_gyr = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
        //对平均角速度和时间的乘积构成的旋转值组成的四元数左乘旋转四元数,获得当前时刻body中的旋转向量(四元数表示)
        result_delta_q = delta_q * Quaterniond(1, un_gyr(0) * _dt / 2, un_gyr(1) * _dt / 2, un_gyr(2) * _dt / 2);
        //用计算出来的旋转向量左乘当前的加速度,表示将线加速度从世界坐标系下转到了body坐标系下
        Vector3d un_acc_1 = result_delta_q * (_acc_1 - linearized_ba);
        //计算两个时刻的平均加速度
        Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1);
        //当前的位移:当前位移=前一次的位移+(速度×时间)+1/2×加速度的×时间的平方
        //匀加速度运动的位移公式:s_1 = s_0 + v_0 * t + 1/2 * a * t^2
        result_delta_p = delta_p + delta_v * _dt + 0.5 * un_acc * _dt * _dt;
        //速度计算公式:v_1 = v_0 + a*t
        result_delta_v = delta_v + un_acc * _dt;
        // 预积分的过程中Bias并未发生改变,所以还保存在result当中
        result_linearized_ba = linearized_ba;
        result_linearized_bg = linearized_bg;        
        //是否更新IMU预计分测量关于IMU Bias的雅克比矩阵
        if(update_jacobian)
        {
            //计算平均角速度
            Vector3d w_x = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
            //计算_acc_0这个观测线加速度对应的实际加速度
            Vector3d a_0_x = _acc_0 - linearized_ba;
            //计算_acc_1这个观测线加速度对应的实际加速度
            Vector3d a_1_x = _acc_1 - linearized_ba;
            Matrix3d R_w_x, R_a_0_x, R_a_1_x;
            /**
             *         | 0      -W_z     W_y |
             * [W]_x = | W_z     0      -W_x |
             *         | -W_y   W_x       0  |
            */
            R_w_x<<0, -w_x(2), w_x(1),
                w_x(2), 0, -w_x(0),
                -w_x(1), w_x(0), 0;
            R_a_0_x<<0, -a_0_x(2), a_0_x(1),
                a_0_x(2), 0, -a_0_x(0),
                -a_0_x(1), a_0_x(0), 0;
            R_a_1_x<<0, -a_1_x(2), a_1_x(1),
                a_1_x(2), 0, -a_1_x(0),
                -a_1_x(1), a_1_x(0), 0;
            //F是一个15行15列的数据类型为double,数据全部为0的矩阵,Matrix创建的矩阵默认按列存储
            MatrixXd F = MatrixXd::Zero(15, 15);
            /**
             * matrix.block(i,j, p, q) : 表示返回从矩阵(i, j)开始,每行取p个元素,每列取q个元素所组成的临时新矩阵对象,原矩阵的元素不变;
             * matrix.block<p,q>(i, j) :<p, q>可理解为一个p行q列的子矩阵,该定义表示从原矩阵中第(i, j)开始,获取一个p行q列的子矩阵,
             * 返回该子矩阵组成的临时矩阵对象,原矩阵的元素不变;
            */
            //从F矩阵的(0,0)位置的元素开始,将前3行3列的元素赋值为单位矩阵
            /**
             * 下面F和V矩阵为什么这样构造,是需要进行相关推导的。这里的F、V矩阵的构造理解可以参考论文附录中给出的公式
            */
            F.block<3, 3>(0, 0) = Matrix3d::Identity();
            F.block<3, 3>(0, 3) = -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<3, 3>(0, 6) = MatrixXd::Identity(3,3) * _dt;
            F.block<3, 3>(0, 9) = -0.25 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt * _dt;
            F.block<3, 3>(0, 12) = -0.25 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * _dt * -_dt;
            F.block<3, 3>(3, 3) = Matrix3d::Identity() - R_w_x * _dt;
            F.block<3, 3>(3, 12) = -1.0 * MatrixXd::Identity(3,3) * _dt;
            F.block<3, 3>(6, 3) = -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<3, 3>(6, 6) = Matrix3d::Identity();
            F.block<3, 3>(6, 9) = -0.5 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt;
            F.block<3, 3>(6, 12) = -0.5 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * -_dt;
            F.block<3, 3>(9, 9) = Matrix3d::Identity();
            F.block<3, 3>(12, 12) = Matrix3d::Identity();
            //cout<<"A"<<endl<<A<<endl;
 
            MatrixXd V = MatrixXd::Zero(15,18);
            V.block<3, 3>(0, 0) =  0.25 * delta_q.toRotationMatrix() * _dt * _dt;
            V.block<3, 3>(0, 3) =  0.25 * -result_delta_q.toRotationMatrix() * R_a_1_x  * _dt * _dt * 0.5 * _dt;
            V.block<3, 3>(0, 6) =  0.25 * result_delta_q.toRotationMatrix() * _dt * _dt;
            V.block<3, 3>(0, 9) =  V.block<3, 3>(0, 3);
            V.block<3, 3>(3, 3) =  0.5 * MatrixXd::Identity(3,3) * _dt;
            V.block<3, 3>(3, 9) =  0.5 * MatrixXd::Identity(3,3) * _dt;
            V.block<3, 3>(6, 0) =  0.5 * delta_q.toRotationMatrix() * _dt;
            V.block<3, 3>(6, 3) =  0.5 * -result_delta_q.toRotationMatrix() * R_a_1_x  * _dt * 0.5 * _dt;
            V.block<3, 3>(6, 6) =  0.5 * result_delta_q.toRotationMatrix() * _dt;
            V.block<3, 3>(6, 9) =  V.block<3, 3>(6, 3);
            V.block<3, 3>(9, 12) = MatrixXd::Identity(3,3) * _dt;
            V.block<3, 3>(12, 15) = MatrixXd::Identity(3,3) * _dt;
 
            //step_jacobian = F;
            //step_V = V;
            /**
             * 求矩阵的转置、共轭矩阵、伴随矩阵:可以通过成员函数transpose()、conjugate()、adjoint()来完成。注意:这些函数返回操作后的结果,
             * 而不会对原矩阵的元素进行直接操作,如果要让原矩阵进行转换,则需要使用响应的InPlace函数,如transpoceInPlace()等
            */
            //雅克比jacobian的迭代公式:J_(k+1)​=F*J_k​,J_0​=I
            jacobian = F * jacobian;
            /**
             * covariance为协方差,协方差的迭代公式:P_(k+1) ​= F*P_k*​F^T + V*Q*V^T,P_0​ = 0
             * P_k就是协方差,Q为noise,其初值为18*18的单位矩阵
            */
            covariance = F * covariance * F.transpose() + V * noise * V.transpose();
        }
 
    }

值得注意的:VINS论文中使用的是欧拉积分而代码却是以中值积分实现,这里的噪声在原来的角加速度,线加速度,ba,bg的随机游走(12维)上加上了上一帧角加速度,线加速度的随机游走噪声(18维)。

最后更新雅可比和状态斜方差,用于积分加权残差和预积分bias的补偿量的计算。

            jacobian = F * jacobian;
            /**
             * covariance为协方差,协方差的迭代公式:P_(k+1) ​= F*P_k*​F^T + V*Q*V^T,P_0​ = 0
             * P_k就是协方差,Q为noise,其初值为18*18的单位矩阵
            */
            covariance = F * covariance * F.transpose() + V * noise * V.transpose();

3.imu运动学方程推导。

标签:acc,Vector3d,Mono,IMU,result,delta,VINS,dt,gyr
From: https://blog.csdn.net/zoufeizhiba/article/details/144360346

相关文章

  • Maximum Area Rectangle With Point Constraints II
    MaximumAreaRectangleWithPointConstraintsIITherearenpointsonaninfiniteplane.YouaregiventwointegerarraysxCoordandyCoordwhere(xCoord[i],yCoord[i])representsthecoordinatesoftheithpoint.Yourtaskistofindthemaximum areaof......
  • 风光互补发电耦合制氢仿真模型Matlab/Simulink
      风光互补发电耦合氢储能系统是一种结合了风能、太阳能以及氢能的高效能源利用系统。该系统通过风光互补发电,利用电解水制氢技术将电能转化为氢能储存,再通过氢燃料电池等技术实现氢能的利用。  典型的风光互补发电制氢、储氢、用氢一体化应用系统主要包括光伏发电、......
  • 风光储交直流微电网matlab/simulink仿真模型
      风光储交直流微电网系统算是当下本硕课题中最常见的研究方向,无论是电力系统的智能算法还是电力电子的控制算法,都可以基于风光储这个框架下进行研究,本篇博文介绍的为风光储交直流微电的控制算法,具体控制结构如下:光伏+MPPT控制  根据光伏电池的数学模型,建立基于环境......
  • [LeetCode] 2684. Maximum Number of Moves in a Grid
    Youaregivena0-indexedmxnmatrixgridconsistingofpositiveintegers.Youcanstartatanycellinthefirstcolumnofthematrix,andtraversethegridinthefollowingway:Fromacell(row,col),youcanmovetoanyofthecells:(row-1,col+......
  • 题解:AT_abc368_d[ABC368D] Minimum Steiner Tree
    题目大意题目给定一棵由$N$个节点组成的无根树,删除其中的一些点和边,使剩下的点和边仍然能够组成一棵树,且包含给定的$K$个特殊点,问最少剩下几个点。思路我们可以发现,这棵无根树的根必须是给定的特殊点之一,不然根节点就可以删除,答案就不是最优。所以我们使用深度优先搜索遍......
  • CF2050F Maximum modulo equality 题解
    【题意简述】你有一个长度为\(n\)的数组\(a\)。每一次询问给定\(l,r\),寻找最大的\(m\)使得\(a_l\)到\(a_r\)的所有数对\(m\)同余,【前置数学芝士】首先是一个非常Naive的结论,请自行感性证明:设\(a>b\),\(a\)和\(b\)对\(a-b\)同余。理性证明:设\(p=a-b\),\(......
  • 免费大屏来啦!!JimuReport v1.9.1发布,首个大屏稳定版
    项目介绍积木报表JimuReport,是一款免费的数据可视化报表,含报表、仪表盘和大屏设计,像搭建积木一样完全在线设计!功能涵盖:数据报表、打印设计、图表报表、门户设计、大屏设计等!Web版报表设计器,类Excel操作风格,通过拖拽完成报表设计,所见即所得。大屏采用类word风格,可以随意拖动......
  • 基于PI控制器的DC-DC结构PWM系统simulink建模与仿真
    1.课题概述      基于PI控制器的DC-DC结构PWM系统simulink建模与仿真。包括IGBT结构,PI控制器结构,PWM模块等。 2.系统仿真结果 3.核心程序与模型版本:MATLAB2022a   4.系统原理简介      在电力电子领域,特别是在直流电源变换系统(DC-DC转换器)中,脉......
  • Simulink Coverage基础概念和应用
    SimulinkCoverage是MATLABSimulink中的一个功能,它用于测量和报告代码覆盖率,帮助用户验证模型的测试是否全面。本文将介绍SimulinkCoverage的基本概念、覆盖率指标的评估和收集模型覆盖率的方法,探讨如何对覆盖率进行分析和记录,生成覆盖度结果和报告的相关内容。首先介绍S......
  • 基于RBF-PID控制器的风力发电系统simulink建模与仿真
    1.课题概述      基于RBF-PID控制器的风力发电系统simulink建模与仿真,对比PID控制器和RBF-PID控制器的控制结果。 2.系统仿真结果  3.核心程序与模型版本:MATLAB2022a   4.系统原理简介      在风力发电系统中,传统的PID控制器虽然简单实......