反编译.NET程序及调试

想要了解一个开源项目,可以直接读代码,想了解别的,就得反编译了。

其实.NET的反编译算是方便的,毕竟是IL中间语言而不是机器码或者汇编,不过当我需要这个功能并开始找的时候发现选择并不多,其实就两个:Reflector和ILSpy。

Reflector更有名,不过2011年开始转向了收费,ILSpy则是?Development started after Red Gate?announced?that the free version of .NET Reflector would cease to exist by end of February 2011.

但是2012年开始ILSpy也停止了更新,按理说一个开源软件停止更新基本两个原因:1.社区人跑光了;2.一些法律或者其他公司的干预。ILSpy和SharpDevelop是一个社区,一直非常活跃,所以开始我觉得是M$又动手脚了。

实际情况是ILSpy的项目整合到了SharpDevelop里面,不过没有任何明确的公告,我想原因可能是遭到了M$的压力或者核心成员有争论。

ILSpy1.0开始就支持直接反编译DLL然后直接attach调试,设断点什么的,但是2.0开始变成了插件需要自己编译(二进制包里这个被删了,源码包里有)。2.1的时候连源码里也没有了而且最新的一个发布里面有冲突。ILSpy的Debug功能消失几乎是和ILSpy停止开发同时,再同时,SharpDevelop出了类似的功能,可以在调试的同时反编译第三方库。

本质上这其实是一个功能,但是实际上ILSpy原来的用法更方便,因为不需要构造Solution敲任何代码,直接可以用。

我想原因可能是:

1.过于简单的反编译操作使得门槛降低,可能受到了一些压力。

2.开发这个项目的社区目的是方便开发人员调试自己的程序,而不是去剽窃他人的劳动,所以人为地加了一些限制。

不管怎么样,现在的确不方便了,虽然可以用2.0版,不过2.0不支持64位的环境= =。

 

关于个人前途的胡言乱语

完全不知道写这个东西的意义所在,只是想找人聊又无人可聊。

最近几周心情都不太好,主要是因为临近回国却前途未卜,再加上右脚拇指发炎,这种有点低烧的感觉应该是我的免疫系统在工作吧。

出国前对现在的情况有过估计,也有心理准备,不过想到了心情还是不好,看来心志是需要锻炼的。

出来半年后以前的直系领导调部门升职,又过了两个月左右得知组织结构重组,且我的职位已经被人顶替了,虽然组织架构图没有变,但是各种邮件列表和开会名单都开始包含那位同志的名字。最后,几天前我的名字被排除出了各个群发列表,按照正常推理,这是温水煮青蛙,赶紧辞职的意思吧……不过我花公司钱出来这一年还没有结束,出来前还签了三年补充合同,这是什么节奏啊。皇上臣妾不明白啊,我也很想辞职啊,不过你们想让我走,我可以不赔钱么?不会因为现在现金流是负的于是拿我找齐吧。。。也不对,我花的是去年的钱啊。

现在的说法是我回去以后弄一个新的部门,不过人数,规模,工作性质,任务,指标,什么都没有提,本来领导说下周来德国开会(不是我的事),可以找她谈谈,结果临时找了个借口不来了,那我就要不明不白的回国吃闲饭了么,想想也不错,如果什么都不干,现在的薪水还算是可以接受的。

其实我现在还是愿意接受挑战的,自从知道要弄新部门,在德国这面就各种努力找资源,不过臣妾做不到啊……有些破东西我自己反编译看代码重新打包都好几个月了,一问还是说:“这个我要找管理层问问,看看他们的意见。”你妹的,7个多月了还没意见呢,我也懒得问了,反正公司就这样,没权限的事情有能力做也不允许做。等回去了更没办法Push了。

现在的想法是,回去以后先考MBA,三年后补充合同到期恢复自由身,学位也差不多拿到手了,到时进退自由。

说到读这个,想起了水木社区上N多小朋友叫嚣类似“本科不是清华的不要自称清华校友”,其实这个现象蛮普遍的,有点像之前流行过一阵的鄙视链,土著非土著,Top2,985,211……

其实人的圈子不同,看这种事情的观点就不同,在学校的时候,周围的人都是一个圈子(或者阶级)的,看某件事情就会非常相似,比如:

专科生:公司招聘要求学历就是浪费,没什么不一样。

清华的:你是清华的?几字班的?

但是实际情况呢?
没有本科学历的人可能知道Top2,但是应该不知道985和211的差别,211和一般重点大学的区别。
重点大学或者那些985高校的呢,可能知道有二本和三本,但是他们应该不知道有专升本,大专,查不到证的大专,成人高考,高自考。

