从零学习VINS-Mono/Fusion源代码(四):误差卡尔曼滤波

本节推导误差卡尔曼滤波公式,用以计算误差状态量的置信度。

VINS-Mono/Fusion代码学习系列:
从零学习VINS-Mono/Fusion源代码(一):主函数
从零学习VINS-Mono/Fusion源代码(二):前端图像跟踪
从零学习VINS-Mono/Fusion源代码(三):IMU预积分公式推导
从零学习VINS-Mono/Fusion源代码(四):误差卡尔曼滤波
从零学习VINS-Mono/Fusion源代码(五):VIO初始化
从零学习VINS-Mono/Fusion源代码(六):后端优化


误差卡尔曼滤波就是以误差值作为滤波状态量. IMU预积分中使用误差卡尔曼滤波来计算置信度,得到误差的协方差矩阵.
(主要是因为旋转四元数引起的过参数化问题)

1 为什么要用误差卡尔曼滤波?

  • IMU预积分中使用四元数表示旋转,但是四元数是过参数化的,需要用4x4的矩阵来描述置信度;在误差卡尔曼滤波中,用旋转向量作为参数,这样就可以用3x3的协方差矩阵来衡量旋转的置信度.
  • 误差通常是很小的,接近0,这样就避免了旋转向量的周期性问题.
  • 误差量很小,可以忽略二阶导数的计算,只计算一阶导数更快.
  • 误差变化很慢,用卡尔曼滤波进行状态修正时,可以降低观测频率.

2 连续时间IMU误差状态传递

vins论文公式:
在这里插入图片描述
推导速度:
在这里插入图片描述在这里插入图片描述

推导姿态:在这里插入图片描述

3 离散时间预积分误差传递

在这里插入图片描述
同样,利用中值积分推算,在两帧IMU时刻之间,认为零偏不变。

请添加图片描述

在这里插入图片描述


4 IMU零偏建模

在上面的推导中认为IMU零偏是不变的,但是VINS优化过程中,零偏也会被优化,导致对应的IMU预积分可能需要重新计算。因此,利用误差雅克比对IMU预积分进行修正。
在这里插入图片描述

为了方便维护,VINS代码中建立了一个15x15的雅克比矩阵,在初始状态下为单位阵I:
在这里插入图片描述
在这里插入图片描述


5 代码阅读

在integration_base.h头文件中找到类IntegrationBase,看到它的构造函数:它依赖于初始时刻的加速度计和陀螺零偏;维护了15x15的jacobian矩阵,初始值为单位阵;构造误差协方差矩阵covariance,初始为0.
noise对应噪声协方差矩阵V.

IntegrationBase(const Eigen::Vector3d &_acc_0, const Eigen::Vector3d &_gyr_0,
                    const Eigen::Vector3d &_linearized_ba, const Eigen::Vector3d &_linearized_bg)
        : acc_0{_acc_0}, gyr_0{_gyr_0}, linearized_acc{_acc_0}, linearized_gyr{_gyr_0},
          linearized_ba{_linearized_ba}, linearized_bg{_linearized_bg},
            jacobian{Eigen::Matrix<double, 15, 15>::Identity()}, covariance{Eigen::Matrix<double, 15, 15>::Zero()},
          sum_dt{0.0}, delta_p{Eigen::Vector3d::Zero()}, delta_q{Eigen::Quaterniond::Identity()}, delta_v{Eigen::Vector3d::Zero()}

    {
        noise = Eigen::Matrix<double, 18, 18>::Zero();
        noise.block<3, 3>(0, 0) =  (ACC_N * ACC_N) * Eigen::Matrix3d::Identity();
        noise.block<3, 3>(3, 3) =  (GYR_N * GYR_N) * Eigen::Matrix3d::Identity();
        noise.block<3, 3>(6, 6) =  (ACC_N * ACC_N) * Eigen::Matrix3d::Identity();
        noise.block<3, 3>(9, 9) =  (GYR_N * GYR_N) * Eigen::Matrix3d::Identity();
        noise.block<3, 3>(12, 12) =  (ACC_W * ACC_W) * Eigen::Matrix3d::Identity();
        noise.block<3, 3>(15, 15) =  (GYR_W * GYR_W) * Eigen::Matrix3d::Identity();
    }

