☰
text-to-cad 实战:从自然语言到 STEP/STL/GLB 的工程化落地
2026/10/26 23:51:35 网站建设 项目流程

1. 从一句话到三维模型:text-to-cad 到底在解决什么问题

第一次听到 “text-to-cad” 这个词,很多人脑子里蹦出来的画面大概是:对着电脑敲一句“给我画一个带四个安装孔的法兰盘”,然后屏幕上就自动出现一个可以旋转、可以导出、可以拿去加工的 3D 模型。这个画面听起来像是科幻,但它背后要解决的真实痛点其实非常朴素——从“想法”到“可用的几何文件”之间,横着一条又长又陡的学习曲线。

传统流程是这样的:你脑子里有一个零件,先要打开 CAD 软件,然后从草图开始,画线、约束、标注尺寸、拉伸、倒角、打孔,最后导出成 STEP 或者 STL。对于熟练的机械工程师,这可能就是十几分钟的事;但对于一个只想验证结构的设计师、一个做机器人仿真的算法工程师、一个需要批量生成测试模型的学生来说,光是记住“约束怎么加”“基准面怎么选”就够劝退了。text-to-cad 想做的事情,就是把这条曲线拉直:用自然语言描述几何意图,由程序把它翻译成参数化的 CAD 操作,最终产出 STEP、GLB、STL 这类标准格式。

这里有几个关键词必须先说清楚,因为它们决定了整个项目的技术走向。CAD是最终目标领域,它强调的是“精确几何”和“可编辑的参数”;STEP是 CAD 世界里的通用交换格式,保留 B-rep(边界表示)实体信息,适合后续在 SolidWorks、中望 CAD、FreeCAD 里继续编辑;STL是三角网格格式,只有表面三角形,没有参数历史,适合 3D 打印和仿真;GLB是 glTF 的二进制版本,带材质和层级,适合在网页、Three.js、Qt OpenGL 场景里做可视化展示。换句话说,同一个 text-to-cad 系统,面向不同下游用途,要输出不同格式,这本身就是设计上的第一个分叉点。

那它适合谁?我把它分成三类人。第一类是做快速原型的设计和硬件工程师,他们需要把口头需求快速变成能看、能打印的模型;第二类是做仿真和机器人算法的开发者,他们需要批量生成带特定尺寸的零件,比如六轴机械臂的连杆、夹具、测试台;第三类是CAD 初学者和教学场景,通过自然语言降低入门门槛,先建立“几何意图”的直觉,再去学软件操作。这三类人的需求差异很大,也直接决定了你是做一个“玩具级”的文本生成网格,还是做一个“工程级”的文本生成参数化实体。

我个人的判断是:text-to-cad 的核心难点从来不是“文本理解”,而是“几何可执行”。大模型可以很轻松地输出一段看起来像模像样的 Python 代码,但这段代码能不能跑通、生成的实体是不是封闭的、尺寸是不是符合工程习惯、导出 STEP 后有没有破面,这些才是真正决定项目能不能用的地方。所以这篇博文不会停留在“概念很酷”的层面,而是把整个链路拆开,讲清楚每一步该怎么选、怎么落地、怎么避坑。

2. 整体架构设计:文本、代码、几何三层怎么串起来

2.1 为什么主流方案都走“文本生成代码”这条路

如果你去翻目前公开的 text-to-cad 实现思路,会发现一个高度一致的模式:不让模型直接输出几何,而是让模型输出一段建模脚本,再由 CAD 内核去执行这段脚本。这个选择不是偶然的,而是被现实逼出来的。

直接让模型输出顶点坐标或者三角面片,理论上可行,但问题一大堆。首先,模型很难保证几万个顶点之间的拓扑关系正确,生成的网格经常自交、破洞、法线翻转;其次,这种方式没有参数化,你改一个孔径就得重新生成整个网格;最后,它无法输出 STEP 这种带实体信息的格式,因为 STEP 需要的是精确的曲面和边界,而不是近似三角形。

