ICP(迭代最近点)迭代过程的单步可视化程序

ICP(迭代最近点)迭代过程的单步可视化程序

一个逐次迭代点云的程序, 可以逐步显示icp迭代的过程, 观察到点云逐步靠近的过程.

其中红色点云为target点云, 蓝色点云为带先验位姿的source点云, 绿色为无先验位姿的source点云

在程序中, 先验位姿存与实际变换之间的变化为, x, y, z轴误差5厘米, 三轴旋转均为0.05弧度

在程序运行后, 会在当前目录创建一个result.txt的文件, 保存的是初始变换, 即最终匹配应该输出的矩阵,

可以将控制台输出的矩阵与该文件中的矩阵进行比较, 判断对齐效果,

注: 有的值会是相反数, 因为旋转次序或方向可能不一样
测试数据集没记错的话应该是 frame005.pcd

运行结果

在这里插入图片描述

运行: 以ubuntu为例

mkdir ~/icp_test -p
cd ~/icp_test
vim main.cpp # 将下面cpp代码拷贝进来, 注意修改pcd文件的路径
vim CMakeLists.txt #将下面的CMakelist拷贝进来
cmake .
make
./icp_example

main.cpp

#include <iostream>
#include <fstream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/transforms.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/filter.h>
#include <pcl/filters/voxel_grid.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Dense>
#include <boost/random.hpp>
#include <chrono>
#include <thread>Eigen::Matrix4f generateRandomTransformation() {unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();boost::random::mt19937 gen(seed);  // 随机数生成器boost::random::uniform_real_distribution<> dis(-0.5, 0.5);  // 位移范围boost::random::uniform_real_distribution<> angle_dis(-M_PI, M_PI);  // 旋转范围Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();transform(0, 3) = dis(gen); // X轴平移transform(1, 3) = dis(gen); // Y轴平移transform(2, 3) = dis(gen); // Z轴平移// 绕Z轴旋转float angle = angle_dis(gen);transform(0, 0) = cos(angle);transform(0, 1) = -sin(angle);transform(1, 0) = sin(angle);transform(1, 1) = cos(angle);return transform;
}void saveTransformation(const Eigen::Matrix4f &transform, const std::string &filename) {std::ofstream file(filename);if (file.is_open()) {file << transform;file.close();}
}Eigen::Matrix4f generateCloseTransformation(const Eigen::Matrix4f &original) {// 单位 [m]Eigen::Matrix4f closeTransform = original;closeTransform(0, 3) += 0.05; // X轴微调closeTransform(1, 3) += 0.05; // Y轴微调closeTransform(2, 3) += 0.05; // Z轴微调// 单位 [rad]Eigen::Matrix3f rotation = closeTransform.block<3, 3>(0, 0);Eigen::AngleAxisf rotationX(0.05, Eigen::Vector3f::UnitX());Eigen::AngleAxisf rotationY(0.05, Eigen::Vector3f::UnitY());Eigen::AngleAxisf rotationZ(0.05, Eigen::Vector3f::UnitZ());rotation *= (rotationX * rotationY * rotationZ).matrix();closeTransform.block<3, 3>(0, 0) = rotation;return closeTransform;
}int main() {// 加载点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ>("/home/smile/ros/icp/test.pcd", *cloud_in) == -1) {PCL_ERROR("Couldn't read file test.pcd \n");return -1;}// 移除NaN值std::vector<int> indices;pcl::removeNaNFromPointCloud(*cloud_in, *cloud_in, indices);// 进行体素滤波pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;voxel_grid.setInputCloud(cloud_in);voxel_grid.setLeafSize(0.08f, 0.08f, 0.08f);  // 设置体素大小pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);voxel_grid.filter(*cloud_filtered);// 生成变换并保存到文件Eigen::Matrix4f base_transformation = generateRandomTransformation();Eigen::Matrix4f base_transformation_prior = base_transformation;std::cout << "Base Transformation Matrix:\n" << base_transformation << std::endl;saveTransformation(base_transformation, "/home/smile/ros/icp/result.txt");// 生成接近的变换作为先验位姿Eigen::Matrix4f prior_pose = generateCloseTransformation(base_transformation);// 应用初始变换pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_transformed(new pcl::PointCloud<pcl::PointXYZ>);pcl::transformPointCloud(*cloud_filtered, *cloud_transformed, base_transformation);// 设置ICP实例pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp, icp_with_prior;icp.setInputSource(cloud_transformed);icp.setInputTarget(cloud_filtered);icp.setMaximumIterations(1); // 每次调用align时执行一次迭代icp_with_prior.setInputSource(cloud_transformed);icp_with_prior.setInputTarget(cloud_filtered);icp_with_prior.setMaximumIterations(1);// 初始化可视化pcl::visualization::PCLVisualizer viewer("ICP demo");viewer.setBackgroundColor(0, 0, 0);viewer.addPointCloud<pcl::PointXYZ>(cloud_filtered, "cloud_filtered");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_filtered");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "cloud_filtered");pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp(new pcl::PointCloud<pcl::PointXYZ>(*cloud_transformed));pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp_prior(new pcl::PointCloud<pcl::PointXYZ>(*cloud_transformed));viewer.addPointCloud<pcl::PointXYZ>(cloud_icp, "cloud_icp");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_icp");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 0.0, "cloud_icp");viewer.addPointCloud<pcl::PointXYZ>(cloud_icp_prior, "cloud_icp_prior");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_icp_prior");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 0.0, 1.0, "cloud_icp_prior");viewer.addCoordinateSystem(1.0);viewer.initCameraParameters();// 创建初始变换的矩阵, 其中无先验位姿的是一个单位阵Eigen::Matrix4f icp_result = Eigen::Matrix4f::Identity();// 先验的矩阵为之前生成的, 在实际变换的基础上添加了扰动的变换矩阵Eigen::Matrix4f icp_result_prior = prior_pose;// 计数器int icp_cnt = 0; // icp迭代次数int icp_prior_cnt = 0; // 先验icp迭代次数bool icp_fitness_reached = false; bool icp_prior_fitness_reached = false;int iteration_counter = 0;  // 迭代频率计数器, 迭代的频率按照 10ms x iteration_counter 可以在下面的循环中修改while (!viewer.wasStopped()) {// 如果都完成了收敛, 则不再更新if(icp_fitness_reached && icp_prior_fitness_reached) continue;viewer.spinOnce();  // 图像化界面刷新频率10ms, 方便使用鼠标进行控制视角 std::this_thread::sleep_for(std::chrono::milliseconds(10));// 创建icp之后的新点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp_it(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp_prior_it(new pcl::PointCloud<pcl::PointXYZ>);// 每10ms x 100 = 1000ms = 1s 即每1秒做一次icp并更新点云if (++iteration_counter >= 100) {// 如果没有达到0.0001的分值, 则icp继续迭代if(!icp_fitness_reached){// 对普通ICP和带有先验位姿的ICP进行迭代icp.align(*cloud_icp_it,icp_result);icp_result = icp.getFinalTransformation();// 检查是否收敛(肯定收敛, 因为最多迭代1次,所以每一次都会收敛)if (icp.hasConverged()) {double fitness_score = icp.getFitnessScore();if(icp_fitness_reached) icp_cnt=icp_cnt;else icp_cnt += 1;std::cout << "[ICP] 分数为 " << fitness_score <<std::endl;// 获取最新一次的变换, 并将该变换应用到带先验的点云上, 更新该点云base_transformation = icp.getFinalTransformation().cast<float>();pcl::transformPointCloud(*cloud_transformed, *cloud_icp_it, base_transformation);viewer.updatePointCloud<pcl::PointXYZ>(cloud_icp_it, "cloud_icp");//真正的停止条件(收敛条件)if(fitness_score<=0.001){icp_fitness_reached = true;std::cout << "======================================================="<<std::endl;std::cout << "[ICP]完成收敛 " <<std::endl;std::cout << "[ICP]迭代次数为 " << icp_cnt <<std::endl;std::cout << "[ICP]变换矩阵 " << std::endl;std::cout << icp.getFinalTransformation() << std::endl;std::cout << "======================================================="<<std::endl;}     }}       if(!icp_prior_fitness_reached){icp_with_prior.align(*cloud_icp_prior_it, icp_result_prior);icp_result_prior = icp_with_prior.getFinalTransformation();// 同理, 这里并不是真正的停止条件if (icp_with_prior.hasConverged()) {double fitness_score_prior = icp_with_prior.getFitnessScore();if(icp_prior_fitness_reached) icp_prior_cnt = icp_prior_cnt;else icp_prior_cnt += 1;std::cout << "[ICP+先验] 分数为 " << fitness_score_prior <<std::endl;// 带先验的停止条件也是0.0001分以下终止if(fitness_score_prior<=0.001){icp_prior_fitness_reached = true;std::cout << "======================================================="<<std::endl;std::cout << "[ICP+先验]完成收敛 " <<std::endl;std::cout << "[ICP+先验]迭代次数为 " << icp_prior_cnt <<std::endl;std::cout << "[ICP+先验]变换矩阵 " <<std::endl;std::cout << icp_with_prior.getFinalTransformation() << std::endl;std::cout << "======================================================="<<std::endl;}// 获取最新一次的变换, 并将该变换应用到带先验的点云上, 更新该点云base_transformation_prior = icp_with_prior.getFinalTransformation().cast<float>();pcl::transformPointCloud(*cloud_transformed, *cloud_icp_prior_it, base_transformation_prior);viewer.updatePointCloud<pcl::PointXYZ>(cloud_icp_prior_it, "cloud_icp_prior"); }}// 重置迭代计数器iteration_counter = 0;}}
return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(icp_example)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)find_package(PCL 1.8 REQUIRED)include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})add_executable(icp_example main.cpp)
target_link_libraries(icp_example ${PCL_LIBRARIES})

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TS 36.213 V12.0.0-PMCH相关过程

