找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 4243|回复: 9

[研讨] 最小二乘法拟合圆

[复制链接]

已领礼包: 40个

财富等级: 招财进宝

发表于 2016-6-5 13:57:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 newer 于 2016-6-5 14:06 编辑

有一系列的数据点 {xi,yi},我们知道这些数据点近似的落在一个圆上,根据这些数据估计这个圆的参数就是一个很有意义的问题。今天就来讲讲如何来做圆的拟合。圆拟合的方法有很多种,最小二乘法属于比较简单的一种。今天就先将这种。

我们知道圆方程可以写为:

(x-x_c{2})+(y-y_c{2})=R{2}


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

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 没求出来, 也很简单。


∑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<int> POINT;bool circleLeastFit(const std::vector<POINT> &points, double ¢er_x, double ¢er_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.real();         double y = points.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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

下图是个实际测试的结果。对这种均匀分布的噪声数据计算的结果还是很准确的。

0.jpg

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

1.jpg

数据点中有 20% 是干扰数据。拟合出的圆就明显被拽偏了。

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


论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 5583个

财富等级: 富甲天下

发表于 2016-6-5 17:38:54 | 显示全部楼层
期待尊敬的版主能翻译成纯Lisp语言的版本。

点评

论坛已经有了,使用XDGE几何库求得最小二乘圆圆心,见: http://bbs.xdcad.net/thread-676857-1-1.html  详情 回复 发表于 2016-6-5 21:13
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 1861个

财富等级: 堆金积玉

发表于 2016-6-5 20:04:03 | 显示全部楼层
本帖最后由 aimisiyou 于 2016-6-5 20:10 编辑

是否可以采取一些措施过滤掉那些偏差较大的点,比如先拟合得到一圆,到圆心的距离为半径的95%~105%的点保留,其余的点过滤掉。再进行迭代拟合,直到所有点都保留为止。当然区间取值也影响结果的好坏。

点评

最小二乘本身就是去求解未知的可能的状态,算法本身已经被证明是在大概率上是有效的了,但是关键还是离散的数据是否“真”的合适。 过滤本身也是对未知的操作,也可能把有用的给过滤掉了。所以未必是个好方案。  详情 回复 发表于 2016-6-5 21:16
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 145个

财富等级: 日进斗金

发表于 2016-6-5 21:13:06 | 显示全部楼层
HLCAD 发表于 2016-6-5 17:38
期待尊敬的版主能翻译成纯Lisp语言的版本。

论坛已经有了,使用XDGE几何库求得最小二乘圆圆心,见: http://bbs.xdcad.net/thread-676857-1-1.html
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 145个

财富等级: 日进斗金

发表于 2016-6-5 21:16:00 | 显示全部楼层
aimisiyou 发表于 2016-6-5 20:04
是否可以采取一些措施过滤掉那些偏差较大的点,比如先拟合得到一圆,到圆心的距离为半径的95%~105%的点保留 ...

最小二乘本身就是去求解未知的可能的状态,算法本身已经被证明是在大概率上是有效的了,但是关键还是离散的数据是否“真”的合适。 过滤本身也是对未知的操作,也可能把有用的给过滤掉了。所以未必是个好方案。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

发表于 2016-6-7 16:35:43 | 显示全部楼层
Pdf转成dwg时,全是折线。窗选自动拟合一些就好了

点评

贴一个这样的图片 和 DWG 传上论坛吧。  详情 回复 发表于 2016-6-7 17:10
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 40个

财富等级: 招财进宝

 楼主| 发表于 2016-6-7 17:10:09 | 显示全部楼层
WhoCanSay 发表于 2016-6-7 16:35
Pdf转成dwg时,全是折线。窗选自动拟合一些就好了

贴一个这样的图片 和 DWG 传上论坛吧。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

发表于 2016-6-8 08:33:06 | 显示全部楼层
test内含两个文件,Drawing1-Model.pdf和Drawing1-Model.dwg, Drawing1-Model.dwg由Drawing1-Model.pdf转化而来,全是短线了:'(
1.png

Drawing1-Model.pdf

3.31 KB, 下载次数: 1, 下载积分: D豆 -1 , 活跃度 1

test.rar

12.77 KB, 下载次数: 7, 下载积分: D豆 -1 , 活跃度 1

论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 1268个

财富等级: 财源广进

发表于 2016-6-9 23:09:47 来自手机 | 显示全部楼层
这个代码直观,改成alisp方便
http://blog.sina.cn/dpool/blog/s/blog_703f59920100n4zm.html?vt=4
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

发表于 2017-2-24 15:18:46 | 显示全部楼层
楼主能不能做个 lsp 程序?供网友测试。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-4-20 04:10 , Processed in 0.481918 second(s), 57 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表