走“生成代码”这条路,好处就非常明显了。代码是参数化的,孔径、长度、圆角半径都是变量,改一个数字就能重新生成;代码是可执行的,交给 CadQuery、build123d、OpenSCAD 这类库去跑,几何正确性由内核保证;代码是可追溯的,出问题能定位到具体哪一行,而不是面对一堆顶点发呆。这就像你让一个厨师做菜,不是直接给他一盘成品,而是给他一份精确到克的菜谱,他自己有锅有灶,做出来的东西才稳定。

目前主流的几何内核选择大概有这么几类。CadQuery基于 OpenCASCADE,Python 生态,能输出 STEP、STL、DXF,是工程级方案里最成熟的一个;build123d是 CadQuery 团队后来推的新一代 API,语法更接近 Python 原生,写起来更顺;OpenSCAD是脚本化建模的老牌选手,语法简单但能力偏弱,适合教学和简单零件;FreeCAD 的 Python API也能做,但接口比较重,适合已经在用 FreeCAD 的场景。选哪个,取决于你的下游是要 STEP 还是只要 STL,是要工程精度还是要快速可视化。

2.2 三层架构的职责划分与数据流

一个能用的 text-to-cad 系统,我习惯把它拆成三层:语义层、代码层、几何层。这三层各司其职,边界清晰,出问题的时候才能快速定位。

语义层负责把自然语言变成结构化的建模意图。比如用户说“一个 80 毫米长、40 毫米宽、10 毫米厚的板,四角各有一个直径 6 毫米的孔,孔中心距边缘 8 毫米”,这一层要提取出:基体是长方体,尺寸 80×40×10,特征是在四个角打孔,孔径 6,边距 8。这一步可以用大模型做,也可以用规则模板做,取决于你的输入多样性。

代码层负责把结构化意图翻译成可执行的建模脚本。这一层是确定性的,输入同样的意图,输出的代码应该一致。代码里会调用 CadQuery 或者 build123d 的 API,把长方体、孔、倒角这些操作串起来。这一层的关键是模板化,不要指望模型每次都能写出完美代码,而是准备一批经过验证的代码模板,让模型只负责填参数。

几何层就是 CAD 内核本身,负责执行代码、生成实体、做布尔运算、导出文件。这一层是黑盒,你只需要保证输入代码合法,输出就是可靠的。导出的时候根据用途选格式:要后续编辑就导 STEP,要 3D 打印就导 STL,要在网页展示就导 GLB。

数据流是这样的:用户输入文本 → 语义层解析成 JSON 意图 → 代码层套模板生成 Python 脚本 → 几何层执行脚本 → 导出目标格式 → 返回文件路径或预览图。整个链路里,最容易出问题的是语义层和代码层的衔接,因为自然语言有歧义,而代码要求精确。比如“孔距边缘 8 毫米”,到底是孔中心到边缘,还是孔边到边缘?这种歧义必须在语义层就消解掉,否则后面全错。

2.3 格式选型:STEP、STL、GLB 各自的使用场景

很多人做 text-to-cad 的时候,一上来就纠结输出什么格式,其实这个问题应该反过来问:你的下游要拿这个模型干什么。不同的用途,对格式的要求完全不同。

格式本质优势局限典型用途
STEPB-rep 实体精确、可编辑、带参数历史文件大、解析慢后续 CAD 编辑、工程出图
STL三角网格通用、轻量、打印软件都认无参数、精度受网格密度影响3D 打印、有限元仿真
GLBglTF 二进制带材质、层级、适合实时渲染不是工程格式、精度低网页展示、Qt OpenGL 预览

我实测下来的经验是:如果你的系统定位是工程辅助,STEP 必须是第一优先级,因为用户最终大概率要拿回 CAD 软件里改。STL 作为附带输出,用于快速预览和打印。GLB 则是锦上添花,用于做网页端的可视化,让用户不用装软件就能看模型。三者不是互斥的,一个成熟的 text-to-cad 系统应该能同时输出多种格式,让用户按需选择。

