3.迭代最近点算法`ICP`及`SVD`求解

文章目录

    • `ICP`问题
    • SVD分解来求解`ICP`
    • `SVD`求解`ICP`实战


欢迎访问个人网络日志知行空间


ICP问题

迭代最近点算法Iterative Closet Points,ICP算法,是根据两组已知的三维空间点来估计采集两组点时传感器位姿发生的旋转和平移变化

在使用相机时,我们可以通过图像的特征匹配,找到两组三维点之间每个点的匹配关系。而在使用激光雷达时,由于点云是稀疏的,但是采集频率很高,通常认为两帧之间离的最近的点就是匹配点,因此称此算法为迭代最近点算法。

使用代数式描述为:

假设我们得到,

t 1 t_1 t1时刻传感器位置一采集到的一组三维空间点 P = { p 1 , . . . , p n } P=\{p_1,...,p_n\} P={p1,...,pn}

t 2 t_2 t2时刻传感器运动到位置二采集到的一组三维空间点 Q = { q 1 , . . . , q n } Q=\{q_1,...,q_n\} Q={q1,...,qn}

这里, P P P Q Q Q中的点是传感器坐标系中的表示,这两组点对应的是世界坐标系 { W } \{W\} {W}下相同的 n n n个点 { w 1 , . . . . , w 2 } \{w_1,....,w_2\} {w1,....,w2},且 p 1 , q 1 p_1,q_1 p1,q1对应 w 1 w_1 w1, p 2 , q 2 p_2,q_2 p2,q2对应 w 2 w_2 w2,依次类推。传感器坐标系是运动坐标系,从位置1到位置2姿态和位置发生的变化分别是 R , t R,t R,t,则可以得到如下关系,

p i = R q i + t , i = 1 , . . . , n p_i=Rq_i+t, i=1,...,n pi=Rqi+t,i=1,...,n

观察上式,我们有 n n n对匹配点就能得到 n n n个方程, R , t R,t R,t是待求量,一般情况下可以得到的匹配点对都会远远多于求解 R , t R,t R,t所需的量,所以得到的方程组都是超定的,只能求解其最小二乘解。

实际为了求解 R , t R,t R,t定义误差项,

对于第 i i i个点

e i = p i − ( R q i + t ) e_i=p_i-(Rq_i+t) ei=pi(Rqi+t)

对于所有 n n n个点,求解 R , t R,t R,t变成如下最优化问题,

min ⁡ R , t 1 2 ∑ i n ∣ ∣ p i − ( R q i + t ) ∣ ∣ 2 2 \min\limits_{R,t}\frac{1}{2}\sum\limits_{i}^n||p_i-(Rq_i+t)||_2^2 R,tmin21in∣∣pi(Rqi+t)22

要求解此最小二乘问题,有两种解法,一种使用SVD分解,一种使用非线性优化算法。

本文中,我们来一起学习SVD方法。

SVD分解来求解ICP

定义两组点的质心为 p c , q c p_c,q_c pc,qc

p c = 1 n ∑ i n p i p_c=\frac{1}{n}\sum\limits_i^np_i pc=n1inpi

q c = 1 n ∑ i n q i q_c=\frac{1}{n}\sum\limits_i^nq_i qc=n1inqi

将质心代入误差函数,

1 2 ∑ i n ∣ ∣ p i − ( R q i + t ) ∣ ∣ 2 2 = 1 2 ∑ i n ∣ ∣ p i − R q i − t − p c + R q c + p c − R q c ∣ ∣ 2 2 = 1 2 ∑ i n ∣ ∣ p i − p c − R ( q i − q c ) + ( p c − R q c − t ) ∣ ∣ 2 2 = 1 2 ∑ i n ( ∣ ∣ p i − p c − R ( q i − q c ) ∣ ∣ 2 2 + ∣ ∣ p c − R q c − t ∣ ∣ 2 2 + 2 ( p i − p − R ( q i − q ) ) T ( p c − R q c − t ) ) \begin{align}\frac{1}{2}\sum\limits_{i}^n||p_i-(Rq_i+t)||_2^2 = & \frac{1}{2}\sum\limits_{i}^n||p_i-Rq_i-t-p_c+Rq_c+p_c-Rq_c||_2^2\\ = & \frac{1}{2}\sum\limits_{i}^n||p_i-p_c-R(q_i-q_c)+(p_c-Rq_c-t)||_2^2\\ = &\frac{1}{2}\sum\limits_{i}^n(||p_i-p_c-R(q_i-q_c)||_2^2+||p_c-Rq_c-t||_2^2+2(p_i-p-R(q_i-q))^T(p_c-Rq_c-t))\end{align} 21in∣∣pi(Rqi+t)22===21in∣∣piRqitpc+Rqc+pcRqc2221in∣∣pipcR(qiqc)+(pcRqct)2221in(∣∣pipcR(qiqc)22+∣∣pcRqct22+2(pipR(qiq))T(pcRqct))

