• 点云 ICP学习-IterativeClosestPoint


    目录

    一、pcl中 点云配准算法 

     二、关于svd原理求解部分

    三、pcl IterativeClosestPoint 完成demo


    一、pcl中 点云配准算法 

    PCL 库中 ICP 的接口及其变种:

    • 点到点:pcl::IterativeClosestPoint< PointSource, PointTarget, Scalar >
    • 点到面:pcl::IterativeClosestPointWithNormals< PointSource, PointTarget, Scalar >
    • 面到面:pcl::GeneralizedIterativeClosestPoint< PointSource, PointTarget >

    其中,IterativeClosestPoint 模板类是 ICP 算法的一个基本实现,其优化求解方法基于 Singular Value Decomposition (SVD),算法迭代结束条件包括:

    • 最大迭代次数:Number of iterations has reached the maximum user imposed number of iterations (via setMaximumIterations)
    • 两次变换矩阵之间的差值:The epsilon (difference) between the previous transformation and the current estimated transformation is smaller than an user imposed value (via setTransformationEpsilon)
    • 均方误差:The sum of Euclidean squared errors is smaller than a user defined threshold (via setEuclideanFitnessEpsilon)

    基本用法:

    1. IterativeClosestPoint<PointXYZ, PointXYZ> icp;
    2. // Set the input source and target
    3. icp.setInputCloud (cloud_source);
    4. icp.setInputTarget (cloud_target);
    5. // Set the max correspondence distance to 5cm (e.g., correspondences with higher distances will be ignored)
    6. icp.setMaxCorrespondenceDistance (0.05);
    7. // Set the maximum number of iterations (criterion 1)
    8. icp.setMaximumIterations (50);
    9. // Set the transformation epsilon (criterion 2)
    10. icp.setTransformationEpsilon (1e-8);
    11. // Set the euclidean distance difference epsilon (criterion 3)
    12. icp.setEuclideanFitnessEpsilon (1);
    13. // Perform the alignment
    14. icp.align (cloud_source_registered);
    15. // Obtain the transformation that aligned cloud_source to cloud_source_registered
    16. Eigen::Matrix4f transformation = icp.getFinalTransformation ();

    两帧点云配置算法可以看这里

    How to incrementally register pairs of clouds — Point Cloud Library 0.0 documentation (pcl.readthedocs.io)

    GitHub - geekerboy/pairwise_incremental_registration: 修复参考书代码中Segmentation fault (core dumped) 问题

     二、关于svd原理求解部分

    高翔视觉SLAM十四讲求解 ICP 的代码

    1. void pose_estimation_3d3d(const vector<Point3f>& pts1,
    2. const vector<Point3f>& pts2,
    3. Mat& R, Mat& t)
    4. {
    5. // 求质心
    6. Point3f p1, p2;
    7. int N = pts1.size();
    8. for (int i=0; i<N; i++)
    9. {
    10. p1 += pts1[i];
    11. p2 += pts2[i];
    12. }
    13. p1 /= N;
    14. p2 /= N;
    15. // 去质心
    16. vector<Point3f> q1(N), q2(N);
    17. for (int i=0; i<N; i++)
    18. {
    19. q1[i] = pts1[i] - p1;
    20. q2[i] = pts2[i] - p2;
    21. }
    22. // 计算 q1*q2^T
    23. Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    24. for (int i=0; i<N; i++)
    25. {
    26. W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x,
    27. q2[i].y, q2[i].z).transpose();
    28. }
    29. cout << "W=" << W << endl;
    30. // 对W进行SVD求解Rt
    31. Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
    32. Eigen::Matrix3d U = svd.matrixU();
    33. Eigen::Matrix3d V = svd.matrixV();
    34. cout << "U=" << U << endl;
    35. cout << "V=" << V << endl;
    36. Eigen::Matrix3d R_ = U * (V.transpose());
    37. Eigen::Vector3d t_ = Eigen::Vector3d(p1.x, p1.y, p1.z) - R_ * Eigen::Vector3d(p2.x, p2.y, p2.z);
    38. // Eigen 转换成 cv::Mat
    39. R = (Mat_<double>(3, 3) <<
    40. R_(0, 0), R_(0, 1), R_(0,2),
    41. R_(1, 0), R_(1, 1), R_(1,2),
    42. R_(2, 0), R_(2, 1), R_(2,2));
    43. t = (Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
    44. }

    另外的方法求RT,本质也是svd分解

    1. /// <summary>
    2. /// 通过svd分解求解旋转和平移
    3. /// </summary>
    4. /// <param name="A"></param>
    5. /// <param name="B"></param>
    6. /// <returns>返回值为4*4变换矩阵T</returns>
    7. Eigen::Matrix4d best_fit_transform(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B) {
    8. /*
    9. Notice:
    10. 1/ JacobiSVD return U,S,V, S as a vector, "use U*S*Vt" to get original Matrix;
    11. 2/ matrix type 'MatrixXd' or 'MatrixXf' matters.
    12. */
    13. Eigen::Matrix4d T = Eigen::MatrixXd::Identity(4, 4);
    14. Eigen::Vector3d centroid_A(0, 0, 0);
    15. Eigen::Vector3d centroid_B(0, 0, 0);
    16. Eigen::MatrixXd AA = A;
    17. Eigen::MatrixXd BB = B;
    18. int row = A.rows();
    19. for (int i = 0; i < row; i++) {
    20. centroid_A += A.block<1, 3>(i, 0).transpose();
    21. centroid_B += B.block<1, 3>(i, 0).transpose();
    22. }
    23. centroid_A /= row;
    24. centroid_B /= row;
    25. for (int i = 0; i < row; i++) {
    26. AA.block<1, 3>(i, 0) = A.block<1, 3>(i, 0) - centroid_A.transpose();
    27. BB.block<1, 3>(i, 0) = B.block<1, 3>(i, 0) - centroid_B.transpose();
    28. }
    29. Eigen::MatrixXd H = AA.transpose() * BB;
    30. Eigen::MatrixXd U;
    31. Eigen::VectorXd S;
    32. Eigen::MatrixXd V;
    33. Eigen::MatrixXd Vt;
    34. Eigen::Matrix3d R;
    35. Eigen::Vector3d t;
    36. JacobiSVD<Eigen::MatrixXd> svd(H, ComputeFullU | ComputeFullV);
    37. U = svd.matrixU();
    38. S = svd.singularValues();
    39. V = svd.matrixV();
    40. Vt = V.transpose();
    41. R = Vt.transpose() * U.transpose();
    42. if (R.determinant() < 0) {
    43. Vt.block<1, 3>(2, 0) *= -1;
    44. R = Vt.transpose() * U.transpose();
    45. }
    46. t = centroid_B - R * centroid_A;
    47. T.block<3, 3>(0, 0) = R;
    48. T.block<3, 1>(0, 3) = t;
    49. return T;
    50. }

    icp求解是利用pcl工具来做,省时省力。

    Introduction — Point Cloud Library 1.12.1-dev documentation (pointclouds.org)

    Interactive Iterative Closest Point — Point Cloud Library 1.12.1-dev documentation (pointclouds.org)

    三、pcl IterativeClosestPoint 完成demo

    代码:

    1. #include <iostream>
    2. #include <numeric>
    3. #include "icp.h"
    4. #include "Eigen/Eigen"
    5. using namespace std;
    6. using namespace Eigen;
    7. /// <summary>
    8. /// 通过svd分解求解旋转和平移
    9. /// </summary>
    10. /// <param name="A"></param>
    11. /// <param name="B"></param>
    12. /// <returns>返回值为4*4变换矩阵T</returns>
    13. Eigen::Matrix4d best_fit_transform(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B) {
    14. /*
    15. Notice:
    16. 1/ JacobiSVD return U,S,V, S as a vector, "use U*S*Vt" to get original Matrix;
    17. 2/ matrix type 'MatrixXd' or 'MatrixXf' matters.
    18. */
    19. Eigen::Matrix4d T = Eigen::MatrixXd::Identity(4, 4);
    20. Eigen::Vector3d centroid_A(0, 0, 0);
    21. Eigen::Vector3d centroid_B(0, 0, 0);
    22. Eigen::MatrixXd AA = A;
    23. Eigen::MatrixXd BB = B;
    24. int row = A.rows();
    25. for (int i = 0; i < row; i++) {
    26. centroid_A += A.block<1, 3>(i, 0).transpose();
    27. centroid_B += B.block<1, 3>(i, 0).transpose();
    28. }
    29. centroid_A /= row;
    30. centroid_B /= row;
    31. for (int i = 0; i < row; i++) {
    32. AA.block<1, 3>(i, 0) = A.block<1, 3>(i, 0) - centroid_A.transpose();
    33. BB.block<1, 3>(i, 0) = B.block<1, 3>(i, 0) - centroid_B.transpose();
    34. }
    35. Eigen::MatrixXd H = AA.transpose() * BB;
    36. Eigen::MatrixXd U;
    37. Eigen::VectorXd S;
    38. Eigen::MatrixXd V;
    39. Eigen::MatrixXd Vt;
    40. Eigen::Matrix3d R;
    41. Eigen::Vector3d t;
    42. JacobiSVD<Eigen::MatrixXd> svd(H, ComputeFullU | ComputeFullV);
    43. U = svd.matrixU();
    44. S = svd.singularValues();
    45. V = svd.matrixV();
    46. Vt = V.transpose();
    47. R = Vt.transpose() * U.transpose();
    48. if (R.determinant() < 0) {
    49. Vt.block<1, 3>(2, 0) *= -1;
    50. R = Vt.transpose() * U.transpose();
    51. }
    52. t = centroid_B - R * centroid_A;
    53. T.block<3, 3>(0, 0) = R;
    54. T.block<3, 1>(0, 3) = t;
    55. return T;
    56. }
    57. /*
    58. typedef struct{
    59. Eigen::Matrix4d trans;
    60. std::vector<float> distances;
    61. int iter;
    62. } ICP_OUT;
    63. */
    64. ICP_OUT icp(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, int max_iterations, int tolerance) {
    65. int row = A.rows();
    66. Eigen::MatrixXd src = Eigen::MatrixXd::Ones(3 + 1, row);
    67. Eigen::MatrixXd src3d = Eigen::MatrixXd::Ones(3, row);
    68. Eigen::MatrixXd dst = Eigen::MatrixXd::Ones(3 + 1, row);
    69. NEIGHBOR neighbor;
    70. Eigen::Matrix4d T;
    71. Eigen::MatrixXd dst_chorder = Eigen::MatrixXd::Ones(3, row);
    72. ICP_OUT result;
    73. int iter = 0;
    74. for (int i = 0; i < row; i++) {
    75. src.block<3, 1>(0, i) = A.block<1, 3>(i, 0).transpose();
    76. src3d.block<3, 1>(0, i) = A.block<1, 3>(i, 0).transpose();
    77. dst.block<3, 1>(0, i) = B.block<1, 3>(i, 0).transpose();
    78. }
    79. double prev_error = 0;
    80. double mean_error = 0;
    81. for (int i = 0; i < max_iterations; i++)
    82. {
    83. neighbor = nearest_neighbot(src3d.transpose(), B);
    84. for (int j = 0; j < row; j++)
    85. {
    86. dst_chorder.block<3, 1>(0, j) = dst.block<3, 1>(0, neighbor.indices[j]);
    87. }
    88. T = best_fit_transform(src3d.transpose(), dst_chorder.transpose());
    89. src = T * src;
    90. for (int j = 0; j < row; j++)
    91. {
    92. src3d.block<3, 1>(0, j) = src.block<3, 1>(0, j);
    93. }
    94. mean_error = std::accumulate(neighbor.distances.begin(), neighbor.distances.end(), 0.0) / neighbor.distances.size();
    95. if (abs(prev_error - mean_error) < tolerance)
    96. {
    97. break;
    98. }
    99. prev_error = mean_error;
    100. iter = i + 2;
    101. }
    102. T = best_fit_transform(A, src3d.transpose());
    103. result.trans = T;
    104. result.distances = neighbor.distances;
    105. result.iter = iter;
    106. return result;
    107. }
    108. /*
    109. typedef struct{
    110. std::vector<float> distances;
    111. std::vector<int> indices;
    112. } NEIGHBOR;
    113. */
    114. NEIGHBOR nearest_neighbot(const Eigen::MatrixXd& src, const Eigen::MatrixXd& dst) {
    115. int row_src = src.rows();
    116. int row_dst = dst.rows();
    117. Eigen::Vector3d vec_src;
    118. Eigen::Vector3d vec_dst;
    119. NEIGHBOR neigh;
    120. float min = 100;
    121. int index = 0;
    122. float dist_temp = 0;
    123. for (int ii = 0; ii < row_src; ii++) {
    124. vec_src = src.block<1, 3>(ii, 0).transpose();
    125. min = 100;
    126. index = 0;
    127. dist_temp = 0;
    128. for (int jj = 0; jj < row_dst; jj++) {
    129. vec_dst = dst.block<1, 3>(jj, 0).transpose();
    130. dist_temp = dist(vec_src, vec_dst);
    131. if (dist_temp < min) {
    132. min = dist_temp;
    133. index = jj;
    134. }
    135. }
    136. // cout << min << " " << index << endl;
    137. // neigh.distances[ii] = min;
    138. // neigh.indices[ii] = index;
    139. neigh.distances.push_back(min);
    140. neigh.indices.push_back(index);
    141. }
    142. return neigh;
    143. }
    144. float dist(const Eigen::Vector3d& pta, const Eigen::Vector3d& ptb) {
    145. return sqrt((pta[0] - ptb[0]) * (pta[0] - ptb[0]) + (pta[1] - ptb[1]) * (pta[1] - ptb[1]) + (pta[2] - ptb[2]) * (pta[2] - ptb[2]));
    146. }

    截图: 

     

  • 相关阅读:
    第十四届蓝桥杯省赛 C/C++ A 组 H 题——异或和之和(AC)
    pgsql中实现按周统计,计算日期是周几
    Kubectl 使用详解——k8s陈述式资源管理
    instanceOf原理及手动实现
    .NET 跨平台应用开发动手教程 |用 Uno Platform 构建一个 Kanban-style Todo App
    《Effective C++》学习笔记——区分接口继承和实现继承
    LVS+Keepalived群集
    【Jetson Nano】jetson nano一些基本功能命令
    遮挡Windows电脑上烦人的微信/企业微信/钉钉消息闪烁提醒
    vue项目嵌套(vue2嵌套vue3)
  • 原文地址:https://blog.csdn.net/cangqiongxiaoye/article/details/127935346