​本文的内容主要涉及TS 36.213&#xff0c;版本是C00&#xff0c;也就是V12.0.0。

全自动网页生成系统网站源码重构版

源码优点: 所有模板经过精心审核与修改&#xff0c;完美兼容小屏手机大屏手机&#xff0c;以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 免费制作 为用户使用方便考虑&#xff0c;全自动网页制作系统无需繁琐的注册与登入&#xff0c;直…

12. SSM整合

1.新建一个maven项目,添加web支持 创建项目 设定项目名 右键添加框架支持: 添加web应用支持: 完成后目录结构: 2.添加jar包依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0…

linux(ubuntu)中crontab定时器命令详解 以及windows中定时器

linux&#xff08;ubuntu&#xff09;中crontab定时器命令详解 crontab 是一个用于创建、编辑和管理用户的定时任务的命令&#xff0c;它可以让用户在指定的时间自动执行指定的命令或脚本。 基本语法 -e&#xff1a;编辑用户的 crontab 文件&#xff1b;-l&#xff1a;列出用…

Apache ECharts | 一个数据可视化图表库

文章目录 1、简介1.1、主要特点1.2、使用场景 2、安装方式一&#xff1a;从下载的源代码或编译产物安装方法二&#xff1a;从 npm 安装方法三&#xff1a;⭐定制安装echarts.js 3、使用 官网&#xff1a; 英语&#xff1a;https://echarts.apache.org/en/index.html 中文&a…

