vSLAM学习笔记——特征点三角化

记录最近学习vSLAM的一些理论和代码实现。


三角化的数学描述

  1. 考虑某路标点 y 在若干个关键帧 k = 1,…,n 中看到。
    y∈ R 4 R^4 R4,取齐次坐标。每次观测为 x k x_k xk=[ u k u_k uk, v k v_k vk,1] T ^T T,取归一化平面坐标(这样可以忽略掉内参)。

  2. 从World系到Camera系的投影矩阵 P k = [ R K , t k ] ∈ R 3 × 4 P_k=[R_K,t_k]∈R^{3×4} Pk=[RK,tk]R3×4

  3. 投影关系: ∀ k , λ k x k = P k y . . . . ( 1 ) \forall k,\lambda_k x_k= P_k y....(1) k,λkxk=Pky....(1)其中 λ k \lambda_k λk为观测点的深度值(未知)

  4. 根据上式第三行:
    λ k = P k , 3 T y . . . . ( 2 ) \lambda_k=P^T_{k,3} y....(2) λk=Pk,3Ty....(2) 其中 P k , 3 T P^T_{k,3} Pk,3T P k P_k Pk的第3行

  5. 将(2)代入(1)的前两行:
    u k P k , 3 T y = P k , 1 T y u_kP_{k,3}^T y=P_{k,1}^T y ukPk,3Ty=Pk,1Ty v k P k , 3 T y = P k , 2 T y v_kP_{k,3}^T y=P_{k,2}^T y vkPk,3Ty=Pk,2Ty

  6. 每次观测将提供两个这样的方程,视 y y y为未知量,并将 y y y移到等式一侧:
    [ u 1 P 1 , 3 T − P 1 , 1 T v 1 P 1 , 3 T − P 1 , 2 T ⋮ u n P n , 3 T − P n , 1 T u n P n , 3 T − P n , 2 T ] y = 0 → D y = 0 \begin{bmatrix} u_1P^T_{1,3}-P^T_{1,1} \\ v_1P^T_{1,3}-P^T_{1,2} \\ \vdots \\ u_nP^T_{n,3}-P^T_{n,1} \\ u_nP^T_{n,3}-P^T_{n,2} \end{bmatrix}y=0\rightarrow Dy=0 u1P1,3TP1,1Tv1P1,3TP1,2TunPn,3TPn,1TunPn,3TPn,2Ty=0Dy=0于是, y y y D D D零空间中的一个非零元素。

  7. 由于 D ∈ R 2 n × 4 D∈R^{2n×4} DR2n×4,在观测次数大于等于两次时,很可能 D D D满秩(因为容易得到4个线性无关组),无零空间。

  8. 寻找最小二乘解: m i n y ∣ ∣ D y ∣ ∣ 2 2 \underset{y}{min}||Dy||^2_2 yminDy22 s t . ∣ ∣ y ∣ ∣ = 1 st.||y||=1 st.y=1解法:对 D T D D^TD DTD进行SVD分解: D T D = V Σ V T D^TD=V\Sigma V^T DTD=VΣVT其中V为4×4的酉矩阵。或者还可以表示为: D T D = ∑ i = 1 4 σ i 2 u i u j T D^TD=\sum_{i=1}^4 \sigma^2_i u_iu_j^T DTD=i=14σi2uiujT
    其中 σ i \sigma_i σi为奇异值,且由大到小排列, u i u_i ui, u j u_j uj正交。

  9. 此时取 y = u 4 y=u_4 y=u4,那么该问题的目标函数值为 σ 4 \sigma_4 σ4
    10.判断该解有效性的条件: σ 4   < < σ 3 \sigma_4\ << \sigma_3 σ4 <<σ3。若该条件成立,则认为三角化有效,否则认为三角化无效。

代码实现

//
// Created by hyj on 18-11-11.
//
#include <iostream>
#include <vector>
#include <random>  
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Eigenvalues>

