简体   繁体   English

求解方程从3个点找到圆心

[英]Solving equation to find center point of circle from 3 points

在此处输入图像描述

I'm looking for a high precision solution to find the center point of a circle from 3 data points on a canvas (x,y).我正在寻找一种高精度解决方案,以从 canvas (x,y) 上的 3 个数据点找到圆的中心点。 I found this example in the attached screenshot above, now I'm using the Math.NET package to solve the equation and I'm comparing the results against this online tool: https://pl.netcalc.com/8116/ .我在上面附带的屏幕截图中找到了这个示例,现在我正在使用 Math.NET package 来求解方程,并将结果与此在线工具进行比较: https://pl.netcalc.com/8116/

However, when I calculate the radius its completely off and often a negative number???但是,当我计算半径时,它完全偏离并且通常是负数???

using MathNet.Numerics.LinearAlgebra.Double.Solvers;
using MathNet.Numerics.LinearAlgebra.Double;
using System;

namespace ConsoleAppTestBed
{
  class Program
  {
    static void Main(string[] args)
    {
        var dataPoints = new double[,]
        {

            { 5, 80 },
            { 20, 100 },
            { 40, 140 }
        };


        var fitter = new CircleFitter();
        var result = fitter.Fit(dataPoints);

        var x = -result[0];
        var y = -result[1];
        var c = result[2];

        Console.WriteLine("Center Point:");
        Console.WriteLine(x);
        Console.WriteLine(y);
        Console.WriteLine(c);

        //// (x^2 + y^2 - c^2)
        var radius = Math.Pow(x, 2) + Math.Pow(y, 2) - Math.Pow(c, 2);
        //// sqrt((x^2 + y^2 - c^2))
        radius =  Math.Sqrt(radius);
        Console.WriteLine("Radius:");
        Console.WriteLine(radius);
        Console.ReadLine();
    }

    public class CircleFitter
    {
        public double[] Fit(double[,] v)
        {
            var xy1 = new double[] { v[0,0], v[0,1] };
            var xy2= new double[] { v[1, 0], v[1, 1] };
            var xy3 = new double[] { v[2, 0], v[2, 1] };

            // Create Left Side Matrix of Equation
            var a = CreateLeftSide_(xy1);
            var b = CreateLeftSide_(xy2);
            var c = CreateLeftSide_(xy3);
            var matrixA = DenseMatrix.OfArray(new[,] 
            {
                { a[0], a[1], a[2] },
                { b[0], b[1], b[2] },
                { c[0], c[1], c[2] }
            });

            // Create Right Side Vector of Equation
            var d = CreateRightSide_(xy1);
            var e = CreateRightSide_(xy2);
            var f = CreateRightSide_(xy3);
            double[] vector = { d, e, f };
            var vectorB =  Vector<double>.Build.Dense(vector);

            // Solve Equation
            var r = matrixA.Solve(vectorB);
            var result = r.ToArray();

            return result;
        }

        //2x, 2y, 1
        public double[] CreateLeftSide_(double[] d) 
        {
            return new double[] { (2 * d[0]), (2 * d[1]) , 1};
        
        }

        // -(x^2 + y^2)
        public double CreateRightSide_(double[] d) 
        { 
            return -(Math.Pow(d[0], 2) + Math.Pow(d[1], 2));

        }
    }
  }

}

Any ideas?有任何想法吗?

Thanks in advance.提前致谢。

The solution to your problem is here: The NumberDecimalDigits property您的问题的解决方案在这里: NumberDecimalDigits 属性

Code:代码:

using System;
using System.Globalization;
namespace ConsoleApp1
{
class Program
{
    static void Main()
    {
        double x1 = 1, y1 = 1;
        double x2 = 2, y2 = 4;
        double x3 = 5, y3 = -3;
        findCircle(x1, y1, x2, y2, x3, y3);
        Console.ReadKey();
    }
    static void findCircle(double x1, double y1,
                           double x2, double y2,
                           double x3, double y3)
    {
        NumberFormatInfo setPrecision = new NumberFormatInfo();
        setPrecision.NumberDecimalDigits = 3; // 3 digits after the double point

        double x12 = x1 - x2;
        double x13 = x1 - x3;

        double y12 = y1 - y2;
        double y13 = y1 - y3;

        double y31 = y3 - y1;
        double y21 = y2 - y1;

        double x31 = x3 - x1;
        double x21 = x2 - x1;

        double sx13 = (double)(Math.Pow(x1, 2) -
                        Math.Pow(x3, 2));

        double sy13 = (double)(Math.Pow(y1, 2) -
                        Math.Pow(y3, 2));

        double sx21 = (double)(Math.Pow(x2, 2) -
                        Math.Pow(x1, 2));

        double sy21 = (double)(Math.Pow(y2, 2) -
                        Math.Pow(y1, 2));

        double f = ((sx13) * (x12)
                + (sy13) * (x12)
                + (sx21) * (x13)
                + (sy21) * (x13))
                / (2 * ((y31) * (x12) - (y21) * (x13)));
        double g = ((sx13) * (y12)
                + (sy13) * (y12)
                + (sx21) * (y13)
                + (sy21) * (y13))
                / (2 * ((x31) * (y12) - (x21) * (y13)));

        double c = -(double)Math.Pow(x1, 2) - (double)Math.Pow(y1, 2) -
                                    2 * g * x1 - 2 * f * y1;
        double h = -g;
        double k = -f;
        double sqr_of_r = h * h + k * k - c;

        // r is the radius
        double r = Math.Round(Math.Sqrt(sqr_of_r), 5);

        Console.WriteLine("Center of a circle: x = " + h.ToString("N", setPrecision) + 
        ", y = " + k.ToString("N", setPrecision));
        Console.WriteLine("Radius: " + r.ToString("N", setPrecision));
        }
        }
        }

