一、声明:

本人作为初学者,才开始接触点云配准这一块,如有错误地方,望大家指出,我将及时修改,共同进步。

二、需提前知道理论知识

代码只实现ICP算法中理论部分,其他部分后续实现。

对于ICP算法了解不够熟悉,推荐其下博客(尤其是第二篇):

三维点云配准 -- ICP 算法原理及推导 - 知乎 (zhihu.com)

SLAM常见问题(四):求解ICP,利用SVD分解得到旋转矩阵 | RealCat (gitee.io)

对于奇异值分解不够熟悉,可以看一下:

奇异值分解(SVD) - 知乎 (zhihu.com)

三、ICP代码算法实现步骤

step1. 计算两组匹配点的质心,每个点的位置为:{},{}:

 , 

step2. 得到去质心的点集:

step3. 根据公式算其奇异分解钱的3x3的举证:

其中:X为去除质心的源点云矩阵,Y为去除质心的目标点云矩阵

step4. 对S进行SVD 奇异值分解,得到旋转矩阵R:

其中V求算公式为(为特征向量组成的矩阵):

其中U求算公式为(为特征向量组成的矩阵):

step5. 计算平移量:

四、代码实现(按照步骤三实现)

1、主函数(如果你要修改初始点云的位置,可以修改main函数下第二个for):

int main()
{MatrixXd input(n,m);MatrixXd output(n,m);//给数组赋值for (int i = 0; i < m; i++){input(0,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(1,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(2,i) = 1024 * rand() / (RAND_MAX + 1.0f);output(0, i) = input(0, i);output(1, i) = input(1, i);output(2, i) = input(2, i);}//打印初始点云cout << input << endl;//将点云的x坐标进行先前移动0.7个坐标for (int i = 0; i < m; i++){output(0,i) = output(0,i) + 0.7f;}cout << "-----------------" << endl;//打印出输出点云cout << output << endl;//icp算法icp(input, output);
}

1.1 结果:

目标点云:
1.28125 828.125 358.688   764.5 727.531
577.094 599.031 917.438 178.281 525.844
197.938 491.375 842.562 879.531 311.281
向X轴平移0.7f个单位的点云:
1.98125 828.825 359.387   765.2 728.231
577.094 599.031 917.438 178.281 525.844
197.938 491.375 842.562 879.531 311.281

2、计算矩阵的质心函数

MatrixXd computer3DCentroid(MatrixXd& input)
{float sum_x = 0;float sum_y = 0;float sum_z = 0;for (int i = 0; i < m ; i++){sum_x += input(0, i);sum_y += input(1, i);sum_z += input(2, i);}sum_x = sum_x / m;sum_y = sum_y / m;sum_z = sum_z / m;MatrixXd Centroid(1,3);Centroid << sum_x, sum_y, sum_z;return Centroid;
}

2.1 结果:

目标矩阵的质心:
536.025 559.537 544.537
经旋转和平移矩阵的质心:
536.725 559.537 544.537

3、对目标点云矩阵和源点云矩阵去质心并且计算旋转矩阵R和平移量T

     //计算目标矩阵的质心并去质心MatrixXd inCenterI = computer3DCentroid(Target);MatrixXd myinCenterI = inCenterI.transpose() * Sing;MatrixXd  MyInCenterI = Target - myinCenterI;//计算源矩阵的质心并去质心MatrixXd inCenterO = computer3DCentroid(Trans);MatrixXd myinCenterO = inCenterO.transpose() * Sing;MatrixXd MyInCenterO = Trans - myinCenterO;//计算RMatrixXd H = MyInCenterO * MyInCenterI.transpose();MatrixXd  U = H * H.transpose();EigenSolver<MatrixXd> es(U);MatrixXd  Ue = es.pseudoEigenvectors();MatrixXd V = H.transpose() *H;EigenSolver<MatrixXd> es1(V);MatrixXd  Ve = es1.pseudoEigenvectors();R = Ve * Ue.transpose();MatrixXd  gR = MatrixXd::Identity(n, n);gR(2, 2) = R.determinant();R = Ve *gR * Ue.transpose();cout << "旋转矩阵R为:" << endl;cout << R << endl;cout << "---------------------这里行列式一定要为1-----------------" << endl;std::cout << "旋转矩阵R行列式: " << R.determinant() << std::endl;cout << "---------------------------------------------------------" << endl;//计算TT = inCenterI - R * inCenterO;cout << "转移量T:" << endl << T << endl;T = T.transpose() * Sing;cout << "转移量T矩阵:" << endl << T << endl;

3.1 结果

旋转矩阵R为:1 -8.57647e-15 -7.82707e-158.88178e-15            1 -3.88578e-157.82707e-15  3.83027e-15            1
---------------------这里行列式一定要为1-----------------
旋转矩阵R行列式: 1
---------------------------------------------------------
转移量T:-0.699951 2.27374e-13 2.27374e-13
转移量T矩阵:-0.699951   -0.699951   -0.699951   -0.699951   -0.699951
2.27374e-13 2.27374e-13 2.27374e-13 2.27374e-13 2.27374e-13
2.27374e-13 2.27374e-13 2.27374e-13 2.27374e-13 2.27374e-13

