程序员人生 网站导航

3D打印切片软件Cura及CuraEngine原理分析

栏目:综合技术时间:2016-03-31 08:47:52

引言

        年初开始进入3D打印行业,受命以Cura为基础,研发1款自主的3D打印切片软件。

        自主研发要取其长处,补其不足,首先自然是要弄清楚Cura到底做了甚么,读Cura的代码是必须的。我1向都觉得比起自己写代码来,读他人的代码是1个漫又而痛苦的进程,读者的思想与写者总有偏差,常常又没法验证自己的料想是不是正确,只叹人脑不是电脑,没法把眼前的代码从头到尾履行1遍。不知道各位资深程序会有甚么办法,我的办法是“翻译”,看着他人写的代码,加上自己的理解以后,按自己的喜好重新写出来,看1段翻译1段,等全部翻译完成,理论上作者的思路也明白了,同时还有了1份功能1模1样的代码,自己的理解是不是正确,也能够通过履行“翻译”出来的代码验证。

        计划总是美好的,中间的工程却是充满变数,之间的曲折折叠不说。经过若干次推倒重写,委曲算是有了1份自己的切片软件,又经过了半年的斟酌摸索和打印经验积累,1个还算另自己满意的切片软件终究诞生。起名Pango,先观大略。

        Pango的介绍和说明先按下不表,以会有机会另外发文详述。
        在Pango的开发进程中,我对Cura的理解也日趋深入。时至本日,我也有了信心可以把我的这些经验、理解和心得分享出来,供大家参详12,若能对后来的Cura研究者有所助益,那是再好不过。


Cura的架构

        Cura是1个python语言实现,使用wxpython图形界面框架的3D打印切片界面软件,说它是界面软件是由于Cura本身其实不会进行实际的切片操作。实际的切片工作是由另外1个C++语言实现的CuraEngine命令行软件来具体负责的,用户在Cura界面上的绝大多数操作,如加载模型、安稳旋转缩放、参数设置等终究会转换成并履行1条CuraEngine命令;CuraEngine把输入的STL、DAE或OBJ模型文件切片输出成gcode字符串返回给Cura;Cura再把gcode在3D界面上可视化成路径展现给用户。

        我主要参考的代码是CuraEngine,本文主要篇幅也会放在CuraEngine上。而Pango的界面代码就主要靠我自己发挥了。

        Cura和CuraEngine都可以Github上找到,地址:


        https://github.com/daid/Cura

        https://github.com/Ultimaker/CuraEngine

        我所参考的版本是15.04,15.06以后Cura和CuraEngine都有较大的改动,但核心流程没变。所以本文分析的版本也到15.04为止。

        言归正传,下面我们将开始1步1步揭开CuraEngine把1个模型文件转换成为gcode的进程。

切片流程

        从整体上讲,CuraEngine的切片分为5个步骤:


步骤1:模型载入

        有1点3D编程经验的人都知道,计算机中的3D模型大多是以3角形面组合成的表面所包裹的空间来表示的。3角形作为3D模型的基本单元,有结构简单,通用性强,可组合成任意面的特点;空间坐标中只要3个点就能够表示1个唯1的3角形,两点只能表示1条直线,而再多的直线也没法组成1个平面;空间中的任意3个不共线的点都可以组成1个3角形,而4个点所组成的4边形就必须要求4点共面;任意的表面都可以拆解成3角形,1个4边形可以拆解成两个3角形,但1个3角形却没有办法用4边形组合而成。计算机所善于的事情就是把简单的事情不断重复,而3角形正是由于这些特性,成了计算机3D世界的基石。

        CuraEngine内部也是用3角形组合来表示模型的,不过一样1个3角形组合,却有没有穷多种数据结构来进行存储。CuraEngine切片的第1步,就是从外部读入模型数据,转换成以CuraEngine内部的数据结构所表示的3角形组合。

        有了3角形组合还不够,CuraEngine在载入模型阶段还要对3角形进行关联。两个3角形共有1条边的,就能够判断它们为相邻3角形。1个3角形有3条边,所以最多可以有3个相邻3角形。1般而言,如果模型是封闭的,那它的每个3角形都会有3个相邻3角形。

        有了3角形的相邻关系,可以大幅提高低1个步骤分层进程的处理速度。Cura之所以成为当前市场上切片速度最快的软件,这是其中最显著的优化之1。

        模型载入更详细的进程会另文分析,敬请期待。