这里有个细节值得注意:从 STEP 转 STL 是降级,从 STL 转 STEP 是伪升级。网上很多人问“sw 中 stl 转 stp”,这个操作本质上是用网格拟合曲面,得到的 STEP 是一堆碎面,没有参数历史,编辑起来非常痛苦。所以 text-to-cad 的正确做法是在几何层直接生成实体,然后分别导出,而不是先生成 STL 再转 STEP。这个顺序搞反了,后面全是坑。

3. 核心细节拆解:从文本到可执行脚本的关键环节

3.1 语义解析:把口语变成结构化意图

语义解析这一步,决定了整个系统的上限。用户不会按照你设计的模板说话,他们会说“来个板子,大概这么长”,也会说“做一个法兰,DN50 的,四个螺栓孔”。前者模糊,后者专业,你的解析器得同时应付。

我的做法是分层解析。第一层做意图分类,判断用户要的是板类、轴类、法兰类还是壳体类零件。这个用大模型做 few-shot 分类就够了,准确率很高。第二层做参数抽取,从文本里把尺寸、数量、位置关系抠出来,转成 JSON。第三层做缺省补全,用户没说的参数,用行业默认值填上,比如没给倒角就默认不倒角,没给孔径就按板厚推算。

这里有个很实用的技巧:让模型输出 JSON 而不是直接输出代码。JSON 结构固定,容易校验,出错了能一眼看出来是哪个字段的问题。而代码是自由文本,模型稍微发挥一下就可能引入语法错误。我试过让模型直接写 CadQuery 代码,十次里有三次会因为 API 记错而报错;改成先出 JSON 再套模板,稳定性提升非常明显。

JSON 的结构大概长这样:

{ "part_type": "plate", "base": {"length": 80, "width": 40, "thickness": 10}, "features": [ {"type": "hole", "diameter": 6, "positions": [[8,8],[72,8],[8,32],[72,32]]} ], "units": "mm" }

这个结构清晰表达了“一块 80×40×10 的板,四角打四个直径 6 的孔,孔中心坐标分别是那四个点”。代码层拿到这个 JSON,直接往模板里填就行,不需要再做任何理解。

3.2 代码模板:为什么不能完全依赖模型写代码

我见过一些项目,思路是“让大模型直接生成 CadQuery 代码,然后执行”。这个思路在 demo 阶段能跑通,但一到生产环境就崩。原因很简单:大模型对 API 的记忆是不稳定的。CadQuery 的Workplane链式调用、hole()和cboreHole()的区别、fillet()需要选边,这些细节模型经常搞混。更麻烦的是,模型生成的代码可能语法正确但几何错误,比如孔打在了实体外面,布尔运算后什么都没剩下,但代码不报错。

所以我的建议是模板为主,模型为辅。针对常见的零件类型,准备一批经过验证的代码模板。模型只负责把 JSON 意图映射到模板,并填充参数。模板里的几何操作都是手工调过的,保证正确性。这样系统的稳定性就掌握在自己手里,而不是赌模型的发挥。

以板类零件为例,模板大概是这样:

import cadquery as cq def build_plate(params): base = params["base"] result = cq.Workplane("XY").box( base["length"], base["width"], base["thickness"] ) for feat in params["features"]: if feat["type"] == "hole": result = result.faces(">Z").workplane().pushPoints( feat["positions"] ).hole(feat["diameter"]) return result if __name__ == "__main__": params = {...} # 从 JSON 加载 model = build_plate(params) cq.exporters.export(model, "output.step") cq.exporters.export(model, "output.stl")

这个模板覆盖了板类零件最常见的需求,参数从 JSON 来,几何操作固定。用户说“加个倒角”,你只需要在 JSON 里加一个 fillet 字段,模板里加几行处理逻辑,不需要模型重新写代码。

