用C#完成最小二乘法拟合平面方程,再计算点到面的距离
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConsoleApp2
{internal class Program{static void Main(string[] args){double[] Xs ={ 1, 2, 3, 4,1, 2, 3, 4,1, 2, 3, 4,1, 2, 3, 4 };double[] Ys ={ 1, 1, 1, 1,2, 2, 2, 2,3, 3, 3, 3,4, 4, 4, 4};double[] Zs ={0, 0, 0, 0,0, 1, 1, 0,0, 1, 1, 0,0, 0, 0, 0 };double A, B, C, D;string strMsg = string.Empty;Operator3D.FitPlane(Xs, Ys, Zs, out A, out B, out C, out D, out strMsg);double dist = Operator3D.DistanceP2Plane(0, 0, 0, A, B, C, D, out strMsg);}}class Operator3D{public static bool FitPlane(double[] Xs, double[] Ys, double[] Zs, out double A, out double B, out double C, out double D, out string error){error = string.Empty;A = B = C = D = 0;try {if (Xs == null || Ys == null || Zs == null){error = "输入Xs、Ys、Zs数据为null";return false;}else if (Xs.Length != Ys.Length || Xs.Length != Zs.Length){error = "输入Xs、Ys、Zs数据长度不同";return false;}else if (Xs.Length < 3){error = "输入数据点数小于3";return false;}int n = Xs.Length;double x0 = Average(Xs);double y0 = Average(Ys);double z0 = Average(Zs);double[,] cov = new double[3, 3];for (int i = 0; i < n; i++){double dx = Xs[i] - x0;double dy = Ys[i] - y0;double dz = Zs[i] - z0;cov[0, 0] += dx * dx;cov[0, 1] += dx * dy;cov[0, 2] += dx * dz;cov[1, 1] += dy * dy;cov[1, 2] += dy * dz;cov[2, 2] += dz * dz;}cov[1, 0] = cov[0, 1];cov[2, 0] = cov[0, 2];cov[2, 1] = cov[1, 2];if (!JacobiEigenvalue(cov, out double[] eigenvalues, out double[,] eigenvectors)){error = "雅可比特征值分解失败";return false;}int minIndex = 0;for (int i = 1; i < 3; i++)if (eigenvalues[i] < eigenvalues[minIndex])minIndex = i;A = eigenvectors[0, minIndex];B = eigenvectors[1, minIndex];C = eigenvectors[2, minIndex];D = -(A * x0 + B * y0 + C * z0);double norm = Math.Sqrt(A * A + B * B + C * C);if (norm < 1e-12) {error = "除数不能为零";return false;}A /= norm;B /= norm;C /= norm;D /= norm;return true;}catch {error = "拟合平面失败";return false;}}private static double Average(double[] arr){double sum = 0;foreach (double d in arr)sum += d;return sum / arr.Length;}private static bool JacobiEigenvalue(double[,] matrix, out double[] eigenvalues, out double[,] eigenvectors){int n = 3;eigenvalues = new double[n];eigenvectors = new double[n, n];for (int i = 0; i < n; i++){eigenvectors[i, i] = 1.0;for (int j = 0; j < n; j++)if (i != j)eigenvectors[i, j] = 0.0;}const int maxIterations = 50;const double epsilon = 1e-12;for (int k = 0; k < maxIterations; k++){int p = 0, q = 0;double maxVal = 0;for (int i = 0; i < n; i++)for (int j = i + 1; j < n; j++)if (Math.Abs(matrix[i, j]) > maxVal){maxVal = Math.Abs(matrix[i, j]);p = i;q = j;}if (maxVal < epsilon)break;double theta = 0.5 * Math.Atan2(2 * matrix[p, q], matrix[q, q] - matrix[p, p]);double c = Math.Cos(theta);double s = Math.Sin(theta);double new_pp = c * c * matrix[p, p] - 2 * c * s * matrix[p, q] + s * s * matrix[q, q];double new_qq = s * s * matrix[p, p] + 2 * c * s * matrix[p, q] + c * c * matrix[q, q];double new_pq = (c * c - s * s) * matrix[p, q] + c * s * (matrix[p, p] - matrix[q, q]);matrix[p, p] = new_pp;matrix[q, q] = new_qq;matrix[p, q] = matrix[q, p] = new_pq;for (int i = 0; i < n; i++){if (i != p && i != q){double new_ip = c * matrix[i, p] - s * matrix[i, q];double new_iq = s * matrix[i, p] + c * matrix[i, q];matrix[i, p] = matrix[p, i] = new_ip;matrix[i, q] = matrix[q, i] = new_iq;}}for (int i = 0; i < n; i++){double temp = eigenvectors[i, p];eigenvectors[i, p] = c * temp - s * eigenvectors[i, q];eigenvectors[i, q] = s * temp + c * eigenvectors[i, q];}}for (int i = 0; i < n; i++)eigenvalues[i] = matrix[i, i];return true;}public static double DistanceP2Plane(double X, double Y, double Z, double A, double B, double C, double D, out string error){error = string.Empty;try{double numerator = A * X + B * Y + C * Z + D;double denominator = Math.Sqrt(A * A + B * B + C * C);if (Math.Abs(denominator) < 1e-12){error = "非法平面参数";return double.NaN; }return numerator / denominator;}catch (Exception){error = "非法平面参数,计算失败";throw;}}
