本文主要介绍最小二乘的直接求解法和SVD分解法,利用Eigen库进行矩阵运算求解。 假设有一散点集合,当各点v到一平面S的距离d的平方和最小,该平面s即为最小二乘下的最优解。将距离平方和描述为平面拟合误差

                            (1)

定义点类如下

class vec
{
public:float v[3];vec(){}vec(float x,float y ,float z){v[0] = x; v[1] = y; v[2] = z;}const float &operator [] (int i) const{return v[i];}float &operator [] (int i){return v[i];}
};

一、直接求解法

平面方程表示为

两边同时除以C,

方程则可以表示为

先介绍下用定义求解的方式:

(1)式误差可以描述为

要使e最小,应满足

列出如下方程

改写为矩阵

求解上述矩阵获得a0,a1,a2的值,即可求得平面方程。代码如下

void ComputePlane(const vector<vec>&points, float &A, float &B, float &C, float &D)
{int n = points.size();MatrixXd mleft = MatrixXd::Zero(3, 3);//初始化左边项VectorXd b = VectorXd::Zero(n); //初始化右边项for (int i = 0; i < n; ++i)//遍历点装填两个矩阵{mleft(0, 0) += points[i][0] * points[i][0];//a11,xi平方和mleft(0, 1) += points[i][0] * points[i][1];//a12=a21,xiyi和mleft(0, 2) += points[i][0];//a13=a31,xi和mleft(1, 1) += points[i][1] * points[i][1];//a22,yi平方和mleft(1, 2) += points[i][1];//a23=a32,yi和b[0] += points[i][0] * points[i][2];//xizi和b[1] += points[i][1] * points[i][2];//yizi和b[2] += points[i][2];//zi和}mleft(2, 2) = n;//a33,nmleft(1, 0) = mleft(0, 1);mleft(2, 0) = mleft(0, 2);mleft(2, 1) = mleft(1, 2);VectorXd x = mleft.fullPivHouseholderQr().solve(b);//QR分解求解float a0 = x[0]; float a1 = x[1]; float a2 = x[2];//为了便于理解,赋值给临时变量A = a0; B = a1; C = -1; D = a2;
}

然而这种方式还需要计算左边项和右边项各元素的值,相对繁琐,Eigen库可以直接求解超定方程组,左边项为散点x,y值与1,右边项为z值,列出如下方程组直接求解得到的即是最小二乘解。

实现代码如下,传入点集points和待求解平面系数ABCD。不论是采用定义法建立3*3的左边项还是解如下超定方程组,获得的结果是一样的,证明Eigen库求解超静定问题内部采用的是最小二乘的思想。

void ComputePlane(const vector<vec>&points, float &A, float &B, float &C, float &D)
{int n = points.size();MatrixXd mleft = MatrixXd::Ones(n, 3);//初始化左边项VectorXd b = VectorXd::Zero(n); //初始化右边项for (int i = 0; i < n; ++i)//遍历点装填两个矩阵{mleft(i, 0) = points[i][0];mleft(i, 1) = points[i][1];b[i] = points[i][2];}VectorXd x = mleft.fullPivHouseholderQr().solve(b);//QR分解求解float a0 = x[0]; float a1 = x[1]; float a2 = x[2];//为了便于理解,赋值给临时变量A = a0; B = a1; C = -1; D = a2;
}

二、SVD分解法

平面方程表示为ax+by+cz = d,(1)

约束条件为

计算散点坐标的平均值,应满足 (2)

(1)式代入各个散点,(1)-(2)得(3)

列为矩阵,式(3)等价于AX = 0

目标函数为,约束条件,对做奇异值分解,则,且,假设D的最后一个对角元素为最小奇异值,当且仅当

时,取得,此时最小奇异值对应的特征向量即是平面的法向量,即a,b,c。

在Eigen库中,有现成的JacobiSVD类供调用,代码实现如下

