ICP学习笔记

来自slam14讲

ICP(Iterative Closest Point,ICP)

求解3D3D 3维点之间的对应关系。
P = { p 1 , p 2 , . . . , p 3 } : 一 组 3 D 点 P=\{p_1,p_2,...,p_3\}:一组3D点 P={p1,p2,...,p3}:3D
P ′ = { p 1 ′ , p 2 ′ , . . . , p 3 , } : 与 P 对 应 的 一 组 3 D 点 P'=\{p_1',p'_2,...,p_3,\}:与P对应的一组3D点 P={p1,p2,...,p3,}:P3D
求解 P 与 P ′ P与P' PP之间的对应关系即求解 R , T R,T R,T:
P i ′ = R i P i + T P'_i=R_iP_i+T Pi=RiPi+T
两种方法:
(1):构建线性最优化问题,SVD求解
(2):使用非线性方法,BA求解方程

构建线性最优化问题,SVD求解

  1. 构建最小二乘
    m i n R , t = 1 2 ∑ i = 1 n ∣ ∣ p i − ( R p i ′ + t ) ∣ ∣ 2 2 min_{R,t}=\frac{1}{2}\sum^n_{i=1}||p_i-(Rp_i'+t)||^2_2 minR,t=21i=1npi(Rpi+t)22

  2. 化简:
    p = 1 n ∑ i = 1 n ( p i ) p=\frac{1}{n}\sum^n_{i=1}(p_i) p=n1i=1n(pi)
    p ′ = 1 n ∑ i = 1 n ( p i ′ ) p'=\frac{1}{n}\sum_{i=1}^n(p_i') p=n1i=1n(pi)
    ∑ i = 1 n ∣ ∣ p i − ( R p i ′ + t ) ∣ ∣ 2 2 = ∑ i = 1 n ∣ ∣ p i − R p i − t − p + R p ′ + p − R p ′ ∣ ∣ 2 = ∑ i = 1 n ∣ ∣ ( ( p i − p ) − R ( p i − p ′ ) ) + ( p − R p ′ − t ) ∣ ∣ 2 = ∑ i = 1 n ( ∣ ∣ ( p i − p ) − R ( p i − p ′ ) ∣ ∣ 2 + ∣ ∣ ( p − R p ′ − t ) ∣ ∣ 2 + 2 ( p i − p − R ( p i − p ′ ) T ( p − R p ′ − t ) ) = ∑ i = 1 n ( ∣ ∣ ( p i − p ) − R ( p i − p ′ ) ∣ ∣ 2 + ∣ ∣ ( p − R p ′ − t ) ∣ ∣ 2 s t : ∑ i = 1 n ( p i − p − R ( p i − p ′ ) T ( p − R p ′ − t ) ) ∑ i = 1 n ( p i − 1 n ∑ i = 1 n ( p i ) − R ( p i ′ − 1 n ∑ i = 1 n ( p i ′ ) ) ) = 0 \sum^n_{i=1}||p_i-(Rp_i'+t)||^2_2=\sum^n_{i=1}||p_i-Rp_i-t-p+Rp'+p-Rp'||^2\\ =\sum^n_{i=1}||((p_i-p)-R(p_i-p'))+(p-Rp'-t)||^2\\ =\sum^n_{i=1}(||(p_i-p)-R(p_i-p')||^2+||(p-Rp'-t)||^2+2(p_i-p-R(p_i-p')^T(p-Rp'-t))\\ =\sum^n_{i=1}(||(p_i-p)-R(p_i-p')||^2+||(p-Rp'-t)||^2\\ st:\\ \sum^n_{i=1}(p_i-p-R(p_i-p')^T(p-Rp'-t))\\ \sum^n_{i=1}(p_i-\frac{1}{n}\sum^n_{i=1}(p_i)-R(p_i'-\frac{1}{n}\sum_{i=1}^n(p_i')))\\ =0 i=1npi(Rpi+t)22=i=1npiRpitp+Rp+pRp2=i=1n((pip)R(pip))+(pRpt)2=i=1n((pip)R(pip)2+(pRpt)2+2(pipR(pip)T(pRpt))=i=1n((pip)R(pip)2+(pRpt)2st:i=1n(pipR(pip)T(pRpt))i=1n(pin1i=1n(pi)R(pin1i=1n(pi)))=0

  3. 得到求解步骤如下:
    (1):计算质心位置
    (2):求解旋转矩阵的优化解:
    R ∗ = a r g m i n R 1 2 ∑ i = 1 n ∣ ∣ q i − R q i ′ ∣ ∣ 2 R*=argmin_R\frac{1}{2}\sum_{i=1}^n||q_i-Rq_i'||^2 R=argminR21i=1nqiRqi2
    化简:
    a r g m i n R ∑ i = 1 n ∣ ∣ q i − R q i ′ ∣ ∣ 2 = a r g m i n R ∑ i = 1 n ( q i T q i + q ′ i T R T R q i − 2 q i T R q i ′ ) 因 为 : q i T q i 与 R 无 关 的 常 数 项 ; R T R = I , q ′ i T R T R q i 也 与 R 无 关    ⟺    a r g m i n R ∑ i = 1 n ( − q i T R q i ′ ) a r g m i n R ∑ i = 1 n ( − q i T R q i ′ ) = a r g m i n R [ x y z ] [ R 11 R 12 R 13 R 21 R 22 R 23 R 31 R 32 R 33 ] [ x ′ y ′ z ′ ] = a r g m i n R − ( ( R 11 + R 12 + R 13 ) x x ′ + ( R 21 + R 22 + R 23 ) y y ′ + ( R 31 + R 32 + R 33 ) ) z z ′ = − t r ( R ∑ i = 1 n q i ′ q i ′ T ) argmin_R\sum_{i=1}^n||q_i-Rq_i'||^2=argmin_R\sum_{i=1}^n(q_i^Tq_i+{q'}_i^TR^TRq_i-2q^T_iRq'_i)\\ 因为:\\ q_i^Tq_i与R无关的常数项;R^TR=I,{q'}_i^TR^TRq_i也与R无关\\ \iff argmin_R\sum_{i=1}^n(-q^T_iRq'_i)\\ \\argmin_R\sum_{i=1}^n(-q^T_iRq'_i)\\ =argmin_R\left[\begin{matrix}x&y&z\end{matrix}\right]\left[\begin{matrix}R_{11}&R_{12}&R_{13}\\R_{21}&R_{22}&R_{23}\\R_{31}&R_{32}&R_{33}\end{matrix}\right]\left[\begin{matrix}x'\\y'\\z'\end{matrix}\right]\\ =argmin_R-((R_{11}+R_{12}+R_{13})xx'+(R_{21}+R_{22}+R_{23})yy'+(R_{31}+R_{32}+R_{33}))zz'\\ =-tr(R\sum^n_{i=1}q'_iq'^{T}_i)\\ argminRi=1nqiRqi2=argminRi=1n(qiTqi+qiTRTRqi2qiTRqi)qiTqiR;RTR=I,qiTRTRqiRargminRi=1n(qiTRqi)argminRi=1n(qiTRqi)=argminR[xyz]R11R21R31R12R22R32R13R23R33xyz=argminR((R11+R12+R13)xx+(R21+R22+R23)yy+(R31+R32+R33))zz=tr(Ri=1nqiqiT)
    求解: t r ( R ∑ i = 1 n q i ′ q i ′ T ) tr(R\sum^n_{i=1}q'_iq'^{T}_i) tr(Ri=1nqiqiT)
    W = ∑ i = 1 n q i q i ′ T 令 : W = U ∑ V T ( s v d 分 解 ) R = U V T ( W 满 秩 ) W=\sum_{i=1}^nq_iq'^{T}_i\\ 令:W=U\sum V^T(svd分解)\\ R=UV^T(W满秩) W=i=1nqiqiTW=UVT(svd)R=UVT(W)
    (3):根据求解出的R计算t:
    t ∗ = p − R p ′ t*=p-Rp' t=pRp

非线性求解

使用李代数求解

  1. 目标函数
    m i n ξ = 1 2 ∑ i = 1 n ∣ ∣ ( p i − e ξ × p i ′ ) ∣ ∣ 2 2 min_{\xi}=\frac{1}{2}\sum^n_{i=1}||(p_i-e^{\xi×}p_i')||_2^2 minξ=21i=1n(pieξ×pi)22
  2. 一阶导数(雅克比矩阵):
    ∂ e ∂ δ ξ = − ( e ξ × p i ′ ) ∨ = [ − I h a t ( R p + t ) ] \frac{\partial e}{\partial \delta\xi}=-(e^{\xi×}p_i')^\vee=\left[\begin{matrix}-I &hat{(Rp+t)}\end{matrix}\right] δξe=(eξ×pi)=[Ihat(Rp+t)]

实现

slams十四讲

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/SVD>
#include <opencv/cv.hpp>
#include <g2o/core/base_binary_edge.h>
#include <g2o/core/base_edge.h>
#include <g2o/core/base_multi_edge.h>
#include <g2o/core/base_vertex.h>
#include <g2o/core/block_solver.h>
#include <sophus/se3.hpp>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <g2o/solvers/dense/linear_solver_dense.h>
#include <g2o/core/base_unary_edge.h>
#include <chrono>
using namespace std;
using namespace Eigen;
using namespace g2o;
using namespace cv;
void find_feature(const Mat img1,const Mat img2, vector<KeyPoint> &p1, vector<KeyPoint> &p2,vector<DMatch> &Matchers);
Point2d pix2cam( Point2d pt,Mat K){
    return Point2d((pt.x-K.at<double>(0,2))/K.at<double>(0,0),(pt.y-K.at<double>(1,2))/K.at<double>(1,2));
};
void ICP_SVD(vector<Point3d> &point_3d_1,vector<Point3d> &point_3d_2,Matrix3d &R,Vector3d &t);
void ICP_BA(vector<Point3d> &point_3d_1,vector<Point3d> &point_3d_2,Matrix3d &R,Vector3d &t);
int main(int argc, const char** argv) {
    Mat K = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
    Matrix3d R1,R2;Vector3d T1,T2;
    Mat img1=imread("/home/n1/notes/3d3d/ICP/1.png",CV_LOAD_IMAGE_COLOR);
    Mat img2=imread("/home/n1/notes/3d3d/ICP/2.png",CV_LOAD_IMAGE_COLOR);
    Mat img1_depth=imread("/home/n1/notes/3d3d/ICP/1_depth.png",CV_LOAD_IMAGE_UNCHANGED);
    Mat img2_depth=imread("/home/n1/notes/3d3d/ICP/2_depth.png",CV_LOAD_IMAGE_UNCHANGED);
    vector<DMatch> Matchers;
    vector<KeyPoint> Point1,Point2;
    find_feature(img1,img2,Point1,Point2,Matchers);
     cout << "一共找到了" << Matchers.size() << "组匹配点" << endl;
    vector<Point3d> point_3d_1,point_3d_2;
    for(DMatch m:Matchers){
        ushort depth_1=img1_depth.at<unsigned short>(int(Point1[m.queryIdx].pt.y),int(Point1[m.queryIdx].pt.x));
        ushort depth_2=img1_depth.at<unsigned short>(int(Point1[m.trainIdx].pt.y),int(Point1[m.trainIdx].pt.x));
        double dd1=depth_1/5000;
        double dd2=depth_2/5000;
        if (depth_1 == 0 || depth_2 == 0)   // bad depth
            continue;
        Point2d p2d_1=pix2cam(Point1[m.queryIdx].pt,K);
        Point2d p2d_2=pix2cam(Point2[m.trainIdx].pt,K);
        Point3d Point1_i=Point3d(p2d_1.x*dd1,p2d_1.y*dd1,dd1);//单位化
        Point3d Point2_i=Point3d(p2d_2.x*dd2,p2d_2.y*dd2,dd2);
        
        point_3d_1.push_back(Point1_i);
        point_3d_2.push_back(Point2_i);
    }

    ICP_SVD(point_3d_1,point_3d_2,R1,T1);
    cout<<"R1"<<R1<<endl;
    cout<<"T1"<<T1<<endl;
    ICP_BA(point_3d_1,point_3d_2,R2,T2);
    cout<<"R2"<<R2<<endl;
    cout<<"T2"<<T2<<endl;
    //验证
      for (int i = 0; i < 5; i++) {
        cout << "p1 = " << point_3d_1[i] << endl;
        cout << "p2 = " << point_3d_2[i] << endl;
        Matrix<double,3,1> p2;
        p2<<point_3d_2[i].x, point_3d_2[i].y, point_3d_2[i].z;

        cout << "(R*p2+t) = " <<R1*p2 + T2<< endl;
        cout << endl;
        }
    waitKey();
    return 0;
}
bool cmp( DMatch &m1, DMatch &m2) { return m1.distance < m2.distance; }
void find_feature(const Mat img1,const Mat img2, vector<KeyPoint> &p1, vector<KeyPoint> &p2,vector<DMatch> &Matchers){
    Mat description1,description2;
    Ptr<FeatureDetector> Detector=ORB::create();
    Ptr<DescriptorExtractor> Descriptor=ORB::create();
    Ptr<DescriptorMatcher> Macher= DescriptorMatcher::create("BruteForce-Hamming");

    Detector->detect(img1,p1);
    Detector->detect(img2,p2);

    Descriptor->compute(img1,p1,description1);
    Descriptor->compute(img2,p2,description2);
    vector<DMatch> match;
    double min_dist = 10000, max_dist = 0;
    Macher->match(description1,description2,match);
   
    for(int i=0;i<match.size();i++){
        if(min_dist>match[i].distance)min_dist=match[i].distance;
        if(max_dist<match[i].distance)max_dist=match[i].distance;
    }
    cout<<"max_value:"<<max_dist<<endl;
    cout<<"min_dist:"<<min_dist<<endl;
    for(int dist=0;dist<match.size();dist++){
        if(match[dist].distance<=max(2*min_dist,30.0)){
            Matchers.push_back(match[dist]);
        }
    }
    Mat Match_img;
    drawMatches(img1,p1,img2,p2,Matchers,Match_img);
   // imshow("1",Match_img);
}
void ICP_SVD(vector<Point3d> &point_3d_1,vector<Point3d> &point_3d_2,Matrix3d &R,Vector3d &t){
    Point3d center_1,center_2;//两组向量质心坐标
    Point3d sum_p1,sum_p2;//两组坐标的和
    vector<Point3d> p1,p2;//两组向量去质心坐标后的值
    //获取质心坐标
    int size_points=point_3d_1.size();//数组的大小
   
    for(int i=0;i<size_points;i++){
        sum_p1+=point_3d_1[i];
        sum_p2+=point_3d_2[i];
    }
    //cout<<sum_p1<<endl;
    ;
    center_1=sum_p1/size_points;
    center_2=sum_p2/size_points;
    //cout<<center_1<<endl;cout<<center_2<<endl;
   
    //获取去质心坐标
    for(int i=0;i<size_points;i++){
        p1.push_back(point_3d_1[i]-center_1);
        p2.push_back(point_3d_2[i]-center_2);
    }

    //计算矩阵M
    Eigen::Matrix3d W=Matrix3d::Zero();
    for(int i=0;i<size_points;i++){
        W +=Eigen::Vector3d(p1[i].x,p1[i].y,p1[i].z)*(Eigen::Vector3d(p2[i].x,p2[i].y,p2[i].z).transpose());
    };
    Eigen::JacobiSVD<Eigen::Matrix3d> svd(W,ComputeFullU|ComputeFullV);
    Matrix3d U=svd.matrixU();
    Matrix3d V=svd.matrixV();
    cout << "U=" << U << endl;
    cout << "V=" << V << endl;
     R=U*(V.transpose());
     if(R.determinant()<0){R=-R;}
     t=Vector3d(center_1.x,center_1.y,center_1.z)-R*Vector3d(center_2.x,center_2.y,center_2.z);
}

class Vertpose:public g2o::BaseVertex<6,Sophus::SE3d>{
    public:
        EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
        virtual void setToOriginImpl() override{
            _estimate=Sophus::SE3d();
        };
        virtual void oplusImpl(const double *update){
             Eigen::Matrix<double,6,1> M_update;
             M_update<<update[0],update[1],update[2],update[3],update[4],update[5];
             _estimate=Sophus::SE3d::exp(M_update)*_estimate;
        };
        virtual bool read(istream &in)override{};
        virtual bool write(ostream &out)const override{};
};
class MyEdge:public g2o::BaseUnaryEdge<3,Eigen::Vector3d,Vertpose>{
    public:
        EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
        virtual bool read(istream &in)override{};
        virtual bool write(ostream &out)const override{};
        MyEdge(const Eigen::Vector3d &point):_point(point){};
        virtual void  computeError()override{
            const Vertpose *pose=static_cast<const Vertpose *>(_vertices[0]);
            _error=_measurement-pose->estimate()*_point;
        }
          virtual void linearizeOplus() override {
            Vertpose *pose = static_cast<Vertpose *>(_vertices[0]);
            Sophus::SE3d T = pose->estimate();
            Eigen::Vector3d xyz_trans = T * _point;
            _jacobianOplusXi.block<3, 3>(0, 0) = -Eigen::Matrix3d::Identity();
            _jacobianOplusXi.block<3, 3>(0, 3) = Sophus::SO3d::hat(xyz_trans);
        }
    private:
        Eigen::Vector3d _point;
};
void ICP_BA(vector<Point3d> &point_3d_1,vector<Point3d> &point_3d_2,Matrix3d &R,Vector3d &t){
    typedef g2o::BlockSolver<g2o::BlockSolverTraits<6,3> > BlockType;
    typedef g2o::LinearSolverDense<BlockType::PoseMatrixType > linearType;//定义线性求解器
    auto slover= new g2o::OptimizationAlgorithmLevenberg(
    g2o::make_unique<BlockType>(g2o::make_unique<linearType>()));
    g2o::SparseOptimizer Optimer;//图模型
    Optimer.setAlgorithm(slover);//添加求解器
    Optimer.setVerbose(true);    //设置显示输出
    
    //添加顶点
    Vertpose *pose=new Vertpose();
    pose->setId(0);
    pose->setEstimate(Sophus::SE3d());
    Optimer.addVertex(pose);

    //添加边
    int index=1;
    for(int i=0;i<point_3d_1.size();i++){
        MyEdge *edge=new MyEdge(Vector3d(point_3d_2[i].x,point_3d_2[i].y,point_3d_2[i].z));
        edge->setId(index);
        edge->setVertex(0,pose);
        edge->setMeasurement(Vector3d(point_3d_1[i].x,point_3d_1[i].y,point_3d_1[i].z));
        edge->setInformation(Eigen::Matrix3d::Identity());//设置协防差矩阵初值
        Optimer.addEdge(edge);
    }
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
    Optimer.initializeOptimization();//创建实例
    Optimer.optimize(10);//迭代次数
      chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
    chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cout << "optimization costs time: " << time_used.count() << " seconds." << endl;
    cout << endl << "after optimization:" << endl;
    cout << "T=\n" << pose->estimate().matrix() << endl;
    R=pose->estimate().rotationMatrix();
    t=pose->estimate().translation();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值