python最小二乘法拟合圆_最小二乘法拟合圆

本文介绍了如何使用Python的最小二乘法来拟合数据点近似落在圆上的情况。通过数学推导和代码实现,展示了求解圆心坐标和半径的方法。虽然最小二乘法对离群点敏感,但对均匀分布的噪声数据能提供较准确的拟合结果。
摘要由CSDN通过智能技术生成

有一系列的数据点 {xi,yi}。我们知道这些数据点近似的落在一个圆上。依据这些数据预计这个圆的參数就是一个非常有意义的问题。今天就来讲讲怎样来做圆的拟合。圆拟合的方法有非常多种,最小二乘法属于比較简单的一种。

今天就先将这样的。

我们知道圆方程能够写为:

(x?xc)2+(y?yc)2=R2

通常的最小二乘拟合要求距离的平方和最小。也就是

f=∑((xi?xc)2+(yi?yc)2??????????????????√?R)2

最小。

这个算起来会非常麻烦。

也得不到解析解。

所以我们退而求其次。

f=∑((xi?xc)2+(yi?yc)2?R2)2

这个式子要简单的多。我们定义一个辅助函数:

g(x,y)=(x?xc)2+(y?yc)2?R2

那么上面的式子能够表示为:

f=∑g(xi,yi)2

依照最小二乘法的通常的步骤,可知f 取极值时相应以下的条件。

?f?xc=0?f?yc=0?f?R=0

先来化简 ?f?R=0

?f?R=?2R×∑((xi?xc)2+(yi?yc)2?R2)=?2R×∑g(xi,yi)=0

我们知道半径 R 是不能为 0 的。所以必定有:

∑g(xi,yi)=0

这是个非常实用的结论。剩下的两个式子:

?f?xc=?4∑((xi?xc)2+(yi?yc)2?R2)(xi?xc)=?4∑g(xi,yi)(xi?xc)=?4∑xig(xi,yi)=0?f?yc=?4∑((xi?xc)2+(yi?yc)2?R2)(yi?yc)=?4∑g(xi,yi)(yi?yc)=?4∑yig(xi,yi)=0

也就是以下两个等式:

∑xig(xi,yi)=0∑yig(xi,yi)=0

这里设:

ui=xi?xˉuc=xc?xˉvi=yi?yˉvc=yc?yˉ

当中:

xˉ=∑xi/Nyˉ=∑yi/N

那么简单的推导一下,就会发现:

∑uig(ui,vi)=0∑vig(ui,vi)=0

事实上都不须要推导,这个变量替换仅仅只是是改动了坐标原点的位置。而我们的等式根本就与坐标原点的详细位置无关。

所以必定成立。

这两个式子展开写是这样的:

∑((ui?uc)2+(vi?vc)2?R2)ui=0∑((ui?uc)2+(vi?vc)2?R2)vi=0

进一步展开:

∑(u3i?2u2iuc+uiu2c+uiv2i?2uivivc+uiv2c?uiR2)=0∑(u2ivi?2uiviuc+viu2c+v3i?2v2ivc+viv2c?viR2)=0

我们知道 ∑ui=0, ∑vi=0。所以上面两个式子是能够化简的。

∑(u3i?2u2iuc+uiv2i?2uivivc)=0∑(u2ivi?2uiviuc+v3i?2v2ivc)=0

为了简化式子,我们定义几个參数:

Suuu=∑u3iSvvv=∑v3iSuu=∑u2iSvv=∑v2iSuv=∑uiviSuuv=∑u2iviSuvv=∑uiv2i

那么上面的式子能够写为:

Suuuc+Suvvc=Suuu+Suvv2Suvuc+Svvvc=Suuv+Svvv2

至此,就能够解出uc 和vc 了。

uc=SuuvSuv?SuuuSvv?SuvvSvv+SuvSvvv2(S2uv?SuuSvv)vc=?SuuSuuv+SuuuSuv+SuvSuvv?SuuSvvv2(S2uv?SuuSvv)

那么:

xc=uc+xˉyc=vc+yˉ

还剩下个 R 没求出来。 也非常easy。

∑g(xi,yi)=0∑((xi?xc)2+(yi?yc)2?R2)=0

所以:

R2=∑((xi?xc)2+(yi?yc)2)

好了。

以下给出个代码,这个代码的详细公式和我这里给出的有一点小差异。可是原理是同样的。

/**

* 最小二乘法拟合圆

* 拟合出的圆以圆心坐标和半径的形式表示

* 此代码改编自 newsmth.net 的 jingxing 在 Graphics 版贴出的代码。

* 版权归 jingxing, 我仅仅是搬运工外加一些简单的改动工作。

*/

typedef complex POINT;

bool circleLeastFit(const std::vector &points, double &center_x, double &center_y, double &radius)

{

center_x = 0.0f;

center_y = 0.0f;

radius = 0.0f;

if (points.size() < 3)

{

return false;

}

double sum_x = 0.0f, sum_y = 0.0f;

double sum_x2 = 0.0f, sum_y2 = 0.0f;

double sum_x3 = 0.0f, sum_y3 = 0.0f;

double sum_xy = 0.0f, sum_x1y2 = 0.0f, sum_x2y1 = 0.0f;

int N = points.size();

for (int i = 0; i < N; i++)

{

double x = points[i].real();

double y = points[i].imag();

double x2 = x * x;

double y2 = y * y;

sum_x += x;

sum_y += y;

sum_x2 += x2;

sum_y2 += y2;

sum_x3 += x2 * x;

sum_y3 += y2 * y;

sum_xy += x * y;

sum_x1y2 += x * y2;

sum_x2y1 += x2 * y;

}

double C, D, E, G, H;

double a, b, c;

C = N * sum_x2 - sum_x * sum_x;

D = N * sum_xy - sum_x * sum_y;

E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x;

G = N * sum_y2 - sum_y * sum_y;

H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y;

a = (H * D - E * G) / (C * G - D * D);

b = (H * C - E * D) / (D * D - G * C);

c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N;

center_x = a / (-2);

center_y = b / (-2);

radius = sqrt(a * a + b * b - 4 * c) / 2;

return true;

}

下图是个实际測试的结果。对这样的均匀分布的噪声数据计算的结果还是非常准确的。

可是当数据中有部分偏向某一个方向的干扰数据时。结果就不那么乐观了。下图就非常说明问题。

数据点中有 20% 是干扰数据。

拟合出的圆就明显被拽偏了。

之所以出现这个问题就是由于最小二乘拟合的平方项对离群点非常敏感。解决问题就要用其它的拟合算法,比方用距离之和作为拟合判据。等我有空了再写一篇博客介绍其它几种方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值