这个社会就是这么等级分明,虽然专科毕业也有大富翁,清华出来也有地青,但是差别是存在的,我也招聘过,我几乎不考虑专科生,除非简历能打动我(当然目前还没遇到),连本科线都过不了,我怎么能指望你理解坐标系的转换可以用仿射矩阵运算?上次有个客户和我在QQ上聊天,一边说专科本科能力一样,一边问我怎么能让自己的专科背景有更多的工作机会,我当时的建议是,去读个专升本或者成人高考吧。那哥们最后也没有什么行动,因为他认为他的能力足够,而这种性质的学历认证没有用。

的确,我也不认为国内这个体系的教育制度有什么真正的能力培养,不过,对于一个已经不可能把自己的第一学历变成本科的人来说,选择这条路意味着:我承认这个社会的游戏规则和等级制度,我认可自己之前失败了,我愿意在这个等级制度下进步一点。那么你的机会肯定会比原地踏步的人多许多,我们国家就是人多,只要你进步一点点,你就可以多把几万人甩在身后。

当我下意识得给客户提出这个意见,然后又梳理出原因后,我忽然意识到我和我的客户没有什么不同。

1.在清华北大,或者一些世界级名校出身的人看来,985还是211,可能和专科生也没有什么差别。
2.现在的能力,生活已经超出了儿时能理解的上限,我有时也会觉得自己不比清华北大毕业的人差。
3.之前我也没有弄一个研究生学历的想法,考研失败(其实中途放弃)后,就下意识的厌恶这个话题,即使大学同学中70%是硕士,10%是博士。

这件事大概发生在两年前吧,从那里开始,我就经常想找一个我自己的“专接本”,为了能让自己在这个阶级社会里再上一级。

不过一直没什么行动,一是没有合适的,对于我的情况,要找一个研究生读而且学校比本科高一个级别的,国内只有那两所了,或者出国,需要大量的时间成本和经济基础,在职研究生的学位证又不一样。不过我的运气还比较好,在我下定决心出国读书的时候,公司有个外派机会。

但是问题依旧在,最后我的解决办法是读MBA,像大多数人一样,我以前也认为MBA是混学历,花钱买学历,但是随着在这个社会上混得时间变长,我发现所谓“大多数人认为”就是扯淡,每个人的情况都不一样,而且我的目标又不是当大多数人。

有没有用?不知道,开始时不知道,后来静下心里看一些人的经历和总结,又觉得这个问题没什么意义了,社会的价值观是不统一的,不像在学校,你多背十个单词考试分就会高一点,所以有用。而商品社会的规则是,你付出东西,得到东西,可能赚钱,可能赔钱,当然有时钱也会蒸发,赚了一样倒霉。所以一些之前脑海里抵触MBA的情绪一扫而空。

花钱买学历,没错,而且没什么丢人的,十几万买个硕士学历也是投资,比买辆三系五系的贬值强。不算清华校友,自己往脸上贴金,真不用,规则是,你NB了,大家抢着让你当校友。

心情好多了,“国王长了个驴耳朵”疗法还是有用的。

 

又一次在求实BBS上和人吵架

除了一些已经没人发言的俱乐部版面,在求实收藏夹里只有三个版了,留学,工作还有鹊桥。

这次吵架是在留学版,起因其实蛮简单,某个刚拿到offer还没开学的小姑娘写了个系列贴——“从零开始留学申请”。刚刚开始还好,还能算是个技术贴——对我而言,判断的标准很简单:你是在介绍经验的同时做点生意,还是打着介绍经验的幌子来做生意——但是当这位小姑娘发了三四篇不太丰满的贴子后宣布更新暂停,理由是(摘抄原文):

?为了解决当务之急,开了个淘宝店来赚取赴美帝机票,由于生意起色不大,因此申请等经验的分享可能要受到影响而放慢脚步并有可能搁置了,还请各位见谅。?
生意顺畅之后会再继续更新留学指导和经验分享,并为大家答疑。?

以及:

这几天忙着装修淘宝店铺,给大家带来的不便尽情谅解,经验分享何时继续?店铺生意不需要我担心时即会在回来分享的。

为了避免断章取义:原文链接,地球人都知道淘宝店要想稳定运行起码几个月吧,我于是就认为这位小姑娘本意只是做生意,于是我的毛病就犯了,回帖说了几句(当然不好听),然后就是开始吵。

话说回来,这次我意识到了这个混求实养成的恶习,遇到和自己看不惯的事情,就要冷嘲热讽一番,还要装作很清高的站在道德至高点上。其实完全没必要:一、人家只是做生意,虽然说找了个借口,但是也算是常见的推销手段,版主站务作为维护论坛的人员都不管,我的确没有权利更没有义务去管人家;二、对我没有好处,我在争吵中付出的是时间精力以及用胡搅蛮缠的思路去思考,不但没好处,还影响自己;三、实际受益者不领情,如果说真有什么收益者,就是那些没有看出小姑娘是在做广告,经过我们这番争吵意识到了,不过实际情况是,看出来的不需要我提醒,没看出来的依然质问我为什么多管闲事影响人家小姑娘分享经验。