步骤2:分层

        如果把模型放在XY平面上,Z轴对应的就是模型高度。我们把XY平面抬高1定高度再与模型的表面相交,就能够得到模型在这个高度上的切片。所谓的分层就是每隔1定高度就用1个XY平面去和模型相交作切片,层与层之间的距离称为层高。全部层高切完后就能够得到模型在每个层上的轮廓线。就像是切土豆片1样,把1个圆的或不圆的异或不管甚么奇形怪状的土豆用菜刀1刀1刀切开,最后就可以得到1盘薄如纸片的土豆片,固然那还得你的刀功要足够好才行。

        分层本质上就是1个把3D模型转化为1系列2D平面的进程,自此以后的所有操作就都是在2D图形的基础上进行了。

        在前面模型载入阶段我说到了CuraEngine埋了1个3角形关联的伏笔,作用是甚么,现在可以揭晓答案了。我们知道,两个平面相交,得到的是1条直线,1个平面和1个3角形相交,就得到1条线段。固然也有可能甚么也得不到,平台平行啦,3角形的3个点都在平面的同1面之类。这些我们可以不管,我们现在只关心和平面有交集的那些3角形便可。我们把1个平面和所有的3角形都相交了1遍,得到了许许多多的线段。但是我们需要的是2D图形,3角形是2D图形,4边形,任意多边形都是2D图形,而线段不是。所以我们就要把这些线段试着连成1个多边形,那末问题来了,要把这些线段连起来,只能两个两个地去试,看看它们是否是共端点。粗算1下,每层都是平方级的复杂度,再算上层数,那就是3次方级。但现在,我们知道了3角形的关联关系。两个关联的3角形,如果都与1个平面相交,那它们的交线1定也是关联的。如此1来,每条线段只需要判断3个与它相邻3角形,看看与这个平面有无交线便可,1下子就把问题的复杂度降了1个次元。速度自然可以有质的提升。

        分层更详细的进程会另文分析,敬请期待。

步骤3:划分组件

        经过分层以后,我们得到了1叠2D平面图形。接下来需要做的事情就是对每层的平面图形进行跑马圈地,标记出哪里是外墙、内墙、填充、上下表面、支持等等。

         3D打印在每层是以组件单位,所谓组件指的就是每层2D平面图形里可以连通的区域,比如左图就能够拆分为黄、绿、蓝3个组件。而打印的顺序就每打印完1个组件,接着会挑选1个离上1个组件最近的组件作为下1个进行打印,如此循环直至1层的组件全部打印完成;接着会Z轴上升,重复上述步骤打印下1层的所有组件。

        至于每个组件怎样打印,就和我们手工画画1样,先打边线再对边线内部填充。边线可以打印多圈,最外层圈边线称为外墙,其它的统称为内墙,CuraEngine之所以要对内外墙进行辨别,是为了可以为它们定制不同的打印参数:外墙会被人视察到,所以可以采取低速以提高表面质量,内墙只是起增加强度的作用,可以稍略加快打印速度以节省时间。这些都可以在Cura界面的高级选项里进行配置。

        有1点值得注意,这也是我半年打印的经验:由于FDM挤出装置的特性所至,挤出机是通过影响加热腔里的熔丝压力,间接决定喷嘴挤出速度的。而加热腔本身对压力就有1个缓冲作用,所以挤出机进丝速度的突变其实不会使得喷嘴的挤出速度立即随着变化,而是有1个延迟。这1点在远端送丝的机器上更加明显,而恰恰我们公司的主打产品F3CL就是远端送丝,在Pango中斟酌到这个问题,并加上了特殊处理,事实证明的确对打印质量有1定的提升。具体办法是甚么,我先卖个关子,会Pango的专文里进行讲授。

        内外墙标记完以后就是填充和上下表面的标记了。填充有1个填充率,0%填充率就是无填充,100%就是打成1个密实的平面,上下表面就是填充率为100%的填充。中间的填充率自然介于二者之间,就像1张鱼网,填充率越高网眼越细。

        软件会先把内墙之内部份统统标记成填充,以后再进1步判断其中有哪些部份要转换成为上下表面。是哪些部份呢?在Cura的基本设置里有1个上下表面层数的设置,它代表了模型的上下与空气接触的表面有几层,它就在这里会被用到。CuraEngine会把当前层上下n层(上下表面层数)取出来与当前层进行比较,凡是当前层有而上下n层没有的部份就会被划归到表皮。而原来的填充区域在割除被划到表皮的部份后剩下的部份就是终究的填充区域。

        CuraEngine在处理进程中大量用到了2D图形运算操作。有关2D图形的运算,有很多人研究,也被做成许多成熟的库以供调用。CuraEngine的作者拿来主义,选取了1个他认为比较好用的库,叫ClipperLib的库直接内嵌到软件当中。ClipperLib所使用的2D图形算法也很著名,叫Vattis Clipping Algorithm,很复杂,我也没有完全弄懂,有兴趣的读者要是弄懂了可以多多交换。ClipperLib的网址是:http://www.angusj.com/delphi/clipper.php

        这里我先简单介绍1下CuraEngine所用到的几种2D图形的运算,都是由ClipperLib实现的:交、并、减、偏移。它们与集合操作类似,先看图:

 