3.3 几何内核执行:CadQuery 与 build123d 的取舍

CadQuery 和 build123d 都是基于 OpenCASCADE 的 Python 建模库,能力上重叠很大,但风格不同。CadQuery 是链式调用,写起来像流水线;build123d 是上下文管理器,写起来更像原生 Python。

我两个都用过,说说实际感受。CadQuery 的生态更成熟,文档多,例子多,遇到问题容易搜到答案,导出 STEP、STL、DXF 都很方便。缺点是链式调用有时候不太直观,尤其是做复杂布尔运算的时候,代码可读性会下降。build123d 的语法更现代,用with块组织建模步骤,逻辑清晰,适合复杂零件。但它的社区还在成长,遇到冷门问题可能找不到现成答案。

如果你的 text-to-cad 系统面向的是标准化零件批量生成,我建议用 CadQuery,因为模板化写起来直接,稳定性好。如果面向的是复杂自定义零件,build123d 的表达能力更强,值得投入。两者都能导出 STEP 和 STL,这一点没有差别。

执行环节有个必须注意的点:几何内核是重量级依赖,启动慢、内存占用高。如果你做的是 Web 服务,不要每次请求都新起一个进程,而是用常驻进程池,把内核加载一次,后续请求复用。我实测过,CadQuery 冷启动大概要两三秒,热执行一个简单零件只要几百毫秒,差距非常大。用进程池能把响应时间压到可接受的范围。

4. 实操过程:从零搭一个可用的 text-to-cad 原型

4.1 环境准备与依赖安装

先把环境搭起来。我推荐用 Python 3.10 或 3.11,太新的版本有些几何库还没跟上。用 conda 或者 venv 都行,我个人习惯 conda,因为 OpenCASCADE 的依赖比较重,conda 处理二进制依赖更省心。

conda create -n text2cad python=3.11 conda activate text2cad pip install cadquery pip install openai # 或者你用的其他模型 SDK pip install trimesh # 用于 STL 后处理和校验

CadQuery 的安装是最容易出问题的一步。它在 Windows 上依赖 OpenCASCADE 的预编译包,正常情况下 pip 直接装就行。如果遇到OCP相关的报错,大概率是版本不匹配,可以指定版本重装:

pip install cadquery==2.4.0

装完之后跑一个最小验证:

import cadquery as cq box = cq.Workplane("XY").box(10, 10, 10) cq.exporters.export(box, "test.step") print("ok")

能生成test.step就说明几何内核没问题。这一步看起来简单,但我见过太多人卡在环境上,所以务必先验证再往下做。

4.2 语义解析模块的实现

语义解析模块的核心是一个函数:输入文本,输出 JSON。我用大模型的 function calling 或者 JSON mode 来做,提示词里把零件类型、参数字段、单位约定都写清楚。

提示词的关键是给例子。不要只描述字段,要给两三个完整的输入输出示例。比如:

输入:一块 100x50x5 的板,中间一个直径 10 的孔 输出:{"part_type":"plate","base":{"length":100,"width":50,"thickness":5},"features":[{"type":"hole","diameter":10,"positions":[[50,25]]}],"units":"mm"}

有了例子,模型输出的结构就稳定多了。另外要强制单位,默认毫米,如果用户说了厘米或者英寸,在解析阶段就换算成毫米,后面统一处理。

解析完之后要做校验。检查尺寸是否为正数,孔的位置是否在基体范围内,特征之间有没有冲突。校验不通过就返回错误信息,让用户重新描述,而不是硬着头皮往下走。这一步能挡掉大部分低级错误。

4.3 代码生成与几何执行

拿到 JSON 之后,根据part_type选择对应的模板函数,把参数传进去,执行,导出。这个过程是确定性的,不需要模型参与。

import json import cadquery as cq def generate(intent_json, output_prefix): intent = json.loads(intent_json) if intent["part_type"] == "plate": model = build_plate(intent) elif intent["part_type"] == "flange": model = build_flange(intent) else: raise ValueError("unsupported part type") cq.exporters.export(model, f"{output_prefix}.step") cq.exporters.export(model, f"{output_prefix}.stl") return f"{output_prefix}.step"

