VINS-Mono-紧耦合VIO中视觉残差的优化原理和代码解析

前言

承接上一篇博客,此篇分析残差:
∑ ( l , j ) ∈ C ρ ( ∥ r C ( z ^ l c j , χ ) ∥ P l c j 2 \sum_{(l,j)\in C}\rho(\left\| \mathbf{r}_{C}(\hat{\mathbf{z}}_{l}^{c_{j}},\boldsymbol{\chi}) \right\|^{2}_{\mathbf{P}_{l}^{c_{j}}} (l,j)Cρ(rC(z^lcj,χ)Plcj2

原理

定义残差方程

视觉残差即重投影误差,但VINS-Mono中所使用的重投影误差模型与传统的针孔相机模型不同,其在一个单位球面上定义残差,因为这种残差定义方式可以满足几乎所有的相机模型(广角、鱼眼…).我们考虑第 l l l个特征第一次被观测到是在第 i i i张图像中,则特征在第 j j j张图像中被观察到时的残差定义如下:
r C ( z ^ l c j , χ ) = [ b 1 , b 2 ] T ⋅ ( p l c j ∥ p l c j ∥ − p ˉ ^ l c j ) (1) \mathbf{r}_{C}(\hat{\mathbf{z}}_{l}^{c_{j}},\boldsymbol{\chi}) = [\mathbf{b}_{1},\mathbf{b}_{2}]^{T}\cdot (\frac{\mathbf{p}_{l}^{c_{j}}}{\left\| \mathbf{p}_{l}^{c_{j}} \right\|} - \hat{\bar{\mathbf{p}}}_{l}^{c_{j}}) \tag{1} rC(z^lcj,χ)=[b1,b2]T(plcjplcjpˉ^lcj)(1)
式中 p ˉ ^ l c j = π c − 1 ( [ u ^ l c j v ^ l c j ] ) \hat{\bar{\mathbf{p}}}_{l}^{c_{j}} = \pi_{c}^{-1}(\begin{bmatrix}\hat{u}_{l}^{c_{j}} \\ \hat{v}_{l}^{c_{j}}\end{bmatrix}) pˉ^lcj=πc1([u^lcjv^lcj]) 为第 l l l个特征在第 j j j张图像中的像素通过相机内参反向投影到单位球面上的三维坐标,式中 p l c j = R b c ( R w b j ( R b i w ( R c b 1 λ l p ˉ l c i + p c b ) + p b i w − p b j w ) − p c b ) \mathbf{p}_{l}^{c_{j}}=\mathbf{R}_{b}^{c}(\mathbf{R}_{w}^{b_{j}}(\mathbf{R}_{b_{i}}^{w}(\mathbf{R}_{c}^{b}\frac{1}{\lambda_{l}}\bar{\mathbf{p}}_{l}^{c_{i}}+\mathbf{p}_{c}^{b})+\mathbf{p}_{b_{i}}^{w}-\mathbf{p}_{b_{j}}^{w})-\mathbf{p}_{c}^{b}) plcj=Rbc(Rwbj(Rbiw(Rcbλl1pˉlci+pcb)+pbiwpbjw)pcb) 为通过第 l l l个特征在第 i i i张图像中的像素推算出的该特征在第j张图像中的估计值, [ b 1 , b 2 ] T [\mathbf{b}_{1},\mathbf{b}_{2}]^{T} [b1,b2]T p ˉ ^ l c j \hat{\bar{\mathbf{p}}}_{l}^{c_{j}} pˉ^lcj 向量的流平面上一对任意的互相垂直的基向量(这样做的原因是因为视觉残差的自由度是2,所以此处通过投影将三维残差限制为了2维).

计算雅克比矩阵

从上述残差方程可以看出参与优化的状态变量有 ( p b i w , q b i w ) , ( p b j w , q b j w ) , ( p c b , q c b ) (\mathbf{p}_{b_{i}}^{w}, \mathbf{q}_{b_{i}}^{w}), (\mathbf{p}_{b_{j}}^{w}, \mathbf{q}_{b_{j}}^{w}), (\mathbf{p}_{c}^{b},\mathbf{q}_{c}^{b}) (pbiw,qbiw),(pbjw,qbjw),(pcb,qcb)

  • 结果
    在这里插入图片描述

  • 推导
    在这里插入图片描述
    在这里插入图片描述

代码解析

bool ProjectionFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{
    TicToc tic_toc;
   // 第i帧位姿
    Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);
    Eigen::Quaterniond Qi(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);
	// 第j帧位姿
    Eigen::Vector3d Pj(parameters[1][0], parameters[1][1], parameters[1][2]);
    Eigen::Quaterniond Qj(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);
	// imu与相机之间的相对位姿关系
    Eigen::Vector3d tic(parameters[2][0], parameters[2][1], parameters[2][2]);
    Eigen::Quaterniond qic(parameters[2][6], parameters[2][3], parameters[2][4], parameters[2][5]);
	// 第i特征的逆深度
    double inv_dep_i = parameters[3][0];

    Eigen::Vector3d pts_camera_i = pts_i / inv_dep_i;//特征点在第i帧相机坐标系下坐标
    Eigen::Vector3d pts_imu_i = qic * pts_camera_i + tic;//特征点在第i帧imu坐标系下坐标
    Eigen::Vector3d pts_w = Qi * pts_imu_i + Pi;// 特征点在世界坐标系的坐标
    Eigen::Vector3d pts_imu_j = Qj.inverse() * (pts_w - Pj);//特征点在第j帧imu坐标系下坐标
    Eigen::Vector3d pts_camera_j = qic.inverse() * (pts_imu_j - tic);//特征点在第j帧图像坐标系下坐标
    Eigen::Map<Eigen::Vector2d> residual(residuals);

// 计算残差值,公式(1)
#ifdef UNIT_SPHERE_ERROR 
    residual =  tangent_base * (pts_camera_j.normalized() - pts_j.normalized());
#else
    double dep_j = pts_camera_j.z();
    residual = (pts_camera_j / dep_j).head<2>() - pts_j.head<2>();
#endif

    residual = sqrt_info * residual;

    if (jacobians)
    {
        Eigen::Matrix3d Ri = Qi.toRotationMatrix();
        Eigen::Matrix3d Rj = Qj.toRotationMatrix();
        Eigen::Matrix3d ric = qic.toRotationMatrix();
        Eigen::Matrix<double, 2, 3> reduce(2, 3);
#ifdef UNIT_SPHERE_ERROR
        double norm = pts_camera_j.norm();
        Eigen::Matrix3d norm_jaco;
        double x1, x2, x3;
        x1 = pts_camera_j(0);
        x2 = pts_camera_j(1);
        x3 = pts_camera_j(2);
        norm_jaco << 1.0 / norm - x1 * x1 / pow(norm, 3), - x1 * x2 / pow(norm, 3),            - x1 * x3 / pow(norm, 3),
                     - x1 * x2 / pow(norm, 3),            1.0 / norm - x2 * x2 / pow(norm, 3), - x2 * x3 / pow(norm, 3),
                     - x1 * x3 / pow(norm, 3),            - x2 * x3 / pow(norm, 3),            1.0 / norm - x3 * x3 / pow(norm, 3);
        reduce = tangent_base * norm_jaco;
#else
        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);
#endif
        reduce = sqrt_info * reduce;
		// 计算残差方程相对于第i帧imu位姿的雅克比矩阵
        if (jacobians[0])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);

            Eigen::Matrix<double, 3, 6> jaco_i;
            jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose();
            jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * -Utility::skewSymmetric(pts_imu_i);

            jacobian_pose_i.leftCols<6>() = reduce * jaco_i;
            jacobian_pose_i.rightCols<1>().setZero();
        }
		// 计算残差方程相对于第j帧imu位姿的雅克比矩阵
        if (jacobians[1])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_j(jacobians[1]);

            Eigen::Matrix<double, 3, 6> jaco_j;
            jaco_j.leftCols<3>() = ric.transpose() * -Rj.transpose();
            jaco_j.rightCols<3>() = ric.transpose() * Utility::skewSymmetric(pts_imu_j);

            jacobian_pose_j.leftCols<6>() = reduce * jaco_j;
            jacobian_pose_j.rightCols<1>().setZero();
        }
        // 计算残差方程相对于imu与相机外参的雅克比矩阵
        if (jacobians[2])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_ex_pose(jacobians[2]);
            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;
            jacobian_ex_pose.rightCols<1>().setZero();
        }
        // 计算残差方程相对于特征点逆深度状态的雅克比矩阵
        if (jacobians[3])
        {
            Eigen::Map<Eigen::Vector2d> jacobian_feature(jacobians[3]);
#if 1
            jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i * -1.0 / (inv_dep_i * inv_dep_i);
#else
            jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i;
#endif
        }
    }
    sum_t += tic_toc.toc();

    return true;
}

参考

https://github.com/StevenCui/VIO-Doc
https://github.com/HKUST-Aerial-Robotics/VINS-Mono

注释代码路径

https://github.com/chennuo0125-HIT/vins-note

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值