图形相交

        2元图形操作,终究结果为两个图形共同包括的区域。记作:A * B

图形相并

        2元图形操作,终究结果为两个图形其中之1或二者所包括的区域。记作:A + B

图形相减

        2元图形操作,终究结果为属于前者但不属于后者的区域。记作:A - B

图形偏移(外扩)

        1元图形操作,终究结果为图形区域的边界向外扩大指定的距离。

图形偏移(内缩)

        1元图形操作,终究结果为图形区域的边界向内收缩指定的距离。内缩与外扩互为逆运算。

        这些就是CuraEngine所用到的2D图形操作。运算不多,却可以做许许多多的事情。比如上面所说的上下表面计算,就能够用数学公式来表示:

表面(i) = [填充(i) - 层(i + n)] + [填充(i) - 层(i - n)]

填充(i) = 填充(i) - 表面(i)

        其中,i为当前层号,n为上下表面层数(可以不1样)。多简单,数学就是这么任性!

        一样的,组件里面内外墙,填充怎样划分,只用1个内缩运算就能够弄定:

外墙 = 组件.offset(-线宽)

内墙1 = 组件.offset(-线宽 * 2)

...

内墙n = 组件.offset(-线宽 * (n + 1))

填充 = 组件.offset(-线宽 * (n + 2))

        如果模型无需支持,那组件划分到这里就能够收工了。否则,接下就是计算支持的时间。

        我用CuraEngine半年下来觉得它最大的不足就是在支持上,这也是我在Pango投入最大精力要改进的地方,这里就先简单介绍1下CuraEngine所用的支持算法。

        CuraEngine首先把全部打印空间在XY平台上划分成为200um*200um的网格。每一个网格的中心点再延Z轴向上作1条直线,这条直线可能会与组成3D模型的3角形相交。3角形与直线的交点和这个3角形的倾斜度会被记录到网格里面。

        现在每一个网格里记录下了1串被称为支持点的列表,每一个支持点包括1个高度和1个倾斜度信息。接下来会对每一个网格的支持点列表依照高度从低到高排序。根据这些信息就能够判断模型上任意1个点是不是需要支持了,怎样判断,我们看图说话:

        让我们从底面开始延着1根网格中心线往上走。起始我们是在模型外部的,当遇到第1个支持点的时候,就从模型外部进入到了模型内部。我们称这个支持点为进点。

        继续向上,遇到了第2个支持点,又从模型内部又退到了模型外部。我们称这个支持点为出点。

        接着向上,我们可以发现,进点与出点总是交替出现的。

        利用这个规律,对模型上任何1个点,我们只要找到这个点所对应的网格,再找到这个网格里在这个点以上最近的1个支持点,我们就能够得到两个信息:这个点之上是不是有模型悬空;这个点上面的悬空点的面的倾斜度是多少。

        Cura界面的专家设置里面有支持角度的设置,如果1个点处于模型悬空部份以下,并且悬空点倾斜度大于支持角度,那这个点就是需要支持的。所1个平台上所有的需要支持的点连接起来围成的2D图形就是支持区域。

        CuraEngine所使用的支持算法比较粗糙,但胜在速度很快。先不说网格化后失去了精度,通过倾斜角度来判断,模型下方1旦倾斜角产生了突变,像左图这类从负45

