open_vins(三):imu静止初始化

2023-05-16

      • 一.静止初始化原理
      • 二.理论公式
      • 三.相关代码
      • 四.小结:

初始化是指在系统启动阶段,需要估计重力方向(gravity direction)、加速度计以及陀螺仪biases (accelerometer and gyroscope biases)、初始速度(initial velocity)、初始位置(initial position)等。

OpenVINS系统中,只估计了重力方向、加速度计/陀螺仪biases。
初始化函数位于:ov_msckf/src/core/VioManager.cpp 中
Manager::try_to_initialize()

一.静止初始化原理

静止初始化是指在视频序列开始阶段,需要有一段静止状态。
系统利用静止状态的IMU数据来估计重力方向、加速度计陀螺仪biases。

静止初始化需要检测imu的跳变过程:
其中包含两个状态:静止状态、跳变状态
在这里插入图片描述

通过设定一个阈值[_imu_excite_threshold],可以判断imu是否得到足够的激励,从得到运动激励前的一段静止状态下的imu测量值,根据静止状态测量值,估算出imu初始化参数。

主要步骤:

  1. 将imu数据按固定时间的窗口(0.5s - 2s)划分
  2. 求最新的窗口w1 中imu加速度的方差 a_var,判断是否出现"跳变"
  3. 若a_var1 < imu激励阈值,则说明窗口w1是一段静止状态,返回false;
    若a_var1 >= imu激励阈值,则说明窗口w1出现跳变,下步4 -> 对第二近的窗口w2进行判断
  4. 若a_var2 < imu激励阈值,则使用w2窗口的imu数据进行系统初始化;
    计算窗口w2的平均加速度与角速度 以及方差.
  5. 静止初始化求出重力方向(gravity direction)、初始速度(initial velocity)、初始位置(initial position)、加速度计以及陀螺仪biases (accelerometer and gyroscope biases)

代码框图如下:
在这里插入图片描述

二.理论公式

step1:计算初始旋转矩阵

窗口w2的加速度计平均值 : am2_avg
窗口w2的陀螺仪平均值 : wm2_avg

因为,在世界系下,静止时的加速度测量值为(0,0,g)
将窗口w2的am2_avg除以其模长,得到平均加速度的方向(单位向量),即为世界系z轴的方向在imu坐标系上的投影:
在这里插入图片描述
确定z轴方向后,通过施密特正交化构建单位坐标系。

假设,e1为imu系的xi轴对应的单位方向向量:
在这里插入图片描述
将xi轴投影到 zw-xw平面,求出xw轴在imu坐标系下的方向向量:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
y轴方向由x轴和z轴叉乘得到:
在这里插入图片描述
通过上述步骤,得到了从世界系w到imu坐标系的初始旋转 Rwi=[x y z]

step2:计算陀螺仪bias:
静止时刻的陀螺仪理想测量值(角速度)应该为0,因此陀螺仪的bias即窗口w2陀螺仪数据的平均值 [wm2_avg]
在这里插入图片描述
step3:计算加速度计bias:
静止时的加速度计bias为加速度平均值与实际重力加速度的差:
在这里插入图片描述
g为世界系下的重力加速度,R为第一步求出来的旋转矩阵,将重力矢量从世界系投影到imu坐标系下。

三.相关代码

在 feed_measurement_stereo/feed_measurement_monocular 过程中调用初始化函数try_to_initialize()

bool VioManager::try_to_initialize() {
    // Returns from our initializer
    double time0;
    Eigen::Matrix<double, 4, 1> q_GtoI0;
    Eigen::Matrix<double, 3, 1> b_w0, v_I0inG, b_a0, p_I0inG;
    // Try to initialize the system
    // We will wait for a jerk if we do not have the zero velocity update enabled
    // Otherwise we can initialize right away as the zero velocity will handle the stationary case
    bool wait_for_jerk = (updaterZUPT == nullptr);
    bool success = initializer->initialize_with_imu(time0, q_GtoI0, b_w0, v_I0inG, b_a0, p_I0inG, wait_for_jerk);
    // Return if it failed
    if (!success) {
        return false;
    }
    // Make big vector (q,p,v,bg,ba), and update our state
    // Note: start from zero position, as this is what our covariance is based off of
    Eigen::Matrix<double,16,1> imu_val;
    imu_val.block(0,0,4,1) = q_GtoI0;
    imu_val.block(4,0,3,1) << 0,0,0;
    imu_val.block(7,0,3,1) = v_I0inG;
    imu_val.block(10,0,3,1) = b_w0;
    imu_val.block(13,0,3,1) = b_a0;

    state->_imu->set_value(imu_val);
    state->_imu->set_fej(imu_val);
    state->_timestamp = time0;
    startup_time = time0;
    // Cleanup any features older then the initialization time
    trackFEATS->get_feature_database()->cleanup_measurements(state->_timestamp);
    if(trackARUCO != nullptr) {
        trackARUCO->get_feature_database()->cleanup_measurements(state->_timestamp);
    }
    // Else we are good to go, print out our stats
    printf(GREEN "[INIT]: orientation = %.4f, %.4f, %.4f, %.4f\n" RESET,state->_imu->quat()(0),state->_imu->quat()(1),state->_imu->quat()(2),state->_imu->quat()(3));
    printf(GREEN "[INIT]: bias gyro = %.4f, %.4f, %.4f\n" RESET,state->_imu->bias_g()(0),state->_imu->bias_g()(1),state->_imu->bias_g()(2));
    printf(GREEN "[INIT]: velocity = %.4f, %.4f, %.4f\n" RESET,state->_imu->vel()(0),state->_imu->vel()(1),state->_imu->vel()(2));
    printf(GREEN "[INIT]: bias accel = %.4f, %.4f, %.4f\n" RESET,state->_imu->bias_a()(0),state->_imu->bias_a()(1),state->_imu->bias_a()(2));
    printf(GREEN "[INIT]: position = %.4f, %.4f, %.4f\n" RESET,state->_imu->pos()(0),state->_imu->pos()(1),state->_imu->pos()(2));
    return true;
}

