RANSAC平面分割的结果取决于点云的大小



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

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

对于n的小值,我确实从RANSAC分割中接收到期望的系数,但对于较大的值,不知何故会产生误差。

示例输出:

$ ./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() << " pointsn";
// 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。


与Ubuntu 20.04:的结果相同

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

和Ubuntu 22.04:

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

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

简而言之,通过设置seg.setOptimizeCoefficients(false);,即使使用seg.setMaxIterations(1);,我们也可以获得n=5000的正确系数。

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

在有噪云的情况下,在没有最终优化的情况下得到的系数可能不够准确。如果是这种情况,并且确实需要系数优化步骤,则您的其他选项是使用double坐标进行计算。任一:

  1. 复制粘贴PCL类并将float更改为double
  2. 在不进行优化的情况下使用SACSegmentation,但使用inliers来计算SACSegmenting类之外的平面系数(使用double(

最新更新