text-to-cad 实战:从自然语言到三维模型的完整链路解析 1. 从一段文字到三维模型text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词很多人脑子里冒出来的画面可能是对着电脑敲一句“给我画一个法兰盘”然后屏幕上就自动出现一个带螺栓孔的 STEP 模型。这个画面放在三五年前还属于科幻范畴但放到今天它已经是一条可以跑通的技术链路了。text-to-cad 本质上做的事情是把自然语言描述翻译成参数化几何数据再输出成 CAD 领域通用的文件格式比如 STEP、GLB、STL 这些。它解决的核心痛点很直接传统 CAD 建模需要人手动拉伸、旋转、倒角、打孔一个中等复杂度的零件熟手也要画上半小时到几小时而 text-to-cad 想做的是让“描述需求”这件事本身变成建模动作。我接触这个方向是从一个很具体的场景开始的。当时手上有一批非标件需要快速出概念模型用来做装配干涉检查精度要求不高但数量多、迭代快。用传统方式一个个画时间成本完全扛不住。于是我开始研究能不能用代码和语言模型把这条路自动化。实测下来text-to-cad 目前最适合的定位是概念设计阶段的快速原型生成而不是替代精密工程出图。它能帮你把想法在几十秒内变成一个可视化的三维实体导出 STL 直接丢进切片软件或者导出 STEP 进入后续的 CAD 流程做二次编辑。这篇文章适合几类人看一是做机械设计、工业设计想了解 AI 辅助建模边界在哪的工程师二是做 3D 打印、创客项目需要快速把文字想法变成可打印模型的玩家三是做软件开发想在自己的工具链里集成 text-to-cad 能力的程序员。不管你用的是中望 CAD、SolidWorks 还是 FreeCAD底层的几何逻辑是相通的。我会把整条链路的思路、关键参数、踩过的坑都摊开讲尽量让你看完就能自己搭一套跑起来。2. 整体架构设计为什么是“语言模型 几何内核”这套组合2.1 核心链路的四个阶段拆解text-to-cad 的完整链路我把它拆成四个阶段语义解析、参数提取、几何构建、格式导出。这四个阶段环环相扣任何一个环节出问题最终模型都会跑偏。语义解析阶段输入是一句人话比如“一个外径 80mm、内径 40mm、厚度 10mm 的圆环边缘倒角 2mm”。这一步要做的不是简单关键词匹配而是理解尺寸之间的从属关系——外径和内径是同心圆厚度是拉伸方向倒角作用在边缘。早期我用正则表达式硬匹配遇到稍微绕一点的描述就崩后来换成语言模型做结构化抽取稳定性提升非常明显。参数提取阶段是把解析结果映射成一组数值和约束。这里有个关键决策用参数化建模还是用网格建模。参数化建模输出的是带特征的实体可以后续修改尺寸网格建模输出的是三角面片改起来就麻烦了。text-to-cad 如果目标是工程可用必须走参数化路线这也是为什么 STEP 格式比 STL 更有价值——STEP 保留了几何拓扑关系STL 只是一堆三角形。几何构建阶段是真正调用几何内核的地方。常见的选择有 OpenCASCADE、CGAL、Manifold 这几套。OpenCASCADE 功能最全STEP 支持最好但学习曲线陡Manifold 轻量、速度快适合做布尔运算密集的场景但格式支持少一些。我最终选的是 OpenCASCADE原因后面细说。格式导出阶段就是把内核里的实体序列化成文件。STEP 用于工程交换GLB 用于网页预览和渲染STL 用于 3D 打印。三种格式面向三种下游缺一不可。2.2 为什么选 OpenCASCADE 而不是自己造轮子有人可能会想不就是画几个圆柱、方块、圆环吗自己用 numpy 算顶点坐标不就行了。我一开始也这么天真过。手写一个立方体确实不难八个顶点十二条边闭着眼睛都能写。但一旦涉及布尔运算——比如从一个大圆柱里挖掉一个小圆柱——事情就复杂了。两个实体相交的边界需要重新计算产生新的边和面还要保证法线方向一致、没有自相交。这套东西自己实现没有几个月根本做不稳。OpenCASCADE 是工业级的几何内核布尔运算、倒角、圆角、抽壳这些操作都是现成的而且经过了几十年的工程验证。它的 Python 绑定 pythonocc 用起来也还算顺手。代价是包比较大安装稍微折腾但在概念设计这个场景下稳定性比轻量更重要。我试过用 Manifold 做快速原型简单形状确实快但一到复杂倒角就出各种退化面最后还是回到 OpenCASCADE。提示如果你只是想做网页端轻量预览Manifold 加 three.js 的组合足够但如果要导出 STEP 进入正式 CAD 流程OpenCASCADE 是绕不开的选择。2.3 语言模型在链路中的角色定位语言模型在这条链路里只做一件事把非结构化文本变成结构化 JSON。它不负责几何计算也不负责文件读写。这个边界一定要划清楚否则很容易陷入“让模型直接输出坐标点”的误区。我试过让模型直接生成顶点列表结果它编造出来的坐标经常自相矛盾围不成一个封闭实体。正确的做法是定义一套中间表示比如{ type: ring, outer_diameter: 80, inner_diameter: 40, thickness: 10, chamfer: 2, unit: mm }模型只负责把“外径八十内径四十厚十毫米倒角两毫米的圆环”翻译成上面这段 JSON。几何构建由确定性代码完成这样每次同样的输入输出完全一致可复现、可测试。这个设计决策是我踩了无数次坑之后才定下来的早期让模型自由发挥结果同一个描述跑两次出来两个不同的模型根本没法用。3. 核心细节解析从文字到实体的关键环节3.1 语义解析的粒度控制与容错设计语义解析最难的地方不是理解标准描述而是处理模糊、口语化、有歧义的输入。用户不会像写工程图纸那样严谨他可能说“来个粗一点的管子”也可能说“直径大概五厘米左右”。这时候如果解析器太死板直接报错体验就很差。我的做法是引入默认值兜底和置信度标注。比如“粗一点”映射到默认壁厚 3mm同时在输出 JSON 里加一个confidence字段标记哪些参数是明确给出的哪些是猜的。后续几何构建时低置信度的参数可以用不同颜色高亮显示提醒用户确认。这个设计在实际使用中非常有用用户一眼就能看出哪些地方需要自己核对。另一个关键是单位处理。CAD 领域毫米和米混用是常态有人习惯说“10 个厚”有人习惯说“0.01 米”。解析器必须统一到毫米并且在 JSON 里显式记录单位。我遇到过因为单位没统一一个本该 80mm 的圆环变成了 80m导出 STL 后切片软件直接崩溃的情况。3.2 几何构建中的参数校验与自动修正拿到结构化参数后不能直接丢给几何内核必须先做一轮参数校验。常见的非法情况包括内径大于外径、厚度为零或负数、倒角半径超过最小边长的二分之一。这些如果不在构建前拦住内核会抛出各种难以理解的异常排查起来很痛苦。我整理了一份校验规则表放在几何构建之前执行校验项规则处理方式直径关系内径 外径自动交换或报错厚度 0 且 外径超限时截断并警告倒角 最小壁厚 / 2自动缩小到安全值单位必须为 mm/m统一转换为 mm孔位不超出实体边界裁剪或报错这张表看起来简单但每一条都是实际踩坑总结出来的。特别是倒角那条倒角半径设得太大OpenCASCADE 会直接生成失败而且报错信息非常隐晦新手根本看不懂。提前校验能省掉大量调试时间。3.3 三种导出格式的适用场景与参数选择STEP、GLB、STL 这三种格式很多人搞不清楚什么时候该用哪个。我用一张表说清楚格式本质适用场景关键参数STEP边界表示实体工程交换、二次编辑精度模式、单位GLB三角网格 材质网页预览、渲染网格细分精度STL纯三角网格3D 打印、切片弦高、角度公差STEP 导出时精度模式选“精确”还是“近似”影响很大。精确模式保留解析曲面文件小、质量高但某些下游软件兼容性差近似模式把所有曲面三角化兼容性好但文件大。我的经验是如果下游是主流 CAD 软件用精确模式如果是自研工具或网页端用近似模式。STL 导出的弦高参数直接决定打印质量。弦高设得小曲面更光滑但三角形数量暴增文件可能从几 MB 涨到几百 MB。我一般根据模型尺寸动态设置小零件用 0.01mm大零件用 0.1mm。角度公差同理默认 0.5 弧度对大多数场景够用追求极致光滑可以调到 0.1。注意GLB 导出时如果模型有内部空腔记得开启双面渲染否则从某些角度看会“透”过去看起来像破面。4. 实操过程从零搭一套可跑的 text-to-cad 流程4.1 环境准备与依赖安装先把环境搭起来。我用的技术栈是 Python pythonocc 一个语言模型 API。pythonocc 的安装是第一个坎不同操作系统差异很大。在 Linux 上相对简单conda install -c conda-forge pythonocc-coreWindows 上建议用 conda 而不是 pip因为 pip 版本的 pythonocc 经常缺 DLL。macOS 上 conda 同样是最省心的路径。装完之后跑一句from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder验证一下不报错就说明环境 OK。语言模型这块我用的是通用的文本生成接口输入提示词输出 JSON。提示词的设计很关键我后面单独讲。文件读写用标准库就够STEP 和 STL 的导出 pythonocc 都有现成接口GLB 需要额外装一个 trimesh 做转换。4.2 提示词工程让模型稳定输出结构化 JSON提示词写得好不好直接决定解析成功率。我试过很多版本最终稳定下来的结构是这样的你是一个 CAD 参数解析器。用户会用自然语言描述一个三维零件。 请把描述转换成如下 JSON 格式只输出 JSON不要任何解释 { type: 零件类型, params: { ... }, unit: mm, confidence: { ... } } 支持的零件类型cylinder, box, ring, sphere, plate_with_holes。 如果某个参数未给出使用合理默认值并在 confidence 中标记为 low。这个提示词有几个要点一是限定输出格式明确说只输出 JSON二是给出类型白名单防止模型自由发挥造出无法处理的类型三是要求标注置信度方便后续校验。实测下来加了这几条之后解析成功率从六成提升到九成以上。对于复杂零件比如“一个 100x60x10 的板四角各有一个直径 6 的通孔孔中心距边缘 10mm”模型也能正确解析成 plate_with_holes 类型参数完整。这种描述如果靠正则基本没戏。4.3 几何构建代码的核心逻辑几何构建这块我以圆环为例展示核心逻辑。圆环的本质是两个同心圆柱做布尔差集from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Ax2, gp_Pnt, gp_Dir def make_ring(outer_d, inner_d, thickness): axis gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)) outer BRepPrimAPI_MakeCylinder(axis, outer_d/2, thickness).Shape() inner BRepPrimAPI_MakeCylinder(axis, inner_d/2, thickness).Shape() ring BRepAlgoAPI_Cut(outer, inner).Shape() return ring这段代码看起来简单但有几个细节要注意。坐标轴的方向决定拉伸方向如果设成 (1,0,0)圆环就横过来了。布尔运算的顺序不能反大减小才是挖孔小减大得到的是空集。还有布尔运算之后最好做一次ShapeUpgrade_UnifySameDomain把共面的碎片合并否则导出的 STEP 里会有大量冗余面下游软件打开很卡。倒角操作在布尔运算之后做from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeChamfer from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_EDGE chamfer BRepFilletAPI_MakeChamfer(ring) explorer TopExp_Explorer(ring, TopAbs_EDGE) while explorer.More(): chamfer.Add(2.0, explorer.Current()) explorer.Next() result chamfer.Shape()这里遍历所有边加倒角实际项目中通常只对特定边操作需要根据边的几何特征筛选。全部加倒角在圆环上效果还行但在复杂零件上会把不该倒的边也倒了看起来很奇怪。4.4 导出环节的参数配置与实测数据导出 STEP 的代码from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs writer STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write(output.step)STEPControl_AsIs表示按原样导出保留解析曲面。如果换成STEPControl_ManifoldSolidBrep会做一些简化。实测一个外径 80 内径 40 厚 10 的圆环精确模式导出文件约 12KB近似模式约 45KB。文件大小差异在简单零件上不明显但复杂装配体上能差十倍。STL 导出from OCC.Core.StlAPI import StlAPI_Writer from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh BRepMesh_IncrementalMesh(shape, 0.01, False, 0.5, True) writer StlAPI_Writer() writer.Write(shape, output.stl)BRepMesh_IncrementalMesh的第一个参数是弦高第二个是角度公差。弦高 0.01mm 对 80mm 的圆环来说圆周被分成约 250 段表面非常光滑。如果调到 0.1mm段数降到 80 左右肉眼在屏幕上几乎看不出差别但文件小很多。这个权衡要根据实际用途来定。GLB 导出需要先把 OCC 的实体转成 trimesh 的网格再导出。这一步稍微绕但网上有现成的转换脚本可以参考。转换过程中要注意坐标系OCC 默认 Z 轴向上GLB 常用 Y 轴向上需要做一次旋转。5. 常见问题与排查技巧实录5.1 解析失败与模型跑偏的典型场景实际用下来问题主要集中在几个地方。第一类是描述过于抽象比如“画个好看的东西”这种模型也无能为力只能返回默认的方块。第二类是尺寸矛盾比如“直径 10mm 的管子壁厚 8mm”内径就变成负数了校验层会拦截并提示。第三类是类型识别错误把“圆盘”识别成“圆柱”虽然都是圆形但一个扁一个高差别很大。针对类型识别我在提示词里加了几个易混淆类型的区分说明比如“圆盘是厚度远小于直径的圆柱圆柱是厚度和直径相当的圆形实体”。加了这段之后误判率明显下降。5.2 几何内核报错的排查思路OpenCASCADE 的报错信息出了名的难懂经常是一串十六进制地址加一句“Standard_Failure”。我的排查套路是先看是不是参数非法用校验层拦住再看是不是布尔运算失败尝试调整运算顺序或加一个微小的偏移最后看是不是内存问题大模型有时候会爆内存。布尔运算失败最常见的原因是两个实体恰好相切比如内径刚好等于外径或者孔刚好在边缘上。这种临界情况内核处理不好解决办法是加一个 0.001mm 的容差让它们要么完全相交要么完全分离。5.3 导出文件在下游软件打不开的原因STEP 文件在 SolidWorks 里打不开十有八九是单位设置不一致。OCC 默认单位是毫米但有些导出设置会写成米下游软件按米解析模型就变得巨大或极小。检查方法很简单用文本编辑器打开 STEP 文件看头部有没有SI_UNIT和MILLI字样。STL 文件在切片软件里显示破面通常是法线方向不一致。OCC 导出的 STL 默认法线朝外但如果模型有内部空腔内壁的法线可能朝内切片软件就分不清哪里是实体哪里是空气。解决办法是在导出前统一法线方向或者用切片软件的修复功能自动处理。GLB 在网页端加载后颜色不对多半是材质丢失。GLB 支持 PBR 材质但 OCC 转出来的网格默认没有材质信息需要在 trimesh 里手动指定一个基础材质。5.4 性能优化从几十秒到几秒的提速经验早期我的流程跑一个中等零件要三十多秒主要时间花在语言模型调用和几何构建上。优化之后降到五秒以内几个关键改动缓存解析结果相同的描述直接返回缓存的 JSON省掉模型调用。并行构建多个零件同时构建时用多进程OCC 的 GIL 限制用多进程绕开。降低网格精度预览用低精度导出用高精度分两档。延迟倒角倒角操作最耗时放到最后做且只对必要边做。这几条改下来体验提升非常明显。特别是缓存那条实际使用中大量描述是重复或相似的缓存命中率能到四成以上。5.5 常见问题速查表现象可能原因排查方法解决方式模型尺寸离谱单位不一致检查 JSON 的 unit 字段统一转 mm布尔运算失败实体相切打印两个实体的包围盒加 0.001mm 容差STEP 打不开精度模式不兼容换近似模式重导用 STEPControl_ManifoldSolidBrepSTL 破面法线不一致在切片软件里看导出前统一法线倒角生成失败半径过大检查最小边长自动缩小到边长一半解析结果为空描述太抽象看模型原始输出补充默认值兜底导出文件巨大网格精度过高看三角形数量调大弦高参数GLB 颜色丢失无材质信息检查 trimesh 材质手动指定基础材质这张表我贴在工位上遇到问题先查表八成能直接定位。剩下两成再深入调试。6. 这套流程还能怎么扩展跑通基础链路之后我陆续加了一些扩展能力实用性提升不少。一个是批量生成读一个 CSV每行一个描述批量出模型适合做系列化零件的概念设计。另一个是参数化模板把常用零件做成模板用户只填关键尺寸减少模型解析的不确定性。还有一个是版本对比同一个描述生成多个版本并排显示方便挑选。再往深了走可以接约束求解器让用户用“A 面平行于 B 面”这种关系式描述而不是死板的尺寸。这块我还在摸索目前用语言模型做关系抽取再映射到 OCC 的约束接口效果还不稳定但方向是对的。最后分享一个我踩过的小坑导出 STL 时如果模型有薄壁结构弦高设得太大会导致薄壁消失打印出来是个破洞。解决办法是对薄壁区域单独加密网格或者干脆把弦高设小一档。这个坑我是在打印一个壁厚 1mm 的盒子时发现的切片预览里看着好好的打出来侧面全是缝排查了半天才定位到弦高参数上。