PCL点云分割(2)

关于点云的分割算是我想做的机械臂抓取中十分重要的俄一部分,所以首先学习如果使用点云库处理我用kinect获取的点云的数据,本例程也是我自己慢慢修改程序并结合官方API 的解说实现的,其中有很多细节如果直接更改源程序,可能会因为数据类型,或者头文件等各种原因编译不过,会导致我们比较难得找出其中的错误,首先我们看一下我自己设定的一个场景,然后我用kinect获取数据

观察到kinect获取的原始图像的,然后使用简单的滤波,把在其中的NANS点移除,因为很多的算法要求不能出现NANS点,我们可以看见这里面有充电宝,墨水,乒乓球,一双筷子,下面是两张纸,上面分别贴了两道黑色的胶带,我们首先就可以做一个提取原始点云的平面的实验,那么如果提取点云中平面,之前有一些基本的实例,使用平面分割法

程序如下

#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/console/parse.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_plane.h>
#include <pcl/sample_consensus/sac_model_sphere.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>

int
 main (int argc, char** argv)
{
  // 读取文件 
  pcl::PCDReader reader;
  pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGBA>), cloud_f (new pcl::PointCloud<pcl::PointXYZRGBA>);
 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr final (new pcl::PointCloud<pcl::PointXYZRGBA>);
  reader.read ("out0.pcd", *cloud);
  std::cout << "PointCloud before filtering has: " << cloud->points.size () << " data points." << std::endl; //*

  // 下采样,体素叶子大小为0.01
  pcl::VoxelGrid<pcl::PointXYZRGBA> vg;
  pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZRGBA>);
  vg.setInputCloud (cloud);
  vg.setLeafSize (0.01f, 0.01f, 0.01f);
  vg.filter (*cloud_filtered);
  std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
  pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
  // Create the segmentation object
  pcl::SACSegmentation<pcl::PointXYZRGBA> seg;
  // Optional
  seg.setOptimizeCoefficients (true);
  // Mandatory
  seg.setModelType (pcl::SACMODEL_PLANE);
  //  seg.setModelType (pcl::SACMODEL_LINE );
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setDistanceThreshold (0.01);

  seg.setInputCloud (cloud_filtered);
  seg.segment (*inliers, *coefficients);

  if (inliers->indices.size () == 0)
  {
    PCL_ERROR ("Could not estimate a planar model for the given dataset.");
    return (-1);
  }

  std::cerr << "Model coefficients: " << coefficients->values[0] << " " 
                                      << coefficients->values[1] << " "
                                      << coefficients->values[2] << " " 
                                     << coefficients->values[3] <<std::endl;
  return (0);
}

运行生成的可执行文件会输出平面模型的参数

                                                               平面模型的参数

                                                                      此图是采样后的点云图

也可以在这个程序中直接实现平面的提取,但是为了更好的说明,我是将获取平面参数与平面提取给分成两个程序实现,程序如下

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/ModelCoefficients.h>
#include <pcl/filters/project_inliers.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>


boost::shared_ptr<pcl::visualization::PCLVisualizer>
simpleVis (pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{

  boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
  viewer->setBackgroundColor (0, 0, 0);
  viewer->addPointCloud<pcl::PointXYZ> (cloud, "project_inliners cloud");
  viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
  //viewer->addCoordinateSystem (1.0, "global");
  viewer->initCameraParameters ();
  return (viewer);
}


int
 main (int argc, char** argv)
{
   // 读取文件 
  pcl::PCDReader reader;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_projected (new pcl::PointCloud<pcl::PointXYZ>);

 pcl::PointCloud<pcl::PointXYZ>::Ptr final (new pcl::PointCloud<pcl::PointXYZ>);
  reader.read ("out0.pcd", *cloud);
  std::cout << "PointCloud before filtering has: " << cloud->points.size () << " data points." << std::endl; //*

  // 下采样,体素叶子大小为0.01
  pcl::VoxelGrid<pcl::PointXYZ> vg;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
  vg.setInputCloud (cloud);
  vg.setLeafSize (0.01f, 0.01f, 0.01f);
  vg.filter (*cloud_filtered);
  std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*

  // Create a set of planar coefficients with X=Y=
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());
  coefficients->values.resize (4);
  coefficients->values[0] = 0.140101;
  coefficients->values[1] = 0.126715;
  coefficients->values[2] = 0.981995;
  coefficients->values[3] = -0.702224;

  // Create the filtering object
  pcl::ProjectInliers<pcl::PointXYZ> proj;
  proj.setModelType (pcl::SACMODEL_PLANE);
  proj.setInputCloud (cloud_filtered);
  proj.setModelCoefficients (coefficients);
  proj.filter (*cloud_projected);

 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
  viewer = simpleVis(cloud_projected);
  while (!viewer->wasStopped ())
  {
    viewer->spinOnce (100);
    boost::this_thread::sleep (boost::posix_time::microseconds (100000));
  }

  return (0);
}

