:從自然語言到 STEP/URDF/G-code 的三段式管線)
1. 從一句話到三維模型text-to-cad 到底在解決什么問題第一次聽到 “text-to-cad” 這個詞很多人腦子里蹦出來的畫面可能是對著電腦說一句“給我畫個法蘭盤”然后 CAD 軟件里就自動生成了一張圖紙。這個理解方向沒錯但實際落地遠沒有這么簡單。text-to-cad 本質(zhì)上是一套把自然語言描述轉(zhuǎn)換成結構化三維模型數(shù)據(jù)的技術鏈路它的輸出通常不是一張 DWG 圖紙而是 STEP、URDF、G-code 這類可以被下游工具直接消費的中間格式。我接觸這個方向是從一個很具體的需求開始的手頭有一批參數(shù)化零件每次改尺寸都要手動在 CAD 里重新拉伸、打孔、倒角重復勞動太多。后來想能不能用一段文字描述直接驅(qū)動模型生成試了幾輪之后發(fā)現(xiàn)text-to-cad 真正的價值不在于“替代畫圖”而在于把設計意圖變成可復用的數(shù)據(jù)流。你描述的是“一個外徑 80、內(nèi)徑 40、厚度 10 的法蘭均布 6 個直徑 8 的孔”系統(tǒng)輸出的是帶完整幾何拓撲的 STEP 文件這個文件可以進仿真、可以進切片、可以進機器人仿真環(huán)境。它適合誰三類人最值得關注一是做參數(shù)化設計的機械工程師二是搞機器人仿真需要批量生成 URDF 的技術人員三是做增材制造想把文字需求直接轉(zhuǎn)成 G-code 的創(chuàng)客。如果你只是偶爾畫兩張二維圖那 text-to-cad 對你的邊際收益不大但如果你面對的是成百上千個變體零件這套思路能省掉大量重復操作。需要先明確一點text-to-cad 不是某一個軟件的功能按鈕而是一條從語義解析到幾何生成再到格式導出的完整管線。下面我按自己實際搭過的流程把每個環(huán)節(jié)拆開講。2. 整體架構設計為什么是“解析—建?!獙С觥比问?.1 核心思路與方案選型我試過兩種路線。第一種是端到端生成用大模型直接輸出 CAD 腳本或網(wǎng)格數(shù)據(jù)第二種是分階段管線先把文字解析成結構化參數(shù)再調(diào)用幾何內(nèi)核建模最后按目標格式導出。實測下來第一種路線在簡單形狀上能跑通但一旦涉及孔位陣列、倒角、裝配關系生成結果就很不穩(wěn)定而且出錯之后幾乎沒法調(diào)試。所以我最終采用的是三段式架構語義解析層把自然語言拆成“實體類型 尺寸參數(shù) 特征操作”的結構化描述幾何建模層用參數(shù)化建模內(nèi)核根據(jù)結構化描述生成三維實體格式導出層把實體轉(zhuǎn)換成 STEP、URDF、G-code 等目標格式這個設計的核心考量是可調(diào)試性。文字解析錯了我只需要改解析規(guī)則幾何生成錯了我只需要調(diào)建模參數(shù)導出格式不對我只需要換導出器。每一層都可以單獨驗證不會出現(xiàn)“一句話進去、一坨亂碼出來、完全不知道哪里錯了”的情況。提示如果你剛開始做 text-to-cad強烈建議不要一上來就追求端到端。先把中間的結構化描述定義清楚后面所有工作都會輕松很多。2.2 各層職責與數(shù)據(jù)流結構化描述我用的是 JSON字段設計參考了常見 CAD 特征的參數(shù)組織方式。一個典型的法蘭描述長這樣{ entity: flange, params: { outer_diameter: 80, inner_diameter: 40, thickness: 10, holes: { count: 6, diameter: 8, pcd: 60 } }, units: mm }這個 JSON 就是整條管線的“合同”。解析層負責產(chǎn)出它建模層負責消費它導出層不關心它怎么來的。這樣做的好處是哪天我想換一個更好的語言模型來解析文字只要它還能輸出同樣結構的 JSON后面兩層完全不用動。幾何建模層我選的是基于OpenCASCADE的內(nèi)核因為它對 STEP 格式的支持最成熟布爾運算也穩(wěn)定。URDF 導出則需要額外處理關節(jié)和連桿的層級關系這部分后面單獨講。G-code 導出相對獨立本質(zhì)上是把三維模型切片后生成路徑我一般用現(xiàn)成的切片引擎來處理。2.3 為什么不用直接生成網(wǎng)格有人會問現(xiàn)在網(wǎng)格生成模型那么多為什么不直接文字生成 STL我的經(jīng)驗是網(wǎng)格數(shù)據(jù)不可參數(shù)化。你拿到一個 STL想改一個孔的直徑只能重新生成整個網(wǎng)格。而 STEP 是邊界表示B-rep每個面、每條邊都是帶參數(shù)的改一個尺寸只需要更新對應參數(shù)。對于需要反復迭代的設計場景這個差別是致命的。另外URDF 和 G-code 對幾何精度的要求不同。URDF 關心的是碰撞體和視覺體的近似G-code 關心的是切片路徑兩者都需要從精確的 B-rep 模型出發(fā)做不同處理。如果中間只有網(wǎng)格很多下游操作會變得很別扭。3. 語義解析層把“人話”變成“機器能懂的參數(shù)”3.1 解析策略規(guī)則優(yōu)先還是模型優(yōu)先我一開始用純規(guī)則做解析寫了一大堆正則表達式。簡單句還能應付比如“直徑 50 的圓盤”但一旦出現(xiàn)“外徑 80 內(nèi)徑 40 厚度 10 均布 6 個直徑 8 的孔”這種復合描述規(guī)則就開始打架。后來改成模型抽取 規(guī)則校驗的混合策略先用語言模型把句子里的實體、尺寸、數(shù)量、位置關系抽出來再用規(guī)則檢查參數(shù)是否完整、單位是否明確、數(shù)值是否在合理范圍內(nèi)。這個混合策略的關鍵在于校驗環(huán)節(jié)。語言模型有時候會把“內(nèi)徑”和“外徑”搞反或者漏掉單位。規(guī)則校驗會檢查外徑是否大于內(nèi)徑孔的數(shù)量是否是整數(shù)孔徑是否小于壁厚這些檢查能攔掉大部分低級錯誤。3.2 參數(shù)完整性檢查清單在實際操作中我發(fā)現(xiàn)以下幾類問題出現(xiàn)頻率最高建議在解析層就做好攔截問題類型典型表現(xiàn)處理方式單位缺失“直徑 80”沒說是 mm 還是 inch默認 mm但在輸出中標注假設參數(shù)矛盾內(nèi)徑大于外徑直接報錯要求重新描述特征沖突孔的位置超出實體范圍計算孔邊距超出則報錯數(shù)量模糊“幾個孔”沒有具體數(shù)字追問或使用默認值并標注術語歧義“大孔”沒有具體尺寸要求補充具體數(shù)值注意不要小看單位問題。我踩過一次坑一批零件默認用了 inch結果導出 STEP 之后進仿真尺寸差了 25.4 倍排查了半天才發(fā)現(xiàn)是單位沒統(tǒng)一。3.3 從描述到 JSON 的實操示例假設輸入是“一個長 100 寬 60 高 20 的方塊四個角各有一個直徑 6 的通孔孔中心距邊緣 10。”解析層的工作流程是識別實體類型方塊box抽取主尺寸長 100、寬 60、高 20識別特征操作通孔through hole抽取孔參數(shù)直徑 6、數(shù)量 4推斷孔位置四角中心距邊緣 10生成結構化 JSON對應的 JSON 如下{ entity: box, params: { length: 100, width: 60, height: 20, holes: { type: through, diameter: 6, count: 4, positions: [ {x: 10, y: 10}, {x: 90, y: 10}, {x: 10, y: 50}, {x: 90, y: 50} ] } }, units: mm }這里的位置計算是解析層做的孔中心距邊緣 10所以四個角的坐標分別是 (10,10)、(90,10)、(10,50)、(90,50)。這個計算邏輯要寫清楚因為不同人對“距邊緣”的理解可能不一樣——是孔中心到邊緣還是孔邊到邊緣我默認按孔中心算并在文檔里注明。4. 幾何建模層從參數(shù)到實體的關鍵操作4.1 建模內(nèi)核的選擇與初始化幾何建模層我用的 OpenCASCADEPython 綁定用的是 pythonocc。初始化流程大概是from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir # 創(chuàng)建方塊 box BRepPrimAPI_MakeBox(100, 60, 20).Shape() # 創(chuàng)建圓柱并做布爾減 for pos in hole_positions: cylinder BRepPrimAPI_MakeCylinder(3, 20, gp_Ax2(gp_Pnt(pos.x, pos.y, 0), gp_Dir(0, 0, 1))).Shape() box BRepAlgoAPI_Cut(box, cylinder).Shape()這段代碼的邏輯很直接先建主實體再逐個建特征實體最后用布爾運算把特征從主實體上“挖”掉。但實際操作中有幾個細節(jié)要注意。4.2 布爾運算的穩(wěn)定性技巧布爾運算是幾何建模里最容易出問題的環(huán)節(jié)。我遇到過圓柱和方塊表面剛好相切導致運算失敗的情況也遇到過孔的位置太靠近邊緣導致生成薄壁碎片。幾個實用技巧留余量做減運算的圓柱高度要比主實體厚度多出一點確保完全穿透。比如方塊高 20圓柱高度用 22兩端各多 1mm。避免相切孔中心到邊緣的距離不要剛好等于孔徑的一半稍微錯開一點避免相切。分步運算如果有多個孔不要一次性做所有布爾運算逐個做每做一步檢查一下實體是否有效。容差設置OpenCASCADE 有默認容差如果模型尺寸很小比如微米級需要手動調(diào)整容差參數(shù)。提示布爾運算失敗時先檢查兩個實體是否有重疊體積。如果只是面接觸運算結果可能為空或報錯。4.3 特征順序?qū)Y果的影響建模順序會影響最終結果尤其是涉及倒角和圓角的時候。我的經(jīng)驗是先做減運算再做倒角。如果先倒角再打孔孔可能會切掉倒角的一部分導致邊緣不完整。正確的順序是創(chuàng)建主實體執(zhí)行所有減運算孔、槽、腔執(zhí)行倒角和圓角檢查實體有效性這個順序不是絕對的但大多數(shù)情況下能避免特征干涉。如果倒角之后還需要打孔那就要重新評估順序。4.4 模型有效性檢查每次布爾運算之后我都會跑一遍有效性檢查from OCC.Core.BRepCheck import BRepCheck_Analyzer analyzer BRepCheck_Analyzer(shape) if not analyzer.IsValid(): print(模型無效需要排查)無效模型導出 STEP 后下游工具可能讀不出來或者讀出來是破面。與其等到下游報錯不如在建模層就攔住。5. 格式導出層STEP、URDF、G-code 的差異化處理5.1 STEP 導出最直接的路徑STEP 導出是最成熟的OpenCASCADE 自帶導出器from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs writer STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write(output.step)這里的關鍵參數(shù)是STEPControl_AsIs表示按原樣導出不做額外處理。如果模型有裝配關系需要額外處理裝配層級。對于單實體零件這樣導出就夠了。STEP 的好處是通用性強幾乎所有 CAD 軟件都能讀。我試過用 FreeCAD、中望 CAD、SolidWorks 分別打開同一個 STEP 文件幾何信息都能正確識別。但要注意STEP 不保留特征樹導出的是一堆面片和邊想改參數(shù)只能回到生成端重新生成。5.2 URDF 導出機器人仿真的特殊需求URDF 是機器人描述格式它關心的不是精確幾何而是連桿、關節(jié)、碰撞體、視覺體的層級關系。從 text-to-cad 生成 URDF需要額外做幾件事定義連桿每個獨立運動的部件作為一個 link定義關節(jié)link 之間的連接關系包括關節(jié)類型旋轉(zhuǎn)、平移、固定和運動范圍簡化碰撞體URDF 的碰撞體通常用簡單幾何方塊、圓柱、球近似不需要精確 B-rep指定視覺體視覺體可以用網(wǎng)格文件通常導出為 STL 或 DAE我一般的工作流是先用 text-to-cad 生成精確 STEP 模型再根據(jù)裝配關系手動或半自動地定義 URDF 結構。完全自動從文字生成 URDF 目前還比較困難因為關節(jié)類型和運動范圍很難從自然語言中準確推斷。注意URDF 導入 CoppeliaSim 時路徑問題是最常見的坑。建議用相對路徑并且把網(wǎng)格文件和 URDF 放在同一個目錄下。5.3 G-code 導出從模型到制造路徑G-code 導出本質(zhì)上是切片加路徑規(guī)劃。text-to-cad 生成的模型需要先導出為 STL再進切片引擎。我常用的流程是從 B-rep 模型導出 STL注意設置合適的網(wǎng)格精度用切片引擎如 CuraEngine、PrusaSlicer生成 G-code根據(jù)打印機參數(shù)調(diào)整速度、溫度、層高這里的關鍵是網(wǎng)格精度。STL 是三角網(wǎng)格精度太低會導致圓弧變成多邊形精度太高會導致文件過大。我的經(jīng)驗是對于一般零件弦高公差設 0.01mm角度公差設 0.5 度能在文件大小和精度之間取得平衡。格式主要用途精度要求導出難度STEPCAD 交換、仿真高B-rep低URDF機器人仿真中簡化碰撞體中G-code增材制造中切片路徑中STL3D 打印、可視化中三角網(wǎng)格低6. 實操全流程從一句話到可用的 STEP 文件6.1 環(huán)境準備與依賴安裝我用的環(huán)境是 Python 3.10 pythonocc-core numpy。安裝 pythonocc 在 Windows 上稍微麻煩一點推薦用 condaconda install -c conda-forge pythonocc-core如果不用 condapip 安裝可能會缺依賴。Linux 下相對簡單apt 裝好 OpenCASCADE 開發(fā)庫之后 pip 裝 pythonocc 基本能跑通。6.2 完整代碼示例下面是一個從文字描述到 STEP 導出的最小可運行示例import json from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs from OCC.Core.BRepCheck import BRepCheck_Analyzer # 假設解析層已經(jīng)產(chǎn)出結構化描述 spec { entity: box, params: { length: 100, width: 60, height: 20, holes: { type: through, diameter: 6, count: 4, positions: [ {x: 10, y: 10}, {x: 90, y: 10}, {x: 10, y: 50}, {x: 90, y: 50} ] } }, units: mm } # 建模 p spec[params] shape BRepPrimAPI_MakeBox(p[length], p[width], p[height]).Shape() hole_r p[holes][diameter] / 2 hole_h p[height] 2 # 留余量 for pos in p[holes][positions]: cyl BRepPrimAPI_MakeCylinder( hole_r, hole_h, gp_Ax2(gp_Pnt(pos[x], pos[y], -1), gp_Dir(0, 0, 1)) ).Shape() shape BRepAlgoAPI_Cut(shape, cyl).Shape() # 有效性檢查 if not BRepCheck_Analyzer(shape).IsValid(): raise RuntimeError(模型無效) # 導出 STEP writer STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write(output.step) print(STEP 文件已生成)這段代碼跑通之后你會得到一個帶四個通孔的方塊 STEP 文件。用 FreeCAD 打開應該能看到完整的幾何。6.3 參數(shù)計算的實際案例再舉一個法蘭的例子說明參數(shù)計算過程。輸入描述“外徑 120內(nèi)徑 60厚度 15均布 8 個直徑 10 的孔孔中心圓直徑 90?!庇嬎氵^程外半徑 120 / 2 60內(nèi)半徑 60 / 2 30孔半徑 10 / 2 5孔中心圓半徑 90 / 2 45孔中心坐標角度間隔 360 / 8 45 度第 i 個孔的角度 i * 45 度x 45 * cos(角度)y 45 * sin(角度)這些計算在解析層完成建模層直接消費坐標。這樣做的好處是如果以后要改孔的數(shù)量只需要改解析層的計算邏輯建模層不用動。7. 常見問題與排查技巧實錄7.1 布爾運算失敗怎么辦這是最高頻的問題。表現(xiàn)是BRepAlgoAPI_Cut返回空形狀或者拋異常。排查順序檢查兩個實體是否有實際重疊體積而不只是面接觸檢查實體是否有效用BRepCheck_Analyzer驗證嘗試調(diào)整容差BRepAlgoAPI_Cut可以設置模糊容差如果還不行把減運算的實體稍微移動一點避開相切位置我遇到過一次圓柱和方塊的一個面剛好共面導致布爾運算結果不穩(wěn)定。把圓柱沿軸向移動 0.01mm 之后就正常了。7.2 STEP 導出后下游讀不出來通常是模型本身有問題。先用BRepCheck_Analyzer檢查如果模型有效但下游還是讀不出來可能是 STEP 版本問題。OpenCASCADE 默認導出 AP214有些老軟件只支持 AP203??梢栽趯С鰰r指定協(xié)議writer.Transfer(shape, STEPControl_AsIs) # 或者嘗試其他協(xié)議另外文件名不要用中文和特殊字符有些下游工具對路徑編碼支持不好。7.3 URDF 導入仿真環(huán)境后位置不對URDF 的坐標系定義和 CAD 的坐標系定義可能不一致。CAD 通常 Z 軸向上URDF 通常也是 Z 軸向上但有些仿真環(huán)境默認 Y 軸向上。導入之后如果發(fā)現(xiàn)模型躺倒了檢查一下坐標系約定。還有一個常見問題是單位。URDF 默認用米CAD 默認用毫米。導出 URDF 時要把尺寸除以 1000否則模型會大 1000 倍。7.4 G-code 路徑異常G-code 路徑異常通常出在切片參數(shù)上。檢查層高是否合理一般 0.1-0.3mm噴嘴直徑是否和切片參數(shù)匹配模型是否封閉非封閉模型切片會出問題模型是否在打印平臺范圍內(nèi)我踩過一次坑模型導出 STL 時精度設得太低圓弧變成了明顯的多邊形切片后路徑抖動很厲害。把弦高公差從 0.1mm 調(diào)到 0.01mm 之后就順滑了。7.5 常見問題速查表問題現(xiàn)象可能原因排查方向布爾運算返回空實體無重疊或相切檢查位置關系留余量STEP 導出失敗模型無效或路徑含特殊字符檢查模型有效性換路徑URDF 導入后尺寸不對單位不一致毫米轉(zhuǎn)米URDF 導入后姿態(tài)不對坐標系約定不同檢查軸向定義G-code 路徑抖動STL 精度太低提高網(wǎng)格精度模型面數(shù)過多網(wǎng)格精度太高降低弦高公差孔位置偏移坐標計算錯誤檢查解析層計算邏輯8. 工具鏈選型與擴展思路8.1 建模內(nèi)核對比內(nèi)核優(yōu)勢劣勢適用場景OpenCASCADE開源STEP 支持好學習曲線陡通用 CAD 生成CGAL幾何算法豐富偏學術工程化弱研究性項目FreeCAD API上手快Python 友好依賴 FreeCAD 環(huán)境快速原型CadQuery語法簡潔參數(shù)化強底層還是 OCC參數(shù)化零件我目前主力用 OpenCASCADE因為可控性最強。如果只是做簡單零件CadQuery 的語法更友好寫起來快很多。8.2 從單零件到裝配體單零件跑通之后下一步自然是裝配體。裝配體的 text-to-cad 需要額外處理零件間的位置關系配合、對齊、距離裝配層級子裝配和總裝配的關系干涉檢查零件之間是否有碰撞這部分我還在摸索目前的做法是先用文字分別生成每個零件再手動或半自動地定義裝配關系。完全自動的裝配體生成難點在于自然語言中的位置描述往往很模糊比如“把軸插進孔里”到底插多深、什么配合都需要額外推斷。8.3 批量生成的工程化考慮如果要做批量生成比如一次生成 100 個不同尺寸的法蘭需要考慮模板化把常用零件做成模板只改參數(shù)隊列處理生成任務排隊避免內(nèi)存爆掉錯誤重試某個零件生成失敗時自動重試或跳過日志記錄每個零件的生成參數(shù)和結果都要記錄方便追溯我用 Python 的 concurrent.futures 做并行生成但要注意 OpenCASCADE 不是線程安全的每個線程需要獨立的建模上下文。簡單做法是用多進程每個進程獨立初始化。9. 我踩過的坑與實操心得第一個坑是單位不統(tǒng)一。前面提過一批零件默認用了 inch導出 STEP 后進仿真尺寸差了 25.4 倍。后來我在解析層強制要求標注單位沒標注的一律按 mm 處理并在輸出中打印警告。第二個坑是布爾運算的順序。有一次先倒了角再打孔結果孔切掉了倒角的一部分邊緣出現(xiàn)了薄壁碎片。后來改成先打孔再倒角問題消失。這個順序不是絕對的但大多數(shù)情況下先減后倒更安全。第三個坑是URDF 的碰撞體簡化。一開始我直接把精確模型作為碰撞體導出結果仿真時碰撞檢測特別慢。后來改成用簡單幾何近似碰撞體視覺體用精確模型仿真速度提升了一個數(shù)量級。第四個坑是G-code 的模型封閉性。有一個模型因為布爾運算出了一個小破面肉眼看不出來但切片時路徑直接亂掉。后來每次導出 STL 之前都跑一遍封閉性檢查不通過就不切片。提示text-to-cad 的產(chǎn)出物最終是要給下游用的所以下游能讀、能用比生成得快更重要。寧可多花幾秒做有效性檢查也不要生成一堆廢文件。10. 這個方向后續(xù)還能怎么擴展目前我跑通的鏈路是“文字 → 結構化參數(shù) → 單零件 STEP/URDF/G-code”。下一步想嘗試的是從裝配描述直接生成裝配體比如“一個減速箱輸入軸和輸出軸平行中心距 80”系統(tǒng)自動推斷齒輪參數(shù)和箱體尺寸。這個難度不小因為涉及工程計算的推斷不只是幾何生成。另一個方向是反向鏈路從現(xiàn)有 STEP 文件反推出結構化描述再用文字描述去修改。這樣就能實現(xiàn)“打開一個現(xiàn)有模型說一句‘把孔徑改成 12’模型自動更新”。這個在參數(shù)化設計場景下很有用技術上也比正向生成容易一些因為幾何信息是現(xiàn)成的只需要做特征識別和參數(shù)提取。還有一個實用擴展是和現(xiàn)有 CAD 軟件集成。比如生成 STEP 之后自動導入中望 CAD 或 FreeCAD 做后續(xù)處理或者把 G-code 直接推送到打印機。這些集成本身不難主要是接口適配的工作量。我個人在實際操作中的體會是text-to-cad 目前最適合的場景是參數(shù)化變體生成而不是從零開始的全新設計。你給它一個明確的參數(shù)化模板它能快速生成大量變體但你讓它憑空設計一個復雜零件它還很吃力。把預期放在“提效工具”而不是“替代設計師”心態(tài)會好很多落地也會順利很多。