导出 STL 的时候有个参数值得注意:** tolerance**。默认的网格精度可能不够,导致圆柱面看起来像多边形。对于要 3D 打印的零件,我会把 tolerance 调小:

cq.exporters.export(model, "output.stl", tolerance=0.01, angularTolerance=0.1)

这样生成的 STL 更平滑,打印出来的圆孔才真的是圆的。代价是文件变大,但对于单个零件来说完全可以接受。

4.4 输出格式的转换与预览

STEP 和 STL 都有了,如果还需要 GLB 做网页预览,可以用 trimesh 做转换:

import trimesh mesh = trimesh.load("output.stl") mesh.export("output.glb")

trimesh 读 STL 然后导出 GLB,这一步很轻量,不需要几何内核参与。GLB 可以直接丢给 Three.js 或者 Qt 的 OpenGL 场景加载,做实时旋转和缩放。

预览图的话,可以用 CadQuery 自带的导出 SVG 功能,或者用 trimesh 渲染一张 PNG。我一般会生成一张等轴测视角的 PNG,作为返回给用户的缩略图,这样用户不用下载模型就能看到大概长什么样。

5. 常见问题与排查技巧实录

5.1 几何相关的高频问题

做 text-to-cad 最让人头疼的就是几何问题,因为报错信息往往很抽象。我整理了几个高频问题和对应的排查思路。

问题现象可能原因排查方法解决方案
导出的 STEP 打开是空布尔运算把实体减没了在导出前打印model.val().Volume()检查孔位是否在实体外,调整位置
STL 有破面网格精度不够或实体本身有问题用 trimesh 检查is_watertight提高导出精度,或检查实体是否封闭
圆柱面显示成多边形STL 的 angularTolerance 太大观察网格密度调小 tolerance 和 angularTolerance
孔打在了错误的面workplane 选面逻辑不对打印选中的面法向量明确指定faces(">Z")或faces("<Z")
倒角失败倒角半径大于相邻边长度检查半径和边长关系减小半径或先做倒角再做其他特征

其中布尔运算减没了实体是最隐蔽的问题,因为代码不报错,导出也成功,但打开一看什么都没有。我的习惯是在导出前加一句体积检查,体积为零或者异常小就抛异常,这样能在早期发现问题。

5.2 文本解析的歧义处理

自然语言的歧义是另一个大坑。用户说“四个孔”,可能是四角各一个,也可能是均匀分布四个。用户说“厚 5”,可能是 5 毫米,也可能是 5 厘米。这些歧义如果不在解析阶段消解,后面全错。

我的处理原则是:能推断的推断,不能推断的问。对于“四个孔”,如果基体是矩形,默认按四角对称分布,这是工程上最常见的做法;如果基体是圆形,默认按圆周均布。对于单位,默认毫米,但如果数值特别小(比如厚度 0.5),可以提示用户确认是不是厘米。对于实在无法推断的,返回一个澄清问题,让用户补充,而不是瞎猜。

这个策略的核心是宁可多问一句,不要生成错模型。用户重新描述一次的成本,远低于拿到一个错误模型再返工的成本。

5.3 性能与稳定性优化

当你的系统从单次调用变成批量生成,性能和稳定性就成了必须考虑的问题。我踩过的坑主要有这几个。

内存泄漏。CadQuery 的实体对象如果不显式释放,长时间运行会累积内存。我的做法是每次生成完把 model 置为 None,并定期重启工作进程。虽然粗暴,但有效。

并发冲突。OpenCASCADE 不是线程安全的,多线程同时调用会出各种诡异问题。解决方案是用多进程而不是多线程,每个进程独立加载内核,通过队列分发任务。这样虽然资源占用高一点,但稳定性有保障。

