☰
text-to-cad 实战:从自然语言到 STEP/GLB/STL 的批量建模方案
2026/10/26 16:23:19 网站建设 项目流程

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

第一次听到 “text-to-cad” 这个说法,很多人脑子里浮现的画面可能是:对着电脑敲一句“给我画一个法兰盘”,然后屏幕上就自动出现一个带孔、带倒角的回转体。这个想象不算离谱,但也不完全准确。更贴近现实的理解是:用自然语言描述几何意图,由程序把它翻译成 CAD 能识别的建模指令或中间格式,最终产出 STEP、GLB、STL 这类可继续加工或渲染的文件。它解决的核心痛点不是“替代工程师”,而是把那些重复性高、结构相对固定、参数化程度高的建模动作,从手工点击变成一句话触发。

我最早接触这个方向,是因为手头有一批标准件要反复出模型:垫片、支架、简单的壳体。每次在 CAD 里拉伸、打孔、倒角,动作几乎一模一样,只是尺寸不同。后来我就想,能不能把“外径 80、内径 40、厚 6 的圆环”这种描述直接喂给程序,让它自己生成。text-to-cad 这个思路,本质上就是这条路的延伸。它适合谁?适合做批量零件建模的工程师、做参数化设计的开发者、做 3D 内容生成的创作者,也适合想入门 CAD 二次开发但不知道从哪下手的人。哪怕你只是想把一堆文字规格快速变成 STL 去打印,这套思路也能帮上忙。

需要先泼一盆冷水:text-to-cad 不是“说人话就能出完美模型”的魔法。自然语言有歧义,“一个大一点的圆角”到底是 R5 还是 R10,机器不知道。所以真正能落地的方案,通常是“自然语言 + 结构化参数”的混合模式,或者限定在一个明确的领域词汇表内。下面我会把整套思路、格式选型、实操步骤和踩过的坑,尽量讲透。

2. 整体方案设计与技术选型思路

2.1 为什么不能直接让大模型吐出一个 STEP 文件

很多人第一反应是:既然大模型能写代码,那让它直接输出 STEP 文件不就行了?我试过,结论是不现实。STEP 是 ISO 10303 定义的结构化文本格式,里面有大量的实体引用、坐标系定义、拓扑关系。一个中等复杂度的零件,STEP 文件动辄几千行,而且行与行之间有严格的引用编号依赖。让模型逐行生成,只要中间错一个#123的引用,整个文件就废了,而且这种错误极难排查。

更靠谱的分层是这样的:自然语言层 → 结构化参数层 → 几何内核层 → 文件导出层。自然语言负责“理解意图”,结构化参数负责“消除歧义”,几何内核负责“真正建出实体”,导出层负责“转成 STEP/GLB/STL”。这四层各司其职,任何一层出问题都能单独定位。这也是我在实际项目里验证下来最稳的架构。

2.2 几何内核的选择:为什么我最终选了 CadQuery 而不是直接调 OpenCASCADE

几何内核是整个方案的发动机。常见的选择有 OpenCASCADE(OCCT)、CadQuery、FreeCAD 的 Part 模块,以及一些商业内核。OpenCASCADE 是最底层的,功能最全,但 API 极其繁琐,写一个带孔的板子可能要几十行。CadQuery 是建立在 OCCT 之上的 Python 库,用链式调用描述几何,可读性高很多。

我选 CadQuery 的理由很直接:它的代码几乎就是自然语言到几何的中间态。比如“一个 80x60 的板,四角 R5 圆角,中间一个直径 20 的通孔”,用 CadQuery 写出来就是:

import cadquery as cq result = ( cq.Workplane("XY") .box(80, 60, 6) .edges("|Z").fillet(5) .faces(">Z").workplane().hole(20) )

这段代码几乎可以逐词对应中文描述。也就是说,大模型只需要负责把自然语言翻译成这种链式调用,而不需要理解底层拓扑。翻译错了,改一行就行;翻译对了,CadQuery 负责保证几何合法。这个分工让整个系统的可靠性提升了一个量级。

