我需要根据圆柱形物体的多个表面测量值找到圆心。
我目前使用基于三个点的简化算法(用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个点。
我猜测现有算法应该可以扩展更多点,但我无法弄清楚如何。
答案 0 :(得分:3)
在我的数学大师同事的帮助下,我使用最小二乘矩阵计算得出了解决方案。
使用的算法描述为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();
}
这个解决方案产生了非常好的可重复结果,至少对我的数据而言。
DenseMatrix
是MathNet库的一部分。
修改强>
为了进一步降低噪音,正如用户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);
答案 1 :(得分:0)
(会留下这个评论,但我不能)
我认为您正在寻找的术语是&#34;姿势估计&#34;。您是否考虑过使用像RANSAC这样的迭代方法来为观察找到最合适的模型?
答案 2 :(得分:0)
如果您知道点数是均匀分布的,那么解决方案非常简单:平均值。你有很多要点可以使中心的平均值相当准确。
如果你的分数分布不均匀(即你在形状的一侧有更多的点),那么你仍然可以做到,但它有点复杂。您可以考虑使用二次方程组,并且您正在寻找一个点,该点可以最小化每个点与中心之间的距离与猜测的半径之间的差异。这是相当难的数学,所以你应该尝试找一个库来为你解决它。