I have just converted William Li's answer to Swift 5 for those who like to cmd+C and cmd+V like me:)对于像我这样喜欢 cmd+C 和 cmd+V 的人,我刚刚将William Li 的答案转换为Swift 5 :)

 func calculateCircle(){
    
    
    let x1:Float = 0
    let y1:Float = 0
    
    let x2:Float = 0.5
    let y2:Float = 0.5
    
    let x3:Float = 1
    let y3:Float = 0
    
    let x12 = x1 - x2
    let x13 = x1 - x3
    
    let y12 = y1 - y2
    let y13 = y1 - y3
    
    let y31 = y3 - y1
    let y21 = y2 - y1
    
    let x31 = x3 - x1
    let x21 = x2 - x1
    
    
    let sx13 = pow(x1, 2) - pow(x3, 2)

    let sy13 = pow(y1, 2) - pow(y3, 2)

    let sx21 = pow(x2, 2) - pow(x1, 2)

    let sy21 = pow(y2, 2) - pow(y1, 2)

    
    let f = ((sx13) * (x12)
                    + (sy13) * (x12)
                    + (sx21) * (x13)
                    + (sy21) * (x13))
                    / (2 * ((y31) * (x12) - (y21) * (x13)))
    
    
    let g = ((sx13) * (y12)
                    + (sy13) * (y12)
                    + (sx21) * (y13)
                    + (sy21) * (y13))
                    / (2 * ((x31) * (y12) - (x21) * (y13)))
    
    
    let c = -pow(x1, 2) - pow(y1, 2) - 2 * g * x1 - 2 * f * y1
                            
    let h = -g
    let k = -f
    let r = sqrt(h * h + k * k - c)
                            
 
    
    print("center x  = \(h)")
    print("center y = \(k)")
    print("r = \(r)")
    
    
                            
}

Updated Answer更新的答案

The equation for radius is incorrect;半径方程不正确; it should be (not c squared): 它应该是(不是 c 的平方):

which is why you get incorrect values for the radius.这就是为什么您得到不正确的半径值的原因。

The original answer was incorrect, but it is still 'interesting'.原来的答案是不正确的,但它仍然是“有趣的”。

(Incorrect) Original Answer (不正确)原始答案

The cause of the problematic calculation is not solely due to the precision of the numbers, but more because problem is ill-conditioned.有问题的计算的原因不仅仅是由于数字的精度,而是因为问题是病态的。 If you look at the three points and where they are located on the circle, you'll see that they are bunched on a small segment of the circumference.如果您查看这三个点及其在圆上的位置,您会发现它们聚集在圆周的一小段上。 When the points are so close to each other, it is a tough ask to find the circle's centre and radius accurately.当点彼此如此接近时,很难准确地找到圆心和半径。

在此处输入图像描述

So the intermediate calculations, which will have small rounding errors, result in hugely exaggerated errors.因此,具有小舍入误差的中间计算会导致极大地夸大误差。

You can see the ill-conditioned nature of the problem by adding the ConditionNumber() method.您可以通过添加ConditionNumber()方法来查看问题的病态性质。

    // Solve Equation
    Console.WriteLine(matrixA.ConditionNumber()); // <<< Returns 5800 -> Big!
    var r = matrixA.Solve(vectorB);               // Existing code

A large result indicates an ill-conditioned problem.较大的结果表明存在病态问题。 In this case a value of 5800 is returned, which is large.在这种情况下,返回的值 5800 很大。 You might get better results using Gaussian Elimination with partial pivoting , but it still does not address the fact that the basic problem is ill-conditioned, which is why you get wildly incorrect answers.使用 Gaussian Elimination with partial pivoting你可能会得到更好的结果,但它仍然没有解决基本问题是病态的事实,这就是为什么你会得到非常错误的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM