点云 ICP学习-IterativeClosestPoint

news2025/7/14 8:05:50

目录

一、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)

基本用法:

IterativeClosestPoint<PointXYZ, PointXYZ> icp;

// Set the input source and target
icp.setInputCloud (cloud_source);
icp.setInputTarget (cloud_target);

// Set the max correspondence distance to 5cm (e.g., correspondences with higher distances will be ignored)
icp.setMaxCorrespondenceDistance (0.05);
// Set the maximum number of iterations (criterion 1)
icp.setMaximumIterations (50);
// Set the transformation epsilon (criterion 2)
icp.setTransformationEpsilon (1e-8);
// Set the euclidean distance difference epsilon (criterion 3)
icp.setEuclideanFitnessEpsilon (1);

// Perform the alignment
icp.align (cloud_source_registered);
// Obtain the transformation that aligned cloud_source to cloud_source_registered
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 的代码

void pose_estimation_3d3d(const vector<Point3f>& pts1,
                          const vector<Point3f>& pts2,
                          Mat& R, Mat& t)
{
    // 求质心
    Point3f p1, p2;
    int N = pts1.size();
    for (int i=0; i<N; i++)
    {
        p1 += pts1[i];
        p2 += pts2[i];
    }
    p1 /= N;
    p2 /= N;

    // 去质心
    vector<Point3f> q1(N), q2(N);
    for (int i=0; i<N; i++)
    {
        q1[i] = pts1[i] - p1;
        q2[i] = pts2[i] - p2;
    }

    // 计算 q1*q2^T
    Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    for (int i=0; i<N; i++)
    {
        W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x,
                q2[i].y, q2[i].z).transpose();
    }
    cout << "W=" << W << endl;

    // 对W进行SVD求解Rt
    Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
    Eigen::Matrix3d U = svd.matrixU();
    Eigen::Matrix3d V = svd.matrixV();
    cout << "U=" << U << endl;
    cout << "V=" << V << endl;

    Eigen::Matrix3d R_ = U * (V.transpose());
    Eigen::Vector3d t_ = Eigen::Vector3d(p1.x, p1.y, p1.z) - R_ * Eigen::Vector3d(p2.x, p2.y, p2.z);

    // Eigen 转换成 cv::Mat
    R = (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 = (Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
}

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

/// <summary>
/// 通过svd分解求解旋转和平移
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
/// <returns>返回值为4*4变换矩阵T</returns>
Eigen::Matrix4d best_fit_transform(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B) {
    /*
    Notice:
    1/ JacobiSVD return U,S,V, S as a vector, "use U*S*Vt" to get original Matrix;
    2/ matrix type 'MatrixXd' or 'MatrixXf' matters.
    */
    Eigen::Matrix4d T = Eigen::MatrixXd::Identity(4, 4);
    Eigen::Vector3d centroid_A(0, 0, 0);
    Eigen::Vector3d centroid_B(0, 0, 0);
    Eigen::MatrixXd AA = A;
    Eigen::MatrixXd BB = B;
    int row = A.rows();

    for (int i = 0; i < row; i++) {
        centroid_A += A.block<1, 3>(i, 0).transpose();
        centroid_B += B.block<1, 3>(i, 0).transpose();
    }
    centroid_A /= row;
    centroid_B /= row;
    for (int i = 0; i < row; i++) {
        AA.block<1, 3>(i, 0) = A.block<1, 3>(i, 0) - centroid_A.transpose();
        BB.block<1, 3>(i, 0) = B.block<1, 3>(i, 0) - centroid_B.transpose();
    }

    Eigen::MatrixXd H = AA.transpose() * BB;
    Eigen::MatrixXd U;
    Eigen::VectorXd S;
    Eigen::MatrixXd V;
    Eigen::MatrixXd Vt;
    Eigen::Matrix3d R;
    Eigen::Vector3d t;

    JacobiSVD<Eigen::MatrixXd> svd(H, ComputeFullU | ComputeFullV);
    U = svd.matrixU();
    S = svd.singularValues();
    V = svd.matrixV();
    Vt = V.transpose();
    R = Vt.transpose() * U.transpose();

    if (R.determinant() < 0) {
        Vt.block<1, 3>(2, 0) *= -1;
        R = Vt.transpose() * U.transpose();
    }

    t = centroid_B - R * centroid_A;

    T.block<3, 3>(0, 0) = R;
    T.block<3, 1>(0, 3) = t;
    return T;

}

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

代码:

#include <iostream>
#include <numeric>
#include "icp.h"
#include "Eigen/Eigen"

using namespace std;
using namespace Eigen;


/// <summary>
/// 通过svd分解求解旋转和平移
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
/// <returns>返回值为4*4变换矩阵T</returns>
Eigen::Matrix4d best_fit_transform(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B) {
    /*
    Notice:
    1/ JacobiSVD return U,S,V, S as a vector, "use U*S*Vt" to get original Matrix;
    2/ matrix type 'MatrixXd' or 'MatrixXf' matters.
    */
    Eigen::Matrix4d T = Eigen::MatrixXd::Identity(4, 4);
    Eigen::Vector3d centroid_A(0, 0, 0);
    Eigen::Vector3d centroid_B(0, 0, 0);
    Eigen::MatrixXd AA = A;
    Eigen::MatrixXd BB = B;
    int row = A.rows();

    for (int i = 0; i < row; i++) {
        centroid_A += A.block<1, 3>(i, 0).transpose();
        centroid_B += B.block<1, 3>(i, 0).transpose();
    }
    centroid_A /= row;
    centroid_B /= row;
    for (int i = 0; i < row; i++) {
        AA.block<1, 3>(i, 0) = A.block<1, 3>(i, 0) - centroid_A.transpose();
        BB.block<1, 3>(i, 0) = B.block<1, 3>(i, 0) - centroid_B.transpose();
    }

    Eigen::MatrixXd H = AA.transpose() * BB;
    Eigen::MatrixXd U;
    Eigen::VectorXd S;
    Eigen::MatrixXd V;
    Eigen::MatrixXd Vt;
    Eigen::Matrix3d R;
    Eigen::Vector3d t;

    JacobiSVD<Eigen::MatrixXd> svd(H, ComputeFullU | ComputeFullV);
    U = svd.matrixU();
    S = svd.singularValues();
    V = svd.matrixV();
    Vt = V.transpose();
    R = Vt.transpose() * U.transpose();

    if (R.determinant() < 0) {
        Vt.block<1, 3>(2, 0) *= -1;
        R = Vt.transpose() * U.transpose();
    }

    t = centroid_B - R * centroid_A;

    T.block<3, 3>(0, 0) = R;
    T.block<3, 1>(0, 3) = t;
    return T;

}

/*
typedef struct{
    Eigen::Matrix4d trans;
    std::vector<float> distances;
    int iter;
}  ICP_OUT;
*/

ICP_OUT icp(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, int max_iterations, int tolerance) {
    int row = A.rows();
    Eigen::MatrixXd src = Eigen::MatrixXd::Ones(3 + 1, row);
    Eigen::MatrixXd src3d = Eigen::MatrixXd::Ones(3, row);
    Eigen::MatrixXd dst = Eigen::MatrixXd::Ones(3 + 1, row);
    NEIGHBOR neighbor;
    Eigen::Matrix4d T;
    Eigen::MatrixXd dst_chorder = Eigen::MatrixXd::Ones(3, row);
    ICP_OUT result;
    int iter = 0;

    for (int i = 0; i < row; i++) {
        src.block<3, 1>(0, i) = A.block<1, 3>(i, 0).transpose();
        src3d.block<3, 1>(0, i) = A.block<1, 3>(i, 0).transpose();
        dst.block<3, 1>(0, i) = B.block<1, 3>(i, 0).transpose();
    }

    double prev_error = 0;
    double mean_error = 0;
    for (int i = 0; i < max_iterations; i++) 
    {
        neighbor = nearest_neighbot(src3d.transpose(), B);
        for (int j = 0; j < row; j++) 
        {
            dst_chorder.block<3, 1>(0, j) = dst.block<3, 1>(0, neighbor.indices[j]);
        }
        T = best_fit_transform(src3d.transpose(), dst_chorder.transpose());
        src = T * src;
        for (int j = 0; j < row; j++) 
        {
            src3d.block<3, 1>(0, j) = src.block<3, 1>(0, j);
        }
        mean_error = std::accumulate(neighbor.distances.begin(), neighbor.distances.end(), 0.0) / neighbor.distances.size();
        if (abs(prev_error - mean_error) < tolerance) 
        {
            break;
        }
        prev_error = mean_error;
        iter = i + 2;
    }

    T = best_fit_transform(A, src3d.transpose());
    result.trans = T;
    result.distances = neighbor.distances;
    result.iter = iter;

    return result;
}

/*
typedef struct{
    std::vector<float> distances;
    std::vector<int> indices;
} NEIGHBOR;
*/

NEIGHBOR nearest_neighbot(const Eigen::MatrixXd& src, const Eigen::MatrixXd& dst) {
    int row_src = src.rows();
    int row_dst = dst.rows();
    Eigen::Vector3d vec_src;
    Eigen::Vector3d vec_dst;
    NEIGHBOR neigh;
    float min = 100;
    int index = 0;
    float dist_temp = 0;

    for (int ii = 0; ii < row_src; ii++) {
        vec_src = src.block<1, 3>(ii, 0).transpose();
        min = 100;
        index = 0;
        dist_temp = 0;
        for (int jj = 0; jj < row_dst; jj++) {
            vec_dst = dst.block<1, 3>(jj, 0).transpose();
            dist_temp = dist(vec_src, vec_dst);
            if (dist_temp < min) {
                min = dist_temp;
                index = jj;
            }
        }
        // cout << min << " " << index << endl;
        // neigh.distances[ii] = min;
        // neigh.indices[ii] = index;
        neigh.distances.push_back(min);
        neigh.indices.push_back(index);
    }

    return neigh;
}


float dist(const Eigen::Vector3d& pta, const Eigen::Vector3d& ptb) {
    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]));
}