继而调用 initialize_with_imu (time0, q_GtoI0, b_w0, v_I0inG, b_a0, p_I0inG, wait_for_jerk);

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) {

    // 若无imu测量值,则返回
    if(imu_data.empty()) {
        return false;
    }
    // 最新的imu数据时间
    double newesttime = imu_data.at(imu_data.size()-1).timestamp;
    
   // _window_length 为launch中设定的imu初始化窗口的采用时间:0.5~2s
    // 取最近两段时间内的imu数据进行用于初始化
    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
    // 计算最近一个窗口内IMU加速度方差
    Eigen::Matrix<double,3,1> a_avg = Eigen::Matrix<double,3,1>::Zero();
    for(IMUDATA data : window_newest) {
        a_avg += data.am;
        //cout<<data.am<<endl;
    }
    a_avg /= (int)window_newest.size();
    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));

    //方差过小
   // _imu_excite_threshold 是launch中设定的判断imu激励的阈值,高于阈值则表示运动,低于阈值为静止
       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;
    }

    // 方差够大
    // 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();

    // 计算最近第二个窗口内IMU加速度方差
    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)
    Eigen::Vector3d z_axis = linavg/linavg.norm();
    // Create an x_axis
    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
    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)
    Eigen::Matrix<double,3,1> bg = angavg;
    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;
}

四.小结:

此处进行imu初始化,是通过静止时加速度计测量值与重力矢量方向一致的原理,得到世界坐标系w到imu坐标系的相对旋转R.(z轴(0,0,1))

在其他常用的vio系统中,通常将视觉的第一帧作为世界系,由于相机与imu之间的相对关系已知,因此可方便地得到相机系相对于世界系的位姿,以及imu系相对于世界系的位姿

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

open_vins(三):imu静止初始化 的相关文章

