目录
内容抄自CSDN点云侠:【2024最新版】PCL点云处理算法汇总(C++长期更新版)。质量无忧,永久免费,可放心复制粘贴。
一、概述
圆柱的Eigen::VectorXf参数为7个。分别是:
圆柱轴上一点的x坐标;
圆柱轴上一点的y坐标;
圆柱轴上一点的z坐标;
圆柱轴方向的x;
圆柱轴方向的y;
圆柱轴方向的z;
圆柱半径r。
1.1 原理
RANSAC(Random Sample Consensus)算法是一种常用的计算机视觉中的迭代优化技术,用于从一组数据点中找到最佳模型,比如圆柱体,在存在噪声和异常值的情况下。它通过随机选择小样本集合(通常称为"候选子集"),尝试对这些点进行某种假设(如圆柱体的参数估计),然后计算这个假设是否能很好地解释大部分原始数据。如果大多数点都能满足该假设,则认为找到了一个好的模型。
对于拟合圆柱,RANSAC会先随机选取一些点作为初始圆柱中心、长度和直径的猜测,接着计算这些点到圆柱表面的距离,如果距离误差在一个阈值内,就认为这组点支持当前的圆柱模型。不断重复这个过程,直到达到预设的迭代次数或找到足够多的数据点支持的“一致”集合。最后,RANSAC会选择那个解释了最大比例点的圆柱参数作为最终结果。
1.2 实现步骤
RANSAC(Random Sample Consensus)算法是一种用于估计模型参数的迭代概率方法,在计算机视觉领域广泛应用,包括圆柱体的拟合。以下是RANSAC圆柱拟合的基本步骤:
- 随机抽样:从输入数据集中随机选取一组点作为初始候选样本。
- 模型构建:基于选定的点集,尝试构造一个圆柱模型,例如通过最小二乘法计算出圆心、半径以及轴向信息。
- 一致性检查:检查这组点是否都近似满足圆柱模型,通常通过计算每个点到模型的残差(距离),如果大部分点的残差在预设的阈值内,则认为这个模型是一致的。
- 计数和投票:重复上述过程多次,每次选择新的随机点集,记录下一致性的次数。当某个模型达到预设的最少一致性支持次数时,就认为找到了一个好的模型。
- 最佳模型选择:所有经过检验的一致性模型中,选择具有最高支持度的那个作为最终圆柱模型。
- 评估和输出:对所有数据点应用选定的模型,得到拟合结果,并计算残差,评估模型的精度。
1.3应用场景
RANSAC(随机采样一致性)算法是一种用于在含有噪声数据的情况下估计模型参数的迭代方法,特别适用于平面、直线、圆、圆柱等基本几何结构的拟合。在实际应用中,RANSAC常用于计算机视觉领域,例如:
- 三维扫描:对激光雷达点云数据进行处理时,可能会包含一些错误测量点,RANSAC可以帮助筛选出最有可能代表真实圆柱的样本,并估计其尺寸和方向。
- 工业检测:在产品质量控制中,通过图像分析识别产品是否符合预期的圆柱形状,比如管材、零件的表面缺陷检测。
- 机器人定位:机器人手臂抓取物体时,需要找到目标物体的中心和旋转轴,RANSAC可以用来检测并拟合一组特征点,确定圆柱坐标。
- 医学成像:在CT或MRI数据中,识别和分割血管或神经纤维束的圆柱结构也是常见应用。
二、关键函数
2.1 头文件
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_cylinder.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
2.2 加载点云数据
// ------------------------------加载点云数据------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>("Cylinder.pcd", *cloud) < 0)
{
PCL_ERROR("Couldn't read file \n");
return -1;
}
2.3 计算法线
// -------------------------------计算法线--------------------------------
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
n.setInputCloud(cloud);
n.setSearchMethod(tree);
n.setKSearch(10); // K近邻搜索个数
n.compute(*normals);
2.4 拟合圆柱
// -------------------------------拟合圆柱--------------------------------
pcl::SampleConsensusModelCylinder<pcl::PointXYZ, pcl::Normal >::Ptr model(new pcl::SampleConsensusModelCylinder<pcl::PointXYZ, pcl::Normal >(cloud));
model->setInputNormals(normals);
pcl::RandomSampleConsensus<pcl::PointXYZ> ransac(model); // 定义RANSAC算法对象
ransac.setDistanceThreshold(0.01); // 设置距离阈值
ransac.setMaxIterations(500); // 设置最大迭代次数
ransac.computeModel();
Eigen::VectorXf coeff;
ransac.getModelCoefficients(coeff); // 参数
std::vector<int> ranSacInliers; // 获取属于拟合出的内点
ransac.getInliers(ranSacInliers);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudInliers(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, ranSacInliers, *cloudInliers);
std::cout << "圆柱轴上一点的x坐标为:" << coeff[0] << "\n圆柱轴上一点的y坐标为:" << coeff[1] << "\n圆柱轴上一点的z坐标为:" << coeff[2]
<< "\n圆柱轴方向的x为:" << coeff[3] << "\n圆柱轴方向的y为:" << coeff[4] << "\n圆柱轴方向的z为:" << coeff[5]
<< "\n圆柱半径为:" << coeff[6]
<< std::endl;
2.5 可视化
// -----------------------------结果展示--------------------------------
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("ModelCylinder"));
viewer->addPointCloud<pcl::PointXYZ>(cloud, "cloud");
viewer->addPointCloud<pcl::PointXYZ>(cloudInliers, pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(cloudInliers, 255, 0, 0), "cloudInliers");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
三、完整代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_cylinder.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main()
{
// ------------------------------加载点云数据------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>("Cylinder.pcd", *cloud) < 0)
{
PCL_ERROR("Couldn't read file \n");
return -1;
}
// -------------------------------计算法线--------------------------------
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
n.setInputCloud(cloud);
n.setSearchMethod(tree);
n.setKSearch(10); // K近邻搜索个数
n.compute(*normals);
// -------------------------------拟合圆柱--------------------------------
pcl::SampleConsensusModelCylinder<pcl::PointXYZ, pcl::Normal >::Ptr model(new pcl::SampleConsensusModelCylinder<pcl::PointXYZ, pcl::Normal >(cloud));
model->setInputNormals(normals);
pcl::RandomSampleConsensus<pcl::PointXYZ> ransac(model); // 定义RANSAC算法对象
ransac.setDistanceThreshold(0.01); // 设置距离阈值
ransac.setMaxIterations(500); // 设置最大迭代次数
ransac.computeModel();
Eigen::VectorXf coeff;
ransac.getModelCoefficients(coeff); // 参数
std::vector<int> ranSacInliers; // 获取属于拟合出的内点
ransac.getInliers(ranSacInliers);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudInliers(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, ranSacInliers, *cloudInliers);
std::cout << "圆柱轴上一点的x坐标为:" << coeff[0] << "\n圆柱轴上一点的y坐标为:" << coeff[1] << "\n圆柱轴上一点的z坐标为:" << coeff[2]
<< "\n圆柱轴方向的x为:" << coeff[3] << "\n圆柱轴方向的y为:" << coeff[4] << "\n圆柱轴方向的z为:" << coeff[5]
<< "\n圆柱半径为:" << coeff[6]
<< std::endl;
// -----------------------------结果展示--------------------------------
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("ModelCylinder"));
viewer->addPointCloud<pcl::PointXYZ>(cloud, "cloud");
viewer->addPointCloud<pcl::PointXYZ>(cloudInliers, pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(cloudInliers, 255, 0, 0), "cloudInliers");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return (0);
}