戰(zhàn):從自然語言到三維模型的工程化落地)
1. 從一句話到三維模型text-to-cad 到底在解決什么問題第一次聽到 text-to-cad 這個詞我腦子里蹦出來的畫面是對著電腦敲一行字比如“一個 80x60x10mm 的法蘭盤中心開 30mm 通孔四角各一個 M6 沉頭孔”然后軟件直接吐出一個能用的三維模型文件。這個畫面在幾年前還屬于科幻范疇但現(xiàn)在已經(jīng)有一批工具和方案在往這個方向走了。text-to-cad 本質(zhì)上是一類把自然語言描述轉(zhuǎn)換成 CAD 幾何模型的技術(shù)方案集合它的輸出通常落在 STEP、GLB、STL 這幾種格式上分別對應(yīng)不同的下游用途。先說清楚這三種格式各自是干嘛的因?yàn)楹芏嗳艘簧蟻砭透慊?。STEP是工業(yè)界通用的邊界表示格式保留完整的實(shí)體幾何信息能精確表達(dá)圓柱面、倒角、螺紋孔這些特征適合后續(xù)在 SolidWorks、中望 CAD、FreeCAD 這類軟件里繼續(xù)編輯或者出工程圖。STL是三角網(wǎng)格格式只有表面的一層三角面片沒有實(shí)體拓?fù)潢P(guān)系適合 3D 打印切片但你要拿它去改尺寸就很痛苦。GLB是 glTF 的二進(jìn)制版本主打輕量和渲染友好適合在網(wǎng)頁、移動端做可視化展示比如產(chǎn)品配置器或者在線預(yù)覽。所以 text-to-cad 的輸出格式選擇直接決定了這個模型后面能干什么。那這個方向到底解決了誰的痛點(diǎn)我觀察下來主要是三類人。第一類是不會 CAD 但需要簡單模型的開發(fā)者比如做機(jī)器人仿真的工程師他需要一個機(jī)械臂的連桿模型來測試運(yùn)動學(xué)算法但讓他去學(xué) SolidWorks 畫圖時間成本太高。第二類是產(chǎn)品經(jīng)理或者設(shè)計(jì)師在早期概念階段想快速把腦子里的想法變成可視化的三維形狀用來跟團(tuán)隊(duì)溝通。第三類是做批量建模的團(tuán)隊(duì)比如參數(shù)化零件庫他們希望用文本描述來驅(qū)動模型生成而不是一個個手動畫。這三類人的共同點(diǎn)是他們要的是“夠用的幾何”而不是“完美的工程圖”。但這里必須潑一盆冷水。text-to-cad 目前的能力邊界非常清晰它能處理的是結(jié)構(gòu)化的、參數(shù)明確的、幾何相對簡單的零件比如方塊、圓柱、法蘭、支架、簡單的裝配體。你讓它生成一個復(fù)雜的自由曲面汽車外形或者一個包含幾百個零件的完整裝配它做不到至少現(xiàn)在做不到。所以我在實(shí)際使用中的策略是把 text-to-cad 當(dāng)成一個“快速原型生成器”而不是“替代 CAD 工程師的萬能工具”。這個定位想清楚了后面的技術(shù)選型和實(shí)操才不會跑偏。2. 技術(shù)路線拆解從文本到幾何到底走了哪幾步2.1 自然語言解析與參數(shù)抽取text-to-cad 的第一步永遠(yuǎn)是把人話變成機(jī)器能理解的參數(shù)。這一步看起來簡單實(shí)際上是最容易翻車的地方。比如你說“一個長 100 寬 50 高 20 的板子四個角倒 R5 圓角中間挖一個直徑 30 的孔”人類工程師一看就懂但機(jī)器需要把它拆成基體是長方體尺寸參數(shù)是 length100, width50, height20特征操作是 fillet(radius5) 作用于四條豎直邊cut_hole(diameter30) 作用于上表面中心。目前主流的做法有兩種。一種是基于規(guī)則模板的解析就是預(yù)先定義好一套語法模式比如“長 X 寬 Y 高 Z”對應(yīng)長方體“直徑 D 的孔”對應(yīng)圓柱切除。這種方式的優(yōu)點(diǎn)是穩(wěn)定可控缺點(diǎn)是只能處理預(yù)設(shè)好的句式稍微換個說法就識別不了。另一種是基于大語言模型的語義解析把自然語言丟給模型讓它輸出結(jié)構(gòu)化的 JSON 或者 DSL領(lǐng)域特定語言。這種方式靈活度高但存在幻覺問題模型可能編造出不存在的參數(shù)或者搞錯單位。我在實(shí)際項(xiàng)目里用的是混合方案先用大語言模型做初步的意圖識別和參數(shù)抽取然后用一套校驗(yàn)規(guī)則去檢查參數(shù)的合理性。比如模型輸出“直徑 30mm 的孔”我會檢查這個直徑是否小于基體的最小邊長如果大于就報錯讓用戶確認(rèn)。這個校驗(yàn)層非常關(guān)鍵因?yàn)榇笳Z言模型在數(shù)值推理上并不可靠你不加約束它真的會給你生成一個直徑 500mm 的孔打在一個 100mm 的板子上。注意單位問題是 text-to-cad 里最隱蔽的坑。用戶說“長 100”到底是毫米還是厘米我的做法是在系統(tǒng)提示里強(qiáng)制要求模型輸出時帶上單位并且在最終生成前做一次單位歸一化統(tǒng)一轉(zhuǎn)成毫米。這一步不做后面生成的模型尺寸可能差十倍。2.2 幾何內(nèi)核與建模操作參數(shù)抽出來之后就需要一個幾何內(nèi)核來執(zhí)行建模操作。這里的選擇直接決定了你能生成什么級別的模型。目前常見的幾何內(nèi)核有 OpenCASCADE、CGAL、Manifold 這幾類。OpenCASCADE 是工業(yè)級的內(nèi)核支持 B-rep 實(shí)體建模能精確表達(dá)曲面和實(shí)體布爾運(yùn)算FreeCAD 底層用的就是它。CGAL 更偏計(jì)算幾何適合做網(wǎng)格處理和布爾運(yùn)算但在 CAD 特征建模上不如 OpenCASCADE 完整。Manifold 是近幾年興起的輕量級內(nèi)核主打快速布爾運(yùn)算適合做實(shí)時預(yù)覽。如果你要輸出 STEP 格式基本只能選 OpenCASCADE 這條路因?yàn)?STEP 本身就是 B-rep 格式需要內(nèi)核支持精確幾何。如果你只要 STL 或者 GLB那可以用更輕量的方案比如用 trimesh 或者 vedo 這類 Python 庫直接做網(wǎng)格級別的布爾運(yùn)算。我個人的經(jīng)驗(yàn)是如果下游需要編輯或者出工程圖老老實(shí)實(shí)上 OpenCASCADE如果只是 3D 打印或者可視化用網(wǎng)格方案更省事。建模操作這一層核心就是幾個基本特征的組合拉伸extrude、旋轉(zhuǎn)revolve、掃掠sweep、放樣loft再加上布爾運(yùn)算union、cut、intersect和修飾特征fillet、chamfer。text-to-cad 系統(tǒng)需要把解析出來的參數(shù)映射到這些操作上。比如“法蘭盤”這個語義對應(yīng)的操作序列可能是畫一個圓拉伸成圓柱再畫一個同心圓拉伸切除成通孔再在四周陣列小圓孔。這個映射關(guān)系需要預(yù)先定義好或者讓大語言模型來生成操作序列。2.3 格式導(dǎo)出與下游對接模型建好之后導(dǎo)出這一步看似簡單其實(shí)也有不少講究。STEP 導(dǎo)出要注意精度設(shè)置公差太大會導(dǎo)致曲面破面公差太小文件體積爆炸。STL 導(dǎo)出要控制三角面片的密度太粗的話 3D 打印出來表面全是棱角太細(xì)的話文件幾百兆。GLB 導(dǎo)出要注意材質(zhì)和法線不然在網(wǎng)頁里顯示出來黑乎乎一片。我一般會提供多個精度檔位讓用戶選預(yù)覽級STL 面片數(shù)控制在 5 萬以內(nèi)、打印級面片數(shù) 20 萬左右、工程級STEP 精度 0.001mm。這樣用戶可以根據(jù)自己的用途來選不用每次都生成最高精度的版本。另外導(dǎo)出路徑和文件命名也要規(guī)范我習(xí)慣用{項(xiàng)目名}_{版本號}_{格式}.{擴(kuò)展名}的格式比如flange_v1.step避免文件多了之后找不到。3. 實(shí)操落地搭一個能跑的 text-to-cad 流水線3.1 環(huán)境準(zhǔn)備與依賴安裝假設(shè)你現(xiàn)在要自己搭一個 text-to-cad 的原型我推薦的技術(shù)棧是 Python OpenCASCADE通過 cadquery 或者 build123d 來調(diào)用 一個大語言模型 API。cadquery 是我用得最順手的它的 API 設(shè)計(jì)很接近人類的建模思維寫起來像在描述幾何而不是在調(diào)底層接口。安裝這塊cadquery 的依賴比較多建議用 conda 來管理環(huán)境pip 裝有時候會缺系統(tǒng)庫。核心依賴包括cadquery幾何建模、trimesh網(wǎng)格處理和 STL 導(dǎo)出、pygltflibGLB 導(dǎo)出、openai或者你用的其他模型 SDK語義解析。如果你不想用 conda也可以用 pip 裝 cadquery但需要確保系統(tǒng)里有 OpenCASCADE 的動態(tài)庫Linux 下一般是libocct-*系列包Windows 下 cadquery 會自帶預(yù)編譯的庫。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install trimesh pygltflib openai裝完之后跑一個簡單的測試確認(rèn)幾何內(nèi)核能正常工作import cadquery as cq result cq.Workplane(XY).box(80, 60, 10).faces(Z).workplane().hole(30) cq.exporters.export(result, test.step) print(STEP 導(dǎo)出成功)如果這一步能跑通說明基礎(chǔ)環(huán)境沒問題。接下來就是接大語言模型做語義解析。3.2 語義解析模塊的實(shí)現(xiàn)語義解析模塊的核心任務(wù)是輸入一句自然語言輸出一個結(jié)構(gòu)化的建模指令 JSON。我設(shè)計(jì)的 JSON schema 大概長這樣{ base_shape: box, dimensions: {length: 80, width: 60, height: 10, unit: mm}, features: [ {type: hole, diameter: 30, position: center, through: true}, {type: fillet, radius: 5, edges: vertical} ] }然后寫一個 prompt 模板把用戶的輸入和這個 schema 一起丟給模型。prompt 里要明確告訴模型只輸出 JSON不要輸出解釋文字所有尺寸必須帶單位如果某個參數(shù)不明確用 null 表示而不是瞎猜。我試過好幾個版本的 prompt最后發(fā)現(xiàn)給模型幾個 few-shot 示例效果最好比單純描述規(guī)則管用得多。拿到 JSON 之后寫一個解析器把它翻譯成 cadquery 的操作鏈。比如base_shape: box對應(yīng)cq.Workplane(XY).box(l, w, h)hole特征對應(yīng).faces(Z).workplane().hole(d)。這個映射層需要處理各種邊界情況比如孔的位置不在中心怎么辦倒角作用在哪些邊上多個特征之間的順序會不會互相干擾。實(shí)操心得特征順序很重要。如果你先倒角再打孔孔可能會切掉倒角的一部分導(dǎo)致結(jié)果跟預(yù)期不符。我的做法是在 JSON 里給每個特征加一個order字段讓模型或者用戶來指定執(zhí)行順序默認(rèn)按“先加料后減料”的原則排。3.3 幾何生成與格式導(dǎo)出幾何生成這一步cadquery 的 API 已經(jīng)封裝得很好了大部分操作就是幾行代碼的事。但有幾個地方需要特別注意。第一是工作平面的選擇cadquery 默認(rèn)在 XY 平面上操作如果你要在側(cè)面打孔需要先切換工作平面這個邏輯在自動生成時容易搞錯。第二是布爾運(yùn)算的容差有時候兩個面看起來貼合了但布爾運(yùn)算會報錯這時候需要手動設(shè)置一個小的容差值。導(dǎo)出 STEP 用cq.exporters.export(result, output.step)就行導(dǎo)出 STL 需要先轉(zhuǎn)成網(wǎng)格import trimesh # 從 cadquery 結(jié)果導(dǎo)出 STL cq.exporters.export(result, output.stl, tolerance0.01, angularTolerance0.1) # 或者用 trimesh 做后處理 mesh trimesh.load(output.stl) mesh.export(output.glb)GLB 導(dǎo)出我一般用 trimesh 來做因?yàn)樗鼘Σ馁|(zhì)和法線的處理比較完善。如果你需要帶顏色的模型可以在 cadquery 里給不同的面賦材質(zhì)然后導(dǎo)出時保留這些信息。不過 cadquery 的材質(zhì)系統(tǒng)比較簡單復(fù)雜材質(zhì)還是得在 Blender 或者 Three.js 里后期加。3.4 一個完整的端到端示例我把整個流程串起來用一個具體的例子走一遍。假設(shè)用戶輸入“生成一個 100x50x20mm 的安裝板四角各有一個直徑 6mm 的通孔孔中心距邊緣 10mm板子四個角倒 R3 圓角?!钡谝徊秸Z義解析輸出{ base_shape: box, dimensions: {length: 100, width: 50, height: 20, unit: mm}, features: [ {type: hole, diameter: 6, pattern: rectangular, count: 4, offset: 10, through: true}, {type: fillet, radius: 3, edges: vertical} ] }第二步生成 cadquery 代碼import cadquery as cq L, W, H 100, 50, 20 hole_d 6 offset 10 fillet_r 3 result ( cq.Workplane(XY) .box(L, W, H) .edges(|Z).fillet(fillet_r) .faces(Z).workplane() .rect(L - 2*offset, W - 2*offset, forConstructionTrue) .vertices() .hole(hole_d) ) cq.exporters.export(result, mounting_plate.step) cq.exporters.export(result, mounting_plate.stl, tolerance0.01)第三步檢查生成的模型。我一般會用 cadquery 的result.val().Volume()算一下體積跟手算的理論值對比。這個板子的理論體積是 100x50x20 減去四個孔的圓柱體積再減去倒角去掉的部分大概在 98000 立方毫米左右。如果算出來差太多說明某個特征沒生效或者參數(shù)錯了。4. 踩坑記錄與常見問題排查4.1 語義解析階段的典型問題問題一模型把“直徑”理解成“半徑”。這個坑我踩過好幾次。用戶說“打一個直徑 10 的孔”模型輸出radius: 10結(jié)果孔大了一倍。解決辦法是在 prompt 里明確區(qū)分 diameter 和 radius并且在 JSON schema 里強(qiáng)制要求字段名寫清楚是diameter還是radius不允許用模糊的size。問題二單位丟失。用戶說“長 100”模型輸出length: 100但沒帶單位。如果系統(tǒng)默認(rèn)是毫米那還好但如果用戶心里想的是厘米那就差了十倍。我的做法是在 prompt 里強(qiáng)制要求所有尺寸字段必須帶unit并且在解析后做一次單位檢查如果發(fā)現(xiàn)沒有單位的數(shù)值直接報錯讓用戶補(bǔ)充。問題三相對位置描述無法解析。用戶說“在板子的左上角打一個孔”這個“左上角”是相對于哪個坐標(biāo)系距離邊緣多少這種模糊的空間描述目前的大語言模型處理得不好。我的策略是對于位置描述要求用戶給出明確的偏移量比如“距左邊 10mm距上邊 15mm”如果用戶不給就默認(rèn)放在中心或者按均勻分布處理并在返回結(jié)果里注明“位置為默認(rèn)推斷請確認(rèn)”。4.2 幾何生成階段的典型問題問題四布爾運(yùn)算失敗。這是最常見的幾何錯誤通常是因?yàn)閮蓚€實(shí)體沒有正確相交或者面重合導(dǎo)致內(nèi)核無法判斷內(nèi)外。排查方法是先把兩個實(shí)體分別導(dǎo)出 STL 看一眼確認(rèn)它們的空間關(guān)系是對的。如果確實(shí)是面重合的問題可以給其中一個實(shí)體做一個微小的偏移比如 0.001mm通常就能解決。問題五倒角導(dǎo)致后續(xù)操作失敗。倒角會改變邊的拓?fù)浣Y(jié)構(gòu)如果后面的操作依賴于原來的邊就會找不到目標(biāo)。解決辦法是調(diào)整特征順序把倒角放在最后做。如果必須中間倒角那就需要在代碼里重新選擇邊而不是依賴之前的引用。問題六STL 導(dǎo)出后文件太大。默認(rèn)的導(dǎo)出精度可能過高導(dǎo)致一個簡單的零件 STL 文件幾十兆。調(diào)整tolerance和angularTolerance參數(shù)可以控制面片密度。我一般用tolerance0.05, angularTolerance0.3做預(yù)覽tolerance0.01, angularTolerance0.1做打印。4.3 常見問題速查表問題現(xiàn)象可能原因排查方法解決方案模型尺寸差十倍單位解析錯誤檢查 JSON 里的 unit 字段強(qiáng)制單位歸一化孔的位置偏了坐標(biāo)系理解錯誤導(dǎo)出 STL 目視檢查明確工作平面和偏移基準(zhǔn)布爾運(yùn)算報錯面重合或不相交分別導(dǎo)出兩個實(shí)體檢查加微小偏移或調(diào)整位置倒角后模型破面倒角半徑過大檢查倒角半徑是否超過邊長一半減小半徑或調(diào)整順序STL 文件過大導(dǎo)出精度過高查看文件體積調(diào)整 tolerance 參數(shù)GLB 顯示全黑法線方向錯誤在查看器里檢查法線重新計(jì)算法線或翻轉(zhuǎn)面避坑技巧每次生成模型后不要直接導(dǎo)出就完事。先在一個輕量級的查看器里過一眼確認(rèn)形狀、尺寸、特征位置都對再導(dǎo)出最終格式。我習(xí)慣用trimesh的show()快速預(yù)覽或者導(dǎo)出 GLB 丟到網(wǎng)頁查看器里看。這一步花不了幾秒鐘但能省掉很多返工。5. 輸出格式選擇與下游工具鏈對接5.1 STEP、STL、GLB 的適用場景對比這三種格式不是隨便選的選錯了下游工具可能根本打不開或者丟信息。我做了一個對比表方便你根據(jù)用途來選格式幾何類型可編輯性文件體積典型下游工具適用場景STEPB-rep 實(shí)體高保留特征中等SolidWorks、中望 CAD、FreeCAD工程編輯、出圖、CNC 加工STL三角網(wǎng)格低只能整體縮放大切片軟件、3D 打印3D 打印、快速成型GLB三角網(wǎng)格材質(zhì)低適合展示小網(wǎng)頁查看器、Three.js、Unity可視化、產(chǎn)品展示、AR如果你要做的是機(jī)械設(shè)計(jì)STEP 是唯一正確的選擇。如果你只是要 3D 打印一個外殼STL 就夠了。如果你要在網(wǎng)頁上給客戶看模型GLB 最合適。我見過有人拿 STL 去 SolidWorks 里編輯結(jié)果發(fā)現(xiàn)所有曲面都變成了小平面根本沒法標(biāo)注尺寸這就是格式選錯了。5.2 與常見 CAD 軟件的對接STEP 文件的兼容性整體很好但不同軟件對 STEP 版本的支持有差異。SolidWorks 對 AP214 和 AP242 支持都不錯中望 CAD 也沒問題FreeCAD 用的是 OpenCASCADE 內(nèi)核理論上最兼容。如果你導(dǎo)出的 STEP 在某個軟件里打不開先檢查一下 STEP 的版本用 AP214 通常最保險。STL 導(dǎo)入 3D 打印切片軟件比如 Cura、PrusaSlicer基本不會出問題但要注意單位。有些切片軟件默認(rèn)單位是毫米有些是英寸導(dǎo)入后先看一眼尺寸對不對。另外 STL 沒有實(shí)體信息如果模型有內(nèi)部空腔或者薄壁結(jié)構(gòu)切片軟件可能會誤判內(nèi)外這時候需要在切片軟件里手動修復(fù)。GLB 在網(wǎng)頁端的兼容性最好Three.js、Babylon.js、model-viewer 這些庫都原生支持。如果你要在 GLB 里保留材質(zhì)和顏色導(dǎo)出時確保勾選了材質(zhì)選項(xiàng)。cadquery 本身對材質(zhì)的支持有限復(fù)雜材質(zhì)建議在 Blender 里做好再導(dǎo)出 GLB。5.3 批量生成與自動化流水線如果你需要批量生成模型比如根據(jù)一個 CSV 參數(shù)表生成幾百個不同尺寸的法蘭盤那就需要把整個流程自動化。我的做法是寫一個 Python 腳本讀 CSV 的每一行把參數(shù)填進(jìn)模板調(diào)用 text-to-cad 流水線生成模型然后按規(guī)則命名導(dǎo)出。這個腳本可以掛到 CI/CD 上每次參數(shù)表更新就自動重新生成所有模型。import csv import cadquery as cq def generate_flange(od, id_, thickness, bolt_count, bolt_d): result ( cq.Workplane(XY) .circle(od/2).extrude(thickness) .faces(Z).workplane().hole(id_) .faces(Z).workplane() .polarArray(od/2 - 10, 0, 360, bolt_count) .hole(bolt_d) ) return result with open(flange_params.csv) as f: reader csv.DictReader(f) for row in reader: model generate_flange( float(row[od]), float(row[id]), float(row[thickness]), int(row[bolt_count]), float(row[bolt_d]) ) cq.exporters.export(model, fflange_{row[id]}.step)這個模式的好處是你只需要維護(hù)一張參數(shù)表模型生成完全自動化改一個參數(shù)重新跑一遍就行。對于做系列化產(chǎn)品的團(tuán)隊(duì)來說效率提升非常明顯。6. 能力邊界與后續(xù)擴(kuò)展方向6.1 目前做不到的事情text-to-cad 不是萬能的有些事你現(xiàn)在別指望它能做好。第一是復(fù)雜自由曲面比如汽車外形、渦輪葉片、人體工學(xué)手柄這些需要 NURBS 曲面建?;蛘呒?xì)分曲面建模自然語言很難描述清楚模型也生成不出來。第二是大型裝配體幾百個零件的裝配關(guān)系、配合約束、運(yùn)動干涉檢查這些目前還是得靠人工在 CAD 軟件里做。第三是工程標(biāo)注尺寸公差、形位公差、表面粗糙度這些制造信息text-to-cad 基本不涉及生成出來的模型只是幾何形狀離能加工的圖紙還有距離。所以我的建議是把 text-to-cad 用在概念設(shè)計(jì)階段和簡單零件批量生成這兩個場景不要試圖用它替代完整的 CAD 工作流。它的價值在于快速把想法變成可視化的三維形狀降低建模的門檻而不是取代專業(yè)工程師。6.2 可以繼續(xù)擴(kuò)展的方向如果你已經(jīng)搭好了一個基礎(chǔ)版本后面可以往幾個方向擴(kuò)展。第一是增加特征庫把常見的機(jī)械特征鍵槽、螺紋孔、中心孔、退刀槽做成模板用戶說“加一個 M6 螺紋孔”就能直接生成。第二是接入?yún)?shù)化約束求解器讓模型支持尺寸驅(qū)動改一個參數(shù)自動更新所有相關(guān)特征。第三是增加裝配功能支持多個零件之間的定位和配合比如“把軸承裝到軸上同心配合”。第四是對接 3D 打印切片軟件生成模型后直接調(diào)切片參數(shù)輸出可打印的 G-code。我個人最看好的方向是特征庫參數(shù)化這個組合。因?yàn)榇蟛糠謾C(jī)械零件都是標(biāo)準(zhǔn)特征的組合你把特征庫做豐富了覆蓋 80% 的常見零件類型再配合參數(shù)化驅(qū)動就能實(shí)現(xiàn)“說一句話生成一個可用零件”的效果。這個目標(biāo)比追求通用 AI 建模要現(xiàn)實(shí)得多也更有工程價值。最后分享一個小技巧如果你在調(diào) prompt 的時候發(fā)現(xiàn)模型總是理解錯某些術(shù)語不要反復(fù)改 prompt直接把那個術(shù)語加到 few-shot 示例里。比如模型總是把“沉頭孔”理解成“通孔”你就在示例里加一個沉頭孔的例子一次就能糾正過來。這比在 prompt 里寫一大段解釋管用得多。