超时处理。复杂零件的布尔运算可能跑很久,必须有超时机制。我给每个生成任务设 30 秒超时,超时就杀掉进程重新起一个,返回错误让用户简化需求。没有超时机制的话,一个卡死的任务会把整个服务拖垮。

文件清理。生成的 STEP、STL、GLB 文件会越积越多,需要定期清理。我一般保留最近 24 小时的文件,更早的自动删除,避免磁盘被撑满。

5.4 独家避坑经验

最后分享几个文档里不会写、但实际做项目一定会遇到的坑。

第一个坑:不要相信模型输出的数值单位。模型有时候会把“80 毫米”理解成“80”,有时候会理解成“0.08 米”,虽然数值上等价,但你的模板如果按毫米写,传进去 0.08 就全错了。我的做法是在 JSON 里强制带units字段,解析后统一换算成毫米再进模板。

第二个坑:STL 的坐标系和 STEP 可能不一致。有些导出器会做坐标变换,导致 STL 和 STEP 对不上。如果你要做多格式联动预览,务必在导出后校验两者的包围盒是否一致。

第三个坑:中文文本的编码问题。如果你的系统接收中文输入,确保从 HTTP 请求到 JSON 解析到文件写入,全链路都是 UTF-8。我遇到过因为编码问题导致中文描述变成乱码,解析出一堆无意义参数的情况。

第四个坑:不要过度依赖大模型的几何直觉。模型可以很好地理解“板”“孔”“法兰”这些概念,但对“这个孔会不会和那个槽干涉”这类空间推理,它经常出错。所以空间校验必须由代码来做,不能交给模型判断。

6. 这个方向还能怎么扩展

把基础链路跑通之后,text-to-cad 能做的事情远不止生成单个零件。我目前看到几个比较有价值的扩展方向。

批量参数化生成。这是工程上最实用的场景。比如你有一批测试件,只是尺寸不同,可以用一个模板加一张参数表,批量生成几十个 STEP 文件。这在做实验设计、参数扫描的时候非常省时间。实现上就是在代码层加一个循环,读 CSV 或者 Excel,逐行生成。

装配体生成。单个零件生成之后,下一步自然是把多个零件组装起来。CadQuery 支持Assembly,可以定义零件之间的约束关系。用户描述“一个底座加一个立柱,立柱在底座中心”,系统就能生成一个装配体,导出成带层级的 STEP 或者 GLB。这个方向对机器人仿真特别有用,因为机械臂的连杆和关节就是典型的装配体。

与仿真工具联动。生成的 STL 可以直接喂给有限元或者运动学仿真工具。我试过把生成的连杆 STL 导入到六轴机械臂的仿真环境里,验证运动范围,整个流程比手工建模快很多。如果你的下游是仿真,可以在导出 STL 的时候顺便做网格质量检查,避免仿真时报网格错误。

反向能力:从模型生成文本描述。这是 text-to-cad 的逆过程,但同样有价值。给定一个 STEP 文件,自动生成它的文字描述,用于文档、检索或者无障碍访问。这个用几何特征提取加语言模型就能做,技术上不难,但应用场景很广。

网页端轻量化预览。把 GLB 和 Three.js 结合,做一个纯浏览器的预览器,用户上传文本,后台生成,前端实时显示。这样连 CAD 软件都不用装,对教学和演示场景非常友好。我实测下来,一个中等复杂度的零件,GLB 文件大概几百 KB,网页加载毫无压力。

我个人在实际操作中的体会是,text-to-cad 的价值不在于替代 CAD 工程师,而在于把重复性的、标准化的建模工作自动化。工程师的时间应该花在创造性的设计上,而不是一遍遍画同样的法兰和支架。把模板做扎实,把校验做严格,把格式转换做顺畅,这个系统就能真正帮到人。至于那些还在纠结“模型能不能自己写出完美代码”的朋友,我的建议是先动手把一条最简单的链路跑通,你会发现很多问题在动手之后自然就有答案了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询