我也有些进步,以前可能会继续吵吧,这次我起码优雅的退出,道歉,承认错误,祝人家留学顺利,当然也想祝人家生意顺利,不过听起来想讽刺就没写,然后消失。

再然后,写在自己的地盘里,想说什么说什么,你回复还要经过老娘批准呢,哼。

不过暂时还没人回复过我任何文章呢,哼唧。

如何成为坐标测量的专家?你离三坐标专家还有多远?

根据ISO/TC213,起草人提出了专家的几项标准,翻译如下(不是我翻译的):

CMM-专家能在一套常无法预测结果的不同环境中运用一系列的基本原理和复杂技术,具有很大的自主权,并能在活动中发展其他能力。

CMM-操作者应该掌握的专业知识有:

——几何学,数据,几何公差和CNC编程

并且应具备的知识有:

——要点-CAD的制造要点

——数字的过滤和评估

——随机的

——测量不确定度的评估和对CMM的监控

——自由形体表面的数字化

——质量管理,质量管理的方面和方法,TQM,CAQ和质量成本

——良好的坐标测量实践

并且应该能够:

——编写并优化测试计划

——对测量和使用者资格进行监控

——对测量不确定度进行评估

——自由形体表面的数字化

——质量管理的实施方法

然后我尝试着翻译成易懂的语言。

CMM专家应该了解通用的基础知识,以及很多领域的专业知识,以及在没有过往经验的条件下,自主分析并辅导他人的能力。

需要拥有如下知识:

——几何学,数据,几何公差和CNC编程
俗称的GDT,结合到日常工作中,可以简单的理解为机械制图和工艺,以前的教材叫互换性测量,现在是精密机械设计,另外就是如何操作三坐标了。
这是最基础也最容易被忽略的一点,我面试过一些自称会编程,会操作三坐标的人,有些基础概念都无法掌握,基准,最大实体,公差,配合什么的。

——要点-CAD的制造要点
翻译的好烂,原文应该是基本的制造业知识和基本的CAD知识。
CAD是计算机辅助设计的缩写,在这里的意思是要有相关的课外知识,三坐标是一种检测设备,对应着质量部门,和质量部门有关的一般是设计部门和制造加工部门。
一个是工作的基础,检测的目的是为了验证产品是否符合设计,那么设计部门的要求就是标准。
另外一个是客户,客户送来产品,需要的是检测结果是否合格。
所以作为专家,必须要了解这些知识以便和这两个部门打交道。

——数字的过滤和评估
数字滤波和评价。这也是我经常遇到的问题,最小二乘法和最小条件法哪个测得准(应该选哪个),滤波是否应该一直选中?
真正明白这些内容需要一定的数学基础和实际的经验。

——随机的
我的理解这里随机指的的概率论里的随机,另外一个可能性是随机过程。
在处理一些数据和情况时,需要了解并排除一些特殊情况,而这些情况往往是随机出现的。不太好解释。

——测量不确定度的评估和对CMM的监控
测量不确定度一直是这一行的难点,通常除了三坐标厂家和一些科研机构,大部分用户并不了解这些。
相当一部分原因是为了商业原因刻意地模糊这一概念,测量不确定度本身是个单独的概念,大学里还有专门的课程,往往和实验课结合在一起。
三坐标的测量不确定度有标准定义ISO10360,对于具体检测,需要将三坐标的既有不确定度与实测尺寸做转换。

——自由形体表面的数字化
数字化就是逆向,将不规则表面逆向为计算机可识别的格式,在大多数人看来只是用一个高科技设备晃一晃就完成了。
实际上这是一个比较综合的题目,根据材料,精度要求等有不同的设备和方法,软件上也有很多的算法。
不过这个要求是比较早之前的文档,现在由于计算机辅助的超高速发展,对于这一块的理论要求已经非常低了。

——质量管理,质量管理的方面和方法,TQM,CAQ和质量成本
这个也是比较好笑的一方面,看到很多简历写我掌握质量七大手法,面试一问就是往已经做好的表格里填数据,怎么算的?不知道。
还有就是一些花了5000块钱外面上了个培训班,就号称自己熟悉ISO9001管理认证体系什么什么的。
这几种情况是纯管理者作的,不能说不对,但是如果是技术人员,这么做完全是本未倒置。

——良好的坐标测量实践
这个就是堆经验了,我有一些客户非常看重自己的工作年限,但是这样不全面,你只测几吨重的铸件,就无法理解模具行业对于效率的要求;
只在模具行业,就无法理解为什么测量大型工件时为什么要考虑温度,材料等等等等。
对专家的要求当然是越广越深越好。

并且应该能够:

——编写并优化测试计划
大白话就是看图纸来编程,问题在于,你能否保证你的方案能保证正确性和完备性么?
干测量是很战战競競的工作,合格放出去的会想是否会有未发现的缺陷,不合格的会想是工件本身的问题还是我的检测有问题。