2.3 输出格式的取舍:STEP、GLB、STL 各自适合什么场景

热词里反复出现 STEP、GLB、STL,这三个格式不是随便选的,它们对应完全不同的下游用途。我在项目里三个都会导出,但用途分得很清:

格式本质适合场景不适合场景
STEP精确 B-rep 边界表示后续 CAM 加工、装配、参数化编辑网页预览、轻量渲染
GLB二进制 glTF,含网格与材质网页 3D 展示、AR/VR、游戏引擎精密加工、尺寸标注
STL三角网格,只有面片3D 打印、快速原型需要精确尺寸的工程交付

这里有个很多人踩过的坑:STL 转 STEP 是有损的。热词里有人问“sw 中 stl 转 stp”,这个操作在 SolidWorks 里能做,但转出来的是“网格面片拼成的实体”,不是真正的 B-rep,后续做圆角、抽壳会非常痛苦。所以我的原则是:源头就用 CadQuery 生成 B-rep,然后按需导出 STEP 和 STL,绝不走“先生成 STL 再转回来”的回头路。GLB 则是在 STEP 基础上做网格化(tessellation)后导出,保证展示效果。

2.4 自然语言解析层:规则优先还是模型优先

解析层我试过两种极端。纯规则(正则匹配“直径”“厚度”“圆角”)在固定领域里非常稳,但换个说法就失效。纯大模型解析灵活,但会“幻觉”出根本不存在的参数。最后我采用的是混合策略:先用规则抽取明确的数值和关键词,把能确定的参数锁死;剩下的模糊描述交给模型补全,并且强制模型输出 JSON 格式,字段名固定。

{ "shape": "plate", "length": 80, "width": 60, "thickness": 6, "corner_radius": 5, "holes": [{"diameter": 20, "position": "center"}] }

这个 JSON 就是整个系统的“合同”。规则层和模型层都往这个结构里填,填完再校验一遍数值范围(比如厚度不能为负、孔径不能大于板宽),校验通过才交给 CadQuery。这一步校验是保命的,我见过模型把“厚 6 毫米”理解成“厚 6 米”,直接生成一个巨型零件,导出时内存爆掉。

3. 核心细节解析与实操要点

3.1 参数化建模的“参数”到底该怎么定义

text-to-cad 的成败,八成取决于参数定义得清不清楚。我总结了一套自己的参数命名规范,核心原则是每个参数必须有明确的物理含义和单位。不要用size1、size2这种名字,要用outer_diameter、wall_thickness。单位统一用毫米,因为 CAD 领域默认就是毫米,混用厘米和米是灾难的开始。

对于回转体(轴、套、盘),我固定用这几个参数:outer_diameter、inner_diameter、height、chamfer。对于板类零件,用length、width、thickness、corner_radius、holes。对于孔,用数组表示,每个孔有diameter、x、y、depth。这套结构覆盖了我遇到的八成以上简单零件。

注意:孔的位置坐标一定要明确是相对于哪个原点。我吃过亏,模型默认把孔放在零件中心,但实际图纸要求是相对于左下角。后来我在 JSON 里强制加了一个origin字段,取值只能是center或corner,杜绝歧义。

3.2 自然语言到参数的映射规则设计

规则层我写了一套关键词到参数的映射表,这部分是纯工程活,但非常值得投入。比如:

  • “直径”“外径”“Φ” →outer_diameter
  • “厚”“高度”“长” → 根据形状上下文判断是thickness还是height
  • “通孔”“贯穿” →depth = -1(CadQuery 里负值表示贯穿)
  • “圆角”“倒角” → 分别映射到fillet和chamfer,这两个不能混