在midPointIntegration()中,中值积分更新完状态量后,更新协方差阵和雅克比矩阵,套用基于中值积分的离散时间预积分误差传递公式:

		if(update_jacobian)
        {
            Vector3d w_x = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
            Vector3d a_0_x = _acc_0 - linearized_ba;
            Vector3d a_1_x = _acc_1 - linearized_ba;
            Matrix3d R_w_x, R_a_0_x, R_a_1_x;

            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;

            MatrixXd F = MatrixXd::Zero(15, 15);
            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;

            //更新雅克比和协方差
            jacobian = F * jacobian;
            covariance = F * covariance * F.transpose() + V * noise * V.transpose();
        }
<think>嗯,用户遇到了C++编译警告Wreorder,关于成员变量初始化顺序的问题,特别是在VocabularyBinary.hpp和VocabularyBinary.cpp文件中。我需要帮他理解这个问题,并提供解决方案。 首先,我得回忆一下这个警告的具体含义。Wreorder警告通常是因为类中的成员变量在初始化列表中的顺序与它们在类定义中的声明顺序不一致。编译器要求成员变量按照声明的顺序进行初始化,否则可能会有未定义行为的风险,比如某个成员变量依赖于另一个先初始化的成员变量,但初始化顺序错误导致依赖问题。 接下来,用户提到了VocabularyBinary.hpp和VocabularyBinary.cpp文件,可能这两个文件中定义了类的成员变量,并且在构造函数中使用初始化列表时顺序不对。需要检查这两个文件中的类定义,特别是成员变量的声明顺序,以及构造函数中的初始化列表顺序是否一致。 然后,我需要给出具体的解决步骤。首先应该检查类定义中的成员变量声明顺序,然后在构造函数初始化列表里调整顺序,使其与声明顺序完全一致。此外,可能需要使用C++11的成员初始化特性来避免这种问题,或者在头文件中使用#pragma GCC diagnostic来抑制特定警告,但这种方法可能不够干净。 另外,用户提供的引用内容里提到了关于hpp文件的注意事项,比如不能有循环引用、静态成员的限制等。虽然当前问题不直接涉及这些,但需要注意如果用户使用的是hpp文件,可能需要确保其他规则也被遵守,避免其他编译错误。 最后,生成相关问题的时候,需要考虑用户可能进一步的问题,比如如何预防这类警告、其他初始化方法、初始化顺序的重要性,或者不同编译器的处理方式。这些相关问题可以帮助用户更全面地理解和解决问题。</think>### C++成员变量初始化顺序警告(Wreorder)解决方案 在C++类构造函数中,成员变量的初始化顺序由其声明顺序决定,而非初始化列表的书写顺序。当两者不一致时,编译器会触发`-Wreorder`警告。该警告提示开发者可能存在的初始化依赖风险[^2][^4]。 **解决步骤:** 1. **检查类声明顺序** 打开`VocabularyBinary.hpp`,确认成员变量的声明顺序。例如: ```cpp class VocabularyBinary { private: int m_width; // 先声明的成员 double m_scale; // 后声明的成员 std::string m_name; }; ``` 2. **修正初始化列表顺序** 在`VocabularyBinary.cpp`的构造函数中,确保初始化列表顺序与声明顺序完全一致: ```cpp // 错误示例(顺序颠倒) VocabularyBinary::VocabularyBinary() : m_scale(0.5), m_width(128) {} // 触发Wreorder // 正确示例 VocabularyBinary::VocabularyBinary() : m_width(128), m_scale(0.5) {} // 顺序与声明一致 ``` 3. **强制初始化顺序验证(可选)** 使用C++11的成员初始化特性可避免隐式错误: ```cpp class VocabularyBinary { private: int m_width = 128; // 直接初始化 double m_scale{0.5}; // 统一初始化语法 std::string m_name{"voc"}; }; ``` **特殊情况处理:** 若存在循环依赖或需要保留特定初始化顺序,可使用编译器指令局部屏蔽警告(谨慎使用): ```cpp #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wreorder" VocabularyBinary::VocabularyBinary() : m_scale(需特殊处理的成员), m_width(依赖m_scale的初始化) {} #pragma GCC diagnostic pop ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值