度1下突变成正45度,倾斜角判断无能为力,除非把它改大到60度,这样的话,全部模型都会被过度支持。这样矫枉过正,既不科学,也浪费材料和打印时间,还会对模型表面质量带来不好的影响。

        科学的支持算法应当是找到模型局部最低点进行支持,最低点以上不1定需要支持。由于FDM材料本身的粘性,使得材料的走线可以有1部份悬空而不坍塌,这个效果被称为

Overhang,只要上层材料的悬空距离小于1定的值,它就不需要支持,这个距离以我的经验应当在1/4到1/2线宽之间。我在Pango中就基于这个思路重新实现了支持的算法,结果虽然速度不如Cura的支持算法那末快,但效果非常好,该撑的地方撑,不该撑的地方也不会多此1举。

        Pango的支持算法我会在以后专文介绍。顺带1说,CuraEngine在下半年做了很大的改动,其中之1就是抛弃了之前的支持算法,而新的算法也和我上面所讲的思想异曲同工。我要声明的是Pango的支持算法和CuraEngine谁也没有抄谁,我的算法是自己拍脑袋想出来的。算是英雄所见略同吧。

        支持范围肯定以后,也和组件1样,可以有外墙、内墙、填充、上下表面。依样画葫芦便可。CuraEngine对支持,只会生成外墙和填充,Pango则会生成更多。

        组件和支持就是CuraEngine在这1步所生成的结果,这1步可以说是全部切片进程的核心,更详细的进程会另文分析,敬请期待。