这里有个细节:中文里的“长”和“高”经常混用。一个立着的圆柱,你说“长 100”和“高 100”是一个意思;但一个躺着的板,“长”是 X 方向,“高”可能是 Z 方向。我的处理方式是结合shape字段来判断,shape是cylinder时“长”映射到height,shape是plate时“长”映射到length。这个判断逻辑写死在规则里,比交给模型猜靠谱得多。

3.3 CadQuery 脚本的生成与安全校验

模型生成的 CadQuery 代码,绝对不能直接exec执行。我在早期版本里图省事直接执行,结果模型生成了一段带import os的代码,虽然没造成实际损害,但吓出一身冷汗。现在的做法是白名单校验:只允许出现cq.Workplane、.box、.circle、.extrude、.fillet、.hole等有限的方法名,出现任何其他函数调用一律拒绝。

ALLOWED_METHODS = { "Workplane", "box", "cylinder", "circle", "rect", "extrude", "cut", "union", "fillet", "chamfer", "hole", "faces", "edges", "workplane", "moveTo" } def validate_script(script): for token in re.findall(r'\.(\w+)\(', script): if token not in ALLOWED_METHODS: raise ValueError(f"不允许的方法调用: {token}")

这段校验代码不长,但它是我整个系统里最重要的一道防线。宁可拒绝生成,也不能执行来路不明的代码。

3.4 导出环节的参数计算:网格精度怎么定

导出 STL 和 GLB 时,网格精度(tessellation tolerance)直接决定文件大小和表面质量。CadQuery 导出 STL 时可以指定tolerance和angularTolerance。我的经验值是:普通展示用 tolerance=0.1mm,3D 打印用 0.05mm,精密配合用 0.01mm。精度每提高一个数量级,三角面片数量大约翻两到三倍。

举个例子,一个直径 80 的圆环,tolerance=0.1 时 STL 大约 200KB,tolerance=0.01 时能到 2MB 以上。所以不要盲目追求高精度,按用途选精度才是正确做法。GLB 导出时还要注意法线(normal)的计算,如果法线不对,网页里看模型会一片黑或者反光异常。CadQuery 导出的 GLB 默认法线是算好的,但如果自己用 trimesh 二次处理,记得调用mesh.fix_normals()。

4. 完整实操流程:从一句话到三个文件

4.1 环境搭建与依赖安装

先把环境搭起来。我用的是 Python 3.10,CadQuery 对 3.11 以上支持还不算特别稳,3.10 是经过验证的版本。安装命令如下:

pip install cadquery==2.4 pip install trimesh pip install numpy

CadQuery 依赖 OCCT,安装包比较大,第一次装可能要几分钟。如果装完import cadquery报错,八成是 OCCT 的动态库没找到,Windows 上可以试试用 conda 装:conda install -c conda-forge cadquery。这个坑我踩过,pip 装的版本在某些 Windows 环境下确实会缺库。

提示:如果你只是想做 STL 导出,trimesh 单独也能用,但它没有 B-rep 能力,做不了精确的圆角和布尔运算。要做 text-to-cad,CadQuery 是绕不开的。

4.2 第一步:把自然语言整理成结构化 JSON

假设用户输入是:“做一个外径 80、内径 40、厚 6 的圆环,边缘倒角 1 毫米”。规则层先抽取:外径 80、内径 40、厚 6、倒角 1。形状关键词“圆环”映射到shape: "ring"。整理成:

{ "shape": "ring", "outer_diameter": 80, "inner_diameter": 40, "height": 6, "chamfer": 1, "origin": "center" }

如果用户说的是“做个环,大概八十多,中间空的那种”,规则层抽不到明确数值,就交给模型补全,但补全后仍然要过一遍数值校验:inner_diameter < outer_diameter,height > 0,chamfer < height/2。最后一条特别重要,倒角不能超过厚度的一半,否则几何会自交,CadQuery 会直接报错。

4.3 第二步:生成并校验 CadQuery 脚本

根据 JSON 生成脚本。圆环的脚本我固定用这个模板:

import cadquery as cq result = ( cq.Workplane("XY") .circle(40) .circle(20) .extrude(6) .edges().chamfer(1) )

