ICP Closest Point Iterative Point Cloud Matching Algorithm

Use the PCL library

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <ros/ros.h>
int main(int argc, char **argv)
{<!-- -->
  ros::init(argc, argv, "iterative_closest_point");

  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>);
  // Fill in the point cloud data
  cloud_in->width = 500;
  cloud_in->height = 1;
  cloud_in->is_dense = false;
  cloud_in->points.resize(cloud_in->width * cloud_in->height);
  for (size_t i = 0; i < cloud_in->points. size(); + + i)
  {<!-- -->
    cloud_in->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud_in->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud_in->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }
  std::cout << "Saved " << cloud_in->points. size() << " data points to input:"
            << std::endl;
  // for (size_t i = 0; i < cloud_in->points. size(); + + i)
  // std::cout << " " << cloud_in->points[i].x << " " << cloud_in->points[i].y << " " << cloud_in- >points[i].z << std::endl;
  *cloud_out = *cloud_in;
  // std::cout << "size:" << cloud_out->points. size() << std::endl;
  for (size_t i = 0; i < cloud_in->points. size(); + + i)
  {<!-- -->
    cloud_out->points[i].x = cloud_in->points[i].x + 0.7f;
    cloud_out->points[i].y = cloud_in->points[i].y + 0.7f;
    cloud_out->points[i].z = cloud_in->points[i].z + 0.7f;
  }
  // std::cout << "Transformed " << cloud_in->points. size() << " data points:"
  // << std::endl;
  // for (size_t i = 0; i < cloud_out->points. size(); + + i)
  // std::cout << " " << cloud_out->points[i].x << " " << cloud_out->points[i].y << " " << cloud_out- >points[i].z << std::endl;
  pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
  icp.setInputCloud(cloud_in);
  icp.setInputTarget(cloud_out);
  icp.setMaxCorrespondenceDistance(100); // The maximum distance threshold between two corresponding points in the target. If the distance is greater than this threshold, the points will be ignored during the alignment
  icp.setMaximumIterations(1000); //The number of iterations has reached the maximum number of iterations imposed by the user
  // icp.setTransformationEpsilon(1e-6); //The epsilon (difference) between the previous transformation and the current estimated transformation (i.e. two pose transformations) is less than the value imposed by the user
  // icp.setEuclideanFitnessEpsilon(1e-6); //The sum of Euclidean squared errors is less than the user-defined threshold
  // icp.setRANSACIterations(0); // Set the number of RANSAC runs
  // icp.setInputCloud(cloud_in);
  pcl::PointCloud<pcl::PointXYZ> Final;
  icp. align(Final);
  std::cout << "has converged:" << icp.hasConverged() << " score: " << icp.getFitnessScore() << std::endl;
  std::cout << icp.getFinalTransformation() << std::endl;
  return (0);
}

Does not use the PCL library

#include <iostream>
#include <ros/ros.h>
#include <eigen3/Eigen/Dense>
#include <vector>
using namespace std;
using namespace Eigen;

#define PI (double)3.141592653589793

// distance of two points (x1, y1, z1) and (x2, y2, z2)
float getDistanceOfTwoPoints(float x1, float y1, float z1, float x2, float y2, float z2)
{<!-- -->
    return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2) + pow(z1 - z2, 2));
}

// get closer point of [x,y,z] in {[sourcePoints_x, sourcePoints_y,sourcePoints_z]}
void getClosestPoint(float x, float y, float z, const vector<Vector3f> & amp; sourcePoints, float & amp; find_x, float & amp; find_y, float & amp; find_z)
{<!-- -->
    double dist_min = 99999;
    for (int i = 0; i < sourcePoints. size(); i ++ )
    {<!-- -->
        double dist = getDistanceOfTwoPoints(x, y, z, sourcePoints[i](0), sourcePoints[i](1), sourcePoints[i](2));
        if (dist < dist_min)
        {<!-- -->
            dist_min = dist;
            find_x = sourcePoints[i](0);
            find_y = sourcePoints[i](1);
            find_z = sourcePoints[i](2);
        }
    }
}

int getClosestPointID(float x, float y, float z, const vector<Vector3f> & sourcePoints)
{<!-- -->
    int id = 0;
    double dist_min = 99999;
    for (int i = 0; i < sourcePoints. size(); i ++ )
    {<!-- -->
        double dist = getDistanceOfTwoPoints(x, y, z, sourcePoints[i](0),sourcePoints[i](1),sourcePoints[i](2));
        if (dist < dist_min)
        {<!-- -->
            dist_min = dist;
            id = i;
        }
    }
    return id;
}