截图: 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RocketMQ——Mac电脑OS系统docker安装Dashboard

文章目录引言安装下载dashboard镜像docker pull镜像查看镜像运行容器启动容器查看容器日志问题解决方案解决方案说明登录dashboard界面关注微信公众号&#xff1a;CodingTechWork&#xff0c;一起学习进步。引言 前面的文章已经介绍过如何在OS系统上安装并启动使用RocketMQ&…

Canal 安装与入门

MySQL Binlog 简介 https://blog.csdn.net/weixin_44371237/article/details/127904514 MySQL 主从复制过程 1&#xff09;Master 主库将改变记录&#xff0c;写到二进制日志(Binary Log)中&#xff1b; 2&#xff09;Slave 从库向 MySQL Master 发送 dump 协议&#xff0c…

基于QT的考试管理系统设计与实现

目录 一、项目概要 4 1.1项目名称 4 1.2项目目标 4 1.3软件概要 4 1.4功能描述 5 1.5开发环境 5 1.6关键技术 6 1.7开发体制 6 1.8开发阶段 6 二、软件详细需求 7 2.1学生登陆主界面 7 2.2管理员登陆主界面 8 2.3 学生考试系统实现 9 2.4学生练习系统实现 10 2.5试题管理系统实…

mongoDB mapreduce使用总结