最后一项 p i − p − R ( q i − q ) p_i-p-R(q_i-q) pipR(qiq)求和后结果为零,因此:

min ⁡ R , t J = 1 2 ∑ i n ∣ ∣ p i − p c − R ( q i − q c ) ∣ ∣ 2 2 + ∣ ∣ p c − R q c − t ∣ ∣ 2 2 \min\limits_{R,t}J=\frac{1}{2}\sum\limits_{i}^n||p_i-p_c-R(q_i-q_c)||_2^2+||p_c-Rq_c-t||_2^2 R,tminJ=21in∣∣pipcR(qiqc)22+∣∣pcRqct22

观察变换后的优化问题形式,第一项只和 R R R有关,因此只要能求得 R R R,然后令第二项为零即可求得 t t t

如何求解旋转矩阵R呢?

取优化问题的第一项,

1 2 ∑ i n ∣ ∣ p i − p c − R ( q i − q c ) ∣ ∣ 2 2 \frac{1}{2}\sum\limits_{i}^n||p_i-p_c-R(q_i-q_c)||_2^2 21in∣∣pipcR(qiqc)22

令, u i = p i − p c v i = q i − q c \begin{matrix}u_i=p_i-p_c\\v_i=q_i-q_c\end{matrix} ui=pipcvi=qiqc

则,

1 2 ∑ i n ∣ ∣ p i − p c − R ( q i − q c ) ∣ ∣ 2 2 = 1 2 ∑ i n ∣ ∣ u i − R v i ∣ ∣ 2 2 \frac{1}{2}\sum\limits_{i}^n||p_i-p_c-R(q_i-q_c)||_2^2=\frac{1}{2}\sum\limits_{i}^n||u_i-Rv_i||^2_2 21in∣∣pipcR(qiqc)22=21in∣∣uiRvi22

根据二范数展开

1 2 ∑ i n ∣ ∣ u i − R v i ∣ ∣ 2 2 = 1 2 ∑ i n ( u i T u i + v i T R T R v i − 2 u i T R v i ) \frac{1}{2}\sum\limits_{i}^n||u_i-Rv_i||^2_2=\frac{1}{2}\sum\limits_{i}^n(u_i^Tu_i+v_i^TR^TRv_i-2u_i^TRv_i) 21in∣∣uiRvi22=21in(uiTui+viTRTRvi2uiTRvi)

展开后的三项中前两项都和 R R R无关,因此只需要关注最后一项,

− ∑ i n u i T R v i -\sum\limits_{i}^nu_i^TRv_i inuiTRvi

使其最小即可,上式中 u i u_i ui是三维空间中的点差,因此是 3 × 1 3\times 1 3×1的矩阵,根据矩阵迹的性质,

− ∑ i n u i T R v i = − ∑ i n t r ( R v i u i T ) = − t r ( R ∑ i n v i u i T ) -\sum\limits_{i}^nu_i^TRv_i=-\sum\limits_{i}^ntr(Rv_iu_i^T)=-tr(R\sum\limits_{i}^nv_iu_i^T) inuiTRvi=intr(RviuiT)=tr(RinviuiT)

引入一点数学知识,对于正定矩阵 A A T AA^T AAT,对于任何正交矩阵 B B B都有 t r ( A A T ) ≥ t r ( B A A T ) tr(AA^T)\ge tr(BAA^T) tr(AAT)tr(BAAT),关于此的证明可参考论文。

W = ∑ i n v i u i T W=\sum\limits_{i}^nv_iu_i^T W=inviuiT

W W W是一个 3 × 3 3\times3 3×3的矩阵,对 W W W进行SVD分解,

W = U Σ V T W=U\Sigma V^T W=UΣVT

U / V U/V U/V 3 × 3 3\times 3 3×3的正交矩阵, Σ \Sigma Σ 3 × 3 3\times 3 3×3的元素非负的对角矩阵。

R = V U T R=VU^T R=VUT就是以上问题的解,可根据 R R R再求出 t t t

关于为什么 R = V U T R=VU^T R=VUT就是以上问题的解,可结合 t r ( A A T ) ≥ t r ( B A A T ) tr(AA^T)\ge tr(BAA^T) tr(AAT)tr(BAAT)来看,若 R = V U T R=VU^T R=VUT

R W = V U T U Σ V T = V Σ V T RW=VU^TU\Sigma V^T=V\Sigma V^T RW=VUTUΣVT=VΣVT