// get all points distance of {sourcePoints_x, sourcePoints_y, sourcePoints_z} and {[targetPoints_x,targetPoints_y,sourcePoints_z]} as loss
float getLossShapeMatch(const vector<Vector3f> & amp; sourcePoints, const vector<Vector3f> & amp; targetPoints)
{<!-- -->
    float loss_sum = 0;
    for (int i = 0; i < targetPoints. size(); i ++ )
    {<!-- -->
        float closest_x, closest_y, closest_z;
        getClosestPoint(targetPoints[i](0), targetPoints[i](1), targetPoints[i](2), sourcePoints,closest_x, closest_y, closest_z);
        float loss = getDistanceOfTwoPoints(targetPoints[i](0), targetPoints[i](1), targetPoints[i](2),closest_x, closest_y,closest_z);
        loss_sum = loss_sum + loss;
    }
    return loss_sum;
}

//
/* Point cloud registration algorithm – kabsch
 * One-to-one correspondence between vertices in the two point clouds P and P'
 * Find the rotation and translation transformation matrix
 * pts1 is the original point cloud pts2 is the point cloud to be matched
 */
void KABSCH(const vector<Vector3f> & amp;pts1, const vector<Vector3f> & amp;pts2)
{<!-- -->

    // Calculate centroid coordinates
    Vector3f p1(0,0,0);
    Vector3f p2(0,0,0); // center of mass

    int N = pts1. size();

    for (int i = 0; i < N; i ++ )
    {<!-- -->
        p1 + = pts1[i];
        p2 + = pts2[i];
    }

    p1 = Vector3f((p1) / N);
    p2 = Vector3f((p2) / N);
    
    // Calculate the centroid coordinates of each point
    std::vector<Vector3f> q1(N), q2(N); // remove the center

    for (int i = 0; i < N; i ++ )
    {<!-- -->
        q1[i] = pts1[i] - p1;

        q2[i] = pts2[i] - p2;
    }

    // compute q1*q2^T sum

    Eigen::Matrix3f W = Eigen::Matrix3f::Zero();

    for (int i = 0; i < N; i ++ )
    {<!-- -->
        W + = Eigen::Vector3f(q1[i](0), q1[i](1), q1[i](2)) * Eigen::Vector3f(q2[i](0), q2[i] (1), q2[i](2)).transpose();
    }
 
    // SVD on W
 
    Eigen::JacobiSVD<Eigen::Matrix3f> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);

    Eigen::Matrix3f U = svd.matrixU();

    Eigen::Matrix3f V = svd.matrixV();

    Eigen::Matrix3f R_12 = U * (V.transpose());

    Eigen::Vector3f t_12 = Eigen::Vector3f(p1(0), p1(1), p1(2)) - R_12 * Eigen::Vector3f(p2(0), p2(1), p2(2));

    // verify

    Eigen::AngleAxisf R_21;
    

    R_21.fromRotationMatrix(R_12.transpose());

    std::cout<<"R_12.transpose(): "<<std::endl;
    std::cout<<R_12.transpose()<<std::endl;

    cout << "aix: " << R_21. axis(). transpose() << endl;

    cout << "angle: " << R_21. angle() * 180 / PI << endl;

    cout << "t: " << (-R_12.transpose() * t_12).transpose() << endl;

}
/*
 * ICP algorithm
 * source_pcls original point cloud target_pcls transformed point cloud
 */