随机推荐

  • 相机标定和双目相机标定标定原理推导及效果展示

    文章目录 前言一 相机标定1 相机的四个坐标系2 相机的畸变 二 张正友标定法1 求解内参矩阵与外参矩阵的积2 求解内参矩阵3 求解外参矩阵4 标定相机的畸变参数5 双目标定6 极线矫正 xff08 立体校正 xff09 三 视差图与深度图
  • keras:tensor从全连接层输出到卷积层

    一 tensor从卷积层输出到全连接层 用过keras的都知道 xff0c 想从卷积层输出tensor到全连接层 xff0c 只需加一层 xff1a model add Flatten shape就不会出现错误 二 但是如果从全连接层输出t
  • 保研面试复习之数据结构篇

    数据项 数据元素和数据结构的概念 数据项是组成数据元素的 xff0c 有独立含义的 xff0c 不可分割的最小单位 数据元素是数据的基本单位 数据结构是带结构的数据元素的集合 数据结构包括逻辑结构和存储结构两个层次 数据结构的三要素是逻辑结
  • 视觉里程计的重定位问题1——SVO的重定位部分

    SVO的重定位部分代码解析与分析 SVO的重定位功能体现在 xff1a 运动跟踪丢失后通过与上一关键帧匹配以及地图点投影 xff0c 找回当前相机位姿 由于没有后端和回环 xff0c SVO的重定位并不是回环校正后的重定位 代码部分被放在运
  • 组合导航(一):定位技术分类与介绍

    组合导航 xff08 一 xff09 xff1a 导航定位技术分类与介绍 一 定位技术分类1 1 基于相对测量的定位 xff08 航位推算 xff09 1 2 基于绝对测量的定位1 3 组合定位 一 定位技术分类 1 1 基于相对测量的定位
  • git bash可以正常commit,但是 VSCode 里不能正常commit使用的解决方法

    问题描述 同一路径下的源码 xff0c 使用git bash可以正常commit xff0c 但是使用vscode提交commit就会一直卡住 xff0c 转圈圈 参考方案链接 xff1a VS CODE GIT 500 问题处理 pudn
  • 组合导航(四):惯性导航系统

    1 惯性导航系统的物理平台2 惯性测量单元IMU3 惯性传感器的测量值3 1静止状态下的加速度测量3 2静止与运动状态下的角速度测量 4 惯性传感器误差4 1 系统误差 xff08 可通过实验进行校正 xff09 4 2 随机误差4 3 惯
  • 组合导航(七):卡尔曼滤波

    Kalman滤波1 离散卡尔曼滤波2 卡尔曼滤波的流程2 1 预测与时间更新2 2 测量更新与校正 3 卡尔曼滤波 算法步骤4 非线性卡尔曼滤波4 1 线性化kalman滤波4 2 扩展kalman滤波 5 卡尔曼滤波发散控制5 1 KF过
  • 组合导航(八):INS/GPS组合导航

    INS GPS组合导航1 误差反馈1 1 开环INS GPS架构1 2 闭环INS GPS架构 2 组合导航的类型2 1 松耦合 的INS GPS组合导航2 2 紧耦合 的INS GPS组合导航2 3 深度耦合的 INS GPS组合导航 3
  • 组合导航(九):三维简化的INS/GPS组合导航系统

    简化INS与GPS组合系统在三维路面上的导航1 MEMS级IMU的三维定位的性能分析2 解决MEMS级IMU在路面导航中存在的问题3 三维简化的惯性传感器系统3 1 3D RISS概述3 2 xff08 轮式车辆 xff09 采用3D RI
  • PX4安装与编译

    第一步 xff1a 下载源码 下载方式一 xff1a git clone https github com PX4 Firmware git recursive 默认下载版本为master 下载时间比较长 xff0c 包含各种包以及依赖工具
  • PX4:【系统架构】

    PX4系统架构 由两个层组成 xff1a 一是飞行控制栈 xff08 flight stack xff09 二是中间件 xff08 middleware xff09 flight stack xff1a 集成了各种自主无人机的制导 导航以及
  • PX4:【uORB通讯机制】

    uORB xff1a Micro Object Request Broker PX4进程间的通讯机制 xff1a 多对多的信息发布与订阅方式 发布消息 xff1a 1 公告 advertise xff1a 相当于初始化 xff0c 在发布消
  • PX4:【传感器校准】

    sensor的校准校准步骤 xff1a 文件目录 xff1a 代码入口 xff1a 求解模型计算公式 sensor的校准 校准步骤 xff1a 首先通过地面站QGC进行校准 xff0c QGC将校准参数设置到sh文件中 此后再基于QGC的校
  • PX4:【sensor_combined】

    功能介绍消息内容sensor combined 产生机制 amp 代码流程 功能介绍 sensor combined 是一个冗余传感器集合的消息 xff0c 通过订阅多个传感器的数据 xff0c 将冗余的数据经过VoteSensorsUpd
  • PX4:【地面站传感器数据校准】

    上电 gt rsC 运行 sensor start commander start 入口函数 xff1a 位于commander文件夹中 Commader cpp Commander run xff08 xff09 commander lo
  • Windows和Linux双系统安装教程

    最近刚刚完成了Windows和Linux双系统 xff08 这里以Ubuntu安装为例 xff09 的安装 xff0c 应某奔同学要求 xff0c 这里简单记录下安装过程 系统启动盘准备Windows系统安装分出给Linux系统的磁盘空间安
  • MSCKF系列论文阅读与代码流程

    MSCKF原理与代码总结 算法原理前端理论 xff08 图像的特征提取与跟踪 xff09 后端理论 xff08 误差状态卡尔曼滤波模型 xff09 1 IMU状态预测1 1 IMU状态传播 xff08 p v q 4阶Runge Kutta
  • open_vins(二):rosbag精度测试

    一 xff0e ros读取与轨迹保存二 xff0e euroc数据集测试三 结论 一 xff0e ros读取与轨迹保存 运行open vins launch 读取ros数据包 xff1a roslaunch pgeneva ros eth
  • open_vins(三):imu静止初始化

    一 xff0e 静止初始化原理二 xff0e 理论公式三 xff0e 相关代码四 xff0e 小结 xff1a 初始化是指在系统启动阶段 xff0c 需要估计重力方向 gravity direction 加速度计以及陀螺仪biases ac