4、进行误差计算:

 Trans = R * Trans + T;err = Trans - Target;err2 = (err.cwiseProduct(err)).sum();cout << err2 << endl;
1.19151e-08

5、打印最后旋转的举证:

 1.2813 828.125 358.688   764.5 727.531
577.094 599.031 917.438 178.281 525.844
197.938 491.375 842.563 879.531 311.281

五、完整代码

# include<iostream>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
using namespace Eigen;
using namespace std;# define m 5
# define n 3
//计算质心的函数
MatrixXd computer3DCentroid(MatrixXd& input)
{float sum_x = 0;float sum_y = 0;float sum_z = 0;for (int i = 0; i < m ; i++){sum_x += input(0, i);sum_y += input(1, i);sum_z += input(2, i);}sum_x = sum_x / m;sum_y = sum_y / m;sum_z = sum_z / m;MatrixXd Centroid(1,3);Centroid << sum_x, sum_y, sum_z;return Centroid;
}void icp(MatrixXd& Target, MatrixXd& Source)
{MatrixXd R = MatrixXd::Identity(n, n);cout << R << endl;MatrixXd T(n, m);MatrixXd Trans = R * Source + T;MatrixXd err = Trans - Target;float err2 = (err.cwiseProduct(err)).sum();while (err2 > 0.5){MatrixXd Sing(1, 5);Sing << 1, 1, 1,1,1;//计算目标矩阵的质心并去质心MatrixXd inCenterI = computer3DCentroid(Target);MatrixXd myinCenterI = inCenterI.transpose() * Sing;MatrixXd  MyInCenterI = Target - myinCenterI;//计算源矩阵的质心并去质心MatrixXd inCenterO = computer3DCentroid(Trans);MatrixXd myinCenterO = inCenterO.transpose() * Sing;MatrixXd MyInCenterO = Trans - myinCenterO;//计算RMatrixXd H = MyInCenterO * MyInCenterI.transpose();MatrixXd  U = H * H.transpose();EigenSolver<MatrixXd> es(U);MatrixXd  Ue = es.pseudoEigenvectors();MatrixXd V = H.transpose() *H;EigenSolver<MatrixXd> es1(V);MatrixXd  Ve = es1.pseudoEigenvectors();R = Ve * Ue.transpose();MatrixXd  gR = MatrixXd::Identity(n, n);gR(2, 2) = R.determinant();R = Ve *gR * Ue.transpose();cout << "旋转矩阵R为:" << endl;cout << R << endl;cout << "---------------------这里行列式一定要为1-----------------" << endl;std::cout << "旋转矩阵R行列式: " << R.determinant() << std::endl;cout << "---------------------------------------------------------" << endl;//计算TT = inCenterI - R * inCenterO;cout << "转移量T:" << endl << T << endl;T = T.transpose() * Sing;cout << "转移量T矩阵:" << endl << T << endl;Trans = R * Trans + T;err = Trans - Target;err2 = (err.cwiseProduct(err)).sum();cout <<"误差为:" << err2 << endl;}cout << "Trans:" << endl;cout << Trans << endl;}
int main()
{MatrixXd input(n,m);MatrixXd output(n,m);//给数组赋值for (int i = 0; i < m; i++){input(0,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(1,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(2,i) = 1024 * rand() / (RAND_MAX + 1.0f);output(0, i) = input(0, i);output(1, i) = input(1, i);output(2, i) = input(2, i);}//打印初始点云cout << "目标点云:" << endl;cout << input << endl;//将点云的x坐标进行先前移动0.7个坐标for (int i = 0; i < m; i++){output(0,i) = output(0,i) + 0.7f;output(1, i) = output(1, i) + 0.1f;}//打印出输出点云cout << "向X轴平移0.7f个单位的点云:" << endl;cout << output << endl;//icp算法icp(input, output);
}

六点:补充:

如果我们将main函数下第二个for循环改为中将点云y坐标重新赋值且向y方向移动500,会发现ICP将不收敛,所以其缺点较为明显,需要一个初配准,且容易陷入局部最优。

int main()
{MatrixXd input(n,m);MatrixXd output(n,m);//给数组赋值for (int i = 0; i < m; i++){input(0,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(1,i) = 1024 * rand() / (RAND_MAX + 1.0f);input(2,i) = 1024 * rand() / (RAND_MAX + 1.0f);output(0, i) = input(0, i);output(1, i) = input(1, i);output(2, i) = input(2, i);}//打印初始点云cout << "目标点云:" << endl;cout << input << endl;//将点云的x坐标进行先前移动0.7个坐标//点云y坐标重新赋值且向y方向移动500ffor (int i = 0; i < m; i++){output(0,i) = output(0,i) + 0.7f;output(1, i) = output(1, i) + 0.7f;}//打印出输出点云cout << "向X轴平移0.7f个单位的点云:" << endl;cout << output << endl;//icp算法icp(input, output);
}

点云配准1-ICP算法 原理代码实现相关推荐

  1. 干货 | 三维点云配准:ICP 算法原理及推导

    编者荐语 点云配准可以分为粗配准(Coarse Registration)和精配准(Fine Registration)两步.粗配准指的是在两幅点云之间的变换完全未知的情况下进行较为粗糙的配准,目的主 ...

  2. 点云配准的传统算法ICP与NDT概述

    公众号致力于分享点云处理,SLAM,三维视觉,高精地图相关的文章与技术,欢迎各位加入我们,一起交流一起进步,有兴趣的可联系微信:920177957.本文来自点云PCL博主的分享,未经作者允许请勿转载, ...

  3. icp算法原理与实现

    ICP算法原理 ICP是一种经典的点云匹配算法,用于估计两个点云PsP_sPs​.PtP_tPt​之间的位姿变换. 点云配准的问题可以描述为: R∗,t∗=arg min⁡R,t1∣Ps∣∑i=1Ps ...

  4. 点云配准NDT+ICP

    点云配准NDT+ICP #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types. ...

  5. 点云配准NDT (P2D)算法详解

    点云配准NDT (P2D)算法详解 最近了解了一些关于点云配准算法NDT的相关文章,进行总结一下. NDT算法的关键是其利用正态分布对参考点云进行了重新表示,使用点云在一个模型特定位置的似然值而不是直 ...

  6. 【机器学习】总结了九种机器学习集成分类算法(原理+代码)

    大家好,我是云朵君! 导读: 本文是分类分析(基于Python实现五大常用分类算法(原理+代码))第二部分,继续沿用第一部分的数据.会总结性介绍集成分类算法原理及应用,模型调参数将不在本次讨论范围内. ...

  7. 总结了九种机器学习集成分类算法(原理+代码)

    公众号后台回复"图书",了解更多号主新书内容作者:云朵君来源: 数据STUDIO 导读: 本文是分类分析(基于Python实现五大常用分类算法(原理+代码))第二部分,继续沿用第一 ...

  8. 【动手学MVG】ICP算法原理和代码实现

    介绍 本文介绍了相同尺度的点云ICP算法,若点云之间尺度不同,则需要估计尺度,可以看一下另一篇博文<通过ICP配准两组尺度不同点云, 统一坐标系(附代码)> 本文介绍的ICP问题,是在3D ...

  9. 两帧点云刚性配准的ICP算法

    点云配准的一般思路是根据两个点云的匹配点,估计刚性变换矩阵[R t]. 空间刚性变换的3×4矩阵[R t]虽然包含12个数,但只有旋转和平移6个自由度(参数).在SLAM中相机的位姿也用[R t]矩阵 ...

  10. 点云配准之NDT算法

    1.算法原理 已知有两幅点云,分别为源点云P和目标云Q. 1)将源点云P所在空间划分为一个一个的单元网格,(即三维空间在二维空间上的投影). 2)根据所划分单元网格内点的分布情况,计算单元网格的正态分 ...

