【问题标题】:Find circle center from a segment of surface points从一段表面点找到圆心
【发布时间】:2016-10-24 01:43:43
【问题描述】:

我需要根据圆柱形物体的多个表面测量值找到圆心。

我目前使用基于三个点的简化算法(用C#编写)来寻找中心(取自How to determine the radius and center of a circle when only three noncollinear points are known?):

private Point CircleCenter(List<Point> points, double p1Skip, double p2Skip, double p3Skip)
{
    var p1 = points.Skip((int)(points.Count * p1Skip)).First();
    var p2 = points.Skip((int)(points.Count * p2Skip)).First();
    var p3 = points.Skip((int)(points.Count * p3Skip)).First();

    double mr = (p2.Y - p1.Y) / (p2.X - p1.X);
    double mt = (p3.Y - p2.Y) / (p3.X - p2.X);

    double centerX = (mr * mt * (p3.Y - p1.Y) + mr * (p2.X + p3.X) - mt * (p1.X + p2.X)) / (2 * (mr - mt));

    double centerY = (-1 / mr) * (centerX - ((p1.X + p2.X) / 2)) + ((p1.Y + p2.Y) / 2);

    return new Point(centerX, centerY, p1.Z);
}

问题是这种方法对噪声非常敏感。如果其中一个点关闭,它当然会影响中心点。 对于每个横截面,我有 640 个表面点可用,并且认为应该可以使用超过 3 个点。

我猜测现有算法应该可以扩展更多点,但我不知道如何。

【问题讨论】:

  • 有趣的问题...固定一个圆的 3 个点在数学上是正确的,即使您知道该圆上有更多点,您也无法做很多事情。我认为方向应该是如何降低噪音,或者如何明智地选择640个点中的3个点来形成一个更好的近似圆(圆心)...
  • 一种想法是使用不同的点进行许多不同的 3 点计算,然后计算这些点的平均值或中位数。我现在正在研究这个
  • 是的......类似的想法(只是概念,完全不知道如何实现),我认为将640点“收缩”成更少的点,比如320,重复(并细化)听起来更可行) 每次迭代,直到剩下约 3 个点,然后用这 3 个点形成一个圆?
  • 首先,准确定义要最小化的内容。例如它是每个点距估计圆的距离的平方和吗?然后你可以把它当作一个优化问题来处理。
  • 我用你的建议改进了我的答案@samgak,它确实改善了结果。

标签: c# algorithm math geometry


【解决方案1】:

在我的数学大师同事的帮助下,我找到了使用最小二乘矩阵计算的解决方案。

使用的算法描述为here

public static void FitCircle(IEnumerable<Point> points, out double x0, out double y0, out double r)
{
    setMklProvider();
    DenseMatrix A = DenseMatrix.Create(3, 3, (i, j) => 0);
    DenseMatrix b = DenseMatrix.Create(3, 1, (i, j) => 0);
    A[0, 0] = points.Sum(point => point.X * point.X);
    A[0, 1] = points.Sum(point => point.X * point.Y);
    A[0, 2] = points.Sum(point => point.X);
    A[1, 0] = A[0, 1];
    A[1, 1] = points.Sum(point => point.Y * point.Y);
    A[1, 2] = points.Sum(point => point.Y);
    A[2, 0] = A[0, 2];
    A[2, 1] = A[1, 2];
    A[2, 2] = points.Count();
    b[0, 0] = points.Sum(point => point.X * (point.X * point.X + point.Y * point.Y));
    b[1, 0] = points.Sum(point => point.Y * (point.X * point.X + point.Y * point.Y));
    b[2, 0] = points.Sum(point => point.X * point.X + point.Y * point.Y);
    var x = A.QR().Solve(b);
    x0 = x[0, 0] / 2;
    y0 = x[1, 0] / 2;
    r = Math.Sqrt(x[2, 0] + x0 * x0 + y0 * y0);
}

private static void setMklProvider()
{
    if (!_mklProviderSet) MathNet.Numerics.Control.LinearAlgebraProvider = new MathNet.Numerics.Algorithms.LinearAlgebra.Mkl.MklLinearAlgebraProvider();
}

这个解决方案产生了非常好的可重复结果,至少对我的数据来说是这样。 DenseMatrixMathNet 库的一部分。

编辑

按照用户 samgak 的建议,为了进一步降低噪音,我添加了一种迭代降低方法来提高准确性:

double x0, y0, r;
FitCircle(surfacePoints, out x0, out y0, out r);
var center = new Point(x0, y0, surfacePoints.First().Z);

int reductionIterations = 10;
var reducedSet = surfacePoints;

for (int i = 1; i < reductionIterations; i++)
{
    var orderedByDistanceToCenter = reducedSet.OrderBy(p => (p-center).GetRho()).ToList();

    reducedSet = orderedByDistanceToCenter
        .Skip((int)(orderedByDistanceToCenter.Count * (i / 10f)))
        .Take((int)(orderedByDistanceToCenter.Count - orderedByDistanceToCenter.Count * (i / 10f)*2))
        .ToList();

    // Reduced to zero, abort
    if (reducedSet.Count < 3)
        break;

    FitCircle(reducedSet, out x0, out y0, out r);
    center = new Point(x0, y0, reducedSet.First().Z);
}

public static double GetRho(this Point p) => Math.Sqrt(p.X * p.X + p.Y * p.Y);

【讨论】:

    【解决方案2】:

    (本来可以留下评论,但我不能)

    我认为您正在寻找的术语是“姿势估计”。您是否考虑过使用像 RANSAC 这样的迭代方法来找到最适合您的观察的模型?

    【讨论】:

      【解决方案3】:

      如果您知道点是均匀分布的,那么解决方案非常简单:平均。你有很多点可以使中心的平均值相当准确。

      如果您的点分布不均匀(即形状的一侧有更多点),那么您仍然可以这样做,但它有点复杂。您可以考虑拥有一个二次方程组,并且您正在寻找一个点,该点可以最小化每个点与中心之间的距离与猜测半径之间的差异。这是一个相当困难的数学,所以你应该尝试找到一个库来为你解决它。

      【讨论】:

      • 我的积分分布不均。它们代表圆柱体的一部分。 IE。它是从一侧测量的。我一直在考虑最小二乘方程之类的,但就像你说的,数学变得复杂得多。
      • 我想我已经按照你的建议做了。感谢您为我指明正确的方向
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 2017-06-13
      相关资源
      最近更新 更多