動(dòng)控制上位機(jī)CAD導(dǎo)圖:DXF解析與離散化實(shí)踐)
做上位機(jī)運(yùn)動(dòng)控制的工程師應(yīng)該都遇到過這種場面客戶拿來一張CAD圖說設(shè)備要照著圖上的輪廓跑一遍。圖紙本身只是一堆線條和圓弧控制設(shè)備真正需要的是刀路數(shù)據(jù)——一段一段帶坐標(biāo)的小線段。從圖形到線段中間隔著導(dǎo)圖、解析、離散化、坐標(biāo)折算這一整條鏈路。今天這篇就聊聊這條鏈路里的第一環(huán)CAD導(dǎo)圖。這套“C#運(yùn)動(dòng)控制開源”系列主要整理我在某激光加工設(shè)備項(xiàng)目里沉淀的上位機(jī)代碼整個(gè)項(xiàng)目源碼是開源的模塊劃分比較清晰。CAD導(dǎo)圖模塊放在最前面因?yàn)樗呛竺嫘【€段速度前瞻、插補(bǔ)輸出和軌跡運(yùn)動(dòng)的基礎(chǔ)。沒有一份干凈、有序、單位統(tǒng)一的線段數(shù)據(jù)速度前瞻算得再漂亮也白搭。這篇文章面向打算自己寫運(yùn)動(dòng)控制上位機(jī)的工程師或者做CAD圖形解析但又不想被第三方庫綁死的人我會(huì)把導(dǎo)圖模塊的設(shè)計(jì)思路、解析重點(diǎn)、離散化細(xì)節(jié)和踩過的坑一起說清楚。1. 為什么要在運(yùn)動(dòng)控制里專門做一個(gè)CAD導(dǎo)圖1.1 人工描點(diǎn)這種傳統(tǒng)做法有多痛苦早年的運(yùn)動(dòng)控制設(shè)備調(diào)試員拿到圖紙以后先量關(guān)鍵點(diǎn)坐標(biāo)再一個(gè)個(gè)手動(dòng)敲進(jìn)參數(shù)表。圖形簡單還好碰上包含幾百個(gè)圓弧、樣條線、復(fù)雜多段線的零件一個(gè)通宵都未必敲得完而且坐標(biāo)敲錯(cuò)幾位數(shù)加工出來就是廢件。我接過一個(gè)項(xiàng)目對(duì)方拿來的圖紙上是一條不規(guī)則的異形輪廓里面是連續(xù)幾段圓弧和直線人工描點(diǎn)描出來的軌跡在機(jī)床上一跑接刀處能明顯看到棱。當(dāng)時(shí)的處理辦法就是手寫一個(gè)小工具把CAD文件里的實(shí)體導(dǎo)出來從此我再也不想回到純手工時(shí)代。1.2 CAD導(dǎo)圖在整個(gè)運(yùn)動(dòng)控制鏈路中的位置運(yùn)動(dòng)控制上位機(jī)的加工流程大致是這樣圖紙文件導(dǎo)入、實(shí)體解析、坐標(biāo)提取、圖層過濾、離散化生成小線段、路徑排序、速度前瞻插補(bǔ)、驅(qū)動(dòng)設(shè)備執(zhí)行。CAD導(dǎo)圖承擔(dān)的是最前面這部分——把圖紙里的幾何信息完整地讀出來轉(zhuǎn)成運(yùn)動(dòng)控制能認(rèn)識(shí)的線段數(shù)據(jù)。后面速度前瞻的輸入源就是這里輸出的小線段列表。如果只把CAD導(dǎo)圖理解成“把圖顯示到界面上”那會(huì)有個(gè)大坑顯示用的圖形數(shù)據(jù)精度要求不高但加工用的路徑數(shù)據(jù)必須精確到小數(shù)點(diǎn)后三到四位。我見過有些同事直接拿PDF轉(zhuǎn)的圖片當(dāng)?shù)讏D再描點(diǎn)生成加工路徑這方法碰上質(zhì)檢嚴(yán)格一點(diǎn)的零件就廢了。真正要做的是解析圖紙里的矢量實(shí)體讓后續(xù)每一個(gè)坐標(biāo)點(diǎn)都來自原始幾何定義。2. CAD文件格式選型與DXF解析方案2.1 該選哪種CAD格式接入上位機(jī)CAD生態(tài)里常見格式有DWG、DXF、PLT等幾種。DWG是原生格式壓縮過、結(jié)構(gòu)復(fù)雜想解析它得依賴商業(yè)組件或逆向工程做上位機(jī)項(xiàng)目時(shí)沒必要在這上面死磕。PLT是HP繪圖儀格式精度有限適合簡單輪廓。DXF是公開的ASCII/二進(jìn)制交換格式最早就是為了在不同CAD軟件之間交換數(shù)據(jù)設(shè)計(jì)的文本格式便于定位問題也方便自己寫解析器。做運(yùn)動(dòng)控制上位機(jī)我建議把DXF作為首選導(dǎo)入格式跟設(shè)計(jì)端約定“導(dǎo)出為DXF”能省掉大量格式兼容的麻煩。實(shí)際項(xiàng)目里我遇到的設(shè)計(jì)軟件不下四五種只要它們能導(dǎo)出DXF文件內(nèi)容基本都能往同一套解析邏輯里塞。個(gè)別軟件導(dǎo)出的DXF不標(biāo)準(zhǔn)主要體現(xiàn)在實(shí)體類型不全、圖層組織混亂、單位信息缺失上面這些問題后面會(huì)在解析層做兼容處理。選DXF還有個(gè)好處文件結(jié)構(gòu)透明出現(xiàn)解析異常時(shí)直接打開文本文件看對(duì)應(yīng)位置比對(duì)著二進(jìn)制流猜字段省事得多。2.2 DXF文件里到底存了什么DXF文件的組織方式是“段”主要有HEADER段、TABLES段、BLOCKS段和ENTITIES段。我們加工路徑關(guān)心的核心內(nèi)容都在ENTITIES段LINE、ARC、CIRCLE、LWPOLYLINE、SPLINE、POINT這幾種實(shí)體基本覆蓋了絕大多數(shù)切割、雕刻、點(diǎn)膠路徑。DXF的每一條信息都由兩行組成第一行是組碼Group Code第二行是組碼對(duì)應(yīng)的值。組碼是有約定的比如10、20、30分別是X、Y、Z坐標(biāo)0表示開頭一個(gè)對(duì)象8是圖層名40是半徑50是角度。整個(gè)文件就是由這樣成對(duì)行的順序流拼出來的。解析DXF不需要高深算法核心就一個(gè)循環(huán)逐行讀入按組碼分類處理。我用C#實(shí)現(xiàn)的讀取框架大致是這樣的public ListCadEntity ParseDxf(string filePath) { var entities new ListCadEntity(); using var reader new StreamReader(filePath, Encoding.ASCII); string line; string lastName ; var pairs new List(int Code, string Value)(); while ((line reader.ReadLine()) ! null) { int code int.Parse(line.Trim()); string value reader.ReadLine()?.Trim() ?? ; pairs.Add((code, value)); if (code 0) { // 上一個(gè)對(duì)象結(jié)束把累積的組碼交給具體實(shí)體解析器處理 if (pairs.Count 1) { var (type, entity) EntityFactory.Create(pairs); if (entity ! null) entities.Add(entity); } pairs.Clear(); lastName value; } } // 處理文件尾部可能殘存的最后一個(gè)對(duì)象 if (pairs.Count 1 !string.IsNullOrEmpty(lastName)) { var (type, entity) EntityFactory.Create(pairs); if (entity ! null) entities.Add(entity); } return entities; }這個(gè)框架不依賴任何第三方庫StreamReader逐行讀取對(duì)大文件很友好。真正的難點(diǎn)在EntityFactory里——不同實(shí)體要把各自的組碼轉(zhuǎn)成有意義的幾何對(duì)象。2.3 自己寫解析器還是用第三方庫很多同事一提到DXF就想引入現(xiàn)成庫我在初期也試過幾個(gè)常見的解析類庫但發(fā)現(xiàn)兩個(gè)問題一是大多數(shù)庫面向CAD查看器場景重顯示、輕提取拿到的實(shí)體類型要么不全要么缺失圖塊展開二是輸出數(shù)據(jù)結(jié)構(gòu)不一定貼合運(yùn)動(dòng)控制需求比如把多段線直接展開成點(diǎn)集但沒保存圖層和閉合信息后面做軌跡分組就很被動(dòng)。自己寫解析器聽上去工作量大其實(shí)直線、圓弧、圓、多段線這四類實(shí)體就能覆蓋九成加工場景。實(shí)體不多的時(shí)候解析代碼比想象中簡單而且數(shù)據(jù)結(jié)構(gòu)完全由你控制。我采用的方案是定義統(tǒng)一基類CadEntity子類有CadLine、CadArc、CadCircle、CadPolyline、CadSpline全部歸入“可離散化實(shí)體”列表。這樣后面寫速度前瞻時(shí)拿到的是一個(gè)統(tǒng)一的“線段源”概念不用關(guān)心來源是直線還是一條被拆散的圓弧。3. CAD導(dǎo)圖核心實(shí)現(xiàn)從實(shí)體到小線段3.1 直線、圓弧、多段線的關(guān)鍵組碼解析直線是最基礎(chǔ)的實(shí)體組碼結(jié)構(gòu)很直白10、20是起點(diǎn)11、21是終點(diǎn)。圓弧稍微拐了個(gè)彎10、20是圓心40是半徑50是起始角度51是終止角度單位是度。這里有個(gè)特別容易踩的坑CAD的圓弧默認(rèn)逆時(shí)針方向從起始角轉(zhuǎn)到終止角。如果圖紙里的圓弧是順時(shí)針CAD會(huì)把它存成起始角大于終止角的逆時(shí)針圓弧比如300度到60度。解析時(shí)得先判斷角度大小關(guān)系必要時(shí)補(bǔ)上360度偏移。圓就更簡單了圓心加半徑?jīng)]有角度一說。多段線分兩種存儲(chǔ)方式POLYLINE是老式的頂點(diǎn)分散存儲(chǔ)LWPOLYLINE是新式的輕量緊湊存儲(chǔ)這兩類實(shí)體在DXF里同時(shí)存在解析時(shí)必須都支持。LWPOLYLINE的關(guān)鍵組碼是90頂點(diǎn)數(shù)量、10和20頂點(diǎn)坐標(biāo)、42凸度。凸度這個(gè)概念我第一次接觸時(shí)繞了不少彎它是相鄰兩個(gè)頂點(diǎn)之間圓弧的半角正切值。凸度為0表示兩個(gè)頂點(diǎn)之間是直線大于0表示逆時(shí)針凸起小于0表示順時(shí)針凹陷。想從頂點(diǎn)坐標(biāo)和凸度算出圓弧的圓心與半徑涉及弦長和矢高的幾何換算我提煉了一個(gè)現(xiàn)成的計(jì)算方法后面代碼里直接套用即可。樣條線在切割圖紙里出現(xiàn)頻率不高但偶爾會(huì)遇到。處理方式不多想直接采樣離散成一系列點(diǎn)再擬合成小線段組精度控制用采樣步長加最大弓高誤差雙重限制。3.2 圓弧離散化小線段生成的誤差控制運(yùn)動(dòng)控制插補(bǔ)不認(rèn)圓弧實(shí)體只認(rèn)小線段。圓弧離散化是CAD導(dǎo)圖模塊的精度命門。很多初版實(shí)現(xiàn)直接按固定角度間隔切圓弧比如每1度切一段結(jié)果大半徑圓弧切出來特別密、數(shù)據(jù)量巨大小半徑圓弧又切得太粗、輪廓失真。合理的做法是按最大弓高誤差來決定切分步長。上面那段代碼里的maxError就是允許的最大弓高誤差。激光切割一般取0.005毫米粗加工可以放到0.02毫米。我項(xiàng)目里默認(rèn)值是0.01毫米實(shí)際跑下來既保證精度又不會(huì)產(chǎn)生過多線段。選這個(gè)值是有講究的它直接決定后續(xù)速度前瞻的計(jì)算代價(jià)——小線段數(shù)量越多前瞻計(jì)算量越大所以離散化不能盲目追求極致精度要跟加工需求匹配。值得提醒的是結(jié)束角度不一定要生成的最后一個(gè)點(diǎn)落在理論終點(diǎn)上但為了路徑閉合準(zhǔn)確還是要把終點(diǎn)強(qiáng)制加入。我代碼里的for循環(huán)從0到steps而不是到steps-1就是為了保證起點(diǎn)和終點(diǎn)都落在離散后的點(diǎn)集里。3.3 圖層過濾和刀路篩選的工程化處理圖紙里除了加工輪廓經(jīng)常還帶著尺寸標(biāo)注、中心線、輔助線、注釋文字。如果一股腦全導(dǎo)入成加工路徑設(shè)備會(huì)沿著標(biāo)注線跑出奇怪軌跡。圖層過濾是CAD導(dǎo)圖里絕對(duì)不能省的一步。我習(xí)慣在界面上開放一個(gè)圖層列表解析完DXF后把所有用到的圖層都列出來用戶勾選哪個(gè)圖層參與加工哪個(gè)圖層只做底圖參考。內(nèi)部實(shí)現(xiàn)就是在處理每個(gè)實(shí)體時(shí)讀取組碼8的圖層名然后跟一個(gè)白名單集合比對(duì)。白名單未命中且不需要顯示的實(shí)體直接跳過連離散化都不用做。這個(gè)做法親測能省掉至少一半的數(shù)據(jù)量。有些設(shè)計(jì)工程師習(xí)慣把無數(shù)標(biāo)注放在同一個(gè)默認(rèn)圖層里過濾做不好后面整條路徑都會(huì)亂套。建議導(dǎo)圖界面上一開始就跑一次、顯示一次確認(rèn)圖層是否無誤后再正式生成加工路徑。這塊其實(shí)還有一個(gè)隱藏收益圖層過濾之后的實(shí)體順序基本還是文件原序?yàn)榱藵M足后續(xù)速度前瞻的路徑連續(xù)性要求還得做路徑排序這部分下一篇文章會(huì)展開。4. 坐標(biāo)轉(zhuǎn)換、顯示交互與數(shù)據(jù)組織4.1 CAD坐標(biāo)系與設(shè)備坐標(biāo)系的換算CAD圖里用的一版是設(shè)計(jì)坐標(biāo)系設(shè)備機(jī)械零點(diǎn)是另一個(gè)坐標(biāo)系這兩者之間通常隔著平移、縮放、鏡像甚至旋轉(zhuǎn)。如果直接把解析出來的坐標(biāo)發(fā)給運(yùn)動(dòng)控制卡設(shè)備跑出來的位置肯定是天南地北。我封裝了一個(gè)Transform類內(nèi)部維護(hù)一個(gè)仿射變換矩陣。加載DXF后從HEADER段嘗試讀取單位信息再從界面輸入框拿到工件原點(diǎn)在設(shè)計(jì)坐標(biāo)系里的坐標(biāo)然后自動(dòng)把縱軸方向按設(shè)備習(xí)慣翻轉(zhuǎn)一下這樣一個(gè)統(tǒng)一變換矩陣就能映射到設(shè)備坐標(biāo)。這套邏輯在好幾個(gè)現(xiàn)場項(xiàng)目里都驗(yàn)證過只要不會(huì)把單位的毫米/英寸搞混定位誤差都能控制在重復(fù)定位精度范圍內(nèi)。位移換算也可以用四組參數(shù)完成原點(diǎn)X、原點(diǎn)Y、縮放比例、鏡像標(biāo)記。建立一個(gè)函數(shù)ConvertPoint(x, y)把原始坐標(biāo)一步步套進(jìn)去即可。不過請(qǐng)注意CAD圖的Y軸方向和部分運(yùn)動(dòng)控制系統(tǒng)的Y軸方向相反漏掉這一步輪廓會(huì)左右/上下整體顛倒這是現(xiàn)場最容易犯的失誤之一。我的經(jīng)驗(yàn)是必須有“預(yù)覽對(duì)比”功能讓用戶在界面看到設(shè)計(jì)原圖跟轉(zhuǎn)換后的路徑疊加效果一眼就能發(fā)現(xiàn)方向問題。4.2 導(dǎo)入后的圖形顯示實(shí)現(xiàn)要點(diǎn)上位機(jī)界面要顯示CAD圖形多數(shù)人會(huì)想到用GDI或WPF繪圖我這里用的是自繪控件。解析完成后的實(shí)體對(duì)象轉(zhuǎn)成屏幕坐標(biāo)進(jìn)行繪制。關(guān)鍵點(diǎn)在于渲染性能幾千個(gè)實(shí)體還好幾萬甚至十幾萬個(gè)小線段時(shí)直接逐段調(diào)用繪圖API會(huì)卡成PPT。我用的優(yōu)化手段是“可見區(qū)域裁剪”加“簡化繪制”。先根據(jù)屏幕縮放比例判斷哪些線段落在當(dāng)前視口內(nèi)只繪制可見部分的實(shí)體。放大到一定程度時(shí)把小線段直接合并成一個(gè)折線批次用一條Path一次畫完。這樣畫面幀率能穩(wěn)定維持在可接受的范圍用戶拖動(dòng)縮放時(shí)也不會(huì)卡頓。另外繪圖層級(jí)上區(qū)分“底圖灰顯”和“加工路徑高亮”避免用戶混淆參考圖形和實(shí)際刀路。關(guān)于顯示性能還有一個(gè)不起眼但是很好用的小技巧離散化小線段時(shí)把首尾坐標(biāo)用結(jié)構(gòu)體存到一個(gè)連續(xù)數(shù)組里繪制時(shí)整個(gè)數(shù)組的坐標(biāo)點(diǎn)一次性提交給繪圖上下文真正卡住性能的往往是逐點(diǎn)構(gòu)造對(duì)象帶來的開銷而不是繪圖本身。4.3 線段數(shù)據(jù)的統(tǒng)一組織與存儲(chǔ)CAD導(dǎo)圖模塊的最終產(chǎn)物是我定義的一個(gè)路徑數(shù)據(jù)模型。它不是零散的坐標(biāo)點(diǎn)而是包含多個(gè)“子路徑”的有序列表。每個(gè)子路徑對(duì)應(yīng)一條連續(xù)的加工輪廓子路徑里是排好序的線段節(jié)點(diǎn)每個(gè)線段節(jié)點(diǎn)包含起點(diǎn)、終點(diǎn)、線型類型、原始實(shí)體引用、圖層名和長度。這個(gè)模型是為了和后面的速度前瞻無縫銜接設(shè)計(jì)的。速度前瞻算法是典型的局部窗口優(yōu)化需要快速知道當(dāng)前線段的下一條線段在哪里以及兩條線段之間的夾角和長度關(guān)系。用結(jié)構(gòu)體數(shù)組存儲(chǔ)線段內(nèi)存連續(xù)Cache命中高幾萬條線段的前瞻計(jì)算在毫秒級(jí)就能完成。我實(shí)際測量過一個(gè)包含2.3萬條小線段的輪廓文件CAD導(dǎo)圖模塊從讀入到離散化完畢、輸出路徑模型總耗時(shí)在0.8秒左右。這個(gè)成績對(duì)大部分運(yùn)動(dòng)控制場景都?jí)蛴昧巳绻F(xiàn)場要求更高下一步還可以用并行解析。但可能落后的地方是圖層白名單要求的UI優(yōu)先級(jí)最高先保證功能正確再考慮性能。5. 為小線段速度前瞻做的數(shù)據(jù)預(yù)處理5.1 線段排序與路徑連續(xù)性處理DXF文件里的實(shí)體順序通常是畫圖的人按設(shè)計(jì)順序畫的但加工必須按空間連續(xù)順序走。離得近的兩條線段可能在文件里隔了十萬八千里如果不做排序設(shè)備就會(huì)在加工區(qū)域內(nèi)來回跳空跑效率極低。排序邏輯我用的是最近鄰貪心算法選一個(gè)尚未訪問的線段作為當(dāng)前起點(diǎn)在所有未訪問線段里找距離當(dāng)前終點(diǎn)最近的那條接上去反復(fù)迭代直到閉合回起點(diǎn)。接近O(n)到O(n2)的復(fù)雜度問題用空間網(wǎng)格索引處理實(shí)際跑起來可以接受。極端情況下文件里有上千條獨(dú)立路徑排序算法也不應(yīng)超過兩秒鐘。需要注意的是如果一條輪廓是閉合的排序結(jié)果需要形成一個(gè)閉環(huán)如果不閉合就要保證起終點(diǎn)順序跟工藝要求一致比如切割順邊還是逆邊。這些規(guī)則在導(dǎo)圖界面里做成選項(xiàng)用戶可以根據(jù)工藝自行選擇。這步排序做完后面速度前瞻拿到的數(shù)據(jù)才是真正的“連續(xù)路徑流”。5.2 輪廓分組與刀補(bǔ)預(yù)留排序后的線列還要分組每個(gè)閉合四邊形或者多段點(diǎn)子集是一個(gè)獨(dú)立的輪廓組。加工時(shí)每個(gè)輪廓組通常對(duì)應(yīng)一次下刀、一次抬刀或者一組補(bǔ)償參數(shù)。這個(gè)信息對(duì)CAM后處理分外重要我在路徑模型里為每個(gè)輪廓組附加了輪廓編號(hào)、包圍盒、層級(jí)關(guān)系。刀補(bǔ)預(yù)留這個(gè)問題屬于“不做不知道一做嚇一跳”。當(dāng)使用刀具半徑補(bǔ)償時(shí)CAD圖里通常畫的是設(shè)計(jì)輪廓實(shí)際切割路徑是偏置后的輪廓。是做外偏置還是內(nèi)偏置偏置量是刀具半徑加補(bǔ)償值這些參數(shù)都要在導(dǎo)圖模塊里提前配置生成加工路徑時(shí)統(tǒng)一處理。如果把這些邏輯堆到速度前瞻階段再處理算法復(fù)雜度會(huì)爆炸式上升。5.3 頂點(diǎn)合并與數(shù)據(jù)去噪圖紙本身不總是干凈的。兩個(gè)看似相連的線段終點(diǎn)可能差0.02毫米多段線里可能混著一截長度幾乎為零的重疊線段。這些“毛刺”會(huì)讓速度前瞻在處理夾角時(shí)產(chǎn)生抖動(dòng)也容易在運(yùn)動(dòng)控制里引起往復(fù)震動(dòng)。我增加了一個(gè)預(yù)處理步驟先按容差比如0.005毫米合并重合頂點(diǎn)再刪除長度小于某個(gè)值的劣化線段。這步看似小但直接把前瞻算法的異常分支減少了很多。現(xiàn)場加工時(shí)也能明顯感覺到路徑過濾得干凈的設(shè)備動(dòng)作更平滑震動(dòng)更小。容差的取值要控制在運(yùn)動(dòng)控制系統(tǒng)分辨率以內(nèi)。設(shè)得太大會(huì)把本該存在的細(xì)小輪廓細(xì)節(jié)吞掉設(shè)得太小過濾不掉噪聲數(shù)據(jù)。我的經(jīng)驗(yàn)是取系統(tǒng)最小進(jìn)給步長的1/2到1倍。這個(gè)值跟設(shè)備有關(guān)別照抄別人的參數(shù)。6. 常見問題與排查技巧實(shí)錄6.1 常見問題速查表整理一份我在現(xiàn)場和調(diào)試中遇到的高頻問題按現(xiàn)象、排查方向、解決方案列成下表方便直接對(duì)照。問題現(xiàn)象排查方向解決方案導(dǎo)入后圖形整體過大或過小文件和設(shè)備的單位不一致讀取HEADER段單位信息換算成設(shè)備毫米并校驗(yàn)比例圖形整體鏡像翻轉(zhuǎn)Y軸方向定義、鏡像標(biāo)記未處理在坐標(biāo)變換處統(tǒng)一處理Y軸翻轉(zhuǎn)用預(yù)覽對(duì)比確認(rèn)圓弧方向反了起始角與終止角判斷不對(duì)按CAD逆時(shí)針規(guī)則處理角度差為負(fù)時(shí)補(bǔ)360度加工時(shí)路徑亂跳圖層未過濾、實(shí)體順序未排序啟用圖層白名單執(zhí)行最近鄰路徑排序?qū)牒笕绷艘徊糠謱?shí)體未處理INSERT塊引用或隱藏圖層展開BLOCKS段塊實(shí)體隱藏圖層實(shí)體默認(rèn)跳過但提供開關(guān)大文件導(dǎo)入慢顯示卡頓逐點(diǎn)構(gòu)建對(duì)象、未裁剪視口用結(jié)構(gòu)體數(shù)組存儲(chǔ)線段坐標(biāo)可見區(qū)域裁剪批量繪制LWPOLYLINE中的圓弧方向不對(duì)凸度符號(hào)判斷問題凸度大于0逆時(shí)針、小于0順時(shí)針轉(zhuǎn)換確認(rèn)方向6.2 被圖層白名單坑過的真實(shí)教訓(xùn)之前有個(gè)樣板項(xiàng)目對(duì)方出的圖紙所有尺寸標(biāo)注都放在主輪廓圖層里我導(dǎo)圖后發(fā)現(xiàn)刀路里多了一堆沿著尺寸數(shù)字跑的短線。前線排查時(shí)盯著實(shí)體類型怎么都沒想到是圖層污染。后來在導(dǎo)圖界面上加了一列“圖層實(shí)體統(tǒng)計(jì)”每個(gè)圖層下面有多少個(gè)實(shí)體一覽無余三分鐘就定位到污染源。所以我現(xiàn)在做任何圖形導(dǎo)入功能一定會(huì)保留圖層級(jí)別的統(tǒng)計(jì)信息這是排查問題最快的一條路徑。6.3 解析不標(biāo)準(zhǔn)DXF的兼容技巧有些CAD軟件導(dǎo)出DXF時(shí)不按標(biāo)準(zhǔn)寫比如組碼順序不是嚴(yán)格按固定順序或者在非標(biāo)準(zhǔn)位置插入額外組碼。我的解析器寫得不死板在每個(gè)對(duì)象內(nèi)循環(huán)收集所有組碼注冊(cè)一個(gè)字典按Key取需要的組碼值而不是死板順序判斷。自由度一下就上去了。還有一個(gè)兼容點(diǎn)是字符串編碼。部分老版本CAD導(dǎo)出為ANSI編碼UTF-8讀取后圖層名可能變成亂碼圖層過濾全部失效。我的辦法是嘗試用StreamReader的編碼檢測先按UTF-8讀發(fā)現(xiàn)非法字符就回退到GBK或ANSI。這個(gè)細(xì)節(jié)在中文圖紙環(huán)境下幾乎避不開調(diào)試時(shí)注意一下能少掉一大半頭發(fā)。結(jié)語CAD導(dǎo)圖這個(gè)模塊從功能上看只是把圖變成線段但真正做起來涉及格式解析、幾何計(jì)算、坐標(biāo)轉(zhuǎn)換、性能優(yōu)化和一堆現(xiàn)場才知道的兼容性問題。我花了蠻多時(shí)間打磨邊界條件現(xiàn)在這套代碼在幾個(gè)不同設(shè)備上跑下來給我省下的調(diào)試時(shí)間遠(yuǎn)超當(dāng)初的開發(fā)投入。如果正在做運(yùn)動(dòng)控制上位機(jī)建議先聚焦核心實(shí)體類型把精度和排序做好再逐步補(bǔ)齊兼容層別一開始就想吃掉所有格式。個(gè)人體會(huì)最深的一點(diǎn)是CAD導(dǎo)圖不僅僅是“讀文件”它決定了后續(xù)速度前瞻的上限。數(shù)據(jù)組織不到位整個(gè)加工質(zhì)量都會(huì)受影響。好在這些坑我已經(jīng)幫大家蹚過一遍照著上面的思路做路徑數(shù)據(jù)這塊會(huì)扎實(shí)很多。CAD導(dǎo)圖聊到這里下一篇我會(huì)接著寫小線段速度前瞻的優(yōu)化——就是拿這份整理好的線段數(shù)據(jù)怎么最合理地規(guī)劃速度既保證效率又不讓設(shè)備抖動(dòng)。那部分的算法細(xì)節(jié)比導(dǎo)圖還要有意思等下次更新一起聊。