注意这里circle(40)是外径的一半,circle(20)是内径的一半,CadQuery 的circle接收的是半径。这个换算我一开始老忘,后来在生成脚本的函数里统一做了d/2的处理,避免手误。脚本生成后过一遍白名单校验,通过才执行。

4.4 第三步:执行建模并导出三种格式

执行脚本得到result对象后,导出就三行:

cq.exporters.export(result, "ring.step") cq.exporters.export(result, "ring.stl", tolerance=0.05) cq.exporters.export(result, "ring.glb")

实测下来,一个圆环从 JSON 到三个文件全部导出,耗时大约 1.5 秒,其中 STEP 导出最慢,占了差不多 1 秒。如果批量处理几百个零件,STEP 导出会是瓶颈,可以考虑多进程并行。我试过用concurrent.futures开 4 个进程,整体速度提升接近 3 倍。

4.5 第四步:结果验证与尺寸复核

导出完不能直接交付,必须复核。我的复核方式是读回 STEP 文件,重新测量关键尺寸。CadQuery 可以导入 STEP 并查询边界框(bounding box):

import cadquery as cq model = cq.importers.importStep("ring.step") bb = model.val().BoundingBox() print(bb.xlen, bb.ylen, bb.zlen)

对于外径 80、厚 6 的圆环,xlen和ylen应该都是 80,zlen是 6。如果对不上,说明建模环节出了问题。这一步看似多余,但它拦住过我好几次错误——有一次模型把“厚 6”理解成了“半径 6”,导出的圆环厚度变成 12,复核时一眼就看出来了。

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

5.1 模型生成失败:几何自交与布尔运算报错

最常见的报错是BRep_API: command not done,翻译过来就是几何运算失败。九成情况是倒角或圆角半径过大,导致相邻面相交。比如一个厚度 6 的板,你给它上下边缘都倒 R4 的角,两个倒角面就会撞在一起。解决办法是限制倒角半径不超过最小壁厚的一半。我在校验层加了这条规则后,这类报错基本消失了。

还有一种情况是布尔运算的实体没有真正相交。比如你想在一个板上打孔,但孔的位置算错了,跑到板外面去了,cut操作就会失败。排查方法是先把两个实体分别导出 STL 看一眼,确认位置关系再合并。

5.2 导出文件打不开:STEP 版本与编码问题

STEP 文件打不开,通常是两个原因。一是版本不兼容,CadQuery 默认导出 AP214,有些老软件只认 AP203。可以在导出时指定:cq.exporters.export(result, "x.step", "STEP", opt={"write_pcurves": False})。二是文件编码,STEP 标准要求 ASCII,如果零件名里有中文,某些软件会解析失败。我的做法是文件名和实体名一律用英文,中文描述只留在 JSON 里。

5.3 STL 打印出来尺寸偏小:网格精度与单位陷阱

有人反馈 STL 拿去打印,尺寸比设计值小了一圈。这几乎肯定是单位问题。STL 本身不带单位,切片软件默认按毫米读,但如果你生成时用的是厘米,就会差 10 倍。CadQuery 默认单位是毫米,但如果你从其他库导入网格再导出,就要特别小心。我的习惯是在导出前打印一次 bounding box,和设计值对一遍,对不上绝不导出。

另一个原因是网格精度太粗,圆弧被三角面片“切”掉了尖角,导致外径偏小。把 tolerance 从 0.1 降到 0.01 就能解决,代价是文件变大。

5.4 批量处理时内存暴涨:及时释放与分批评

批量生成几百个零件时,如果每个都留在内存里,很快就会 OOM。CadQuery 的实体对象比较占内存,一个中等复杂度的零件能占几十 MB。我的做法是每生成一个就导出并释放,用del result显式删除,再配合gc.collect()。另外把批量任务分成每批 50 个,批间休息一下,内存曲线就平稳了。

