最小二乘法拟合空间直线

一、空间直线方程

1.1 一般方程

空间直线可以看成成两个平面的交线,设两个平面方程分别为A1x+B1y+C1z+D1=0A2x+B2y+C2z+D2=0,则直线l的一般方程可以表示为:
{A1x+B1y+C1+D1=0A2x+B2y+C2+D2=0

1.2 点向式


设直线方向向量为v=(m,n,t),直线上一点P0x0,y0,z0,则任意点P:(x,y,z)可以表示为 P=kP0,所以直线也可以表示为:
xx0m=yy0n=zz0t=k

1.3 参数式

点向式经过简单变形即可得到:
{x=x0+mky=y0+nkz=z0+tk

1.4 平面法线叉乘表示

设两平面法线分别为:n1=(x1,y1,z1)n2=(x2,y2,z2),则直线的方向向量v(i,j,k)可以表示为:
v=n1×n2=|ijkx1y1z1x2y2z2|
在到直线上取一点即可表示直线。

二、公式推导

这里参考链接1给出基于最小二乘法拟合直线的推导,设直线点向式方程为:xx0m=yy0n=zz0t,则转换后可以得到

{x=mt(zz0)+x0=k1z+b1y=nt(zz0)+y0=k2z+b2

其中K1=mt,b1=x0mtz0,k2=nt,b2=y0ntz0
所以空间直线可以看作

{x=k1z+b1y=k2z+b2

两个平面相交,因此对空间之间拟合就可以转换为对这两个平面的拟合,假设有n个点(x1,y1,z1),(x2,y2,z2),...,(xn,yn,zn),则可以得到这些点和两个平面的残差的平方和为:

{Q1=i=1n(xik1zib1)2Q2=i=1n(yik2zib2)2

依据最小二乘法有:MinQ1MinQ2,因此分别对k1,b1,k2,b2求导,然后令导数为0可以得到:

{i=1n2(xik1zib1)(zi)=0i=1n2(xik1zib1)(1)=0i=1n2(yik2zib2)(zi)=0i=1n2(yik2zib2)(zi)=0

依据第一个式子有:

i=1n(xizik1zi2+bizi)=0

==>

k1=i=1nxizii=1nbizii=1nzi2

依据第二个式子有:

b1=i=1nxik1i=1nzin

b1代入k1中可以得到:

k1=ni=1nxizii=1nxii=1nzini=1nzi2i=1nzii=1nzi

同理可以得到:

k2=ni=1nyizii=1nyii=1nzini=1nzi2i=1nzii=1nzi

b2=i=1nyik2i=1nzin

求出k1,k2,b1,b2后就可以算出直线的方向向量v=(m,n,t),令t=1,则m=k1,n=k2,所以v(k1,k2,1),还可以求出直线上一个点p(x,y,z) ,令z=1,则x=k1+b1,y=k2+b2,所以点坐标可以表示为:p(k1+b1,k2+b2,1)

三、代码实现


double FitLine(const std::vector<Vector3>& pointArray, Vector3& dir, Vector3& pt)
{
		// ref: https://www.doc88.com/p-8189740853644.html
		core::Vector3 center = core::Vector3(0.0, 0.0, 0.0);
		int n = pointArray.size();
		for (const core::Vector3& pt : pointArray) {
			center += pt;
		} 

		double sumXZ = 0.0, sumYZ = 0.0, sumZ2 = 0.0;
		for (const core::Vector3& pt : pointArray) {
			sumXZ += pt.x() * pt.z();
			sumYZ += pt.y() * pt.z();
			sumZ2 += pt.z() * pt.z(); 
		}
		double den = n * sumZ2 - center.z() * center.z();
		double k1 = (n * sumXZ - center.x() * center.z()) / den;
		double b1 = (center.x() - k1 * center.z()) / n;
		double k2 = (n * sumYZ - center.y() * center.z()) / den;
		double b2 = (center.y() - k2 * center.z()) / n; 

		dir = core::Vector3(k1, k2, 1.0);
		dir.Normalize();
		
        double z = 1.0;
        double x = k1 + b1;
        double y = k2 + b2;
        pt = core::Vector3(x, y, 1.0);
		return 0.0;
}

四、效果

下面是拟合的效果,红色点是输入散点,绿色点表示直线上的两个点。

五、参考资料

https://www.doc88.com/p-8189740853644.html

posted @   半夜打老虎  阅读(348)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示