slam 基本算法 --- VINS-mono 之【相机与IMU外参Ric估计】

slam 基本算法 --- VINS-mono 之【相机到IMU外参Ric估计】

vins-mono是使用 机器人手眼标定的方法,计算出外参中的旋转 R c i R^i_{c} Rci ,也可以是 R c b R^b_{c} Rcb (把imu坐标系当作body坐标系)

本文将详细进行推导,以及与代码对应;

1. 理论

1.1 前言

对于 k k k k + 1 k+1 k+1帧相机之间的位姿 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck,可以直接通过两帧之间的视觉测量(对极约束,分解本质矩阵E计算得到),也可以先通过IMU积分获得 R i k + 1 i k R^{i_k}_{i_{k+1}} Rik+1ik ,然后根据 R c i R^i_{c} Rci,经过下面的变换

R c k + 1 c k = R i c R i k + 1 i k R c i ( 1 ) R^{c_k}_{c_{k+1}} =R^c_{i} R^{i_k}_{i_{k+1}} R^i_{c} (1) Rck+1ck=RicRik+1ikRci1
也可以得到 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck

由相机和imu分别计算出来的 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck 存在差异,构建最小二乘问题, A x = 0 Ax=0 Ax=0,SVD分解A矩阵,解出非零解,即最佳的 R c i R^i_{c} Rci

1.2 利用旋转约束估计外参数

相邻两个时刻 k, k+1 之间 R i k + 1 c k R^{c_k}_{i_{k+1}} Rik+1ck 存在 :

R i c R i k + 1 i k = R c k + 1 c k R i c R^c_{i} R^{i_k}_{i_{k+1}} = R^{c_k}_{c_{k+1}}R^c_{i} RicRik+1ik=Rck+1ckRic

q i c ⊗ q i k + 1 i k = q c k + 1 c k ⊗ q i c q^c_{i} \otimes q^{i_k}_{i_{k+1}} = q^{c_k}_{c_{k+1}}\otimes q^c_{i} qicqik+1ik=qck+1ckqic

四元数 q = [ x y z w ] T = [ q v q w ] q=[x y z w]^{T}=\left[\begin{array}{c} q_{v} \\ q_{w} \end{array}\right] q=[xyzw]T=[qvqw] 的左乘矩阵与右乘矩阵:
[ q ] L = q w I + [ [ q v ] × q v − q v T 0 ] , [ q ] R = q w I + [ − [ q v ] × q v − q v T 0 ] \quad[q]_{L}=q_{w} I+\left[\begin{array}{cc} {\left[q_{v}\right]_{\times}} & q_{v} \\ -q_{v}^{T} & 0 \end{array}\right], \quad[q]_{R}=q_{w} I+\left[\begin{array}{cc} -\left[q_{v}\right]_{\times} & q_{v} \\ -q_{v}^{T} & 0 \end{array}\right] [q]L=qwI+[[qv]×qvTqv0],[q]R=qwI+[[qv]×qvTqv0]

得:
[ q i k + 1 i k ] R q i c = [ q c k + 1 c k ] L q i c [q^{i_k}_{i_{k+1}} ]_{R} q^c_{i} =[q^{c_k}_{c_{k+1}}]_{L} q^c_{i} [qik+1ik]Rqic=[qck+1ck]Lqic

{ [ q c k + 1 c k ] L − [ q i k + 1 i k ] R } q i c = 0 \{[q^{c_k}_{c_{k+1}}]_{L} - [q^{i_k}_{i_{k+1}} ]_{R} \} q^c_{i} = 0 {[qck+1ck]L[qik+1ik]R}qic=0

通过多对 q c k + 1 c k , q i k + 1 i k q^{c_k}_{c_{k+1}},q^{i_k}_{i_{k+1}} qck+1ckqik+1ik 可以构建超定方程
A = { [ q i k i k + 1 1 ] L − [ q c k c k + 1 1 ] R [ q i k i k + 1 2 ] L − [ q c k c k + 1 2 ] R                                      ⋯ [ q i k i k + 1 n ] L − [ q c k c k + 1 n ] R } , A ⋅ q i c = 0 A=\left\{\begin{array}{l}{\left[q_{i_ki_{k+1}}^1\right]}_L-{\left[q_{c_kc_{k+1}}^1\right]}_R\\{\left[q_{i_ki_{k+1}}^2\right]}_L-{\left[q_{c_kc_{k+1}}^2\right]}_R\\\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\cdots\\{\left[q_{i_ki_{k+1}}^n\right]}_L-{\left[q_{c_kc_{k+1}}^n\right]}_R\end{array}\right\},\quad A\cdot q_i^c=0 A=[qikik+11]L[qckck+11]R[qikik+12]L[qckck+12]R[qikik+1n]L[qckck+1n]R,Aqic=0
通过SVD分解 解出 q i c q_i^c qic ,转为旋转矩阵 R i c R_i^c Ric,取逆得 R c i R_c^i Rci

