0.引言

  在点云建模过程中,有时需要对扫描建模的点云进行标定,在实际使用中往往以地面做为参照平面,需要将扫描的三维空间点云进行拟合平面,以便纠正扫描结果。本文对三维空间离散点拟合平面算法进行总结,并给出几种编程语言下的算法实现代码。

1.算法原理

  (1)最小二乘法

  

  (2)平面方程拟合
  

2.算法实现

  (1)C#

using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;  
  
List<List<double>> dLL =new List<List<double>>();  
using (StreamReader sr = new StreamReader("E:\\4.txt", Encoding.UTF8))  
            {  
                string line;  
                // 从文件读取并显示行,直到文件的末尾   
                while ((line = sr.ReadLine()) != null)  
                {  
                    string[] strs = line.Split(',');  
                    List<double> dL = new List<double>();  
                    dL.Add(double.Parse(strs[0]));  
                    dL.Add(double.Parse(strs[1]));  
                    dL.Add(double.Parse(strs[2]));  
                    dLL.Add(dL);  
                }  
    }  
  
Matrix<double> A,b;  
double[,] dA=new double[dLL.Count(), 3];  
double[,] db = new double[dLL.Count(), 1];  
double[,] da = new double[3, 1];  
for (int i = 0; i < dLL.Count(); i++)  
{  
    dA[i, 0] = dLL[i][0];  
    dA[i, 1] = dLL[i][1];  
    dA[i, 2] = 1;  
    db[i,0] = dLL[i][2];  
}  
A = DenseMatrix.OfArray(dA);  
b = DenseMatrix.OfArray(db);  
Matrix<double> a = (A.Transpose() * A).Inverse() * A.Transpose() * b;  
Console.WriteLine("a0,a1,a2:"+a[0,0].ToString("f6")+","+a[1,0].ToString("f6") + ","+a[2,0].ToString("f6"));  

  (2)C++

//planePoints存储需要拟合的三维点云
vector<Eigen::Vector3d> planePoints;  
Eigen::MatrixXd A(planePoints.size(), 3);  
Eigen::VectorXd b(planePoints.size());  
//将观测点输入矩阵  
for (int i = 0; i < planePoints.size(); i++)  
{  
    A(i, 0) = planePoints[i](0);  
    A(i, 1) = planePoints[i](1);  
    A(i, 2) = 1;  
    b(i) = planePoints[i](2);  
}  
  
//使用最小二乘法求得系数向量  
Eigen::Vector3d a = (A.transpose()*A).inverse()*A.transpose()*b;  

  (3)Matlab

%文件名
fileName = "E:\\4.txt";  
points = csvread(fileName , 0, 0);  
length = size(points(:,1));  
A=[points(:,1),points(:,2),ones(length(1),1)];  
b=points(:,3);  
a=inv(A'*A)*A'*b;  

  (4)Java

//s为点文件数据字符串
String[] strs =  s.toString().split("\n");  
double dA[][] = new double[strs.length][3];  
double db[][] = new double[strs.length][1];  
double da[][] = new double[3][1];  
for(int i = 0;i <strs.length;i++){  
    if(strs[i].equals(""))continue;  
    String[] strs2 = strs[i].split(",");  
    dA[i][0]=Double.parseDouble(strs2[0]);  
    dA[i][1]=Double.parseDouble(strs2[1]);  
    dA[i][2] = 1;  
    db[i][0]=Double.parseDouble(strs2[2]);  
}  
//multiply、inverse、transpose分别为矩阵乘法、求逆、转置  
da = multiply(multiply(inverse(multiply(transpose(dA),dA)),transpose(dA)),db);  

  (5)VBA

Imports MathNet.Numerics.LinearAlgebra
Imports MathNet.Numerics.LinearAlgebra.Double  
  
Dim arr() As String, i As Long  
        arr = Split(CreateObject("scripting.filesystemobject").opentextfile("E:\\4.txt").readall.ToString(), vbLf)  
        Dim dA(UBound(arr), 2) As Double, db(UBound(arr), 0) As Double  
        Dim str() As String  
        For i = 0 To UBound(arr)  
            'ReDim Preserve Txt(i)  
            If arr(i) = "" Then  
                Continue For  
            End If  
            str = Split(arr(i), ",")  
            dA(i, 0) = Convert.ToDouble(str(0))  
            dA(i, 1) = Convert.ToDouble(str(1))  
            dA(i, 2) = 1  
            db(i, 0) = Convert.ToDouble(str(2))  
        Next  
        Dim A, b, ma As Matrix  
        A = DenseMatrix.OfArray(dA)  
        b = DenseMatrix.OfArray(db)  
        ma = (A.Transpose() * A).Inverse() * A.Transpose() * b  
        Console.WriteLine("a0,a1,a2:" + ma(0, 0).ToString("f6") + "," + ma(1, 0).ToString("f6") + "," + ma(2, 0).ToString("f6"))

  (6)Python

from numpy import *;
  
f=open('E:\\4.txt', encoding='gbk')  
txt=[]  
strs = []  
A = []  
b = []  
a = []  
for line in f:  
    strs=line.strip().split(',')  
    A.append([float(strs[0]),float(strs[1]),1])  
    b.append([float(strs[2])])  
A = mat(A)  
b = mat(b)  
a = (A.T*A).I*A.T*b  
print(a)

参考资料:
[1] HIIWAR_ZB. 最小二乘法——拟合平面方程(深度相机外参标定、地面标定); 2020-06-23 [accessed 2023-06-25].
[2]哈哈kls . 最小二乘法拟合平面; 2018-09-10 [accessed 2023-06-25].
[3] 脱掉外衣看本质. 三维空间离散点 平面拟合算法 C++实现; 2019-03-07 [accessed 2023-06-25].