——对测量和使用者资格进行监控
如果你没有直接操作设备,你能否发现他人的问题并作出指导?
这个就是我们国内经常看到的审核,客户派到供应商现场一些人,审核有问题的环节。

——对测量不确定度进行评估
如果谁说我的测量是准确的,那么这个人肯定不懂测量,测量永远是测不准的(不是量子力学里的概念-_-b),但是作为专家,你需要知道我测得有多接近于真实的情况。

——自由形体表面的数字化
不规则曲面的逆向(见上文)

——质量管理的实施方法
将质量管理方法同实际工作相结合,生般硬套也不是不行,不过用上了,出了结果,只是看个数是没有意义的,需要将这个工具同上下环节联系起来。

根据这些要求,需要有相当系统的知识,还要有相当多的实际经验,个人感觉在国内这种不太重视质量的环境下很难培养出这类的专家来。

全文结束,你离三坐标专家还有多远?如果您觉得您符合其中60%以上,欢迎来我司应聘。

两件小事的感想——人际关系对日常工作的影响

第一件事比较简单,大约两年前给一个客户做了些单独的解决方案,前天一个同事说在给另外一个客户现场使用的时候有很多问题,发邮件和截图问我原因。

最后我不高兴的原因有两点,一是用之前没有和我商量或询问,由于我有些严苛的版权意识,有些被冒犯的感觉。

二是我针对他的问题和情况,前后回复了四封邮件,包括BUG的原因,参考资料,替代解决方案等。然后——就没有然后了,一点回音都没有。

第二件事细节稍复杂,某销售需要特殊软件,要德国这面的开发部门支持,报价,安装等等等等。

把技术协议(虽然在技术部门看来毫无帮助)往我这一扔,让我去弄。我稀里湖涂帮他翻译了一下就去找人咨询了,从此这件事就成我的了。

但是我跟这件事唯一的关系是,我是中国人,我人在德国……简直无语了。

这两件事对我的直接影响是,这两位同事以后就在我的信任黑名单里了,对于第一位技术同事,那么以后所有他的咨询邮件我都会直接转给他的领导,在经过他的领导确认并确定这件事需要我的支持的情况下,再投入精力去解决。对于第二位销售同事,在没有经过正常流程(他的领导的领导找我的领导,再给我下命令)的情况下,我不会做任何份外的事情,哪怕是转发一封邮件,而且也不会回复,直接无视或放到回收站。

然后反思我的做法,我其实在这两件事中损失的只是一些时间,但换个思路损失的就是资源,根本的问题是这种损失无法量化,可能很少可能很大。

我在这两件事中用处理私人关系的方法来处理了工作,处理私人关系的时候我可以付出资源,来判断一个人是否可交,因为人需要朋友,而得到真正的朋友相当不易,值得去付出资源。对于工作,期望的回报并不是交朋友,而是付出尽可能少的资源,完成任务(获得更多的资源)。

未来应该修正的是投入资源的量吧,先判断做这件事需要投入多少资源,然后再根据回报来判断是否投入这些资源。

最后一条感想,跨部门指挥是能力的体现,但是结果很难理想,还是尽量避免展示这种能力吧。

根据凸轮升程值求轮廓线

通常此类工作由设计人员完成,根据需求计算出合适的轮廓。

但由于国内质量部门地位低下,以及CAD在设计环节上的缺失,经常拿不到CAD Model或者轮廓的数据,只能拿到升程值。
当然另外一个原因就是专门的凸轮检测仪是模拟测量,可以直接得到对应的升程值。

但是对于坐标检测,就需要将升程值转化为轮廓坐标,通过坐标检测出轮廓线,再转化回升程值。

已知基圆半径\(r_0\),滚子半径\(r_r\),对应角度\(n\)的升程值为\(s_n\),从动件与凸轮偏心量为\(e\),则滚子圆心轨迹坐标\(P_n\left(x_n,y_n\right)\)为\[x_n=(r_0+r_r+s_n)\sin(n)+e\cos(n)\]\[y_n=(r_0+r_r+s_n)\cos(n)+e\sin(n)\]

凸轮轮廓是滚子圆心轨迹的包络曲线,只要求得对应点的法向方向,沿法向方向减去滚子半径即可。如果知道升程的解析式,直接求导数即得,通常情况下,用相邻点近似求得该点切向。

对应点\(P_n\left(x_n,y_n\right)\)的切向矢量表示为

\[\begin{bmatrix}i_n\\j_n\end{bmatrix}=\vec{\begin{bmatrix}\Delta x\\\Delta y\end{bmatrix}}=\vec{\begin{bmatrix}x_{n+1}-x_n\\y_{n+1}-y_n\end{bmatrix}}\]

压力角\(\theta_n=\arctan (\Delta y/\Delta x)\)