2. 代码实践

bool InitialEXRotation::CalibrationExRotation(vector<pair<Vector3d, Vector3d>> corres, Quaterniond delta_q_imu, Matrix3d &calib_ric_result)
  • corres 两帧之间匹配的特征点 归一化坐标,通过对极约束,构建本质矩阵E,分解得到两帧之间的旋转矩阵, solveRelativeR(corres) 函数实现
  • delta_q_imu 为相邻两时刻 IMU预计分旋转量,四元数表示
  • 传入进来的 匹配的归一化坐标,IMU预计分旋转量,会先存在vector中,因为需要多对信息,构建超定方程矩阵 A
  • calib_ric_result 为待求的外参数 R c i R_c^i Rci
 Rc.push_back(solveRelativeR(corres));
 Rimu.push_back(delta_q_imu.toRotationMatrix());
 Rc_g.push_back(ric.inverse() * delta_q_imu * ric);
  • Rc 存储直接通过相机测量得到 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck
  • Rimu 存储传入的 delta_q_imu
  • Rc_g 对应上面的公式 (1),通过imu陀螺仪计算出来的 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck ,用于与相机测量得到 R c k + 1 c k R^{c_k}_{c_{k+1}} Rck+1ck,进行比较,若差异过大,使用简易的核函数弱化异常值

frame_count >= WINDOW_SIZE && ric_cov(1) > 0.25成立,则完成计算

bool InitialEXRotation::CalibrationExRotation(vector<pair<Vector3d, Vector3d>> corres, Quaterniond delta_q_imu, Matrix3d &calib_ric_result)
{
    frame_count++;
    Rc.push_back(solveRelativeR(corres));
    Rimu.push_back(delta_q_imu.toRotationMatrix());
    Rc_g.push_back(ric.inverse() * delta_q_imu * ric);

    Eigen::MatrixXd A(frame_count * 4, 4);
    A.setZero();
    int sum_ok = 0;
    for (int i = 1; i <= frame_count; i++)
    {
        Quaterniond r1(Rc[i]);
        Quaterniond r2(Rc_g[i]);

        double angular_distance = 180 / M_PI * r1.angularDistance(r2);
        ROS_DEBUG(
            "%d %f", i, angular_distance);

        double huber = angular_distance > 5.0 ? 5.0 / angular_distance : 1.0;
        ++sum_ok;
        Matrix4d L, R;

        double w = Quaterniond(Rc[i]).w();
        Vector3d q = Quaterniond(Rc[i]).vec();
        L.block<3, 3>(0, 0) = w * Matrix3d::Identity() + Utility::skewSymmetric(q);
        L.block<3, 1>(0, 3) = q;
        L.block<1, 3>(3, 0) = -q.transpose();
        L(3, 3) = w;

        Quaterniond R_ij(Rimu[i]);
        w = R_ij.w();
        q = R_ij.vec();
        R.block<3, 3>(0, 0) = w * Matrix3d::Identity() - Utility::skewSymmetric(q);
        R.block<3, 1>(0, 3) = q;
        R.block<1, 3>(3, 0) = -q.transpose();
        R(3, 3) = w;

        A.block<4, 4>((i - 1) * 4, 0) = huber * (L - R);
    }

    JacobiSVD<MatrixXd> svd(A, ComputeFullU | ComputeFullV);
    Matrix<double, 4, 1> x = svd.matrixV().col(3);
    Quaterniond estimated_R(x);
    ric = estimated_R.toRotationMatrix().inverse();
    //cout << svd.singularValues().transpose() << endl;
    //cout << ric << endl;
    Vector3d ric_cov;
    ric_cov = svd.singularValues().tail<3>();
    if (frame_count >= WINDOW_SIZE && ric_cov(1) > 0.25)
    {
        calib_ric_result = ric;
        return true;
    }
    else
        return false;
}

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值