zoukankan      html  css  js  c++  java
  • 视觉SLAM:VIO的误差和误差雅可比矩阵

    1.两个相机之间的非线性优化

    观测相机方程关于相机位姿与特征点的雅可比矩阵:

    1.1 位姿:

    1.2 3D特征点

    • fx,fy,fz为相机内参
    • X',Y',Z'为3D点在相机坐标系下的坐标
    • 该误差是观测值减去预测值,反过来,预测值减观测值时,去掉或加上负号即可
    • 姿态定义为先平移后旋转,如果定义为先旋转后平移,将该矩阵的前3列与后3列对调即可

    2.vio滑动窗口的BA优化

    1.相机:

    相机误差仍然为重投影误差:
    优化是在机体坐标系下完成,也就是imu系,所以多了一个相机到机体坐标的外参

    根据链式法则,可以分两步走,第一步,误差对(f_{cj})求导,最后再分别相乘即可

    2.1 误差对(f_{cj})求导:

    2.2 (f_{cj})对逆深度的求导:

    2.3 (f_{cj})对各时刻状态量的求导:

    • 对i时刻的位移求导:

      对i时刻的角度增量求导:

    • 对j时刻的位移求导;

      对j时刻的角度增量求导

    2.4 (f_{cj})对imu和相机的外参求导:

    • 对位移求导:
    • 对角度增量求导:
      分为两部分求导: (f_{cj} = f_{cj}^{1} + f_{cj}^{2})
      第一部分:

      第二部分:

      最后相加即可。

    注意:最后别忘了分别乘上误差对(f_{cj})的求导

    2.5 程序示例:

    double inv_dep_i = verticies_[0]->Parameters()[0];
    
        VecX param_i = verticies_[1]->Parameters();  //i时刻位姿
        Qd Qi(param_i[6], param_i[3], param_i[4], param_i[5]); //姿态
        Vec3 Pi = param_i.head<3>();  //位移
    
        VecX param_j = verticies_[2]->Parameters();  //j时刻位姿
        Qd Qj(param_j[6], param_j[3], param_j[4], param_j[5]);
        Vec3 Pj = param_j.head<3>();
    
        VecX param_ext = verticies_[3]->Parameters();
        Qd qic(param_ext[6], param_ext[3], param_ext[4], param_ext[5]);
        Vec3 tic = param_ext.head<3>()
    
        Vec3 pts_camera_i = pts_i_ / inv_dep_i;  
        Vec3 pts_imu_i = qic * pts_camera_i + tic;  
        Vec3 pts_w = Qi * pts_imu_i + Pi;
        Vec3 pts_imu_j = Qj.inverse() * (pts_w - Pj);
        Vec3 pts_camera_j = qic.inverse() * (pts_imu_j - tic);
    
        double dep_j = pts_camera_j.z();   
    
        Mat33 Ri = Qi.toRotationMatrix();
        Mat33 Rj = Qj.toRotationMatrix();
        Mat33 ric = qic.toRotationMatrix();
        Mat23 reduce(2, 3);        //误差对f_cj求导
        reduce << 1. / dep_j, 0, -pts_camera_j(0) / (dep_j * dep_j),
            0, 1. / dep_j, -pts_camera_j(1) / (dep_j * dep_j);
    //    reduce = information_ * reduce;
    
        Eigen::Matrix<double, 2, 6> jacobian_pose_i;
        Eigen::Matrix<double, 3, 6> jaco_i;
        jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose(); //位移求导
        jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * - 
    Sophus::SO3d::hat(pts_imu_i); //角度增量求导
        jacobian_pose_i.leftCols<6>() = reduce * jaco_i;
    
        Eigen::Matrix<double, 2, 6> jacobian_pose_j;
        Eigen::Matrix<double, 3, 6> jaco_j;
        jaco_j.leftCols<3>() = ric.transpose() * -Rj.transpose();
        jaco_j.rightCols<3>() = ric.transpose() * Sophus::SO3d::hat(pts_imu_j);
        jacobian_pose_j.leftCols<6>() = reduce * jaco_j;
    
        Eigen::Vector2d jacobian_feature;
        //逆深度求导
        jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i_ * -1.0 / (inv_dep_i * inv_dep_i);
    
        //IMU和相机外参求导
        Eigen::Matrix<double, 2, 6> jacobian_ex_pose;
        Eigen::Matrix<double, 3, 6> jaco_ex;
        jaco_ex.leftCols<3>() = ric.transpose() * (Rj.transpose() * Ri - Eigen::Matrix3d::Identity());
        Eigen::Matrix3d tmp_r = ric.transpose() * Rj.transpose() * Ri * ric;
        jaco_ex.rightCols<3>() = -tmp_r * Utility::skewSymmetric(pts_camera_i) + Utility::skewSymmetric(tmp_r * pts_camera_i) + Utility::skewSymmetric(ric.transpose() * (Rj.transpose() * (Ri * tic + Pi - Pj) - tic));
        
        jacobian_ex_pose.leftCols<6>() = reduce * jaco_ex;
    
        jacobians_[0] = jacobian_feature;   //2行1列
        jacobians_[1] = jacobian_pose_i;
        jacobians_[2] = jacobian_pose_j;
        jacobians_[3] = jacobian_ex_pose;
    

    2.IMU:

    IMU的真实值为 w,a, 测量值为(w^{~}, a^{~}),则有:

    其中: b为bias随机游走误差,n为白噪声。

    预积分:
    预积分仅仅与imu测量值有关,将一段时间的imu数据直接积分起来就得到了与积分量

    则j时刻的PVQ积分积分方程为:

    其中p为位移,v为速度,q为姿态,b为bias噪声

    2.1 IMU的与积分误差:


    其中,位移,速度,bias噪声的误差都是直接相减,第二项是关于四元数的旋转误差,后缀xyz代表取四元数的虚部(x, y, z)组成的三维向量。

    void EdgeImu::ComputeResidual() {
        VecX param_0 = verticies_[0]->Parameters();
        Qd qi(param_0[6], param_0[3], param_0[4], param_0[5]);
        Vec3 pi = param_0.head<3>();
        SO3d ri(qi);
        SO3d ri_inv = ri.inverse();
    
        VecX param_1 = verticies_[1]->Parameters();
        Vec3 vi = param_1.head<3>();
        Vec3 bai = param_1.segment(3, 3);
        Vec3 bgi = param_1.tail<3>();
    
        VecX param_2 = verticies_[2]->Parameters();
        Qd qj(param_2[6], param_2[3], param_2[4], param_2[5]);
        Vec3 pj = param_2.head<3>();
    
        VecX param_3 = verticies_[3]->Parameters();
        Vec3 vj = param_3.head<3>();
        Vec3 baj = param_3.segment(3, 3);
        Vec3 bgj = param_3.tail<3>();
        SO3d rj(qj);
    
        double dt = pre_integration_->GetSumDt();
        double dt2 = dt * dt;
        SO3d dr;
        Vec3 dv;
        Vec3 dp;
        pre_integration_->GetDeltaRVP(dr, dv, dp);  //获取预积分值
    
        SO3d res_r = dr.inverse() * ri_inv * rj;
        residual_.block<3, 1>(0, 0) = SO3d::log(res_r);
        residual_.block<3, 1>(3, 0) = ri_inv * (vj - vi - gravity_ * dt) - dv;
        residual_.block<3, 1>(6, 0) = ri_inv * (pj - pi - vi * dt - 0.5 * gravity_ * dt2) - dp;
        residual_.block<3, 1>(9, 0) = baj - bai;
        residual_.block<3, 1>(12, 0) = bgj - bgi;
    }
    
    2.2 IMU的误差雅可比矩阵:

    基于泰勒展开的误差传递(EKF):
    非线性系统(x_{k} = f(x_{k-1}, u_{k-1})) 的状态误差的线性递推关系为:

    其中,F是状态量(x_{k})对状态量(x_{k-1})的雅可比矩阵,G是状态量(x_{k}对输入量)u_{k-1}$的雅可比矩阵。
    IMU的误差传递方程为:


    其中的系数为:

    • 速度预积分对各状态量的雅可比,为F的第三行,分别是:位移预积分,旋转预积分,速度预积分,陀螺仪bias噪声,加速度bias噪声
      f33: 速度预积分量对上一时刻速度预积分量的雅可比,为I
      f32: 速度预积分量对角度预积分量的雅可比
      f35: 速度预积分量对k时刻角速度bias噪声的雅可比
      f22: 前一时刻的旋转误差如何影响当前旋转误差
    2.3 IMU相对于优化变量的雅可比矩阵:

    在求解非线性方程式,我们需要知道IMU误差对两个关键帧i,j的状态p,q,v,(b^{a}, b^{g})的雅可比

    • 对i时刻的位移:
    • 对i时刻的旋转:
    • 对i时刻的速度:
    • 对i时刻的加速度bias:
      IMU角度误差相对于优化变量的雅可比
    • 角度误差对i时刻的姿态求导:

      其中[]L 和[]R 为四元数转为左/右旋转矩阵的算子
    • 角度误差对j时刻姿态求导
    • 角度误差对i时刻陀螺仪bias噪声求导
    void EdgeImu::ComputeJacobians() {
    
        VecX param_0 = verticies_[0]->Parameters();
        Qd Qi(param_0[6], param_0[3], param_0[4], param_0[5]);
        Vec3 Pi = param_0.head<3>();
    
        VecX param_1 = verticies_[1]->Parameters();
        Vec3 Vi = param_1.head<3>();
        Vec3 Bai = param_1.segment(3, 3);
        Vec3 Bgi = param_1.tail<3>();
    
        VecX param_2 = verticies_[2]->Parameters();
        Qd Qj(param_2[6], param_2[3], param_2[4], param_2[5]);
        Vec3 Pj = param_2.head<3>();
    
        VecX param_3 = verticies_[3]->Parameters();
        Vec3 Vj = param_3.head<3>();
        Vec3 Baj = param_3.segment(3, 3);
        Vec3 Bgj = param_3.tail<3>();
    
        double sum_dt = pre_integration_->sum_dt;
        Eigen::Matrix3d dp_dba = pre_integration_->jacobian.template block<3, 3>(O_P, O_BA);
        Eigen::Matrix3d dp_dbg = pre_integration_->jacobian.template block<3, 3>(O_P, O_BG);
    
        Eigen::Matrix3d dq_dbg = pre_integration_->jacobian.template block<3, 3>(O_R, O_BG);
    
        Eigen::Matrix3d dv_dba = pre_integration_->jacobian.template block<3, 3>(O_V, O_BA);
        Eigen::Matrix3d dv_dbg = pre_integration_->jacobian.template block<3, 3>(O_V, O_BG);
    
        if (pre_integration_->jacobian.maxCoeff() > 1e8 || pre_integration_->jacobian.minCoeff() < -1e8)
        {
            // ROS_WARN("numerical unstable in preintegration");
        }
    
    //    if (jacobians[0])
        {
            Eigen::Matrix<double, 15, 6, Eigen::RowMajor> jacobian_pose_i;
            jacobian_pose_i.setZero();
    
            jacobian_pose_i.block<3, 3>(O_P, O_P) = -Qi.inverse().toRotationMatrix();
            jacobian_pose_i.block<3, 3>(O_P, O_R) = Utility::skewSymmetric(Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt));
    
            Eigen::Quaterniond corrected_delta_q = pre_integration_->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration_->linearized_bg));
            jacobian_pose_i.block<3, 3>(O_R, O_R) = -(Utility::Qleft(Qj.inverse() * Qi) * Utility::Qright(corrected_delta_q)).bottomRightCorner<3, 3>();
            jacobian_pose_i.block<3, 3>(O_V, O_R) = Utility::skewSymmetric(Qi.inverse() * (G * sum_dt + Vj - Vi));
    
            if (jacobian_pose_i.maxCoeff() > 1e8 || jacobian_pose_i.minCoeff() < -1e8)
            {
            //     ROS_WARN("numerical unstable in preintegration");
            }
            jacobians_[0] = jacobian_pose_i;
        }
    //    if (jacobians[1])
        {
            Eigen::Matrix<double, 15, 9, Eigen::RowMajor> jacobian_speedbias_i;
            jacobian_speedbias_i.setZero();
            jacobian_speedbias_i.block<3, 3>(O_P, O_V - O_V) = -Qi.inverse().toRotationMatrix() * sum_dt;
            jacobian_speedbias_i.block<3, 3>(O_P, O_BA - O_V) = -dp_dba;
            jacobian_speedbias_i.block<3, 3>(O_P, O_BG - O_V) = -dp_dbg;
    
            jacobian_speedbias_i.block<3, 3>(O_R, O_BG - O_V) = -Utility::Qleft(Qj.inverse() * Qi * pre_integration_->delta_q).bottomRightCorner<3, 3>() * dq_dbg;
    
            jacobian_speedbias_i.block<3, 3>(O_V, O_V - O_V) = -Qi.inverse().toRotationMatrix();
            jacobian_speedbias_i.block<3, 3>(O_V, O_BA - O_V) = -dv_dba;
            jacobian_speedbias_i.block<3, 3>(O_V, O_BG - O_V) = -dv_dbg;
    
            jacobian_speedbias_i.block<3, 3>(O_BA, O_BA - O_V) = -Eigen::Matrix3d::Identity();
    
            jacobian_speedbias_i.block<3, 3>(O_BG, O_BG - O_V) = -Eigen::Matrix3d::Identity();
    
            jacobians_[1] = jacobian_speedbias_i;
        }
    //    if (jacobians[2])
        {
            Eigen::Matrix<double, 15, 6, Eigen::RowMajor> jacobian_pose_j;
            jacobian_pose_j.setZero();
    
            jacobian_pose_j.block<3, 3>(O_P, O_P) = Qi.inverse().toRotationMatrix();
            Eigen::Quaterniond corrected_delta_q = pre_integration_->delta_q * Utility::deltaQ(dq_dbg * (Bgi - pre_integration_->linearized_bg));
            jacobian_pose_j.block<3, 3>(O_R, O_R) = Utility::Qleft(corrected_delta_q.inverse() * Qi.inverse() * Qj).bottomRightCorner<3, 3>();
    
            jacobians_[2] = jacobian_pose_j;
    
        }
    //    if (jacobians[3])
        {
            Eigen::Matrix<double, 15, 9, Eigen::RowMajor> jacobian_speedbias_j;
            jacobian_speedbias_j.setZero();
    
            jacobian_speedbias_j.block<3, 3>(O_V, O_V - O_V) = Qi.inverse().toRotationMatrix();
    
            jacobian_speedbias_j.block<3, 3>(O_BA, O_BA - O_V) = Eigen::Matrix3d::Identity();
    
            jacobian_speedbias_j.block<3, 3>(O_BG, O_BG - O_V) = Eigen::Matrix3d::Identity();
    
            jacobians_[3] = jacobian_speedbias_j;
    
        }
    
    
    }
    
  • 相关阅读:
    [C#][控件]WebBrowser 使用范例
    [java]经验集
    [html][easyui]DataGrid 绑定
    [转]jQuery 读取 xml
    [转][html]大文件下载
    [转][javascript]判断传入参数
    [html][javascript] Cookie
    [bat]批处理删默认共享和清理垃圾
    [转]JavaScript RegExp 对象参考手册
    5个编程问题(1小时解决)
  • 原文地址:https://www.cnblogs.com/penuel/p/14661175.html
Copyright © 2011-2022 走看看