凸轮轮廓线上的点\({P’}\left({x’}_n,{y’}_n\right)\)为\[{x’}_n=x_n\mp r_r\cos\theta_n\]\[{y’}_n=y_n\mp r_r\sin\theta_n\]

最小二乘法拟合一些基本空间元素的C#实现 —— (四)C#实例

包含之前几篇文章提到的算法的C#实现。

界面很简单,点按钮选择数据文件,一行一个点的xyz坐标。选择类型,然后计算。

可执行文件同目录下有两个测试用数据文件。

二进制包下载LSQ_Feature_binary

源码及二进制包下载(VS2012 Express)

最小二乘法拟合一些基本空间元素的C#实现 —— (三)圆柱

同前一篇文章相同,算法主要根据这篇文章,修改了其中一些笔误并微调以适合C#代码。

首先是将圆柱参数化并简化该模型。

圆柱的轴线为一空间直线,由一点\(P\)和一空间向量\(V\)定义。

如果经过坐标变换,新坐标系零点为\(P\)且坐标z轴为\(V\),那么该轴线的未知量就由\(x_0,y_0,z_0,a,b,c\)减少为4个。

参数化

圆柱的轴线表示为经过点\(P\left(x_0,y_0,z_0\right)\)向量为\(V\left(a,b,c\right)\)的直线,半径为R

求初始值

我个人觉得这种求法偏繁琐,不过的确相当接近最后拟合值,迭代次数相当少。

\(a,b,c\)的初始值

对于圆柱面上任一点\(P_i\left(x_i,y_i,z_i\right)\)有\[A^2+B^2+C^2=R^2\]其中\[A=c\left(y_i-y_0\right)-b\left(z_i-z_0\right)\]\[B=a\left(z_i-z_0\right)-c\left(x_i-x_0\right)\]\[C=b\left(x_i-x_0\right)-a\left(y_i-y_0\right)\]

将上面的等式代入展开\[\left[c\left(y_i-y_0\right)-b\left(z_i-z_0\right)\right]^2+\left[a\left(z_i-z_0\right)-c\left(x_i-x_0\right)\right]^2+\left[b\left(x_i-x_0\right)-a\left(y_i-y_0\right)\right]^2=R^2\]这个等式很简单(不得不吐槽,当时看到这里我都疯了,现在手打这些公式更疯了),它等价于\[Ax_i^2+By_i^2+Cz_i^2+Dx_iy_i+Ex_iz_i+Fy_iz_i+Gx_i+Hy_i+Iz_i+J=0\]其中\begin{array}{lclp{12}}
A&=&\left(b^2+c^2\right)\\
B&=&\left(a^2+c^2\right)\\
C&=&\left(a^2+b^2\right)\\
D&=&-2ab\\
E&=&-2ac\\
F&=&-2bc\\
G&=&-2\left(b^2+c^2\right)x_0+2aby_0+2acz_0\\
H&=&2abx_0-2\left(a^2+c^2\right)y_0+2bcz_0\\
I&=&2acx_0+2bcy_0-2\left(a^2+b^2\right)z_0\\
J&=&\left(b^2+c^2\right)x_0^2+\left(a^2+c^2\right)y_0^2+\left(a^2+b^2\right)z_0^2-2bcy_0z_0-2acz_0x_0-2abx_0y_0-R^2
\end{array}

两边同除以\(A\)并将\(x\)二次项移到等式右边得
\[\frac{B}{A}y_i^2+\frac{C}{A}z_i^2+\frac{D}{A}x_iy_i+\frac{E}{A}x_iz_i+\frac{F}{A}y_iz_i+\frac{G}{A}x_i+\frac{G}{A}y_i+\frac{I}{A}z_i+\frac{J}{A}=-x^2\]

这是一个线性方程组
\[\begin{bmatrix}y_1^2 &z_1^2 & x_1y_1 & x_1z_1 & y_1z_1 & x_1 & y_1 & z_1 & 1 \\y_2^2 & z_2^2 & x_2y_2 & x_2z_2 & y_2z_2 & x_2 & y_2 & z_2 & 1 \\\vdots &\vdots &\vdots &\vdots &\vdots &\vdots &\vdots &\vdots &\vdots \\y_n^2 &z_n^2 & x_ny_n & x_nz_n & y_nz_n & x_n & y_n & z_n & 1 \end{bmatrix}\begin{bmatrix}B/A\\C/A\\D/A\\E/A\\F/A\\G/A\\H/A\\I/A\\J/A\end{bmatrix}=\begin{bmatrix}-x_1^2\\-x_2^2\\\vdots\\-x_n^2\end{bmatrix}\]

求解:

令\(B’=B/A,C’=C/A,D’=D/A,E’=E/A,F’=F/A,G’=G/A,H’=H/A,I’=I/A,J’=J/A\)
如果\(|D’|\),\(|E’|\)和\(|F’|\)近似等于0(意味着abc中有两个近似于0,向量与某坐标轴平行),如果\(B’\)近似于1向量为\(\left(0,0,1\right)\),如果\(C’\)近似于1向量为\(\left(0,1,0\right)\)

否则,令\[k=\frac{2}{1+B’+C’}\]则\[A=k,B=kB’,C=kC’,D=kD’,E=kE’,F=kF’,G=kG’,H=kH’,I=kI’,J=kJ’\]如果\(A,B\)近似于1,则\begin{array}{lcl}c’&=&\left(1-C\right)^{1/2}\\a’&=&E/-2c’\\b’&=&F/-2c’\end{array}
如果A近似1,B不近似1则\begin{array}{lcl}b’&=&\left(1-B\right)^{1/2}\\a’&=&D/-2b’\\c’&=&F/-2b’\end{array}
如果A不近似于1\begin{array}{lcl}a’&=&\left(1-A\right)^{1/2}\\b’&=&D/-2a’\\c’&=&E/-2a’\end{array}
将向量\(\left(a’,b’,c’\right)\)正规化得到单位向量\[\left(a,b,c\right)\]

注:近似于这个概念很模糊,由于我使用在机械测量中,通常设备采集原始点的精度为0.001左右,而点云本身的标准差在0.001~1,所以我在程序中将近似于定义为\(\Delta<0.001\)
上文两次提到近似于,第一次是\(DEF\),用来判断向量是否平行于某一轴。第二次是\(A,B\),实际上此时已经排除了向量平行于轴线的可能性,这里是用来判断向量更接近于哪个轴,从而避免近似0 的小数开平方造成的精度误差,所以在算法中我将这部分改为了比大小。
另外就是\(ABC\)三个值有可能略微大于1,用原文算法有可能出现负数开方造成无解。

\(x_0,y_0,z_0\)的初始值