步骤4:路径生成

        地圈好了,就该在里面种菜了。这1步路径生成绩要开始计划喷头在不同的组件中怎样运动。路径按大类来分,有轮廓和填充两种。

        轮廓很简单,沿着2D图形的边线走1圈便可。前1步所生成的外墙、内墙都属于轮廓,可以直接把它们的图形以设置里的线宽转换为轮廓路径。

        填充略微要复杂1些,2D图形指定的只是填充的边界,而生成的路径则是在边界的范围内的条纹或网格结构,就像窗帘或鱼网,如左图。这两种就最基本的结构,固然或许你还可以想出其它花式的填充,比如蜂窝状或S型,这些在新的Cura或别的甚么切片软件里可能会实现,但我打印下来还是这两种基本结构更让人放心。CuraEngine在专家设置里可以对填充类型进行选择,里面除条纹和网格外还有1个自动选项,默许就是自动。自动模式会根据当前的填充率进行切换,当填充率小于20%就用条纹填充,否则使用网格填充。由于网格结构虽然更加公道,但它有1个问题,就是交点的地方会打两次。填充率越高,交点越密,对打印质量的影响会越大。我们知道,表面就是100%的填充,如果表面用网格打,不但没法打密实,表面还会坑坑洼洼,所以100%填充只能用条纹打,这就是CuraEngine推荐自动模式的缘由。

        至于填充率,就反应在线与线的间距上。100%填充率间距为0;0%填充率间距无穷大,1根线条也不会有。

        每一个组件独立的路径生成好了,还要肯定打印的前后顺序。顺序先好了可以少走弯路,打印速度和质量都会有提升。路径的顺序以先近后远为基本原则:每打印完1条路径,当前位置是上1条路径的终点;在当前层里剩下还没打印的路径中挑选1条出发点离当前位置最近的1条路径开打。路径的出发点可以是路径中的任意1个点,程序会自行判断。而路径的终点有两种可能:对直线,图形只有两个点,终点就是除出发点以外的那个点;对轮廓,终点就是出发点,由于轮廓是1个封闭图形,从它的出发点开始沿任意方向走1圈,最后还会回到出发点。CuraEngine对路径选择做了1个估值,除斟酌到先近后远外,还顺便参考了下1个点相对当前点的方向,它的物理意义就是减少喷头转弯。赛车在直道上开得1定比弯道快,不是么。

        路径的顺序也肯定了,还有1个问题需要斟酌:如果前后两条路径首尾相连,那直接走就是了,但大多数情况并不是如此,前1条路径的终点常常和后1条路径出发点之间有1段距离。这时候候去往下1点的路上要谨慎了,肯定不能继续挤出材料,否则轻则拉丝,重则模型面目全非。这段路喷头就需要空走,即喷头只移动,不吐丝,那只要把挤出机停下来不转就好了吗?也不行,由于前面分析过,挤出机的速度要传导到喷嘴,有1个延迟,不是你说停它就立即停下来的。这是FDM打印的通病,解决办法就是回抽。所谓回抽,就是在空走之前先让挤出机高速反转1段材料,这样就能够瞬间把加热腔里的材料抽光,再移动过去,中间就不会挤出材料,到了下1个点,在打印之前,先把刚才抽回去的丝再按1样的长度放回来,继续打印。回抽可以很好地解决空走拉丝的问题,但是它很慢,以抽1次0.5秒来算的话,如果打印1个表面,0.4线宽,10厘米的距离最少回抽25下,10几秒钟的时间1层,几百上千层打下来,光回抽所用的时间就是几个小时,是可忍孰不可忍!


        CuraEngine给我们提供了解决方案就是Comb,也就是绕路。我们先来看,是否是所有的回抽都是必须的呢?不回抽会拉丝是肯定的,但如果需要空走的路径本来就要打印的,那拉丝又有何妨。按这个思路,就能够给每一个组件设定1个边界,只要路径的出发点和终点都在这个边界以内的,空走都不回抽。这样可以解决80%的问题,但如果是左图这样的情况就行不通。

        红色是出发点,绿色是终点,直接走过去会走出边界的范围。这时候我们就要绕1点路,走1条曲线到达我们的目的地。这就是Comb所做的事情,在Cura专家设置里面可以对Comb进行设置,选择开启、关闭还有表面不Comb。Comb可以大幅节省打印时间,但是同1个地方打印屡次对模型质量还是会有细微的影响,个中利弊,交给用户自己判断。

        Comb的调剂是个细致活,Pango花了相当多的时间来微调Comb功能以求到达更好的效果,进程繁琐,不再赘述。

        至此路径生成完成,更详细的进程另文分析,敬请期待。

步骤5:gcode生成

        路径都生成好了,还需要翻译对打印机可以实别的gcode代码才行。这1步花样不多,循序渐进便可。

        先让打印机做1些准备工作:归零、加热喷头和平台、抬高喷头、挤1小段丝、风扇设置。

        从下到上1层1层打印,每层打印之前先用G0抬高Z坐标到相应位置。

        依照路径,每一个点生成1条gcode。其中空走G0;边挤边走用G1,Cura的设置里有丝材的直径、线宽,可以算出走这些距离需要挤出多少材料;G0和G1的速度也都在设置里可以调剂。

        若需回抽,用G1生成1条E轴倒退的代码。在下1条G1履行之前,再用G1生成1条相应的E轴前进的代码。

        所有层都打完后让打印机做1些扫尾工作:关闭加热、XY归零、机电释放。

        生成gcode的进程中,CuraEngine也会摹拟1遍打印进程,用来计算出打印所需要的时间和材料长度,这些也会写在gcode的注释里供用户参考。

        gcode生成不用另文详细分析,但是gcode的说明还是可以专文分析1下,敬请期待。

待续

         写了这么多,Cura的切片流程也只能讲个大概,也算是个提纲,希望对大家有所帮助。我计划对上面的第1个步骤再专文分析。除此以外,还有Cura界脸部分和Cura与CuraEngine的通讯也能够讲讲。以后就是我半年创作,自我感觉良好到觉得可以超出Cura的Pango,也是不说不快的。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