動(dòng)控制中的CAD導(dǎo)圖:從DXF解析到小線段速度前瞻的工程實(shí)踐)
做運(yùn)動(dòng)控制的這幾年我最大的一個(gè)體會(huì)是不管算法多漂亮只要圖紙數(shù)據(jù)進(jìn)不來(lái)后面全都白搭。很多做上位機(jī)的人一開(kāi)始沒(méi)把CAD導(dǎo)圖當(dāng)回事覺(jué)得不就是讀個(gè)文件嘛結(jié)果等到接激光切割、水刀、雕刻機(jī)的時(shí)候才發(fā)現(xiàn)從CAD到實(shí)際加工路徑之間藏著無(wú)數(shù)細(xì)節(jié)。所以這個(gè)開(kāi)源項(xiàng)目我第一個(gè)分享的主題就是CAD導(dǎo)圖順便把和它緊密相關(guān)的小線段速度前瞻也帶幾句——畢竟這兩者在運(yùn)動(dòng)控制里是前后腳的關(guān)系。這篇文章適合正在做上位機(jī)軟件開(kāi)發(fā)、想了解運(yùn)動(dòng)控制系統(tǒng)中圖形數(shù)據(jù)如何落地的朋友也適合已經(jīng)在做插補(bǔ)運(yùn)動(dòng)、但被DXF解析和路徑銜接折磨過(guò)的人。我不會(huì)只貼代碼而是把設(shè)計(jì)思路、踩過(guò)的坑、為什么這么選型都講清楚。1. 為什么運(yùn)動(dòng)控制要先做好CAD導(dǎo)圖這一環(huán)1.1 從“畫(huà)圖”到“走軌跡”之間的鴻溝很多人以為CAD導(dǎo)圖就是把DXF里的線條讀出來(lái)再畫(huà)一遍到屏幕上這就大錯(cuò)特錯(cuò)了。運(yùn)動(dòng)控制里要的不是“顯示”而是把圖形數(shù)據(jù)變成機(jī)床能執(zhí)行的指令序列。這個(gè)轉(zhuǎn)換過(guò)程包含了實(shí)體識(shí)別、坐標(biāo)變換、曲線離散化、連接點(diǎn)處理等一系列環(huán)節(jié)。任何一個(gè)環(huán)節(jié)出問(wèn)題加工出來(lái)的東西就是廢品。舉個(gè)例子。CAD圖紙上畫(huà)了一個(gè)帶圓弧的輪廓如果直接把DXF里的圓弧實(shí)體當(dāng)成一個(gè)“圓弧命令”發(fā)給運(yùn)動(dòng)控制器很多控制器是不認(rèn)的或者認(rèn)了但圓弧的起點(diǎn)、終點(diǎn)、方向定義和你的插補(bǔ)器不一致加工軌跡就會(huì)偏。所以普遍的做法是把圓弧等曲線離散成小線段然后交給速度前瞻模塊去做拐角速度規(guī)劃。這個(gè)過(guò)程本身不復(fù)雜復(fù)雜的是離散得夠不夠好、銜接處處理得夠不夠聰明。1.2 開(kāi)源項(xiàng)目里CAD模塊的價(jià)值邊界做開(kāi)源運(yùn)動(dòng)控制項(xiàng)目最容易犯的毛病是貪大求全。我在設(shè)計(jì)這個(gè)項(xiàng)目的時(shí)候給CAD模塊劃定了一條清晰的價(jià)值邊界只管“把圖紙變成軌跡數(shù)據(jù)”不做CAD編輯不做復(fù)雜渲染不做工藝規(guī)劃。這不是偷懶而是運(yùn)動(dòng)控制系統(tǒng)的本職工作就是執(zhí)行圖形處理只是入口。把入口做扎實(shí)比什么都重要。具體來(lái)說(shuō)這個(gè)模塊要解決三件事第一解析常見(jiàn)CAD格式首選DXF因?yàn)樗俏谋靖袷?、公開(kāi)文檔、幾乎所有CAD軟件都支持導(dǎo)出第二把圖元統(tǒng)一轉(zhuǎn)成內(nèi)部軌跡模型包括點(diǎn)的坐標(biāo)、線段屬性、連接關(guān)系第三輸出可供速度前瞻模塊直接使用的數(shù)據(jù)流。這三件事每一件都值得單獨(dú)展開(kāi)。2. DXF文件解析別小看這個(gè)老掉牙的格式2.1 實(shí)體類型的取舍最初我試圖支持DXF里所有實(shí)體類型很快發(fā)現(xiàn)這是給自己挖坑。DXF的實(shí)體類型有幾十種像三維面、網(wǎng)格、代理對(duì)象這些在2D加工場(chǎng)景里根本用不上。硬要去解析它們只會(huì)增加出錯(cuò)概率。我最終只保留了四類核心實(shí)體LINE直線、ARC圓弧、CIRCLE整圓、LWPOLYLINE輕量多段線再加上SPLINE樣條曲線的處理但樣條曲線我后面會(huì)細(xì)講。對(duì)于塊引用INSERT我做了遞歸展開(kāi)也就是把塊里的實(shí)體按變換矩陣展開(kāi)成獨(dú)立實(shí)體。這樣做的原因是很多圖紙里重復(fù)的圖形都做成塊如果你不去展開(kāi)加工路徑會(huì)缺一堆東西。有個(gè)細(xì)節(jié)值得注意DXF里同一個(gè)圖形可能有多重表示。比如一條直線可能在LINE實(shí)體里也可能在LWPOLYLINE里作為一段子線段。解析時(shí)如果不做去重后面生成的軌跡會(huì)重復(fù)加工輕則浪費(fèi)時(shí)間重則在切割場(chǎng)景里直接切壞工件。我的處理方式是在生成內(nèi)部軌跡模型前做一個(gè)歸一化把所有實(shí)體統(tǒng)一成“點(diǎn)序列”的形式然后在點(diǎn)序列層面做距離判定去重。2.2 組碼機(jī)制與解析器設(shè)計(jì)DXF格式的核心就是組碼Group Code每個(gè)數(shù)據(jù)項(xiàng)前面有一個(gè)整數(shù)標(biāo)記它的意義。比如0表示實(shí)體類型、8表示圖層、10和20表示點(diǎn)的X和Y坐標(biāo)、40表示半徑、50和51表示起始角和終止角等。解析器本質(zhì)上就是一個(gè)循環(huán)讀組碼、根據(jù)組碼歸類數(shù)據(jù)的有限狀態(tài)機(jī)。這里我強(qiáng)烈建議不要用正則表達(dá)式去匹配整個(gè)文件而是逐行讀取、用狀態(tài)機(jī)方式解析。原因很簡(jiǎn)單DXF文件的結(jié)構(gòu)是順序的狀態(tài)機(jī)天然匹配這種流式處理正則在大文件上慢而且容易在特殊字符上出錯(cuò)。我用C#寫(xiě)了一個(gè)基于StreamReader逐行讀的解析器配合int.TryParse和double.TryParse做轉(zhuǎn)換每組數(shù)據(jù)本地化存儲(chǔ)。實(shí)測(cè)下來(lái)一個(gè)包含十萬(wàn)個(gè)圖元的DXF文件幾秒鐘就能解析完成。解析器的狀態(tài)機(jī)設(shè)計(jì)大概是這樣的先讀一個(gè)組碼如果為0讀取實(shí)體類型字符串根據(jù)類型創(chuàng)建對(duì)應(yīng)的實(shí)體對(duì)象然后繼續(xù)讀組碼和值直到碰到下一個(gè)0表示當(dāng)前實(shí)體結(jié)束。這里有個(gè)隱藏的坑組碼和值交替出現(xiàn)但值的行不能隨便丟因?yàn)橛行┲悼赡芸缧?。保險(xiǎn)的做法是只要讀到組碼就接著讀下一行作為值無(wú)論這一組數(shù)據(jù)看起來(lái)多“奇怪”。2.3 單位、坐標(biāo)系與縮放這是我認(rèn)為整個(gè)CAD導(dǎo)圖里最陰險(xiǎn)的坑——單位問(wèn)題。DXF文件本身有單位設(shè)置通過(guò)變量$INSUNITS標(biāo)記但這個(gè)值很多畫(huà)圖的人根本不注意導(dǎo)致DXF里寫(xiě)的單位未必是毫米。如果你拿到一張單位是英寸的圖紙直接按毫米處理整個(gè)圖形的尺寸就全錯(cuò)了。我的處理方式是做一個(gè)單位歸一化層解析完文件后讀取$INSUNITS如果是英寸就乘25.4轉(zhuǎn)成毫米如果是英尺就乘304.8其他單位類似。這個(gè)轉(zhuǎn)換必須發(fā)生在整個(gè)數(shù)據(jù)鏈路的最前端一旦坐標(biāo)進(jìn)入內(nèi)部模型后面所有環(huán)節(jié)都基于毫米計(jì)算不要再回頭去改。坐標(biāo)系方面DXF使用世界坐標(biāo)系WCS而加工時(shí)通常以工件的某個(gè)角為原點(diǎn)。所以解析后要做一次坐標(biāo)平移把圖紙的原點(diǎn)映射到工件原點(diǎn)。這里要小心的是不同CAM軟件導(dǎo)出的DXF原點(diǎn)位置可能天差地別有的在圖形中心有的在左下角有的在絕對(duì)零點(diǎn)。所以我在UI上提供了“手動(dòng)拾取基準(zhǔn)點(diǎn)”的功能讓用戶點(diǎn)圖紙上的某個(gè)點(diǎn)作為加工原點(diǎn)這個(gè)原點(diǎn)信息存下來(lái)后續(xù)都基于它做變換。3. 圖元到加工軌跡的變換鏈路3.1 坐標(biāo)系對(duì)齊與工件原點(diǎn)設(shè)置解析完成之后CAD模塊的第二個(gè)核心任務(wù)就是把圖元坐標(biāo)變換成加工坐標(biāo)。這一步聽(tīng)起來(lái)簡(jiǎn)單實(shí)際上涉及旋轉(zhuǎn)、平移、鏡像三個(gè)基本操作。旋轉(zhuǎn)是因?yàn)橛行﹫D紙的X軸并不對(duì)齊機(jī)床的X軸鏡像是因?yàn)镃AD的坐標(biāo)系Y軸向上而有些運(yùn)動(dòng)控制器的坐標(biāo)系Y軸可能向下不做鏡像的話整個(gè)圖形是反的。我在項(xiàng)目里維護(hù)了一個(gè)變換矩陣類采用齊次坐標(biāo)的方式統(tǒng)一處理平移、旋轉(zhuǎn)、縮放。這樣做的好處是不管做多少種變換最后都是矩陣連乘不容易出錯(cuò)也方便在調(diào)試時(shí)直接看變換矩陣的數(shù)值來(lái)檢查問(wèn)題。工件原點(diǎn)的設(shè)置我沒(méi)有做成全局唯一的而是做成每個(gè)零件圖元組獨(dú)立可設(shè)置的。原因是我遇到過(guò)一種情況一張大圖紙上有十幾個(gè)零件排樣圖加工時(shí)是需要一個(gè)一個(gè)零件來(lái)切割的。如果你只有一個(gè)全局原點(diǎn)就沒(méi)法快速定位到每個(gè)零件。所以我在圖元?dú)w一化階段就把實(shí)體分組每個(gè)組有獨(dú)立的包圍盒加工時(shí)可以根據(jù)包圍盒或者用戶點(diǎn)擊來(lái)設(shè)置該組的原點(diǎn)。3.2 曲線離散化精度與效率的平衡圓弧和樣條曲線最終都要離散成小線段。離散的密度直接決定了兩個(gè)矛盾的東西——加工精度和運(yùn)行速度。離散得越密輪廓越光滑但小線段數(shù)量暴增速度前瞻模塊的壓力就會(huì)劇增運(yùn)動(dòng)控制器的插補(bǔ)周期可能吃不消離散得太疏輪廓上就會(huì)出現(xiàn)明顯的折角加工表面質(zhì)量差。這里要用到一個(gè)基礎(chǔ)但極其重要的參數(shù)弓高誤差弦高差。圓弧離散的時(shí)候相鄰兩個(gè)離散點(diǎn)之間的連線是弦圓弧和這根弦之間的最大距離就是弓高誤差。公式不復(fù)雜給定圓弧半徑R、離散夾角θ弓高誤差h ≈ R(1 - cos(θ/2))。反過(guò)來(lái)給定允許的弓高誤差h可以算出圓心角步長(zhǎng)θ 2 * acos(1 - h/R)。我項(xiàng)目里默認(rèn)把弓高誤差設(shè)為0.01mm這個(gè)精度對(duì)大多數(shù)切割場(chǎng)景足夠了。實(shí)測(cè)下來(lái)一個(gè)直徑100mm的圓按0.01mm弓高誤差離散大約會(huì)產(chǎn)生1400多個(gè)小線段。如果在CAD圖里有一千個(gè)圓那就是一百多萬(wàn)個(gè)線段這個(gè)數(shù)量級(jí)對(duì)速度前瞻模塊就有壓力了所以后面我加了一個(gè)二重離散策略先粗離散用于顯示和快速預(yù)覽再精離散用于實(shí)際加工。這樣可以兼顧界面操作流暢度和加工精度。樣條曲線的離散比圓弧麻煩得多。我的做法是用De Boor算法做均勻節(jié)點(diǎn)采樣然后再用同樣的弓高誤差準(zhǔn)則做細(xì)分。具體來(lái)說(shuō)先均勻取一些參數(shù)點(diǎn)計(jì)算曲線上的位置然后檢查相鄰點(diǎn)連線和曲線之間的偏差如果超過(guò)弓高誤差就在中間插入一個(gè)新點(diǎn)遞歸細(xì)分直到滿足精度。這個(gè)算法實(shí)現(xiàn)起來(lái)不算太難但要注意不要對(duì)整個(gè)樣條一次性加密因?yàn)闃訔l可能出現(xiàn)局部曲率特別大的地方而均勻加密會(huì)讓曲率小的區(qū)域浪費(fèi)大量線段。3.3 圓弧方向、法向與補(bǔ)償?shù)念A(yù)處理另一個(gè)容易忽視的點(diǎn)是圓弧方向的一致性。DXF里圓弧的起始角和終止角是逆時(shí)針為正的但運(yùn)動(dòng)控制器的坐標(biāo)系定義可能不同。如果方向搞反了加工的圓弧就會(huì)向外側(cè)偏尺寸直接不對(duì)。我在解析時(shí)就把所有圓弧歸一化成統(tǒng)一的逆時(shí)針?lè)较虿⒂涗浿行?、半徑、起點(diǎn)角度、終點(diǎn)角度和一個(gè)布爾標(biāo)記表示是否跨越了0度角。這里的跨0度角是個(gè)小陷阱——比如起始角350度、終止角20度的圓弧如果你只是簡(jiǎn)單地用“角度遞增”來(lái)計(jì)算它只會(huì)畫(huà)30度而不是應(yīng)該畫(huà)的30度其實(shí)是跨過(guò)0度從350到360再繼續(xù)到20相當(dāng)于走370度。這個(gè)計(jì)算必須對(duì)終止角做修正或調(diào)整為順逆方向加一圈的邏輯。法向補(bǔ)償?shù)膯?wèn)題在激光切割和銑削里特別常見(jiàn)。刀具或激光焦點(diǎn)沿輪廓行走時(shí)需要偏置半個(gè)刀具直徑或光束半徑這個(gè)偏置方向必須垂直于輪廓切線方向。圓弧的偏置方向和直線的偏置方向在連接點(diǎn)上會(huì)產(chǎn)生過(guò)度切割或欠切的風(fēng)險(xiǎn)。我在CAD導(dǎo)圖模塊里雖然不直接做完整的刀具補(bǔ)償?shù)仨氼A(yù)先給每條線段標(biāo)定好左側(cè)/右側(cè)法向信息這樣后續(xù)的補(bǔ)償算法才能避免每個(gè)拐角重新計(jì)算法向。這個(gè)預(yù)處理就是純粹的“臟活”但它能讓后續(xù)算法簡(jiǎn)單不少。4. 速度前瞻與CAD導(dǎo)圖的銜接設(shè)計(jì)4.1 小線段前瞻的基本思路小線段速度前瞻是整個(gè)運(yùn)動(dòng)控制系統(tǒng)的靈魂。它的任務(wù)是給定一連串密集的小線段在滿足最大加速度和最大速度限制的前提下規(guī)劃出一條盡可能快的速度曲線。核心困難在于——相鄰線段之間存在方向變化方向變化越劇烈能通過(guò)的最大速度就越低。如果不做前瞻干脆讓機(jī)床每段都減速到零再加速那速度就上不去加工效率慘不忍睹。前瞻算法的基本思想是“看一窗口”從當(dāng)前線段出發(fā)向前預(yù)讀N條線段提前判斷哪里必須減速、哪里可以巡航從而形成一個(gè)速度曲線。窗口大小很關(guān)鍵。窗口太小前瞻距離不夠遇到連續(xù)拐彎會(huì)頻繁減速窗口太大計(jì)算量劇增實(shí)時(shí)性保不住。我在項(xiàng)目中允許用戶設(shè)置前瞻窗口為50到200條線段實(shí)測(cè)下來(lái)對(duì)大多數(shù)2D輪廓100條的窗口在效率和計(jì)算量之間比較均衡。4.2 為什么CAD導(dǎo)圖質(zhì)量直接決定前瞻上限這里我想強(qiáng)調(diào)一個(gè)很多教程不提的事實(shí)前瞻算法的效果天花板其實(shí)在CAD導(dǎo)圖階段就已經(jīng)被決定了。舉個(gè)例子如果圓弧離散時(shí)弓高誤差太嚴(yán)線段數(shù)暴增但每條線段都很短。短線段的好處是逼近精度高壞處是前瞻窗口能覆蓋的路徑長(zhǎng)度很短——100條線段可能只覆蓋了2mm的距離遇到拐彎時(shí)根本來(lái)不及提前減速。反過(guò)來(lái)如果離散太粗線段很長(zhǎng)方向變化集中在線段交點(diǎn)處拐角就很尖銳。前瞻算法面對(duì)尖銳拐角只能強(qiáng)制降速到很低速度曲線就會(huì)像鋸齒一樣起起落落。所以CAD導(dǎo)圖的離散策略必須和前瞻算法的約束條件聯(lián)動(dòng)設(shè)計(jì)。我在代碼里把弓高誤差和前視距離做了一個(gè)聯(lián)動(dòng)允許用戶設(shè)置最大前視物理距離比如5mm然后根據(jù)當(dāng)前線段密度自動(dòng)調(diào)整前瞻窗口條數(shù)而不是鎖死一個(gè)固定值。4.3 連接點(diǎn)處理G0/G1切換的邊界情況CAD導(dǎo)圖輸出的軌跡里不僅有連續(xù)的加工段G1還有空移段G0——就是從一個(gè)輪廓跳到另一個(gè)輪廓的快速定位移動(dòng)。G0和G1的切換處是最容易出邊界bug的地方。舉個(gè)例子一個(gè)排樣圖要切割五個(gè)零件。切完第一個(gè)零件后機(jī)床需要抬刀或關(guān)激光快速移到第二個(gè)零件起點(diǎn)。這時(shí)候速度規(guī)劃必須區(qū)分在G0段里可以用最大空移速度但進(jìn)入G1段的第一條線段前必須把速度降到滿足輪廓精度要求的值。如果不做特殊處理速度前瞻會(huì)把G0的高速和G1的低速混在一起結(jié)果第一個(gè)加工線段起點(diǎn)速度過(guò)高過(guò)切量極大。我在CAD導(dǎo)圖階段就為軌跡打上“段類型”標(biāo)記并且在段與段的連接處設(shè)置一個(gè)速度硬約束G0到G1切換處的目標(biāo)速度強(qiáng)制設(shè)為0除非用戶明確啟用“預(yù)加速功能”。這么設(shè)計(jì)雖然犧牲了一點(diǎn)效率但換來(lái)了穩(wěn)定性和安全性工業(yè)場(chǎng)景里安全永遠(yuǎn)是第一位的。5. 實(shí)測(cè)性能與踩坑記錄5.1 十萬(wàn)級(jí)圖元加載實(shí)測(cè)項(xiàng)目里我特意準(zhǔn)備了一個(gè)壓力測(cè)試文件里面包含了大約十萬(wàn)個(gè)圖元有大量的小圓孔和復(fù)雜樣條輪廓。實(shí)測(cè)結(jié)果如下解析DXF文件耗時(shí)約2.8秒C#Release模式i5處理器實(shí)體歸一化與去重約1.2秒樣條離散與圓弧離散約5.6秒總耗時(shí)約10秒其中離散化占了超過(guò)一半。輸出軌跡小線段數(shù)量約480萬(wàn)條。這里的問(wèn)題很明顯——480萬(wàn)條線段直接丟給速度前瞻模塊內(nèi)存和CPU都扛不住。所以我加了一個(gè)“加工段合并”的優(yōu)化共線的相鄰直線段合并成一條長(zhǎng)直線段共圓的相鄰圓弧段合并成一條大圓弧。合并之后線段數(shù)量降到了約80萬(wàn)條雖然還是不小但已經(jīng)可以接受了。合并的判據(jù)很簡(jiǎn)單就是三點(diǎn)共線中間點(diǎn)到兩端點(diǎn)連線的距離小于0.001mm和兩點(diǎn)共圓三點(diǎn)共圓的法向偏差和半徑偏差都在容許范圍內(nèi)。這個(gè)優(yōu)化做完之后速度前瞻的計(jì)算時(shí)間幾乎沒(méi)變但插補(bǔ)執(zhí)行時(shí)的內(nèi)存占用減少了很多。5.2 幾個(gè)影響穩(wěn)定性的細(xì)節(jié)問(wèn)題這一部分我記錄幾個(gè)在實(shí)際使用中被反復(fù)折騰的問(wèn)題希望看的人能繞開(kāi)。第一個(gè)是浮點(diǎn)數(shù)精度導(dǎo)致的鋸齒線。DXF里經(jīng)常出現(xiàn)類似“直線端點(diǎn)差0.0000001mm”的情況視覺(jué)上沒(méi)問(wèn)題但在幾何判定時(shí)會(huì)被當(dāng)成兩條不連續(xù)線段。速度前瞻在斷點(diǎn)處強(qiáng)制降速導(dǎo)致實(shí)際加工時(shí)表面出現(xiàn)一個(gè)個(gè)小停頓看起來(lái)就像鋸齒。解決辦法是引入一個(gè)容差機(jī)制所有坐標(biāo)比較都在一個(gè)絕對(duì)容差內(nèi)進(jìn)行默認(rèn)設(shè)成0.001mm后續(xù)如果需要更高精度再調(diào)小但不要直接設(shè)為0。第二個(gè)是塊引用的循環(huán)遞歸問(wèn)題。有些圖紙里A塊引用了B塊B塊又引用了A塊。解析器如果不對(duì)塊引用深度做限制就會(huì)無(wú)限遞歸直到棧溢出。我在展開(kāi)塊引用時(shí)設(shè)了一個(gè)最大深度默認(rèn)10層超過(guò)就報(bào)警提示圖紙異常。第三個(gè)是縮放系數(shù)為0的問(wèn)題。某些CAD軟件導(dǎo)出的DXF會(huì)帶一個(gè)縮放系數(shù)在塊引用屬性里如果這個(gè)系數(shù)是0坐標(biāo)變換矩陣就會(huì)退化所有點(diǎn)都會(huì)跑到原點(diǎn)。解析時(shí)要顯式檢查變換矩陣的行列式是否為0是的話直接跳過(guò)該實(shí)體并記錄警告。第四個(gè)是圖層篩選。加工圖紙里通常有關(guān)標(biāo)注層、尺寸層、輔助線層這些不能拿去加工。我在解析器里加了圖層過(guò)濾規(guī)則默認(rèn)跳過(guò)名稱包含“DIM”“TEXT”“CENTER”“HIDDEN”這類前綴的圖層。這個(gè)規(guī)則可配置因?yàn)椴煌井?huà)圖命名習(xí)慣差別很大。5.3 工具鏈與調(diào)試技巧說(shuō)到調(diào)試我強(qiáng)烈建議在CAD導(dǎo)圖模塊里加一個(gè)可視化調(diào)試器。純靠打斷點(diǎn)和日志排查幾何問(wèn)題太痛苦了。我的做法是在WinForms界面上把解析出的實(shí)體用GDI繪制出來(lái)并且支持把離散后的點(diǎn)列和原始圖元疊加顯示。這樣一眼就能看出離散效果好不好、有沒(méi)有斷點(diǎn)。還有一個(gè)技巧是導(dǎo)出調(diào)試數(shù)據(jù)把中間數(shù)據(jù)比如每個(gè)點(diǎn)的坐標(biāo)、線段長(zhǎng)度、夾角導(dǎo)出成CSV文件用表格或Excel做離線分析。這個(gè)比看界面直接多了。性能瓶頸的定位我用的工具很簡(jiǎn)單——C#自帶的Stopwatch和Diagnostics類在每個(gè)處理階段都打一個(gè)計(jì)時(shí)點(diǎn)。跑完一個(gè)測(cè)試文件后直接在日志里輸出各階段耗時(shí)這樣每次改動(dòng)都能立刻看到是快是慢。對(duì)離散化這種計(jì)算密集部分我用Parallel.For做了簡(jiǎn)單并行化因?yàn)槊織l線段的離散互不依賴并行化收益非常直接。實(shí)測(cè)在四核機(jī)器上離散那段從5.6秒降到了1.9秒提升效果明顯。最后再說(shuō)一個(gè)和我個(gè)人習(xí)慣有關(guān)的經(jīng)驗(yàn)開(kāi)源項(xiàng)目一定要從一開(kāi)始就把數(shù)據(jù)格式定義穩(wěn)定下來(lái)哪怕功能簡(jiǎn)單一點(diǎn)。我最早圖省事直接用了一個(gè)臨時(shí)結(jié)構(gòu)體存軌跡點(diǎn)后面前瞻模塊和插補(bǔ)模塊都基于它寫(xiě)等到想改格式的時(shí)候牽一發(fā)動(dòng)全身幾乎重寫(xiě)了一遍?,F(xiàn)在項(xiàng)目里所有模塊依賴的是一份統(tǒng)一的TrajectoryPoint定義包含坐標(biāo)、線型標(biāo)記G0/G1、弓高誤差、速度約束等字段。改動(dòng)一次以后再?zèng)]有因?yàn)閿?shù)據(jù)格式不一致引發(fā)過(guò)問(wèn)題。如果這篇分享能幫你在CAD導(dǎo)圖這塊少走點(diǎn)彎路那我的目的就達(dá)到了。