根据\(x_0,y_0,z_0\)与\(G,H,I\)的关系,以及等式\(ax_0+by_0+cz_0=0\)(注:原文中使用原点到轴线的垂足作为起始点,因为个人需要,我将之替换为数据第一点投影到轴线的垂足\(ax_0+by_0+cz_0=ax_1+by_1+cz_1\)得到线性方程组

\[\begin{bmatrix}-2\left(b^2+c^2\right)&2ab&2ac\\2ab&-2\left(a^2+c^2\right)&2bc\\2ac&2bc&-2\left(a^2+b^2\right)\\a&b&c\end{bmatrix}\begin{bmatrix}x_0\\y_0\\z_0\end{bmatrix}=\begin{bmatrix}G\\H\\I\\ax_1+by_1+cz_1\end{bmatrix}\]

注:在实际使用中,这个初始值算法有一些问题,当圆柱的轴线非常接近于\((0,0,1)\)时(很常见,通常为了仪器测量方便都会先找正),该方程组变为了\[\begin{bmatrix}-2&0&0\\0&-2&0\\0&0&0\\0&0&1\end{bmatrix}\begin{bmatrix}x_0\\y_0\\z_0\end{bmatrix}=\begin{bmatrix}G\\H\\I\\z_1\end{bmatrix}\]这个一看就知道\(x_0=-\frac{G}{2},y_0=-\frac{H}{2},z_0=z_1\),但是由于第三行都是0的存在,使得矩阵的任何操作都有问题。

\(r\)的初始值

原文使用了前面\(J\)的等式。\[R^2=(b^2+c^2)x_0^2+(a^2+c^2)y_0^2+(a^2+b^2)z_0^2-2bcy_0z_0-2acx_0z_0-2abx_0y_0-J\]

注:依然是在我的实际使用中,发现J的解在特定情况下会是极大的正数(可能是由于圆柱的轴线长度小于直径的原因),于是又出现了负数开方无解的情况,例如这组数据,于是我用了所有测量点半径平均值来代替。

迭代算法

1.坐标变换

在\((x_0,y_0,z_0)\)处以\((a,b,c)\)为Z轴建立一个坐标系U并将所有实测点的一个副本变换到该坐标系下。

\[\begin{bmatrix}x_i\\y_i\\z_i\end{bmatrix}=\left(\begin{bmatrix}x_i\\y_i\\z_i\end{bmatrix}-\begin{bmatrix}x_0\\y_0\\z_0\end{bmatrix}\right)U\]

其中\[U=\begin{bmatrix}1&0&0\\0&c_1&s_1\\0&-s1&c1\end{bmatrix}\begin{bmatrix}c_2&0&s2\\0&1&0\\-s2&0&c2\end{bmatrix}\]

当\((a,b,c)\)为\((1,0,0)\)时\(s_1=0,c_1=1,s_2=-1,c_2=0\)否则\[\begin{array}{lcl}c_1&=&\frac{c}{\sqrt{b^2+c^2}}\\s_1&=&\frac{-b}{\sqrt{b^2+c^2}}\\c_2&=&\frac{cc_1-b_s1}{\sqrt{a^2+(cc_1-bs_1)^2}}\\s_2&=&\frac{-a}{\sqrt{a^2+(cc_1-bs_1)^2}}\end{array}\]

2.构造Jacobian矩阵

参考直线距离公式,并且将\(a=b=0,c=1,z=0\)代入得到\[\begin{bmatrix}-x_1/r_1&-y_1/r_1&-x_1z_1/r_1&-y_1z_1/r_1&-1\\-x_2/r_2&-y_2/r_2&-x_2z_2/r_2&-y_2z_2/r_2&-1\\\vdots&\vdots&\vdots&\vdots&\vdots\\-x_m/r_m&-y_m/r_m&-x_mz_m/r_m&-y_mz_m/r_m&-1\end{bmatrix}\begin{bmatrix}P_{x0}\\P_{y0}\\P_a\\P_b\\P_r\end{bmatrix}=-1\begin{bmatrix}r_1-r\\r_2-r\\\vdots\\r_m-r\end{bmatrix}\]

3.更新参数

\[\begin{bmatrix}x_0\\y_0\\z_0\end{bmatrix}=\begin{bmatrix}x_0\\y_0\\z_0\end{bmatrix}+\begin{bmatrix}P_{x_0}\\P_{y_0}\\-P_{x_0}P_a-P_{y_0}P_b\end{bmatrix}U^T\]

\[\begin{bmatrix}a\\b\\c\end{bmatrix}=\begin{bmatrix}P_a\\P_b\\1\end{bmatrix}U^T\]

\[r=r+P_r\]

4.确定迭代结束条件

重复1~3,直到满足终止条件。

终止条件3选1:

  • 新参数与旧参数对应相减,平方和小于退出值
  • 求所有点的偏差二乘和,新值与旧值相减,差小于退出值
  • 迭代次数超过退出值

包含直线、平面与圆柱算法的C#实例与说明

最小二乘法拟合一些基本空间元素的C#实现 —— (二)直线与平面

本文讨论并实现的算法主要(99%)基于这篇文章,更多参考请阅读这里

大部分能搜索到的关于直线最小二乘拟合的资料是基于2维空间的\[y=ax+ b\]

而从三维空间的角度上来看,这只是一种特例。本文主要讨论三维空间上的直线拟合。

参数化

三维直线的表示如下。\[\left(x,y,z\right)=\left(x_0,y_0,z_0\right) + t\left(a,b,c\right)\]

其中\(\left(x_0,y_0,z_0\right)\)为直线上任一已知点,\(\left(a,b,c\right)\)为向量,所以所有直线都可以使用一个点坐标及一个向量来表示。

对于测量点\(P_i\left(x_i,y_i,z_i\right)\)与直线的距离为\[d_i=\sqrt{{u_i}^2+{v_i}^2+{w_i}^2}\]

其中:\[u_i=c\left(y_i-y_0\right)-b\left(z_i-z_0\right)\]\[v_i=a\left(z_i-z_0\right)-c\left(x_i-x_0\right)\]\[w_i=b\left(x_i-x_0\right)-a\left(y_i-y_0\right)\]

算法

根据原文:拟合的直线过所有点的平均值,没有证明……

所以未知量其实是3个,构建一个矩阵\[A=\begin{bmatrix}x_1-x_0&y_1-y_0&z_1-z_0\\x_2-x_0&y_2-y_0&z_2-z_0\\\vdots & \vdots & \vdots \\x_i-x_0&y_i-y_0&z_i-z_0\end{bmatrix}\]

将矩阵A做奇异值分解(SVD),令:\[A=U_1SV^T\]

矩阵\(V\)的第一列(Column)即是直线向量\(\left(a,b,c\right)\)。

代码实现:

 public override void Estimate(List<Point3D> datas)
        {
            double sum_x = 0;
            double sum_y = 0;
            double sum_z = 0;
            foreach (Point3D temp in datas)
            {
                sum_x += temp.x;
                sum_y += temp.y;
                sum_z += temp.z;
            }
            sum_x /= datas.Count;
            sum_y /= datas.Count;
            sum_z /= datas.Count;

            DenseMatrix jacobian = new DenseMatrix(datas.Count, 3);
            foreach (Point3D temp in datas)
            {
                Vector<double> gradient = new DenseVector(3);
                gradient[0] = temp.x - sum_x;
                gradient[1] = temp.y - sum_y;
                gradient[2] = temp.z - sum_z;
                jacobian.SetRow(datas.IndexOf(temp), gradient);
            }
            Svd svd = jacobian.Svd(true);
            // get matrix of left singular vectors with first n columns of U
            Matrix<double> U1 = svd.U().SubMatrix(0, datas.Count, 0, 3);
            // get matrix of singular values
            Matrix<double> S = new DiagonalMatrix(3, 3, svd.S().ToArray());
            // get matrix of right singular vectors
            Matrix<double> V = svd.VT().Transpose();

            Vector<double> parameters = new DenseVector(3);
            parameters = V.Column(0);
            x = sum_x;
            y = sum_y;
            z = sum_z;
            i = parameters[0];
            j = parameters[1];
            k = parameters[2];
        }

平面的最小二乘拟合

放在一起的原因是这两种元素的算法是完全一样的,直线拟合最后一步得到的矩阵V第一列是直线的向量,而第三列就是平面的向量。

参数化

平面同样可以用一个点\(\left(x_0,y_0,z_0\right)\)和一个向量\(\left(a,b,c\right)\)来表示,只是垂直与该向量。

平面的公式为\[a\left(x-x_0\right)+b\left(y-y_0\right)+c\left(z-z_0\right)=0\)

对于测量点\(P_i\left(x_i,y_i,z_i\right)\)直接的距离为\[d_i=a\left(x-x_0\right)+b\left(y-y_0\right)+c\left(z-z_0\right)\]

算法

与直线的算法完全相同。

包含直线、平面与圆柱算法的C#实例与说明

最小二乘法拟合一些基本空间元素的C#实现 —— (一)术语、基本算法与思路

本系列的主要目的是将拟合算法用C#实现,相关的操作用到了Math.NET数值运算库,所以将一些基本概念,算法和对应的C#代码放到前面。

对于线性模型,例如直线和平面,算法大约分三步:

  1. 参数化:将模型用参数化方式表达出来。
  2. 构造Design(非线性中的Jacobian)矩阵:根据参数公式求所有参数的偏导数作为列(Column),将实测值作为行(Row)。将偏差值作为等式右值。
  3. 求解。

对于非线性模型,例如圆柱,Gauss-Newton算法类似:

  1. 参数化:
  2. 求(或猜)初始解。
  3. 找到Jacobian矩阵及右值
  4. 求解
  5. 通过4计算逼近值,重复3和4
  6. 迭代结束

线性模型的解法是基础,详细的分析请看这篇文章,这里只解释代码部分。在Math.Net

对于矩阵\(A=\begin{bmatrix}A_{11} & A_{12}\\A_{21} & A_{22}\end{bmatrix}\)
A.Transpose() 代表A的转置矩阵\(A^T=\begin{bmatrix}A_{11} & A_{21}\\A_{12} & A_{22}\end{bmatrix}\)

这在坐标运算中是非常重要的,因为坐标运算中坐标系都表示为正交矩阵(抱歉,没找到证明),当计算一个点(用二维举例)\(P=\left(x,y\right)\)在新坐标系A中的坐标\(P^{‘}=\left(x^{‘},y^{‘}\right)\)时。\[P^{‘}=PA\]

而已知坐标系A中的坐标点\(P^{‘}\)求原坐标值可用\[P=P^{‘}A^T\]

一些类似的代码为:

A.Inverse()代表A的逆矩阵\(A^{-1}\)

A.Multiply(B)代表矩阵乘法\(A*B\)或者矩阵乘以向量\(A*b\)

对于常用的算法:

线性模型

已知Jacobian矩阵\(J\)及等式右值(偏差值向量)\(y\),另\[Ax=y\]求解向量x

  1. 正规方程(normal equations)
    \[A^TAx=A^Ty\]
    逆矩阵解法
    \[x=\left(A^TA\right)^{-1}A^Ty\]
    实现代码 
    x = A.Transpose().Multiply(A).Inverse().Multiply(A.Transpose().Multiply(y));

    更快和更稳定的方法将是对左侧的矩阵进行乔姆斯基分解(Cholesky decomposition),实现代码

    x = A.Transpose().Multiply(A).Cholesky().Solve(A.Transpose().Multiply(y));

    这两个在一些限定条件下是等价的,而这些限定条件在点数远大于参数的情况下是满足的。

  2. QR分解
    推论见原文,代码更简单: 
    x=A.QR().Solve(y);
  3. 奇异值分解(Singular Value Decomposition, SVD)
    A是方阵的情况下(即点数等于参数)代码为 
    x = A.Svd(true).Solve(y);

    A不是方阵的情况下代码为

// compute the SVD 
Svd svd = A.Svd(true);
// get matrix of left singular vectors with first n columns of U 
Matrix U1 = svd.U().SubMatrix(0, m,0, n); 
// get matrix of singular values 
Matrix S = new DiagonalMatrix(n, n, svd.S().ToArray()); 
// get matrix of right singular vectors 
Matrix V = svd.VT().Transpose(); 
x = V.Multiply(S.Inverse()).Multiply(U1.Transpose().Multiply(y));

非线性模型

这部分对于代码并没有什么区别,只是需要迭代(循环)及判断终止条件。