You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PCL RANSAC三维圆拟合后拟合质量指标获取异常问题

问题描述

我想用Point Cloud Library(PCL)对一组三维点做三维圆拟合,同时用RANSAC鲁棒剔除离群点。除了得到圆参数,还想获取最终筛选出的点与模型的拟合质量指标——这个指标可以用来提示用户重新采集数据,也能在标定流程里过滤低质量数据。

我写了如下代码:

PCLCIRCLEFITTING_API bool FitCircle(const vec* const points, const UINT32 point_count, const double threshold,
                                    circle3d* circle, double* variance)
{
    const pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

    cloud->width = point_count;
    cloud->height = 1;
    cloud->is_dense = true;
    cloud->points.resize(static_cast<size_t>(cloud->width) * cloud->height);

    // 把自定义结构体的点坐标复制到PCL点云
    for (pcl::index_t i = 0; i < static_cast<pcl::index_t>(cloud->size ()); i++)
    {
        (*cloud)[i].x = points[i].x;
        (*cloud)[i].y = points[i].y;
        (*cloud)[i].z = points[i].z;
    }

    // 创建RANSAC模型并计算
    const pcl::SampleConsensusModelCircle3D<pcl::PointXYZ>::Ptr circle_model (new pcl::SampleConsensusModelCircle3D<pcl::PointXYZ>(cloud));

    pcl::RandomSampleConsensus<pcl::PointXYZ> ransac(circle_model, threshold);
    // 启用并行处理,自动根据硬件选线程数
    ransac.setNumberOfThreads(0);
    const bool success = ransac.computeModel();

    if (success)
    {
        Eigen::VectorXf coefficients;
        ransac.getModelCoefficients(coefficients);

        circle->center.x = coefficients[0];
        circle->center.y = coefficients[1];
        circle->center.z = coefficients[2];
        circle->radius = coefficients[3];
        circle->normal.x = coefficients[4];
        circle->normal.y = coefficients[5];
        circle->normal.z = coefficients[6];

        // 这里返回NaN,明明已经计算出有效模型了?
        *variance = circle_model->computeVariance();
    }
    else
    {
        *variance = 0.0;
    }

    return success;
}

测试时模型拟合成功,参数也正确,但variance始终返回NaN。我查看了computeVariance()的实现:

/** \brief 基于内部估计的距离向量,计算点到模型的误差方差。必须先计算模型(至少调用selectWithinDistance)。
        */
      inline double
      computeVariance () const
      {
        if (error_sqr_dists_.empty ())
        {
          PCL_ERROR ("[pcl::SampleConsensusModel::computeVariance] 无法估计采样一致性模型的距离方差,因为模型尚未计算。请先计算模型或至少运行selectWithinDistance。返回NAN!\n");
          return (std::numeric_limits<double>::quiet_NaN ());
        }
        return (computeVariance (error_sqr_dists_));
      }

error_sqr_dists_是空的,但我明明已经运行RANSAC计算出有效模型参数了。请问我是不是错误使用了computeVariance()?如果是,获取RANSAC拟合质量分数的更好方法是什么?

解决方案

为什么computeVariance()返回NaN?

PCL的RandomSampleConsensus类执行computeModel()时,只会在内部筛选符合模型的内点,但不会自动调用模型的selectWithinDistance()方法填充error_sqr_dists_数组——而computeVariance()完全依赖这个数组计算方差。所以即使你拿到了有效模型参数,circle_model内部的误差距离数组还是空的,自然返回NaN。

获取拟合质量的正确方法

有两种可靠的方式获取拟合质量指标:

方法1:手动调用selectWithinDistance()填充误差数组

在拿到模型系数后,调用circle_model的selectWithinDistance(),传入模型系数和阈值,这样会自动计算所有内点到模型的平方距离并填充error_sqr_dists_,之后再调用computeVariance()就能得到有效结果:

if (success)
{
    Eigen::VectorXf coefficients;
    ransac.getModelCoefficients(coefficients);

    // 先将模型系数传给circle_model,计算内点和误差距离
    std::vector<int> inliers;
    circle_model->selectWithinDistance(coefficients, threshold, inliers);
    
    // 现在可以正常调用computeVariance()了
    *variance = circle_model->computeVariance();

    // 后续赋值圆参数的代码...
    circle->center.x = coefficients[0];
    circle->center.y = coefficients[1];
    circle->center.z = coefficients[2];
    circle->radius = coefficients[3];
    circle->normal.x = coefficients[4];
    circle->normal.y = coefficients[5];
    circle->normal.z = coefficients[6];
}

方法2:直接用RANSAC的内点计算自定义指标

如果你需要更灵活的质量指标(比如内点占比、平均误差、最大误差),可以通过ransac.getInliers()获取内点索引,然后手动计算这些指标:

if (success)
{
    Eigen::VectorXf coefficients;
    ransac.getModelCoefficients(coefficients);
    std::vector<int> inliers;
    ransac.getInliers(inliers);

    // 计算内点占比
    double inlier_ratio = static_cast<double>(inliers.size()) / point_count;

    // 计算内点到模型的平均平方误差(可作为方差的近似,或用严格方差公式计算)
    double total_sqr_error = 0.0;
    for (int idx : inliers)
    {
        pcl::PointXYZ pt = cloud->points[idx];
        double dist_sqr = circle_model->getDistToModel(coefficients, pt.x, pt.y, pt.z);
        total_sqr_error += dist_sqr;
    }
    double mean_sqr_error = total_sqr_error / inliers.size();
    *variance = mean_sqr_error;

    // 后续赋值圆参数的代码...
    circle->center.x = coefficients[0];
    circle->center.y = coefficients[1];
    circle->center.z = coefficients[2];
    circle->radius = coefficients[3];
    circle->normal.x = coefficients[4];
    circle->normal.y = coefficients[5];
    circle->normal.z = coefficients[6];
}

注意事项

  • selectWithinDistance()会使用你传入的阈值筛选内点,和RANSAC过程中用的阈值一致即可,保证结果和RANSAC的内点集匹配。
  • 如果需要严格和RANSAC最终选中的内点一致,建议用ransac.getInliers()获取内点索引再计算误差——因为RANSAC可能在迭代中选择了最优内点集,而selectWithinDistance()是基于最终模型重新筛选的,理论结果一致,但手动计算更可控。

内容的提问来源于stack exchange,提问作者Hydrargyrum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 02:45:02