PCL(Point Cloud Library)中的圆柱模型分割Cylinder Model Segmentation是一种从点云数据中提取圆柱体模型的技术。它通过识别点云中符合圆柱体几何形状的部分,将圆柱体从其他几何形状中分离出来。
预处理:
法线估计:
模型拟合:
分割:
后处理:
代码为圆柱形模型运行一个样本共识分割,由于数据中存在噪声,圆柱模型并不完美。为了使示例更实际一些,将以下操作应用于输入数据集(按顺序):
距离大于1.5米的数据点被过滤
估计每个点的表面法线
一个平面模型(描述我们演示数据集中的表)被分割并保存到磁盘上
一个圆柱形模型(在我们的演示数据集中描述杯子)被分割并保存到磁盘上
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef pcl::PointXYZ PointT;
int
main ()
{
// All the objects needed
pcl::PCDReader reader;
pcl::PassThrough<PointT> pass;
pcl::NormalEstimation<PointT, pcl::Normal> ne;
pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
pcl::PCDWriter writer;
pcl::ExtractIndices<PointT> extract;
pcl::ExtractIndices<pcl::Normal> extract_normals;
pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());
// Datasets
pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);
pcl::PointCloud<PointT>::Ptr cloud_filtered2 (new pcl::PointCloud<PointT>);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2 (new pcl::PointCloud<pcl::Normal>);
pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices);
// Read in the cloud data
reader.read ("table_scene_mug_stereo_textured.pcd", *cloud);
std::cerr << "PointCloud has: " << cloud->size () << " data points." << std::endl;
// Build a passthrough filter to remove spurious NaNs and scene background
//使用直通滤波器沿Z轴裁剪点云,保留Z值在`[0, 1.5]`范围内的点
pass.setInputCloud (cloud);
pass.setFilterFieldName ("z");
pass.setFilterLimits (0, 1.5);
pass.filter (*cloud_filtered);
std::cerr << "PointCloud after filtering has: " << cloud_filtered->size () << " data points." << std::endl;
// Estimate point normals 法线估计
// 使用KdTree搜索方法,对滤波后的点云进行法线估计,每个点的法线基于其最近的50个邻居计算。
ne.setSearchMethod (tree);
ne.setInputCloud (cloud_filtered);
ne.setKSearch (50);
ne.compute (*cloud_normals);
// Create the segmentation object for the planar model and set all the parameters
// 平面分割
seg.setOptimizeCoefficients (true);
seg.setModelType (pcl::SACMODEL_NORMAL_PLANE);
seg.setNormalDistanceWeight (0.1);
seg.setMethodType (pcl::SAC_RANSAC);
seg.setMaxIterations (100);
seg.setDistanceThreshold (0.03);
seg.setInputCloud (cloud_filtered);
seg.setInputNormals (cloud_normals);
// Obtain the plane inliers and coefficients
// 使用RANSAC算法拟合平面模型,并输出平面方程的系数
seg.segment (*inliers_plane, *coefficients_plane);
std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;
// Extract the planar inliers from the input cloud 提取平面点云
extract.setInputCloud (cloud_filtered);
extract.setIndices (inliers_plane);
extract.setNegative (false);
// Write the planar inliers to disk
// 提取平面点云并保存到PCD文件
pcl::PointCloud<PointT>::Ptr cloud_plane (new pcl::PointCloud<PointT> ());
extract.filter (*cloud_plane);
std::cerr << "PointCloud representing the planar component: " << cloud_plane->size () << " data points." << std::endl;
writer.write ("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);
// Remove the planar inliers, extract the rest 去除平面点云
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);
// Create the segmentation object for cylinder segmentation and set all the parameters
// 圆柱体分割
seg.setOptimizeCoefficients (true);
seg.setModelType (pcl::SACMODEL_CYLINDER);
seg.setMethodType (pcl::SAC_RANSAC);
seg.setNormalDistanceWeight (0.1);
seg.setMaxIterations (10000);
seg.setDistanceThreshold (0.05);
seg.setRadiusLimits (0, 0.1);
seg.setInputCloud (cloud_filtered2);
seg.setInputNormals (cloud_normals2);
// Obtain the cylinder inliers and coefficients
// 使用RANSAC算法拟合圆柱体模型,并输出圆柱体的参数
seg.segment (*inliers_cylinder, *coefficients_cylinder);
std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
// Write the cylinder inliers to disk
// 提取圆柱体点云并保存到PCD文件
extract.setInputCloud (cloud_filtered2);
extract.setIndices (inliers_cylinder);
extract.setNegative (false);
pcl::PointCloud<PointT>::Ptr cloud_cylinder (new pcl::PointCloud<PointT> ());
extract.filter (*cloud_cylinder);
if (cloud_cylinder->points.empty ())
std::cerr << "Can't find the cylindrical component." << std::endl;
else
{
std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->size () << " data points." << std::endl;
writer.write ("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder, false);
}
return (0);
}
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(cylinder_segmentation)
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (${PROJECT_NAME} cylinder_segmentation.cpp)
target_link_libraries (${PROJECT_NAME} ${PCL_LIBRARIES})
mkdir build && cd build
cmake ..
make
./cylinder_segmentation
pcl::PointXYZ
)。ExtractIndices
和PassThrough
)。NormalEstimation
)。typedef pcl::PointXYZ PointT;
PointT
为pcl::PointXYZ
,表示点云中的点类型为三维坐标点(x, y, z)。seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_NORMAL_PLANE);
seg.setNormalDistanceWeight(0.1);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setMaxIterations(100);
seg.setDistanceThreshold(0.03);
seg.setInputCloud(cloud_filtered);
seg.setInputNormals(cloud_normals);
seg.segment(*inliers_plane, *coefficients_plane);
std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;
setOptimizeCoefficients(true)
:
setModelType(pcl::SACMODEL_NORMAL_PLANE)
:
setNormalDistanceWeight(0.1)
:
setMethodType(pcl::SAC_RANSAC)
:
setMaxIterations(100)
:
setDistanceThreshold(0.03)
:
setInputCloud(cloud_filtered)
:
cloud_filtered
)。setInputNormals(cloud_normals)
:
cloud_normals
)。seg.segment(*inliers_plane, *coefficients_plane)
:
inliers_plane
中,平面模型的系数存储在coefficients_plane
中。std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;
:
Ax + By + Cz + D = 0
,其中coefficients_plane
存储了[A, B, C, D]
。seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_CYLINDER);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setNormalDistanceWeight(0.1);
seg.setMaxIterations(10000);
seg.setDistanceThreshold(0.05);
seg.setRadiusLimits(0, 0.1);
seg.setInputCloud(cloud_filtered2);
seg.setInputNormals(cloud_normals2);
seg.segment(*inliers_cylinder, *coefficients_cylinder);
std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
setOptimizeCoefficients(true)
:
setModelType(pcl::SACMODEL_CYLINDER)
:
setMethodType(pcl::SAC_RANSAC)
:
setNormalDistanceWeight(0.1)
:
setMaxIterations(10000)
:
setDistanceThreshold(0.05)
:
setRadiusLimits(0, 0.1)
:
setInputCloud(cloud_filtered2)
:
cloud_filtered2
)。setInputNormals(cloud_normals2)
:
cloud_normals2
)。seg.segment(*inliers_cylinder, *coefficients_cylinder)
:
inliers_cylinder
中,圆柱体模型的系数存储在coefficients_cylinder
中。std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
:
参数 | 平面分割 | 圆柱体分割 |
---|---|---|
模型类型 | pcl::SACMODEL_NORMAL_PLANE |
pcl::SACMODEL_CYLINDER |
法线权重 | 0.1 | 0.1 |
最大迭代次数 | 100 | 10000 |
距离阈值 | 0.03 | 0.05 |
半径限制 | 无 | [0, 0.1] |
输入点云 | cloud_filtered |
cloud_filtered2 |
输入法线 | cloud_normals |
cloud_normals2 |