首页 > 解决方案 > RANSAC平面分割的结果取决于点云的大小?

问题描述

为了测试我的 PCL 客户端代码,我生成了一组人工点,这些点都位于一个平面上(类似于a,b,c,d = 1,1,-1,0平面模型a*x+b*y+c*z+d=0)。在拟合平面模型后,我期望任何与上述系数线性相关的东西。

点云的范围(和大小)由n我提供给程序的参数决定。而且这个值似乎对拟合的结果有影响。

我确实从 RANSAC 分段中收到了较小值的预期系数n,但是以某种方式获取较大的值会产生错误。

示例输出:

$ ./a.out 240
point cloud contains 230400 points
a,b,c,d = -0.577313, -0.577376, 0.577362, 1.77026e-05, 
$ ./a.out 280
point cloud contains 313600 points
a,b,c,d = -0.577325, -0.577396, 0.57733, 0.142751, 
$ ./a.out 320
point cloud contains 409600 points
a,b,c,d = 0.577364, 0.577353, -0.577334, -0.202786,
$ ./a.out 1000
point cloud contains 4000000 points
a,b,c,d = -0.578332, -0.577762, 0.575954, 0.276675, 
$ ./a.out 5000
point cloud contains 100000000 points
a,b,c,d = 0.35787, 0.718142, -0.596826, -162.683, 

如您所见,尤其是该d术语似乎随着点云的大小而增长。

为什么会这样?我可以做些什么来解决这个问题吗?

这是我的代码:

#include <iostream>
#include <string>

#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>

int main(int argc, char** argv) {
  // generate point cloud
  double n = 10;
  if (argc > 1) {
    n = std::stod(argv[1]);
  }
  pcl::PointCloud<pcl::PointXYZ>::Ptr plane(new pcl::PointCloud<pcl::PointXYZ>);
  for (double i = -n; i < n; i+=1.) {
    for (double j = -n; j < n; j+=1.) {
      pcl::PointXYZ point;
      // coefficients should be something like: a, a, -a, 0
      // a*x + b*y + c*z + d = 0
      point.x = i; point.y = j; point.z = i + j;
      plane->points.push_back(point);
    }
  }
  std::cout << "point cloud contains " << plane->points.size() << " points\n";
  // fit plane
  // as seen here:
  // https://pcl.readthedocs.io/projects/tutorials/en/latest/planar_segmentation.html
  pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
  pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
  pcl::SACSegmentation<pcl::PointXYZ> seg;
  seg.setOptimizeCoefficients(true);
  seg.setModelType(pcl::SACMODEL_PLANE);
  seg.setMethodType(pcl::SAC_RANSAC);
  seg.setDistanceThreshold(0.01);
  seg.setInputCloud(plane);
  seg.segment(*inliers, *coefficients);
  // print coefficients
  std::cout << "a,b,c,d = ";
  for (auto c : coefficients->values) std::cout << c << ", ";
  std::cout << "\n";
}

这就是我的构建方式:

$ g++ test.cc `pkg-config --cflags --libs pcl_segmentation-1.8`

这应该是来自 Ubuntu 18.04 存储库 ( libpcl1.8-dev) 的标准 PCL。

标签: c++point-cloud-library

解决方案


尽管它是“随机的”,但对于包含没有异常值和任何噪声的平面的云,平面分割在这种情况下应该总是成功的(任何 3 个非共线点都会给你正确的平面)。

简而言之——通过设置seg.setOptimizeCoefficients(false);我们得到了n=5000的正确系数,即使seg.setMaxIterations(1);.

moooeeeep 遇到的问题与可选模型系数优化步骤中协方差矩阵计算的数值不稳定性问题有关。详情请参阅问题 3041澄清- 问题不是“大”坐标,而是云大小(边界框)和分辨率(近点之间的距离)之间的大比例。换句话说,重新调整云不会有帮助。

在嘈杂的云的情况下,未经最终优化得到的系数可能不够准确。如果是这种情况,并且您确实需要系数优化步骤,那么您的其他选择是使用double坐标进行计算。任何一个:

  1. 复制粘贴 PCL 类并更改floatdouble.
  2. 使用 SACSegmentation 而不进行优化,但使用内点计算 SACSegmentation 类之外的平面系数(使用double)。

推荐阅读