void ICP(const vector<Vector3f> & amp; source_pcls, const vector<Vector3f> & amp; target_pcls)
{<!-- -->
    int iter_times = 5000;
    double loss_now;
    double loss_improve = 1000;
    double loss_before = getLossShapeMatch(target_pcls,source_pcls) / source_pcls.size();

    
    int source_pcls_size = source_pcls. size();
    int target_pcls_size = target_pcls. size();

    Vector3f source_pcls_c(0,0,0);
    Vector3f target_pcls_c(0,0,0);

    // Calculate centroid coordinates
    for (size_t i = 0; i < source_pcls_size; i ++ )
    {<!-- -->
        source_pcls_c + = source_pcls[i];
    }
    source_pcls_c = Vector3f((source_pcls_c) / source_pcls_size);

    for (size_t i = 0; i < target_pcls_size; i ++ )
    {<!-- -->
        target_pcls_c + = target_pcls[i];
    }
    target_pcls_c = Vector3f((target_pcls_c) / target_pcls_size);

    // calculate centroid coordinates
  
    vector<Vector3f> q1(source_pcls_size), q2(target_pcls_size); // remove the center

    q1 = source_pcls;

    // for (size_t i = 0; i < source_pcls_size; i ++ )
    // {<!-- -->
    // q1[i] = source_pcls[i] - source_pcls_c;
    // }

    for (size_t i = 0; i < target_pcls_size; i ++ )
    {<!-- -->
        q2[i] = target_pcls[i] - target_pcls_c;
    }

    Eigen::Matrix3f R0, R1;
    R0. setIdentity();
    R1. setIdentity();
    Eigen::Vector3f t0;
    std::vector<Vector3f> q0 ; //Defined as calculation error
    
    int num = 0;
    float loss_st = 9999; //The distance loss from the original point cloud to the target point cloud
    while (num < iter_times & amp; & amp; /* loss_st>0.5 */loss_improve > 0.04)
    {<!-- -->
        Vector3f q1_c(0,0,0);
        // Calculate centroid coordinates
        for (size_t i = 0; i < q1. size(); i ++ )
        {<!-- -->
            q1_c + = q1[i];
        }
        q1_c = Vector3f((q1_c) / q1. size());

        // calculate centroid coordinates
        for (size_t i = 0; i < q1. size(); i ++ )
        {<!-- -->
            q1[i] = q1[i] - q1_c;
        }

        // compute q1*q2^T sum
        Eigen::Matrix3f W = Eigen::Matrix3f::Zero();

        // Find the closest point corresponding to q1 in q2
        for (size_t i = 0; i < source_pcls_size; i ++ )
        {<!-- -->
            int id = getClosestPointID(q1[i](0), q1[i](1), q1[i](2),q2);
            W + = Eigen::Vector3f(q1[i](0), q1[i](1), q1[i](2)) * Eigen::Vector3f(q2[id](0), q2[id] (1), q2[id](2)).transpose();
        }

        // SVD on W

        Eigen::JacobiSVD<Eigen::Matrix3f> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);

        Eigen::Matrix3f U = svd.matrixU();

        Eigen::Matrix3f V = svd.matrixV();

        Eigen::Matrix3f R_12 = U * (V.transpose());

        Eigen::Vector3f t_12 = Eigen::Vector3f(q1_c(0), q1_c(1), q1_c(2)) - R_12 * Eigen::Vector3f(target_pcls_c(0), target_pcls_c(1), target_pcls_c(2));

        Eigen::Vector3f t_21 = (-R_12.transpose() * t_12).transpose();
        Eigen::Matrix3f R_21 = R_12.transpose();
        

        R0 = R0*R_21; // The final rotation transformation matrix from the original point cloud to the target point cloud
        R1 = R1*R_12;

        Eigen::Vector3f t1 = Eigen::Vector3f(source_pcls_c(0), source_pcls_c(1), source_pcls_c(2)) - R1 * Eigen::Vector3f(target_pcls_c(0), target_pcls_c(1), target_pcls_c(2));

        t0 = (-R1.transpose() * t1).transpose();


        // Update the transformed point cloud vector for the next cycle
        for (size_t i = 0; i < source_pcls_size; i ++ )
        {<!-- -->
            Eigen::Vector3f p = R_21 * q1[i] + t_21;
            q1[i] = p;
        }

        
        q0. clear();
        for (size_t i = 0; i < source_pcls_size; i ++ )
        {<!-- -->
            Eigen::Vector3f p = R0 * source_pcls[i] + t0;
            q0.push_back(p);
        }

        loss_st = getLossShapeMatch(target_pcls, q0) / q0.size();
        
        num++;
        float loss_now = getLossShapeMatch(q2, q1) / q1. size();
        loss_improve = abs(loss_before - loss_now); // When the difference between the previous transformation matrix and the current transformation matrix is less than the threshold, it is considered to have converged,
        // std::cout<<"itertime: "<<iter_times<<", loss now: "<<loss_now<<", improve: "<<loss_improve<<std::endl;
        loss_before = loss_now;
    }


    std::cout<<"------ICP RESULT-------"<<std::endl;
    std::cout << "num: " << num << std::endl;
    std::cout << "R0: " << std::endl<< R0 << std::endl;
    std::cout << "t0: "<< t0.transpose() <<std::endl;
    // std::cout << "R_12.transpose(): " << std::endl;
    // std::cout << R_12.transpose() << std::endl;
    // std::cout << "t: " << (-R_12.transpose() * t_12).transpose() << std::endl;
    std::cout << "loss_improve: " << loss_improve << std::endl;
    std::cout << "loss_st: " << loss_st <<std::endl;

    // for (size_t i = 0; i < q0.size(); i ++ )
    // {<!-- -->
    // std::cout<<"q0 "<<i<<" "<<q0[i].transpose()<<std::endl;
    // }
    
    // for (size_t i = 0; i < target_pcls. size(); i ++ )
    // {<!-- -->
    // std::cout<<"target_pcls "<<i<<" "<<target_pcls[i].transpose()<<std::endl;
    // }
    

}