R W RW RW是正定对称矩阵,根据 t r ( A A T ) ≥ t r ( B A A T ) tr(AA^T)\ge tr(BAA^T) tr(AAT)tr(BAAT),对于任何正交矩阵 B B B,都有 t r ( R W ) ≥ t r ( B R W ) tr(RW)\ge tr(BRW) tr(RW)tr(BRW),而旋转矩阵一定是正交矩阵,因此 R = V U T R=VU^T R=VUT可以使 t r ( R W ) tr(RW) tr(RW)取最大值。若求得的 R R R的行列式为负,就取 − R -R R作为最后的旋转矩阵。关于此的详细说明可参考论文。


SVD求解ICP实战

代码参考自视觉SLAM十四讲。

示例使用的RGB-D图像,先使用RGB图像找到两个位置图像中匹配点,再计算匹配点的三维坐标,然后利用SVD来求解Rt

关键的两个函数,一个是特征点匹配,

void ICPSolver::findMatchFeatures(cv::Mat &image1,
                                    cv::Mat &image2,
                                    std::vector<cv::KeyPoint> &kpt1,
                                    std::vector<cv::KeyPoint> &kpt2,
                                    std::vector<cv::DMatch> &matches)
{
    cv::Mat desc1, desc2;
    cv::Ptr<cv::FeatureDetector> detector = cv::ORB::create();
    detector->detectAndCompute(image1, cv::Mat(), kpt1, desc1);
    detector->detectAndCompute(image2, cv::Mat(), kpt2, desc2);

    cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");
    std::vector<cv::DMatch> match;
    matcher->match(desc1, desc2, match);

    double min_dist = 10000, max_dist = 0;
    for(auto &m : match) {
        auto d = m.distance;
        if(d < min_dist) min_dist = d;
        if(d > max_dist) max_dist = d;
    }
    std::cout << "Max Distance: " << max_dist << std::endl;
    std::cout << "Min Distance: " << min_dist << std::endl;

    for(auto &m : match) {
        if(m.distance < 2 * min_dist || m.distance < 30)
            continue;
        matches.push_back(m);
    }
}

SVD求解Rt的代码,

void ICPSolver::svdSolver(std::vector<cv::Point3f> &pts1,
                std::vector<cv::Point3f> &pts2,
                cv::Mat &R, cv::Mat &t)
{
    cv::Point3f pc1, pc2; // center point
    int N = pts1.size();
    for(int i = 0; i < N; i++) {
        pc1 += pts1[i];
        pc2 += pts2[i];
    }
    pc1 = cv::Point3f(cv::Vec3f(pc1) / N);
    pc2 = cv::Point3f(cv::Vec3f(pc2) / N);

    cv::Point3f u, v;
    Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    for(int i = 0; i < N; i++) {
        u = pts1[i] - pc1;
        v = pts2[i] - pc2;

        W += Eigen::Vector3d(u.x, u.y, u.z) * Eigen::Vector3d(v.x, v.y, v.z).transpose();
    }

    Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
    Eigen::Matrix3d U = svd.matrixU();
    Eigen::Matrix3d V = svd.matrixV();

    std::cout << "W=" << W << std::endl;
    std::cout << "U=" << U << std::endl;
    std::cout << "V=" << V << std::endl;

    Eigen::Matrix3d R_ = U * (V.transpose());
    std::cout <<"det(R_)=" << R_.determinant() << std::endl;
    if(R_.determinant() < 0) {
        R_ = -R_;
    }
    std::cout <<"R_R_^T=" << R_ * R_.transpose() << std::endl;

    Eigen::Vector3d t_ = Eigen::Vector3d(pc1.x, pc1.y, pc1.z) 
                            - R_ * Eigen::Vector3d(pc2.x, pc2.y, pc2.z);
    std::cout << "t_ = " << t_ << std::endl;

    R = (cv::Mat_<double>(3,  3) << R_(0, 0), R_(0, 1), R_(0, 2),
                                    R_(1, 0), R_(1, 1), R_(1, 2),
                                    R_(2, 0), R_(2, 1), R_(2, 2));
    t = (cv::Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
}

代码运行的结果可以发现 d e t ( R ) = 1 , R R T ≈ I det(R)=1,RR^T\approx I det(R)=1,RRTI

# det(R_)=1
# R_R_^T= [[1 -2.13805e-16    2.498e-16]
#          [-2.13805e-16            1  4.16334e-16],
#         [2.498e-16  4.22405e-16            1]]


完整可运行代码见https://gitee.com/lx_r/basic_cplusplus_examples

  • 1.https://www.math.pku.edu.cn/teachers/yaoy/Fall2011/arun.pdf
  • 2.视觉SLAM十四讲第二版p196-198

欢迎访问个人网络日志知行空间


你可能感兴趣的:(3D目标检测,算法)