大家都知道&#xff0c;mongodb是一个非关系型数据库&#xff0c;也就是说&#xff0c;mongodb数据库中的每张表是独立存在的&#xff0c;表与表之间没有任何依赖关系。在mongodb中&#xff0c;除了各种CRUD语句之外&#xff0c;还给我们提供了聚合和mapreduce统计的功能&#…

JVM 彻底搞懂JVM内存区域及直接内存

面试题&#xff1a;直接内存会导致OOM么&#xff1f; 程序计数器 代表当前线程所执行的字节码所在的行号&#xff0c;配合字节码解释器获取下一条需要执行的字节码指令。 代码中的分支、循环、跳转、异常处理、线程恢复都要依靠它来实现。 程序计数器是线程私有的&#xff0…

进程控制的一些具体操作

目录进程控制进程终止进程退出的方式进程等待进程等待的方法wait使用方法waitpid使用方法进程程序替换替换函数execl函数execv函数execlp函数execvp函数execle函数execve函数---->只有这一个是系统调用&#xff0c;其他都是库函数execvpe函数补充几个知识: %s/被替换的文件…

代码随想录——冗余连接II(并查集)

题目 在本问题中&#xff0c;有根树指满足以下条件的 有向 图。该树只有一个根节点&#xff0c;所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点&#xff0c;而根节点没有父节点。 输入一个有向图&#xff0c;该图由一个有着 n 个节点&am…

vb.net自定义白板

希沃白板在学校里基本上是一直使用的&#xff0c;但是在非希沃电脑里面是没有启动白板的 简答介绍思路和具体的功能 1、背景颜色和画笔颜色自由切换、画笔粗细1~20可以调节。 2、画笔样式&#xff1a;虚线、点线、短线 3、基本图形&#xff1a;矩形&#xff0c;正方形&…

程序员级别分析,看看你是哪个级别

