@[TOC]PCL中点云分割模块的学习
学习背景
参考书籍:《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,,PCL版本为1.10.0,CMake版本为3.16
学习内容
用一组点云做平面、圆柱分割
源代码及所用函数
源代码
//实现圆柱体模型的分割
#include<pcl/ModelCoefficients.h>//定义名为 pcl::ModelCoefficients 的类,用于存储模型的系数
#include<pcl/io/pcd_io.h>
#include<pcl/point_types.h>
#include<pcl/filters/extract_indices.h>//定义pcl::ExtractIndices类,该类可以根据给定的索引向量从输入点云中提取对应的点子集
#include<pcl/filters/passthrough.h>//定义 pcl::PassThrough 类,该类实现了直通滤波的功能
#include<pcl/features/normal_3d.h>//定义 pcl::NormalEstimation 类,用于计算点云法向量的类
#include<pcl/sample_consensus/method_types.h>//随机参数估计方法头文件
#include<pcl/sample_consensus/model_types.h>//定义 PCL 中用于随机采样一致性 (SAC) 方法的枚举类型
#include<pcl/segmentation/sac_segmentation.h>//提供 PCL 中用于基于随机采样一致性 (SAC) 方法进行点云分割的类和函数
int main(int argc,char** argv)
{
pcl::PCDReader reader;//PCD读取
pcl::PassThrough<pcl::PointXYZ> pass;//直通滤波
pcl::NormalEstimation<pcl::PointXYZ,pcl::Normal> ne;//法线估计对象
pcl::SACSegmentationFromNormals<pcl::PointXYZ,pcl::Normal> seg;//分割对象
pcl::PCDWriter writer;//PCD文件写入对象
pcl::ExtractIndices<pcl::PointXYZ> extract;//点读取对象
pcl::ExtractIndices<pcl::Normal> extract_normals;//法向量提取对象
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
/*****************************数据集*************************************************/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered2(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2(new pcl::PointCloud<pcl::Normal>);
pcl::ModelCoefficients::Ptr coeffcients_plane(new pcl::ModelCoefficients),coeffcients_cylinder(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers_plane(new pcl::PointIndices),inliers_cylinder(new pcl::PointIndices);
/*****************************读取点云*************************************************/
reader.read("/home/jojo/PointCloud/table_scene_mug_stereo_textured.pcd",*cloud);
std::cout << "点云中有:" << cloud->points.size() << "个点" << std::endl;
/*****************************直通滤波,将Z轴不在(0,1.5)范围的点过滤掉,将剩余的点存储到cloud_filtered对象中***************/
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");
pass.setFilterLimits(0,1.5);
pass.filter(*cloud_filtered);
std::cout << "滤波后点云还有:" << cloud_filtered->points.size() << "个数据点" <<std::endl;
/******************过滤后的点云进行法线估计,为后续进行基于法线的分割准备数据****************************/
ne.setSearchMethod(tree);
ne.setInputCloud(cloud_filtered);
ne.setKSearch(50);//设置法向量估计时使用的K近邻搜索方法,并将K的值设置为50。这意味着对于每个查询点,将在其50个最近邻域内搜索用于估计法向量的邻域点。
ne.compute(*cloud_normals);
/***********************************************为平面模型创建分割对象并设置所有参数**********************************/
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_NORMAL_PLANE);
seg.setNormalDistanceWeight(0.1);//设置法向量距离权重系数为0.1
seg.setMaxIterations(100);//设置随机采样一致性(RANSAC)算法的最大迭代次数为100。
seg.setDistanceThreshold(0.03);//这行代码设置了点到模型的距离阈值为0.03
seg.setInputCloud(cloud_filtered);
seg.setInputNormals(cloud_normals);
//获取平面模型的系数和处在平面内的点
seg.segment(*inliers_plane,*coeffcients_plane);
std::cout << "平面系数" << *coeffcients_plane << std::endl;
/**********************************************从点云中提取分割出处于平面上的点云****************************************/
extract.setInputCloud(cloud_filtered);
extract.setIndices(inliers_plane);
extract.setNegative(false);
//将点云保存
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>);
extract.filter(*cloud_plane);
std::cout << "代表平面组件的点云有" <<cloud_plane->points.size() << "个" << std::endl;
writer.write("table_scene_mug_stereo_textured_plane.pcd",*cloud_plane,false);
/**************************************************************移除离群点*******************************************/
extract.setNegative(true);
extract.filter(*cloud_filtered2);
extract_normals.setNegative(true);
extract_normals.setInputCloud(cloud_normals);
extract_normals.setIndices(inliers_plane);
extract_normals.filter(*cloud_normals2);
/***************************************************************创建圆柱体分割对象并设置所有参数**********************/
seg.setOptimizeCoefficients(true);//设置对估计模型优化
seg.setModelType(pcl::SACMODEL_CYLINDER);//设置分割模型为圆柱形
seg.setNormalDistanceWeight(0.1);//设置表面法线权重系数
seg.setMaxIterations(10000);//设置迭代的最大次数10000
seg.setDistanceThreshold(0.05);//设置内点到模型的距离允许最大值
seg.setRadiusLimits(0,0.1);//设置估计出的圆柱模型的半径的范围
seg.setInputCloud(cloud_filtered2);
seg.setInputNormals(cloud_normals2);
/*************************************************获取圆柱体离群值和系数*****************************************/
seg.segment(*inliers_cylinder,*coeffcients_cylinder);
std::cout << "圆柱系数" << *coeffcients_cylinder << std::endl;
//将圆柱体离群值写入磁盘
extract.setInputCloud(cloud_filtered2);
extract.setIndices(inliers_cylinder);
extract.setNegative(false);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cylinder(new pcl::PointCloud<pcl::PointXYZ>());
extract.filter(*cloud_cylinder);
if (cloud_cylinder->points.empty())
{
std::cerr << "找不到圆柱." << std::endl;
}
else
{
std::cerr << "代表圆柱形分量的点云: " << cloud_cylinder->points.size () << std::endl;
writer.write ("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder, false);
}
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)#指定CMake的最低版本要求为3.16
project(project)#设置项目名称
find_package(PCL 1.10 REQUIRED)#查找PCL库,要求版本为1.10或更高。
include_directories(${PCL_INCLUDE_DIRS})#将PCL库的头文件目录添加到包含路径中
link_directories(${PCL_LIBRARY_DIRS})#将PCL库的库文件目录添加到链接器搜索路径中。
add_definitions(${PCL_DEFINITIONS})#添加PCL库的编译器定义
add_executable (cylinder_segmentation cylinder_segmentation.cpp)
target_link_libraries (cylinder_segmentation ${PCL_LIBRARIES})#将PCL库链接到可执行文件目标。
运行结果
原始点云
获得的平面点云
获得的圆柱点云
函数
#include<pcl/features/normal_3d.h>
这个头文件定义了pcl::NormalEstimation
类,该类提供了多种计算点云法线的方法。包括:
可以指定计算法线的搜索方法,如 KD-Tree、Octree 等
可以设置搜索半径或者 K 近邻数量
支持不同的法线估计方法,如基于 PCA 的方法、基于平面拟合的方法等
可以选择是否使用 OpenMP 进行并行加速seg.setNormalDistanceWeight(0.1);
这行代码设置了法向量距离权重系数为0.1。
在平面模型分割中,法向量距离权重用于平衡点到平面的距离和法向量与平面法向量的夹角对分割结果的影响。
权重系数越大,法向量的影响越大,分割结果更倾向于考虑点的法向量方向;权重系数越小,点到平面的距离影响越大,分割结果更倾向于考虑点到平面的距离。通常,法向量距离权重系数的取值范围是[0, 1],需要根据具体问题和数据特点进行调整。seg.setMaxIterations(100);
这行代码设置了随机采样一致性(RANSAC)算法的最大迭代次数为100。
RANSAC算法是一种用于估计模型参数的鲁棒算法,通过随机采样和迭代的方式找到最佳的模型参数。最大迭代次数限制了RANSAC算法的迭代次数上限,以避免算法运行时间过长。迭代次数越大,算法寻找最佳模型的机会越多,但计算时间也越长;迭代次数越小,算法可能无法找到最佳模型,但计算时间较短。seg.setDistanceThreshold(0.03);
这行代码设置了点到模型的距离阈值为0.03。
在平面模型分割中,距离阈值用于判断一个点是否属于平面模型。如果一个点到估计的平面模型的距离小于等于距离阈值,则认为该点是平面的内点(inlier),否则认为是外点(outlier)。距离阈值的选择取决于点云的尺度和噪声水平。阈值越大,更多的点会被视为内点,分割结果可能会包含更多的噪声点;阈值越小,只有距离平面很近的点才会被视为内点,分割结果可能会更精确,但可能会漏掉一些真正属于平面的点。
补充内容
pcl::io::loadPCDFile
和pcl::PCDReader
的区别:pcl::io::loadPCDFile
用于直接从文件路径加载点云数据到pcl::PointCloud对象中。示例如下pcl::PointCloud<pcl::PointXYZ> cloud; pcl::io::loadPCDFile("path/to/file.pcd", cloud);
pcl::PCDReader
提供了更多的功能和控制选项。它需要先创建一个pcl::PCDReader
对象,然后调用相应的成员函数来读取PCD文件。pcl::PointCloud<pcl::PointXYZ> cloud; pcl::PCDReader reader; // 设置读取选项(可选) reader.setKeepOrganized(true); // 从文件路径读取点云数据 reader.read("path/to/file.pcd", cloud);