使用PCL和RANSAC进行墙体检测

时间:2020-08-03 21:31:59

标签: computer-vision point-cloud-library point-clouds ransac

我一直在使用PCL(点云库)对PCD(点云数据)文件中的墙进行检测。 PCD文件已通过深度摄像头生成。我发现在许多类似的应用程序中楼层检测,已使用RANSAC。因此,我想到也要在这里应用RANSAC,并尽力理解了RANSAC,但我仍然有一些与我的应用有关的问题:

  1. 简而言之,RANSAC尝试删除给定数据中的离群值,并通过模型迭代地对离群值进行概括。因此,在地板检测的情况下,是否会将与其他对象对应的其余点云视为离群值,而将地板视为离群值?墙壁也一样吗?

  2. 根据PCL的Plane model segmentation tutorial,RANSAC给出模型平面的系数,即abcd在平面方程中:a*x + b*y + c*z + d = 0coefficients->values向量。因此,我假设在检测墙壁的情况下,它将尝试给出与墙壁相对应的平面方程。但是,如果深度摄像头位于房间的一角并且墙壁的顶视图看起来像这样:

墙1
______________
|
|
|墙2
|
|

那么,在这种情况下,最终的模型平面是什么样的?会是斜边的(三角形)吗?

墙1
---------------------
|
|墙2
|
----------------------
墙3

即使在这种情况下,它仍然是什么样?

  1. 根据PCL的Extracting indices from a PointCloud tutorialExtractIndices <pcl::ExtractIndices>过滤器用于根据分段算法输出的索引从点云中提取点的子集。但是,此过滤器到底在做什么?实际上,在地板检测或墙壁检测的情况下(假设只有一个直墙),RANSAC已经给出了一个平面的方程。那么,有没有必要使用该过滤器?如果是,那为什么以及如何?

  2. 在以下情况下如何检测多个墙壁? ExtractIndices <pcl::ExtractIndices>过滤器可以做到这一点吗?如果是,那怎么办?

墙1
---------------------
|
|墙2
|
----------------------
墙3

如果您认为有比使用RANSAC更好的方法,请告诉我。

1 个答案:

答案 0 :(得分:0)

回答您提出的几个问题:

据我所知,在使用平面模型的情况下,RANSAC从云中随机选择3个点,并将其视为一个平面。(这是一个临时声明,稍后将予以证实)所有更接近的点到这个平面,作为垂直距离的给定阈值被认为是内点。 该算法返回包含其中最多点的平面(找到的平面还取决于您选择的迭代次数,如果它太低可能会错过最大的平面)。 在墙壁的情况下,故事是一样的。可以搜索飞机,但要选择好搜索方向。墙壁随意地垂直于平面 x-y。应该考虑到这一点来设置参数。 示例:

pcl::SACSegmentation<pcl::PointXYZI> seg;
Eigen::Vector3f axis;

//HELPER VARIABLES
float angle = 12.0;
void set_segmentation(float threshold, int max_iteration, float probability) {
    seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    // set cloud, threshold, and other paramatres
    seg.setDistanceThreshold(threshold);//Distance need to be adjusted according 
    to the obj
    seg.setMaxIterations(max_iteration);
    seg.setProbability(probability);
}  
PlaneSegment(float x, float y, float z, float set_angle, float threshold, int 
max_iteration, float probability) {
    axis = Eigen::Vector3f(x, y, z);
    angle = set_angle;
    seg.setAxis(axis);
    seg.setEpsAngle(angle * (3.1415 / 180.0f));
    //SET SEGMENTATION
    set_segmentation(threshold, max_iteration, probability);
}
pcl::PointIndices::Ptr segment_plane(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, 
pcl::PointIndices::Ptr inliers) {
    seg.setInputCloud(cloud);
    seg.segment(*inliers, *coefficients);
    if (inliers->indices.size() == 0)
    {
        PCL_ERROR("COULD NOT ESTIMATE PLANAR MODEL.\n");
        exit(-1);
    }
    return inliers;
}

pcl::PointCloud<pcl::PointXYZI>::Ptr 
extraction(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, pcl::PointIndices::Ptr 
inliers) {
    extract.setInputCloud(cloud);
    extract.setIndices(inliers);
    extract.setNegative(true);
    extract.filter(*cloud);
    return cloud;
}

您可以设置接受角,在本例中为 12 度,也可以设置基于轴的搜索方向。

关于你的第二点:

如果有多个墙,它会返回包含最多点的墙。但是,如果需要,您也应该能够提取其他平面。 (建议,保存所有包含的点数超过您选择的阈值的平面) 我检查了你的问题,这也是一个解决方案:pcl::RANSAC segmentation, get all planes in cloud?。第一条评论给出了很好的答案。

第三点:

检查示例代码。注意,这是一个类,所以这就是为什么有一个构造函数。 segment_plane 函数返回内点。基于此,您可以调用提取函数,它会从云中删除内点。这是一个非常简单和快速的灵魂。您可以避免系数值的痛苦。此外,如果您不想删除它们,只需通过迭代内点为它们着色并将其强度设置为选定的值。

RANSAC 算法可以很健壮,但有时它确实不起作用。由于迭代次数的原因,它也可能很慢。 有多种方法可以用另一种方式解决这个问题。 举个例子:考虑云下面的网格。许多大小相同的方形单元格。在每个单元格中,您检查最小和最大点高度。基于此,您可以获得地平面(如果这些值略有不同,并且彼此接近。在地单元的情况下,最大高度和最小高度的差异非常小)或墙壁。对于墙壁,您可以假设点在每个单元格中分布均匀,并且最大值 - 最小值的差异很高。 最好的问候。

相关问题