执行结果就如下

提取了平面,**********************8

微信公众号号可扫描二维码一起共同学习交流

<think>首先,用户的问题是:“我想使用PCL进行点云分割以提取各个组件 请问PCL 点云分割 提取组件”。这涉及到点云分割,目的是提取点云中的不同组件。 我需要参考提供的引用内容,这些引用是关于点云分割的,特别是PCL库中的方法。引用包括: - 引用[1]: PCL点云分割库包含多种算法,用于将点云分割为不同簇,适合处理由多个隔离区域空间组成的点云。提到平面模型分割和圆柱模型分割的结果。 - 引用[2]: PCL提供10来种点云分割的方法,点云识别的深度学习需要好的点云分割器。 - 引用[3]: 点云分割是物体识别与分类的一个步骤,最终目的是物体识别和分类。 - 引用[4]: 描述基于区域生长的分割算法流程,包括设置聚类区域、种子点序列、法线夹角和曲率阈值。 - 引用[5]: 提到基于欧氏距离、平滑度、RGB颜色的分割方法,用户可自定义约束条件,但存在缺陷如无初始种子、过分割或欠分割。 用户的需求是使用PCL进行点云分割以提取各个组件。所以,我应该提供一个清晰的步骤,解释如何使用PCL来实现这一点。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 结构上,我应该: 1. 介绍点云分割的基本概念。 2. 解释PCL中的点云分割方法。 3. 提供具体步骤或代码示例如何使用PCL提取组件。 4. 基于引用,讨论相关方法。 5. 在末尾生成相关问题。 从引用中,我可以看出PCL有几种分割方法,如基于区域生长(引用[4])、基于欧氏距离(引用[5])等。 关键方法: - 基于区域生长的分割:使用种子点、法线夹角和曲率阈值来聚类点。 - 其他方法如平面模型分割、圆柱模型分割(引用[1])。 在回答中,我应该逐步引导用户: 1. **理解点云分割**:点云分割是将点云分成多个部分的过程,每个部分代表一个对象或组件。 2. **选择PCL方法**:PCL提供多种算法,如区域生长分割、欧氏聚类分割等。 3. **具体实现**:提供一个代码示例或伪代码,展示如何使用这些方法。 4. **注意事项**:讨论可能的缺陷,如过分割或欠分割(引用[5])。 5. **生成相关问题**:在末尾添加相关问题。 由于用户提到“提取各个组件”,这类似于聚类或分割点云中的不同对象。 一个常见的方法是欧氏聚类分割(Euclidean Cluster Extraction),这在PCL中很流行。 从引用[4]和[5],区域生长和条件聚类是选项。 让我草拟回答结构: - 开头:解释点云分割的目的。 - 步骤1:安装和准备PCL环境。 - 步骤2:加载点云数据。 - 步骤3:选择分割方法(例如,欧氏聚类分割或区域生长)。 - 步骤4:设置参数(如距离阈值、种子点等)。 - 步骤5:执行分割并提取组件。 - 步骤6:后处理或可视化。 - 结尾:总结和生成相关问题。 使用代码示例:引用[4]提到部分代码,我可以提供一个简单的PCL代码片段。 在LaTeX中,代码块应该用```...```格式,如示例中所示。 数学表达式:如果涉及,如距离计算,使用$...$。 例如,欧氏距离:$d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}$。 在引用中,没有具体的数学公式,但如果有,我会加入。 引用标识:在回答中,当引用特定内容时添加[^n]。 例如,当提到区域生长算法时,引用[4]。 生成相关问题:类似于示例,3-5个问题。 例如: 1. 如何优化点云分割的参数? 2. PCL中其他分割方法有哪些? 3. 如何处理点云分割中的噪声? 现在,写回答。</think>### PCL点云分割提取组件指南 点云分割点云处理中的关键步骤,旨在将原始点云数据分割成多个独立的组件或簇,每个组件代表一个对象或区域。这对于后续的物体识别、分类和场景理解至关重要。PCL(Point Cloud Library)是一个强大的开源库,提供多种点云分割算法。根据您的需求,我将逐步指导您如何使用PCL提取点云中的各个组件。整个过程包括数据准备、选择分割方法、设置参数、执行分割和提取结果。在回答中,我会引用相关资源以确保可靠性。 #### 步骤1: 理解点云分割基础 点云分割的目标是将点云分解为多个簇,每个簇对应一个物理对象(如桌子、椅子或墙壁)。PCL中的分割算法通常基于点云的空间特征(如欧氏距离、法线方向或曲率)来划分点云。例如: - **欧氏聚类分割**:基于点之间的欧氏距离,将邻近点归为一簇。距离阈值$d_{\text{th}}$是关键参数,表示两个点被视为同一簇的最大距离。 - **区域生长分割**:使用种子点扩散方式,基于法线夹角和曲率阈值来聚类点。法线夹角$\theta$和曲率$c$需预先设定[^4]。 - **其他方法**:PCL还支持模型拟合(如平面或圆柱分割)和条件聚类,用户可自定义约束(如RGB颜色或平滑度)[^1][^5]。 点云分割是物体识别的前置步骤,但本身并非最终目标;它旨在为后续处理提供干净的组件[^3]。在开始前,确保已安装PCL库(推荐使用Ubuntu系统或Windows的预编译版本)。 #### 步骤2: 准备点云数据 首先,加载点云数据。PCL支持多种格式(如`.pcd`或`.ply`)。以下是一个简单的代码示例,读取点云并进行预处理(如降噪和法线估计),因为分割算法通常需要法线信息。 ```cpp #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/filters/voxel_grid.h> #include <pcl/features/normal_3d.h> int main() { // 加载点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ>("input_cloud.pcd", *cloud); // 降噪(可选):使用体素网格滤波简化点云 pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::VoxelGrid<pcl::PointXYZ> voxel; voxel.setInputCloud(cloud); voxel.setLeafSize(0.01f, 0.01f, 0.01f); // 设置体素大小,单位:米 voxel.filter(*filtered_cloud); // 计算法线(许多分割算法需要法线) pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>); pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne; ne.setInputCloud(filtered_cloud); pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>()); ne.setSearchMethod(tree); ne.setRadiusSearch(0.03); // 搜索半径,单位:米 ne.compute(*normals); } ``` 此代码读取点云、降噪(减少计算量),并计算法线。法线估计是区域生长分割的关键步骤[^4]。 #### 步骤3: 选择并实现分割算法 PCL提供多种分割方法,我推荐两种常用方法提取组件:**欧氏聚类分割**(简单高效)和**区域生长分割**(适合复杂形状)。您可以根据点云特性选择。 - **方法A: 欧氏聚类分割(推荐用于简单场景)** 这种方法基于欧氏距离聚类点,速度快,适合提取空间隔离的物体(如室内家具)。距离阈值$d_{\text{th}}$是核心参数:如果点间距小于$d_{\text{th}}$,则归为同一簇。 ```cpp #include <pcl/segmentation/extract_clusters.h> // 使用步骤2的filtered_cloud pcl::search::KdTree<pcl::PointXYZ>::Ptr cluster_tree(new pcl::search::KdTree<pcl::PointXYZ>); cluster_tree->setInputCloud(filtered_cloud); // 创建欧氏聚类对象 std::vector<pcl::PointIndices> cluster_indices; pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec; ec.setClusterTolerance(0.02); // 距离阈值d_th,单位:米(例如2cm) ec.setMinClusterSize(100); // 最小簇点数(过滤噪声) ec.setMaxClusterSize(25000); // 最大簇点数(避免过大簇) ec.setSearchMethod(cluster_tree); ec.setInputCloud(filtered_cloud); ec.extract(cluster_indices); // 提取簇索引 // 提取并保存各组件 int cluster_id = 0; for (const auto& indices : cluster_indices) { pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_cloud(new pcl::PointCloud<pcl::PointXYZ>); for (const auto& idx : indices.indices) { cluster_cloud->points.push_back(filtered_cloud->points[idx]); } cluster_cloud->width = cluster_cloud->points.size(); cluster_cloud->height = 1; std::string filename = "cluster_" + std::to_string(cluster_id++) + ".pcd"; pcl::io::savePCDFile(filename, *cluster_cloud); } ``` 此方法高效,但可能受噪声影响;调整`setClusterTolerance()`和簇大小参数可优化结果[^5]。 - **方法B: 区域生长分割(适合复杂形状)** 这种方法从种子点开始,基于法线相似性和曲率扩散聚类点。适合提取具有平滑表面的物体(如圆柱或曲面)。关键参数包括法线夹角阈值$\theta_{\text{th}}$和曲率阈值$c_{\text{th}}$[^4]。 ```cpp #include <pcl/segmentation/region_growing.h> // 使用步骤2的filtered_cloud和normals pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg; reg.setMinClusterSize(50); // 最小簇点数 reg.setMaxClusterSize(10000); // 最大簇点数 reg.setSearchMethod(tree); // 使用KdTree搜索 reg.setNumberOfNeighbours(30); // 搜索近邻点数 reg.setInputCloud(filtered_cloud); reg.setInputNormals(normals); reg.setSmoothnessThreshold(3.0 / 180.0 * M_PI); // 法线夹角阈值θ_th(弧度),例如3度 reg.setCurvatureThreshold(0.05); // 曲率阈值c_th // 执行分割 std::vector<pcl::PointIndices> clusters; reg.extract(clusters); // 提取组件(类似欧氏聚类代码) for (size_t i = 0; i < clusters.size(); ++i) { pcl::PointCloud<pcl::PointXYZ>::Ptr seg_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::copyPointCloud(*filtered_cloud, clusters[i], *seg_cloud); pcl::io::savePCDFile("segment_" + std::to_string(i) + ".pcd", *seg_cloud); } ``` 区域生长法能处理复杂几何,但需手动设置种子点或依赖自动初始化;过分割(太多小簇)或欠分割(太少大簇)是常见问题[^4][^5]。 #### 步骤4: 参数调优和后处理 - **参数调优**:分割效果高度依赖参数。例如: - 欧氏聚类的$d_{\text{th}}$应略大于点云平均间距(通常0.01-0.1米)。 - 区域生长的$\theta_{\text{th}}$(角度)和$c_{\text{th}}$(曲率)需根据物体表面光滑度调整。建议先用PCL的`pcl_viewer`可视化点云实验。 - **后处理**:分割后,您可能需过滤噪声(如移除小簇)或合并过分割簇。PCL提供工具如`pcl::ConditionalEuclideanClustering`支持自定义约束[^5]。 - **可视化**:使用PCL的`pcl::visualization`模块查看分割结果。例如,不同簇可着色显示。 #### 注意事项和最佳实践 - **性能考虑**:点云分割计算量大,建议在预处理中降采样(如步骤2的体素滤波)。对于大型点云,GPU加速方法可能更优。 - **缺陷与优化**:欧氏聚类对噪声敏感;区域生长需精确法线估计。引用[5]指出,条件聚类可减少过分割,但需用户定义约束函数[^5]。深度学习分割(如PointNet++)是替代方案,但PCL经典方法更易上手[^2]。 - **真实可靠性**:PCL分割算法已在工业中广泛应用,例如提取室内场景中的物体或机械部件。分割结果应作为物体识别的输入,而非最终输出[^3]。 通过以上步骤,您可高效提取点云组件。分割后,每个组件可独立处理,用于识别或分类[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值