1. 从一段文字到三维实体:text-to-cad 到底在解决什么问题
第一次听到 "text-to-cad" 这个词,很多人脑子里浮现的画面大概是:对着电脑敲一句"给我画一个法兰盘",然后屏幕上就自动长出一个三维模型。这个想象不算离谱,但真正落地的时候,它解决的问题比"自动画图"要具体得多,也琐碎得多。
text-to-cad 的核心链路,是把自然语言描述转换成结构化的 CAD 几何数据,最终输出成 STEP、GLB、STL 这类可以被下游软件直接读取的文件格式。它要解决的根本痛点,是"设计意图"和"几何表达"之间的那道鸿沟。一个懂工艺的工程师能清楚说出"外径 80、内径 40、厚度 12、带四个均布 M6 沉头孔"这样的需求,但要把这句话变成一份能加工、能装配的模型,中间要经过草图、约束、拉伸、阵列、倒角等一长串操作。text-to-cad 想做的,就是把这段重复劳动压缩掉。
它适合谁?我观察下来主要是三类人。第一类是做参数化零件批量生成的工程师,比如标准件库、钣金件、管接头这类结构高度重复、只是尺寸不同的零件,用文字驱动比手动画快一个数量级。第二类是做 AI 应用集成的开发者,他们需要一个能把语言模型输出接到几何内核上的中间层。第三类是做快速原型和概念验证的产品经理或创客,手边没有 CAD 基础,但想快速拿到一个能 3D 打印的 STL 去验证外形。
需要先泼一盆冷水:text-to-cad 目前不是"一句话生成任意复杂装配体"的魔法。它能稳定搞定的,是参数明确、拓扑结构相对固定的零件。你让它生成一个带内部流道的涡轮叶片,它大概率会给你一堆破面。但你说"生成一个 100×60×5 的带圆角矩形板,四角各一个直径 4 的通孔",它能给你一个干净利落的 STEP。认清这个边界,后面的所有工作才有意义。
关键词里出现的 STEP、GLB、STL 三个格式,其实对应了三条不同的下游路径,这个后面会专门拆开讲,因为选错格式是新手最容易踩的第一个坑。
2. 拆解 text-to-cad 的技术链路:语言是怎么变成几何的
2.1 从自然语言到结构化参数:这一步决定了成败
整条链路里,最容易被低估、也最容易翻车的一环,是语义解析。很多人以为难点在三维建模,其实真正难的是把"一个带四个孔的板"这种模糊描述,翻译成机器能执行的精确参数。
这里通常有两种做法。一种是基于大语言模型的函数调用(function calling),让模型直接输出 JSON 格式的参数,比如:
{ "shape": "plate", "length": 100, "width": 60, "thickness": 5, "fillet_radius": 3, "holes": [ {"type": "through", "diameter": 4, "positions": [[10,10],[90,10],[10,50],[90,50]]} ] }另一种是基于模板的槽位填充,预先定义好零件模板,让模型只负责把用户的话映射到槽位上。前者灵活但容易产生幻觉参数,后者稳定但覆盖面窄。
我的经验是:生产环境里优先用模板 + 函数调用的混合方案。纯自由生成在 demo 里很惊艳,但一旦用户说"孔稍微往里面挪一点",模型可能给你编出一个根本不存在的坐标。而模板方案虽然死板,但每个参数都有明确的取值范围和默认值,出错时能定位到具体是哪个槽位解析错了。
提示:解析阶段一定要做单位归一化。用户说"5 毫米"和"5mm"和"5"要统一处理,否则后面几何内核会因为单位混乱生成一个放大 25.4 倍的怪物。
2.2 几何内核的选择:为什么绕不开 OpenCASCADE
参数拿到之后,就要交给几何内核去"造形"。这个领域里,OpenCASCADE(简称 OCCT)是开源方案里绕不开的存在。它提供了 B-Rep(边界表示)建模能力,能生成真正意义上的实体,而不是一堆三角面片。
为什么强调 B-Rep?因为 STEP 格式本质上就是 B-Rep 的文本化表达。你用三角网格(mesh)方式建出来的模型,导出 STEP 时会退化成一大堆平面片,下游的 CAM 软件根本没法识别出"这是一个圆柱面",加工路径就无从谈起。而 OCCT 建出来的是带解析曲面的实体,圆柱就是圆柱,倒角就是倒角,导出 STEP 后信息完整。
除了 OCCT,还有几个选择:
| 内核 | 授权 | 特点 | 适用场景 |
|---|---|---|---|
| OpenCASCADE | LGPL | 功能全,B-Rep 强,社区活跃 | 通用参数化建模 |
| CGAL | GPL/商业 | 计算几何算法强 | 布尔运算、网格处理 |
| Manifold | Apache 2.0 | 专注流形网格,速度快 | 3D 打印方向 |
| 商业内核 | 收费 | 稳定、有支持 | 工业级产品 |
如果目标是输出 STEP,OCCT 基本是唯一现实的开源选择。如果只输出 STL 给 3D 打印,Manifold 这类网格内核反而更轻快,因为它不需要维护复杂的拓扑结构。
2.3 从实体到文件:STEP、GLB、STL 三条出口
模型建好之后,导出格式的选择直接决定了这个文件能干什么。这三个格式经常被混用,但它们的定位完全不同。
STEP是工业界的通用交换格式,存的是精确几何(NURBS 曲面、解析曲面),文件里带着完整的拓扑关系。它的优点是精度无损、能被几乎所有 CAD 软件读取、能反向编辑。缺点是文件大、解析慢、不适合网页展示。关键词里那个"sw 中 stl 转 stp"的搜索,恰恰说明很多人是从网格格式往回倒,但 STL 转 STEP 本质上是有损的——三角面片没法还原成解析曲面,转出来的 STEP 里全是平面,只能看不能用。
STL是 3D 打印的事实标准,只存三角面片,没有单位、没有颜色、没有拓扑。它的优点是简单、通用、切片软件都认。缺点是精度靠网格密度堆,文件容易巨大,而且丢失了所有设计意图。
GLB是 glTF 的二进制版本,主打网页和实时渲染。它带材质、带层级、带动画,适合在浏览器里做交互展示。但它同样不是精确几何,做展示可以,做加工不行。
所以选型逻辑很清晰:要加工、要后续编辑,走 STEP;要 3D 打印,走 STL;要在网页里给人看,走 GLB。一个成熟的 text-to-cad 系统,应该支持同一份内部模型导出多种格式,而不是只认一种。
3. 动手搭一个最小可用的 text-to-cad 流程
3.1 环境准备:Python + OCCT 的坑比想象中多
理论讲完,来点能跑的。我用的是 Python 生态里的cadquery,它是对 OCCT 的一层封装,写起来比直接调 OCCT 的 C++ 接口舒服太多。安装本身不复杂:
pip install cadquery但这里有个大坑:cadquery 依赖的 OCCT 版本和系统里已有的 OCCT 经常打架。如果你机器上装过 FreeCAD 或者别的 CAD 软件,它们的动态库路径可能被优先加载,导致 import 时报符号找不到。我的做法是用 conda 建独立环境,把依赖锁死:
conda create -n text2cad python=3.10 conda activate text2cad conda install -c conda-forge cadqueryconda-forge 的包会把 OCCT 一起装进环境目录,不会和系统库冲突。这一步看起来啰嗦,但能省掉后面几个小时的排查时间。
3.2 用一段代码跑通"文字到 STEP"
下面这段是我实际用过的简化版流程,把"解析参数"和"建模导出"串起来。为了聚焦,解析部分先用一个写死的字典模拟大模型的输出:
import cadquery as cq # 假设这是大模型解析出来的结构化参数 params = { "length": 100.0, "width": 60.0, "thickness": 5.0, "fillet_radius": 3.0, "holes": [ {"diameter": 4.0, "x": 10.0, "y": 10.0}, {"diameter": 4.0, "x": 90.0, "y": 10.0}, {"diameter": 4.0, "x": 10.0, "y": 50.0}, {"diameter": 4.0, "x": 90.0, "y": 50.0}, ], } # 1. 建基础板 plate = ( cq.Workplane("XY") .box(params["length"], params["width"], params["thickness"]) ) # 2. 四角倒圆角(先选竖直边) plate = plate.edges("|Z").fillet(params["fillet_radius"]) # 3. 打孔 for h in params["holes"]: # 把坐标从原点系平移到以板中心为原点 x = h["x"] - params["length"] / 2 y = h["y"] - params["width"] / 2 plate = ( plate.faces(">Z") .workplane() .center(x, y) .hole(h["diameter"]) ) # 4. 导出 cq.exporters.export(plate, "plate.step") cq.exporters.export(plate, "plate.stl")跑完这段,你会得到两个文件。用 FreeCAD 打开 STEP,能看到一个带圆角和四个通孔的板;把 STL 丢进切片软件,能直接打印。
3.3 坐标系的坑:为什么孔的位置总是偏
上面代码里有一行x = h["x"] - params["length"] / 2,这是很多人第一次写会漏掉的。cq.Workplane("XY").box()建出来的盒子,原点在几何中心,而用户描述孔位时,脑子里想的往往是"从左下角量 10 毫米"。这两个坐标系差了一个半长半宽。
我踩过这个坑:明明参数写的是 (10,10),导出来的孔却跑到了板子外面。排查了半天才发现是坐标系没对齐。解决办法有两个,要么像上面那样手动平移,要么在建板子的时候就用center把原点挪到角上。建议在解析层就统一约定:所有坐标以零件包围盒的左下角为原点,这样用户描述和代码实现是一致的,减少心智负担。
注意:倒圆角那一步,
edges("|Z")选的是平行于 Z 轴的边。如果你先打了孔再倒角,孔的内壁边也会被选中,结果就是孔口被莫名其妙地倒了一圈。顺序一定是先倒外轮廓,再打孔。
4. 实测中那些文档不会告诉你的坑
4.1 布尔运算失败:几何内核的"玄学"
做参数化建模,布尔运算是绕不开的。打孔是减运算,合并零件是加运算。但 OCCT 的布尔运算有个出了名的毛病:在某些临界情况下会失败或者产生破面。
典型场景:两个面刚好相切、孔壁和倒角面距离极近、薄壁件的壁厚小于内核的容差。这些情况下,内核会报一个BRep_API: command not done之类的错误,然后整个流程中断。
我的应对策略有三条。第一,给所有尺寸加一个最小间隔约束,比如孔边到零件边缘至少留 1 毫米,避免相切。第二,在布尔运算前后做几何检查,用plate.isValid()判断实体是否有效,无效就回退到上一个稳定状态。第三,对失败的操作做降级处理,比如倒角失败就跳过倒角,先保证主体能出来,而不是整个任务崩掉。
这里有个反直觉的点:容差不是越小越好。OCCT 默认的容差是 1e-7 米级别,对于毫米单位的模型来说已经足够。如果你手动把容差调得更小,反而会让布尔运算更容易失败,因为浮点误差被放大了。
4.2 大模型幻觉参数:怎么防住"凭空多出来的孔"
用大模型解析参数,最头疼的是它有时候会"自作主张"。用户说"一个带孔的板",它可能给你生成四个孔,还贴心地均布了。用户说"厚度 5",它可能理解成"半径 5"。
防幻觉的核心思路是约束 + 校验。约束是在 prompt 里明确告诉模型:只输出 JSON,不要解释,缺失的参数用 null,不要自己编。校验是在拿到 JSON 之后,用一套规则检查:尺寸是否在合理范围、孔位是否在零件内部、孔的数量是否和描述一致。
我还会加一层回显确认。系统把解析出来的参数用自然语言复述一遍给用户:"您要的是一个 100×60×5 的板,四角各一个直径 4 的通孔,对吗?"用户确认后再建模。这一步看起来多余,但能拦掉大部分因为解析错误导致的无效建模,省下的算力比多问一句的成本高得多。
4.3 单位与精度:一个 25.4 倍的惨案
前面提过单位问题,这里展开说。CAD 领域里,毫米和英寸的混用是事故高发区。STEP 文件头里会声明单位,但 STL 不会——STL 就是个纯数字的三角面片集合,切片软件默认按毫米处理。如果你的模型是按英寸建的,导出 STL 后会被当成毫米,结果就是零件缩小到 1/25.4,打印出来只有指甲盖大。
我的做法是在系统内部统一用毫米,所有输入在解析阶段就转换好。如果用户输入的是英寸,立刻乘以 25.4。导出时,STEP 明确写入单位声明,STL 则在文件名或元数据里标注单位,避免下游误读。
精度方面,STL 的网格密度要按用途调。做展示可以粗一点,做打印要细一点。cadquery 导出 STL 时可以指定公差:
cq.exporters.export(plate, "plate.stl", tolerance=0.01, angularTolerance=0.1)tolerance是线性偏差,angularTolerance是角度偏差。数值越小网格越密、文件越大。0.01 毫米对于大多数 3D 打印已经足够,再细就是浪费。
5. 格式转换与下游衔接:STEP、STL、GLB 怎么选怎么转
5.1 为什么 STL 转 STEP 是个伪需求
热搜词里有个"sw 中 stl 转 stp",说明很多人想干这件事。但我要说清楚:STL 转 STEP 在几何上是不可逆的。STL 存的是三角面片,一个圆柱面被离散成几十个平面三角形。转成 STEP 时,这些三角形只能被识别成一个个平面,而不是一个圆柱面。结果就是文件体积暴涨、曲面全是棱、CAM 软件无法识别特征。
那为什么还有人要转?通常是因为他们只有 STL,但下游流程要求 STEP。这种情况下,正确的做法不是"转换",而是重新建模——把 STL 导入作为参考,在 CAD 里重新画一遍。或者用逆向工程软件做曲面拟合,但那已经是另一个专业领域了。
所以 text-to-cad 系统的价值在这里就体现出来了:它从一开始就生成精确几何,根本不需要从 STL 往回倒。这也是为什么内部一定要用 B-Rep 内核,而不是直接生成网格。
5.2 GLB 在网页展示里的独特价值
如果 text-to-cad 的产物要放到网页上给客户看,GLB 是最优解。它体积小、加载快、支持 PBR 材质,three.js 这类库能直接渲染。相比之下,STEP 需要专门的 Web 查看器,STL 没有材质和层级。
从 cadquery 的实体导出 GLB,通常要先转成网格再导出。cadquery 本身对 GLB 的支持有限,实践中更常见的做法是导出 STL 或 OBJ,再用trimesh或assimp转成 GLB:
import trimesh mesh = trimesh.load("plate.stl") mesh.export("plate.glb")这样转出来的 GLB 保留了网格,能在网页里流畅旋转缩放。但记住,GLB 只用于展示,不要拿它去做加工。
5.3 三种格式的选型决策表
把上面的讨论浓缩成一张表,实际项目里照着选就行:
| 需求 | 推荐格式 | 理由 | 注意事项 |
|---|---|---|---|
| 后续 CAD 编辑 | STEP | 精确几何,拓扑完整 | 文件较大,解析慢 |
| CNC 加工 / CAM | STEP | CAM 需要识别曲面特征 | 确保单位声明正确 |
| 3D 打印 | STL | 切片软件通用 | 控制网格密度,标注单位 |
| 网页展示 | GLB | 加载快,带材质 | 仅展示,不可加工 |
| 存档备份 | STEP | 信息最完整 | 定期校验文件有效性 |
6. 把 text-to-cad 用起来的几个实战思路
6.1 标准件库的批量生成
这是我觉得 text-to-cad 最容易出成绩的场景。机械设计里大量使用标准件:螺栓、螺母、垫圈、轴承、型材。这些零件的几何结构固定,只是尺寸参数不同。传统做法是一个个画,或者从库里调。用 text-to-cad,你可以写一个批量脚本,读一张 Excel 参数表,一次性生成几百个 STEP 文件。
比如生成一系列不同规格的内六角螺栓,参数就是螺纹直径、长度、头径、头高。把这些参数喂给模板,循环生成,几分钟就能搞定一个系列。这种场景下,模板的复用率极高,投入产出比非常划算。
6.2 和现有 CAD 工作流对接
text-to-cad 不是要取代 CAD 软件,而是要嵌进现有流程。生成的 STEP 可以直接被 SolidWorks、中望 CAD、FreeCAD 等软件打开,继续做装配、出工程图、做仿真。
这里有个实用技巧:在 STEP 文件里写入自定义属性,比如零件编号、材料、生成时间。这样导入 CAD 后,这些信息会跟着零件走,方便做 BOM 统计。cadquery 支持给实体加属性,导出时会写进 STEP 的元数据里。
6.3 参数校验与用户反馈闭环
一个能用的系统,必须有反馈闭环。用户输入描述,系统生成模型,用户看到结果后可能会说"孔太大了"或者"再长一点"。这些反馈要能被系统捕获,转成参数调整,重新生成。
实现上,可以把每次生成的参数和用户的修改记录存下来,形成一个小型数据集。时间长了,你会发现某些参数经常被调整,说明解析层对这些描述的理解有偏差,可以针对性优化 prompt 或模板。这个闭环是 text-to-cad 从"能用"到"好用"的关键。
6.4 性能与并发的现实考量
如果系统要服务多个用户,性能就是个问题。OCCT 的建模是 CPU 密集型的,一个中等复杂度的零件可能要几百毫秒到几秒。并发请求多了,单机扛不住。
我的建议是把建模任务做成异步队列。用户提交描述后,系统返回一个任务 ID,后台 worker 慢慢建,建好了通知用户下载。这样前端不会卡死,后端也能控制并发数。worker 的数量按 CPU 核心数来定,一般留一两个核心给系统,剩下的都跑建模。
另外,相同参数的模型要缓存。很多用户会生成相似的零件,如果参数完全一致,直接返回缓存的文件,省掉重复计算。缓存 key 用参数的哈希值,简单有效。
7. 我对 text-to-cad 的一点个人判断
折腾了这段时间,我最大的体会是:text-to-cad 的价值不在于"替代人画图",而在于把设计意图的表达成本降下来。一个工程师脑子里想清楚一个零件,可能只要几十秒,但把它画出来要几分钟甚至更久。这中间的差距,就是 text-to-cad 的空间。
但它也有明确的天花板。越是需要创造性、需要反复权衡的复杂设计,越不适合交给它。它擅长的是那些"结构已知、只是参数变化"的重复劳动。认清这一点,就不会对它抱有不切实际的期待,也不会因为它做不了复杂装配体就否定它的价值。
最后分享一个我踩过的坑:不要一上来就追求端到端的全自动。我最初想做一个"输入一句话直接出模型"的系统,结果发现解析错误、建模失败、格式转换问题层层叠加,调试起来极其痛苦。后来改成半自动——系统解析参数后先给用户确认,确认无误再建模——稳定性立刻上了一个台阶。有时候,多一步人工确认,比追求全自动更靠谱。