void ComputePlane(const vector<vec>&points, float &A, float &B, float &C, float &D)
{int n = points.size();MatrixXd mleft = MatrixXd::Zero(n, 3);//初始化矩阵Avec averagepoint(0, 0, 0);//平均坐标for (int i = 0; i < n; ++i){averagepoint[0] += points[i][0] / n;averagepoint[1] += points[i][1] / n;averagepoint[2] += points[i][2] / n;}for (int i = 0; i < n; ++i)//遍历点装填矩阵{mleft(i, 0) = points[i][0] - averagepoint[0];mleft(i, 1) = points[i][1] - averagepoint[1];mleft(i, 2) = points[i][2] - averagepoint[2];}JacobiSVD<MatrixXd> svd(mleft, ComputeFullU | ComputeFullV);//对矩阵A分解MatrixXd vt = svd.matrixV().transpose();//获得矩阵v的转置Vector3d b(0, 0, 1);VectorXd x = vt.fullPivHouseholderQr().solve(b);A = x[0]; B = x[1]; C = x[2];D = A*averagepoint[0] + B*averagepoint[1] + C * averagepoint[2];
}

main函数的内容和输出结果如下。输入随机点个数n,随机点的坐标值为0-10的浮点型,输出点坐标集和最后拟合的平面方程。