ChatGPT知名开源项目有哪些

ChatGPT-Next-Web&#xff1a;基于ChatGPT API的私有化部署网页聊天系统 主要功能&#xff1a; 只需在 1 分钟内即可在 Vercel 上一键免费部署&#xff0c;支持私有服务器快速部署&#xff0c;支持使用私有域名支持ChatGPT3.5、4等常见模型Linux/Windows/MacOS 上的紧凑型客户…

NAS使用的一些常见命令 ssh sftp 上传 下载 ALL in one

目录 登陆上传/下载内网穿透 登陆 ssh 登陆 ssh usernameserverIP -p portNumsftp 登陆 sftp -P portNum usernameserverIP上传/下载 如ls等&#xff0c;远程服务器操作 如lls等&#xff0c;本机操作&#xff0c;前缀为l 文件 put **** 将本机上文件上传到远程服务器上当…

Web开发SpringBoot SpringMVC Spring的学习笔记(包含开发常用工具类)

开发框架学习笔记 一.Spring SpringMVC SpringBoot三者的联系SpringMVC工作原理 二.SpringBoot的学习框架学习 2.各个类之间的继承和实现关系3.理解面向对象的思想(其实这个想写在2中的)四.开发常用工具Lombok4.0说在前面(如何快速使用Lombok)4.1了解Lombok4.2Lombok的作用一:减…

拍拍贷数据分析-逾期情况分析

数据背景 所提供数据来自拍拍贷真实业务数据&#xff0c;从2015-01-01到2017-01-30的所有信用标的10%sample样本。数据集包含LC.csv&#xff08;标的特征表数据&#xff09;和LP.csv&#xff08;标的还款计划和还款记录表&#xff09;数据。详情如下&#xff1a; 数据字典 1.LC…

亚信安全深度解读2023年中国网络安全重要政策法规

亚信安全在对2023年国内网络安全政策的持续跟踪和研究基础上进行了详细分析。观察整体态势&#xff0c;本年度网络安全政策的发布呈现出高密度特征&#xff0c;共计引起行业高度关注的政策达50余项。数据安全领域、个人信息保护和数据跨境安全成为关注的热点&#xff0c;分别有…

初识 Elasticsearch 应用知识,一文读懂 Elasticsearch 知识文集(2)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

计算机毕业设计-----ssm+mysql医药进销存系统

功能介绍 医药进销存系统&#xff0c;主要功能包括&#xff1a; 公告管理&#xff1a;发布公告、公告列表&#xff1b; 生产管理&#xff1a;订单列表、增加生产、订单日志&#xff1b; 分店采购&#xff1a;分店审核、采购&#xff1b; 总店仓库&#xff1a;出库管理、仓库列…