struct Pose
{
    Pose(Eigen::Matrix3d R, Eigen::Vector3d t):Rwc(R),qwc(R),twc(t) {};
    Eigen::Matrix3d Rwc;
    Eigen::Quaterniond qwc;
    Eigen::Vector3d twc;

    Eigen::Vector2d uv;    // 这帧图像观测到的特征坐标
};
int main()
{

    int poseNums = 10;
    double radius = 8;
    double fx = 1.;
    double fy = 1.;
    std::vector<Pose> camera_pose;
    for(int n = 0; n < poseNums; ++n ) {
        double theta = n * 2 * M_PI / ( poseNums * 4); // 1/4 圆弧
        // 绕 z轴 旋转
        Eigen::Matrix3d R;
        R = Eigen::AngleAxisd(theta, Eigen::Vector3d::UnitZ());
        Eigen::Vector3d t = Eigen::Vector3d(radius * cos(theta) - radius, radius * sin(theta), 1 * sin(2 * theta));
        camera_pose.push_back(Pose(R,t));
    }

    // 随机数生成 1 个 三维特征点
    std::default_random_engine generator;
    std::uniform_real_distribution<double> xy_rand(-4, 4.0);
    std::uniform_real_distribution<double> z_rand(8., 10.);
    double tx = xy_rand(generator);
    double ty = xy_rand(generator);
    double tz = z_rand(generator);

    Eigen::Vector3d Pw(tx, ty, tz);
    // 这个特征从第三帧相机开始被观测,i=3
    int start_frame_id = 3;
    int end_frame_id = poseNums;
    for (int i = start_frame_id; i < end_frame_id; ++i) {
        Eigen::Matrix3d Rcw = camera_pose[i].Rwc.transpose();
        Eigen::Vector3d Pc = Rcw * (Pw - camera_pose[i].twc);

        double x = Pc.x();
        double y = Pc.y();
        double z = Pc.z();

        camera_pose[i].uv = Eigen::Vector2d(x/z,y/z);
    }
    
    /// TODO::homework; 请完成三角化估计深度的代码
    // 遍历所有的观测数据,并三角化
    Eigen::Vector3d P_est;           // 结果保存到这个变量
    P_est.setZero();
    /* 对应上述公式部分 */
    auto loop_times = camera_pose.size() - start_frame_id;
    Eigen::MatrixXd D((loop_times) * 2, 4);
    for(int j = 0; j < loop_times; j++){
        Eigen::MatrixXd T_tmp(3, 4);
        T_tmp.block<3, 3>(0, 0) = camera_pose[j + start_frame_id].Rwc.transpose();
        T_tmp.block<3, 1>(0, 3) = -camera_pose[j + start_frame_id].Rwc.transpose() 
                                        * camera_pose[j + start_frame_id].twc;
        auto P_k1 = T_tmp.block<1, 4>(0, 0);
        auto P_k2 = T_tmp.block<1, 4>(1, 0);
        auto P_k3 = T_tmp.block<1, 4>(2, 0);

        D.block<1, 4>(2 * j, 0) = camera_pose[j + start_frame_id].uv[0] * P_k3 - P_k1;
        D.block<1, 4>(2 * j + 1, 0) = camera_pose[j + start_frame_id].uv[1] * P_k3 - P_k2;
    }
    Eigen::Matrix4d D_res = D.transpose() * D;
    Eigen::JacobiSVD<Eigen::Matrix4d> svd(D_res, Eigen::ComputeFullU | Eigen::computeFullV);  // 奇异值分解
    auto res_U = svd.matrixU();
    auto res_V = svd.matrixV();
    auto tmp = res_U.rightCols(1);
    std::cout << "res=" << tmp / tmp(3) << std::endl;
    /* END */
    // std::cout <<"D: \n"<< D.transpose()*D <<std::endl;
    // std::cout <<"ground truth: \n"<< Pw.transpose() <<std::endl;
    // std::cout <<"your result: \n"<< P_est.transpose() <<std::endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微小岳在线debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值