问题现象最可能原因排查动作解决方式
几何运算报错倒角/圆角过大检查半径与壁厚比限制半径 ≤ 壁厚/2
STEP 打不开版本或编码换 AP203 试实体名用英文
STL 尺寸偏小单位或精度打印 bounding box统一毫米,降 tolerance
批量 OOM实体未释放看内存曲线分批 + del + gc

5.5 自然语言歧义:那些模型一定会理解错的表达

有些说法模型几乎必错,我整理了一份“黑名单”,遇到就要求用户澄清:

  • “大一点”“小一点”——没有基准,无法量化
  • “差不多”“大概”——同上
  • “和上次一样”——没有上下文,模型不知道上次是什么
  • “标准件”——标准有很多种,必须指明具体标准号

提示:与其让模型猜,不如在交互层直接反问一句“请给出具体数值”。我实测下来,多问一句的体验远好于生成一个错模型再返工。

6. 进阶玩法与个人经验补充

6.1 把 text-to-cad 接到批量表格上

单个零件一句话生成只是玩具,真正有价值的是批量。我现在的做法是维护一个 Excel 或 CSV,每行是一个零件的参数,第一列是自然语言描述,后面几列是结构化参数。程序读一行、生成一个、导出三个格式,几百个零件半小时跑完。这套流程帮我省掉了大量重复建模时间,尤其是标准件库的搭建。

6.2 和现有 CAD 工作流的衔接

生成的 STEP 可以直接拖进 SolidWorks、中望 CAD、Fusion 360 里继续编辑。但要注意,CadQuery 生成的 B-rep 是“哑实体”,没有特征树。也就是说,你在 SolidWorks 里看不到“拉伸 1”“孔 1”这些历史特征,只有一个死实体。如果需要可编辑的特征树,得用 CAD 软件自己的 API 重新建模,这就超出 text-to-cad 的范围了。我的建议是:text-to-cad 负责快速出原型和标准件,精细设计还是回到 CAD 软件里做。

6.3 关于“cad 切地形”这类需求的延伸思考

热词里出现“cad 切地形”,这其实是另一个方向——用 CAD 处理地形网格。这个需求用 text-to-cad 的思路也能沾边:把地形描述成参数(范围、精度、高程数据源),程序自动生成地形实体。但地形数据量大,B-rep 表示不合适,更适合直接用网格(STL/GLB)。所以我的判断是:规则几何走 CadQuery 出 STEP,自由曲面和地形走网格出 STL/GLB,两条路各走各的,不要硬凑。

6.4 我踩过的最大的一个坑:别信模型的“单位”

有一次模型把“厚 6 毫米”输出成thickness: 6,但脚本里我写的是extrude(6),单位是毫米,没问题。另一次模型输出thickness: 0.6,它自作主张把毫米转成了厘米。结果导出的零件薄得像纸。从那以后,我在 JSON 校验层强制加了一条:所有长度参数必须在 0.1 到 10000 之间,超出范围直接拒绝。这条规则拦住了所有单位混乱的问题。

6.5 后续可以扩展的方向

这套框架目前只覆盖了规则几何。如果要支持更复杂的形状,比如带曲面的壳体、齿轮、螺纹,需要扩展 CadQuery 脚本模板库。我的计划是先把常见标准件(螺栓、轴承、型材)的模板攒齐,每个模板对应一套参数和一段脚本,模型只负责选模板和填参数。这样比让模型从零写脚本可靠得多。另外,导出环节可以加一个自动生成预览图的步骤,用 trimesh 渲染一张 PNG,方便快速确认模型长什么样,不用每次都打开 CAD 软件。

最后分享一个我一直在用的小技巧:每次生成新零件前,先用一个极简的“冒烟测试”脚本验证环境是否正常。就是生成一个 10x10x10 的立方体并导出 STL,能跑通说明环境没问题,再跑正式任务。这个习惯帮我排除了无数次“以为是代码问题,其实是环境坏了”的无效排查。

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

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

立即咨询