最新文章

  1. poj3007(set的应用)
  2. 人生最浪费生命的四件事,2017年别再做了!
  3. Qt QWidget实现消息提示控件TipsWidget
  4. SL专题2:加入并熟悉Second Life世界
  5. kafka可视化工具_Kafka值得一用的监控系统
  6. php self this static,PHP 中 self、static、$this 的区别和后期静态绑定详解
  7. Chrome DevTools 调研笔记
  8. 浙江省计算机二级办公软件高级应用技术考试时间,最新浙江省计算机二级办公软件高级应用技术考试大纲...
  9. 升级更新:Oracle关于DB Link在2019年升级的10g版本兼容性
  10. 将微信小视频发送给QQ好友
  11. 点评2009年PHP十大图书(2)
  12. java匿名内部类 内部类_java中的匿名内部类详细总结
  13. 第一次做web项目购物网站项目总结
  14. 安装python缺少dll_解决win7操作系统Python3.7.1安装后启动提示缺少.dll文件问题
  15. qt中实现多语言功能
  16. chcp 437>nul graftabl 936>nul
  17. 哈尔滨工业大学计算机系统大作业2022春
  18. 计算机二级考试题库vb知识点,国家计算机二级考试题库 VB上机试题第13套
  19. 微信二维码扫描下载APK
  20. excel基础-固定某一列的输入内容

热门文章

  1. 触摸屏校准之tslib
  2. win10禁用全角_Win10微软拼音无法切换输入法全角和半角怎么办?
  3. NSString+NSMutableString+NSValue+NSAraay用法汇总
  4. Word 2007 删除页眉横线
  5. 汉字五行归属python实现
  6. java-UI设计(仿QQ登录界面)
  7. 詹姆斯·格雷克《信息简史》读后感记录
  8. 什么是SG?+SG模板
  9. Halcon 汉字识别
  10. 二进制颜色代码大全(含图)