为了测试我的PCL客户机代码,我生成了一组人工点,它们都位于一个平面上(类似于平面模型a*x+b*y+c*z+d=0
的a,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() << " 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存储库的标准PCL(libpcl1.8-dev
)。
与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`
1条答案
按热度按时间fae0ux8s1#
尽管它是“随机”的,但对于包含没有异常值和任何噪声的平面的云,在这种情况下,平面分割应该总是成功的(任何3个非共线点将为您提供正确的平面)。
简而言之-通过设置
seg.setOptimizeCoefficients(false);
,即使使用seg.setMaxIterations(1);
,我们也可以获得 n=5000 的正确系数。mooeeeep遇到的问题与在可选的模型系数优化步骤中计算协方差矩阵的数值不稳定性问题有关。请参阅Issue 3041了解更多详情。澄清-问题不是“大”坐标,而是云大小(边界框)和分辨率(近点之间的距离)之间的大比率。换句话说,重新调整云计算不会有帮助。
在噪声云的情况下,未经最终优化得到的系数可能**不够准确。如果是这种情况,并且您确实需要系数优化步骤,则您的其他选择是使用
double
坐标进行计算。或者:1.复制粘贴PCL类并将
float
更改为double
。1.使用SACSegmentation而不进行优化,但使用内点计算SACSegmentation类之外的平面系数(使用
double
)。