关于程序员的工资众说纷纭&#xff0c;有说开七八千的&#xff0c;也有人说每月上万的&#xff0c;但不管怎么说&#xff0c;程序员的工资是真的比一些文职、行政人员岗位挣得多&#xff0c;大家都是靠自己的能力赚钱&#xff0c;这没什么可比的&#xff0c;况且大家都是在学习…

JAVASE零基础到高级教程(1)------ 集成开发环境安装使用

一 什么是环境变量 环境变量是在操作系统中⼀个具有特定名字的对象&#xff0c;它包含了⼀个或者多个应⽤程序所将使⽤到的 信息。例如Windows和DOS操作系统中的path环境变量&#xff0c;当要求系统运⾏⼀个程序⽽没有告诉它程 序所在的完整路径时&#xff0c;系统除了在当前⽬…

前端框架 Electron 使用总结

目录 一、基础搭建 通过脚手架搭建 1、Electron官方案例搭建环境 2、查看调试 3、菜单的使用 4、图标配置 5、项目打包 web应用相信每位程序员都不陌生&#xff0c;PC端应用可能会底层开发的就不是太多了&#xff0c;下面的这套技术栈就是为前端程序员快速一键搭建windo…

Linux学习——网络编程基础及TCP服务器

目录 一、网络采用分层的思想&#xff1a; 二、各层典型的协议&#xff1a; 三、网络的封包和拆包&#xff1a; 四、网络编程的预备知识 4.1.SOCKET 4.2 IP地址 4.3 端口号 4.4 字节序 五、TCP编程API TCP协议分成了两个不同的协议&#xff1a;可靠传输&#xff1a;用来检测网络…

读书笔记-学习GNU Emacs-3终篇

学习本书目的&#xff1a; emacs的学习一直是陆陆续续看博客和上手实践&#xff0c;这次想通过阅读"学习GNU Emacs"这本书好好系统的再复习下emacs。 yps:读技术书应该是带着一定的目的去读的&#xff0c;最简单的目的可能就是为了学好某一项技术或者复习下某一项技…

[附源码]java毕业设计社区健康服务平台管理系统lunwen

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

IDEA利用maven建立javaWeb项目(IDEA 2021.3.3)

1、在Idea中配置maven (1)、打开Idea&#xff0c;点击File&#xff0c;然后点击Settings&#xff0c;进入设置&#xff0c;或者直接按CtrlAltS进入设置 (2)、先在左上角的搜索框输入maven&#xff0c;找到maven后单击&#xff0c;然后在右边的maven home path的右边选择你的m…

置信度--学习笔记

置信区间是衡量测量精度的一个指标&#xff0c;也能显示出估算有多稳定&#xff0c;也就是说如果重复做某项实验&#xff0c;得到的结果与最初的估计有多接近。步骤&#xff1a; 确定要测试的情况&#xff1a;如“A大学男生的平均体重是80公斤”&#xff0c;则后续就是要测试在…

最新最全面的Spring详解(三)——Resources,验证、数据绑定和类型转换与Spring表达式语言(SpEL)

前言 本文为 【Spring】Resources与Spring表达式语言&#xff08;SpEL&#xff09; 等相关知识&#xff0c;下边将对Resources&#xff08;包含&#xff1a;Resource接口、内置的 Resource的实现、ResourceLoader接口、应用环境和资源路径&#xff09;&#xff0c;验证、数据绑…

浅谈化工生产制造企业软件系统的选择

现在大家都在讨论全球COVID流行和经济衰退对企业的影响&#xff0c;以及一个有作为的企业&#xff0c;在当下的环境下如何求生存和谋发展的问题。“埃森哲的一份报告发现&#xff0c;99%的首席运营官都认为&#xff0c;使用实时数据运营对于应对Covid或经济衰退威胁等至关重要。…

Java的JDBC编程

1. 数据库编程的必备条件 编程语言&#xff0c;如Java&#xff0c;C、C、Python等数据库&#xff0c;如Oracle&#xff0c;MySQL&#xff0c;SQL Server等数据库驱动包&#xff1a;不同的数据库&#xff0c;对应不同的编程语言提供了不同的数据库驱动包&#xff0c;如&#xf…

Telnet SMTP协议关于“535 Error: authentication failed“解决思路

计算机网络中应用层的SMTP(Simple Mail Transfer Protocol)协议可用来发送邮件&#xff0c;在Telnet使用SMTP登陆账号密码时出现“535 Error: authentication failed”问题。现记录解决步骤。 1. 确认在邮箱中已开启SMTP服务。 2. 按照扫码流程&#xff0c;获得授权密码&…