int main()
{vector<vec>points;int n = 0;cin >> n;for (int i = 0; i < n;i++){float x, y, z;//在0-1内取随机数x = double(rand()) / RAND_MAX*10;y = double(rand()) / RAND_MAX*10;z = double(rand()) / RAND_MAX*10;points.push_back(vec(x,y,z));}for (int i = 0; i < n; i++){cout << "(" << points[i][0] << "," << points[i][1] << "," << points[i][2] << ")" << endl;}float A, B, C, D;ComputePlane(points, A, B, C, D);cout << "平面方程为 "<<A << "x+" << B << "y+" << C << "z =" << D << endl;system("pause");return 0;
}

在做自己的项目过程中,发现svd分解法得到的平面更加准确,如下柱状图拟合1000个平面,横坐标表示平面的序号,纵坐标表示拟合点距离该平面的最大距离,左边是svd分解的结果,最大值在1.2以下,而右边直接求解的结果大于1.2的平面很多,当然检查它们的距离平方和结果也是一样的,svd同样优于直接求解,感兴趣的可以自行验证下。

参考链接

最小二乘拟合平面(C++版) - 知乎

利用Eigen库实现最小二乘拟合平面相关推荐

  1. 利用eigen库简单实现矩阵功能

    eigen是目前运行速度较快的C++矩阵运算库,而且其轻便小巧安装方便的特点简直是吸引人啊!特做此笔记,记录一下这个安装简单.体积轻巧.功能强大的C++库. 1. Download and Insta ...

  2. Eigen库最小二乘拟合

    Eigen库最小二乘拟合 最小二乘公式 Eigen实现 其他 在研究zernike多项式过程中,需要使用到矩阵的最小二乘拟合.所以在这里记录分享Eigen库的最小二乘拟合使用方法. by HPC_ZY ...

  3. Linux下MKL库的安装部署与使用,并利用cmake编译器调用MKL库去提升eigen库的计算速度

    文章目录 前言 一.MKL库的下载 二.MKL库的安装与配置 1.MKL库的安装与配置 2.代码测试 总结 前言 在用C/C++编写模型预测控制算法(MPC)的代码时候,由于预测步长和控制步长的设置较 ...

  4. PCL- 最小二乘法拟合平面

    一.最小二乘法 最小二乘法 - least sqaure method_Σίσυφος1900的博客-CSDN博客_二次函数的最小二乘法 PCL最小二乘法进行平面拟合原理_jiangxing11的博客 ...

  5. Matlab 最小二乘法 拟合平面

    一.原理推导 最小二乘法 拟合平面是我们最常用的拟合平面的方法,但是有特殊的情况是用这种方法是不能拟合的,后续会加上这种拟合方法(RANSAC). matlab 最小二乘拟合平面(方法一) - 灰信网 ...

  6. python画抛物线_在python中利用最小二乘拟合二次抛物线函数的方法

    1.最小二乘也可以拟合二次函数 我们都知道用最小二乘拟合线性函数没有问题,那么能不能拟合二次函数甚至更高次的函数呢?答案当然是可以的.下面我们就来试试用最小二乘来拟合抛物线形状的的图像. 对于二次函数 ...

  7. 使用GSL库实现非线性最小二乘拟合—原理与C代码实现(VS2019)

    目录 一.参考 二.非线性最小二乘 三.GSL库中非线性最小二乘拟合部分 1. gsl_multifit_nlinear_parameters结构体 2. gsl_multifit_nlinear_t ...

  8. 利用opencv结合mfc实现识别圆形标记点并计算多个圆形标记点的三维坐标,拟合平面并计算法向量

    利用opencv结合mfc实现识别圆形标记点并计算多个圆形标记点的三维坐标,拟合平面并计算法向量 具体步骤 二.对应代码 1.引入库 2.标定 识别圆形标记点 左右图像中圆形标记点匹配 计算三维坐标 ...

  9. python求抛物线函数_在python中利用最小二乘拟合二次抛物线函数的方法

    1.最小二乘也可以拟合二次函数 我们都知道用最小二乘拟合线性函数没有问题,那么能不能拟合二次函数甚至更高次的函数呢?答案当然是可以的.下面我们就来试试用最小二乘来拟合抛物线形状的的图像. 对于二次函数 ...

  10. PCL:拟合平面直线和曲线以及空间曲线的原理到算法实现

    使用两种思路进行直线拟合: 1.利用逆矩阵思想 --------------进行下列公式的推导需要理解逆矩阵(求A矩阵的逆矩阵,则A矩阵必须是方阵)的知识: (1)为什么要引入逆矩阵呢? 逆矩阵可以类 ...

最新文章

  1. java map 红黑树_Java集合-TreeMap和红黑树
  2. 题目1171:C翻转
  3. Flink on Zeppelin 系列之:Yarn Application 模式支持
  4. win7 easybcd 安装centos7.5 双系统
  5. 云南计算机专升本数据结构_怎么查找云南省2019年专升本计算机专业试题
  6. PHP笔记-使用PHPStorm断点调试php代码
  7. 对比 | Python中超级好用的“列表解析式”、“字典解析式”、“集合解析式”
  8. ASP.NET2.0快速入门--高级数据方案(3)
  9. python面试1000题之7-8
  10. 【树状数组+离线查询】HDU 3333 Turing Tree
  11. stm8s103头文件
  12. UVC 摄像头驱动开发
  13. MacBook上有哪些相见恨晚的神器
  14. 学计算机智商,IQ最高的十大专业公布,考验你们智商的时刻到了!
  15. jvm 内存模型结构
  16. 247 中心对称数 II
  17. 父盒子内子盒子居中的方法
  18. 基于STM32F103的直流电机调速系统
  19. 又到年末“团建”!某企业员工吐槽:这真是一场噩梦……
  20. 简单的proxy之TinyHTTPProxy.py

热门文章

  1. Python黑客攻防(十六)编写Dos脚本,进行简单攻击演示
  2. Sverlet案例小萌神服务器端
  3. excel公式编辑器_V14.0发布:组件化编辑器+数据透视表
  4. 计算机最最最底层的工作原理是怎么运行的
  5. IDEA 代码分屏编辑对比: split vertically
  6. ios点击推送闪退_iphone闪退是什么原因?
  7. iOS闪退日志的收集和解析
  8. SpringMVC复习——B站
  9. RS BCH级联编译码的性能仿真
  10. 程序猿段子_程序员的十个段子,能看懂的都是深有同感!