☰
text-to-cad实战:LLM解析与参数化建模生成STEP/STL/GLB
2026/10/8 8:36:17 网站建设 项目流程

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

第一次听到 text-to-cad 这个词,我脑子里蹦出来的画面是:对着电脑敲一行字,比如“一个 80×60×20mm 的法兰盘,中心开直径 30mm 通孔,四角各一个 M6 沉头孔”,然后软件直接吐出一个可以编辑、可以导出、可以拿去加工的实体模型。这个画面放在五年前还属于科幻范畴,但放到今天,它已经是一条能跑通的技术链路了。

text-to-cad 的核心价值,说白了就是把自然语言描述转换成 CAD 可识别的几何数据。传统流程里,你得打开 SolidWorks、Fusion 360 或者中望 CAD,手动拉伸、打孔、倒角,一个中等复杂度的零件少说半小时。而 text-to-cad 想做的事情,是让这段描述直接变成 STEP、GLB 或 STL 文件——STEP 给加工和工程软件用,GLB 给渲染和 Web 展示用,STL 给 3D 打印和切片软件用。这三个格式基本覆盖了从设计到制造到展示的全链路。

我之所以对这个方向感兴趣,是因为它踩中了几个真实的痛点。第一,非专业设计人员想快速验证结构想法,比如做机器人底盘的创客、做夹具的产线工程师,他们不需要精通参数化建模,只需要一个能用的模型。第二,批量化的标准件生成,比如一批不同孔径的法兰、不同长度的支架,用脚本生成比手动建模快十倍。第三,AI 辅助设计的入口,大语言模型能理解文字,但理解不了几何,text-to-cad 就是那座桥。

这篇文章适合谁看?如果你是会写 Python、懂一点三维几何概念、想自己搭一套文字转模型流水线的开发者,那这篇就是给你写的。如果你只是好奇这个方向怎么落地、有哪些坑,也能从里面拿到足够的信息。我会把整体架构、格式选型、核心代码、参数计算、踩坑经验全部摊开讲,尽量做到你照着做就能跑出一个能用的版本。

需要先说明一点:text-to-cad 目前没有哪个开源方案能做到“任意文字描述直接生成任意复杂模型”,它更适合结构化描述生成参数化零件这个场景。指望它一句话生成一个完整的汽车外壳,现阶段不现实。但生成法兰、支架、齿轮、外壳盒子这类规则几何体,完全可行,而且效果相当稳定。

2. 整体架构设计:为什么我选择“LLM 解析 + 参数化建模”这条路线

2.1 三种技术路线的取舍分析

在动手之前,我调研过三条主流路线,这里把它们的优劣摆出来,方便你判断自己该走哪条。

路线核心思路优势劣势适用场景
端到端生成用扩散模型或 Transformer 直接生成网格顶点理论上能生成任意形状训练成本极高,可控性差,尺寸不准学术研究、艺术造型
代码生成LLM 生成 CadQuery/OpenSCAD 代码再执行灵活,可表达复杂逻辑代码容易出错,需要沙箱执行有一定编程能力的用户
参数化解析LLM 抽取结构化参数,喂给建模库稳定、可控、尺寸精确只能生成预定义类型的零件工程零件、标准件批量生成

我最终选了第三条路,理由很直接:工程场景对尺寸精度的要求是刚性的。一个法兰盘的中心孔如果是 30mm,那它就必须是 30mm,不能是 29.7mm 也不能是 30.3mm。端到端生成和代码生成在这点上都不够可靠,而参数化解析把“理解文字”和“生成几何”两件事解耦了——LLM 只负责把文字变成 JSON,建模库只负责把 JSON 变成几何。每一段都可测试、可调试、可替换。

这个思路还有一个隐藏好处:LLM 的输出是可校验的。JSON 里的字段类型、数值范围、必填项都能用 schema 卡住,一旦 LLM 抽错了参数,你在解析阶段就能发现,而不是等到模型生成出来才发现孔打歪了。

2.2 完整流水线的四个阶段

整条流水线我拆成四段,每段职责清晰:

  1. 文本理解阶段:用户输入自然语言,LLM 抽取零件类型和参数字典。比如“直径 100mm、厚 10mm、中心孔 30mm 的法兰”,抽成{"type": "flange", "outer_dia": 100, "thickness": 10, "bore_dia": 30}。
  2. 参数校验阶段:检查数值是否合理、单位是否统一、必填项是否齐全。这一步是防止 LLM 幻觉的关键闸门。
  3. 几何生成阶段:用 CadQuery 或 build123d 这类参数化建模库,根据参数字典构造实体。
  4. 格式导出阶段:把实体导出成 STEP、GLB、STL,按需分发。

为什么用 CadQuery 而不是直接操作 OpenCASCADE 的 Python 绑定?因为 CadQuery 的 API 是链式的,写起来接近自然语言,比如cq.Workplane("XY").circle(50).extrude(10)就是“在 XY 平面画半径 50 的圆,拉伸 10mm”。这种表达方式对后续维护和扩展极其友好,而且它底层就是 OpenCASCADE,导出的 STEP 是真正的 B-Rep 实体,不是网格近似。

2.3 为什么格式导出要分三路走

很多人会问:生成一个 STL 不就行了吗,为什么要同时支持 STEP 和 GLB?这里涉及三种格式的本质差异,我用一个表格说清楚。

格式数据本质精度典型用途导出要点
STEPB-Rep 边界表示精确数学曲面工程软件、CNC 加工保留实体拓扑,可再编辑
STL三角网格近似,有弦高误差3D 打印、切片需设置合适的线性偏差
GLB三角网格 + 材质近似,可带颜色Web 展示、渲染需三角化并打包材质

关键点在于:STEP 是精确的,STL 和 GLB 是近似的。如果你把模型发给加工厂,必须给 STEP,因为 CAM 软件需要精确的曲面信息来生成刀路。如果你只是想在网页上转一转看看效果,GLB 最合适,体积小、加载快、还能带材质。STL 则是 3D 打印的通用语言,切片软件只认它。

所以我的导出模块设计成三个独立函数,共享同一个实体对象,各自处理各自的三角化参数。这样既保证精度,又保证灵活性。

3. 核心细节解析:LLM 提示词设计与参数 Schema 定义

3.1 提示词怎么写才能让 LLM 稳定输出 JSON

这是整个项目里最容易被低估的环节。我一开始觉得“让 GPT 输出 JSON”很简单,结果实测下来,不加约束的话,LLM 会给你返回带解释文字的 JSON、字段名大小写不一致、数值带单位字符串、甚至把毫米和厘米混着用。

我的解决方案是三段式提示词:角色设定 + 输出格式约束 + 少量示例。

角色设定部分告诉 LLM 它是一个 CAD 参数抽取器,只做抽取不做解释。输出格式约束部分用 JSON Schema 的简化版描述字段,明确每个字段的类型和单位。示例部分给两到三个典型输入输出对,覆盖法兰、支架、盒子三种零件。

实测下来,加上示例之后,字段名错误率从 30% 降到 5% 以下。这个投入产出比非常高,值得花时间打磨示例。

3.2 参数 Schema 的设计原则

Schema 设计我遵循三条原则:

  • 单位统一为毫米:所有长度字段一律用 mm,角度用度。LLM 在抽取时如果遇到“5cm”,必须转换成 50。这个转换在提示词里明确要求。
  • 字段名用蛇形命名:outer_dia、bore_dia、thickness,避免驼峰和空格,方便后续代码直接映射。
  • 必填项和可选项分开:零件类型、主要尺寸是必填,倒角半径、圆角半径是可选,缺省时用默认值。

下面是我实际用的一个简化 Schema 示例,用 Python 的 dataclass 表达:

from dataclasses import dataclass, field from typing import Optional @dataclass class FlangeParams: outer_dia: float # 外径 mm thickness: float # 厚度 mm bore_dia: float # 中心孔直径 mm bolt_count: int = 4 # 螺栓孔数量 bolt_circle_dia: Optional[float] = None # 螺栓孔分布圆直径 bolt_hole_dia: Optional[float] = None # 螺栓孔直径 fillet_radius: float = 0.0 # 边缘圆角

这个 dataclass 既是校验依据,也是建模函数的入参。LLM 输出的 JSON 直接**kwargs展开就能构造,非常顺滑。

3.3 参数校验的边界条件

校验阶段我设了几条硬规则,任何一条不满足就拒绝生成并返回错误信息:

  • 所有尺寸必须为正数,且不超过 1000mm(防止 LLM 把单位搞错,比如把 100mm 写成 100000)。
  • 中心孔直径必须小于外径,否则几何上无法构造。
  • 螺栓孔分布圆直径必须在外径和中心孔之间,否则孔会打到外面或里面。
  • 厚度不能小于 0.5mm,否则实体太薄,导出 STL 时容易破面。

这些规则看起来简单,但每一条都对应我实际踩过的坑。比如有一次 LLM 把“直径 100”理解成了“半径 100”,生成出来的法兰外径 200mm,直接超出打印平台。加上上限校验之后,这类错误在解析阶段就被拦住了。

4. 实操过程:从零搭一套可运行的 text-to-cad 流水线

4.1 环境准备与依赖安装

我用的环境是 Python 3.10 + CadQuery 2.4。CadQuery 的安装是第一个坑,因为它依赖 OpenCASCADE,在 Windows 上直接 pip install 经常失败。我的建议是用 conda 装,命令如下:

conda create -n text2cad python=3.10 conda activate text2cad conda install -c conda-forge cadquery=2.4 pip install openai trimesh

为什么用 conda 而不是 pip?因为 conda-forge 渠道的 CadQuery 包已经把 OpenCASCADE 的二进制依赖打包好了,pip 版本需要你自己编译或者找预编译 wheel,在 Windows 上极其折腾。我试过 pip 装,卡在 OCP 编译上两个小时没动静,换 conda 五分钟搞定。

trimesh 是用来做 GLB 导出的,CadQuery 原生只支持 STEP 和 STL,GLB 需要先转成 trimesh 的 mesh 对象再导出。openai 库是用来调 LLM 的,如果你用其他模型,换成对应的 SDK 即可。

4.2 文本解析模块的实现

解析模块的核心是一个函数,输入自然语言,输出参数字典。我把它拆成两步:调 LLM 拿原始 JSON,再用 dataclass 校验。

import json from openai import OpenAI client = OpenAI(api_key="your-key") SYSTEM_PROMPT = """你是一个CAD参数抽取器。用户会用自然语言描述一个零件, 你需要抽取零件类型和尺寸参数,输出纯JSON,不要任何解释文字。 所有长度单位统一为毫米,角度单位为度。 如果用户给的单位是厘米,乘以10;如果是米,乘以1000。 """ def parse_text(user_input: str) -> dict: resp = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ], response_format={"type": "json_object"} ) raw = resp.choices[0].message.content return json.loads(raw)

这里有个细节:response_format={"type": "json_object"}这个参数非常关键,它强制 LLM 输出合法 JSON,省去了自己写正则提取的麻烦。我用过不带这个参数的版本,LLM 会在 JSON 前后加“好的,以下是抽取结果:”这类废话,解析起来很烦。

4.3 几何生成模块:以法兰盘为例

法兰盘是我用得最多的测试零件,因为它的几何特征典型:一个圆柱主体、一个中心通孔、一圈螺栓孔、可选倒角。用 CadQuery 实现大概二十行代码。

import cadquery as cq def build_flange(p: FlangeParams) -> cq.Workplane: # 主体圆柱 body = cq.Workplane("XY").circle(p.outer_dia / 2).extrude(p.thickness) # 中心通孔 body = body.faces(">Z").workplane().hole(p.bore_dia) # 螺栓孔 if p.bolt_count > 0 and p.bolt_circle_dia and p.bolt_hole_dia: body = (body.faces(">Z").workplane() .polarArray(p.bolt_circle_dia / 2, 0, 360, p.bolt_count) .hole(p.bolt_hole_dia)) # 可选倒角 if p.fillet_radius > 0: body = body.edges("|Z").fillet(p.fillet_radius) return body

这段代码里有几个值得说的点。faces(">Z")是选择 Z 方向最高的那个面作为工作平面,这样打孔方向就是从上往下。polarArray是极坐标阵列,参数依次是半径、起始角、总角度、数量,用来均匀分布螺栓孔。edges("|Z")是选择所有平行于 Z 轴的边,对这些边做圆角,这样法兰的外圆柱面上下边缘会变圆润。

参数计算方面,螺栓孔分布圆直径如果用户没给,我会按经验公式取(outer_dia + bore_dia) / 2,也就是外径和孔径的中间值。这个位置既不会太靠外导致孔壁太薄,也不会太靠内导致和中心孔干涉。实测下来这个默认值在大多数场景下都合理。

4.4 三格式导出与参数设置

导出模块我写了三个函数,分别对应 STEP、STL、GLB。

def export_step(model, path): cq.exporters.export(model, path, exportType="STEP") def export_stl(model, path, tolerance=0.01, angular_tolerance=0.1): cq.exporters.export(model, path, exportType="STL", tolerance=tolerance, angularTolerance=angular_tolerance) def export_glb(model, path): import trimesh vertices, faces = model.val().tessellate(0.01) mesh = trimesh.Trimesh(vertices=vertices, faces=faces) mesh.export(path, file_type="glb")

STL 导出的tolerance参数是线性偏差,单位是毫米,0.01 意味着曲面被三角化时,实际曲面和三角面之间的最大距离不超过 0.01mm。这个值越小,网格越密,文件越大。对于 3D 打印,0.01 到 0.05 都够用;对于精细展示,可以调到 0.005。angular_tolerance是角度偏差,控制圆弧被分成多少段,0.1 弧度大约是 5.7 度一段,视觉上已经足够圆滑。

GLB 导出走的是 trimesh 的路径,因为 CadQuery 没有原生 GLB 支持。tessellate方法把 B-Rep 实体转成三角网格,然后交给 trimesh 打包成 GLB。这里要注意,GLB 的坐标系和 CAD 的坐标系可能不一致,trimesh 默认 Y 轴向上,而 CAD 通常 Z 轴向上,导出后可能需要在查看器里旋转一下。

4.5 完整调用示例

把上面几块拼起来,一个完整的调用长这样:

user_input = "生成一个外径120mm、厚15mm的法兰,中心孔直径40mm,6个直径8mm的螺栓孔均匀分布,螺栓孔分布圆直径90mm" params = parse_text(user_input) flange = FlangeParams(**params) model = build_flange(flange) export_step(model, "flange.step") export_stl(model, "flange.stl") export_glb(model, "flange.glb")

跑通之后,你会得到三个文件。STEP 拖进 FreeCAD 或者中望 CAD 能看到精确实体,STL 拖进切片软件能直接打印,GLB 拖进浏览器能实时旋转查看。整条链路从文字到模型,熟练之后不到十秒。

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

5.1 LLM 抽取参数错误的典型模式

这是最高频的问题,我整理了四种典型错误和对应的解法。

错误模式具体表现根因解法
单位混淆把 5cm 当成 5mm提示词未强调单位转换在 system prompt 里明确要求转换
半径直径混淆直径 100 抽成半径 100中文“直径”和“半径”语义接近字段名用 dia 而非 radius,提示词强调
数值幻觉用户没提螺栓孔,LLM 自己编了 4 个LLM 倾向于补全常见模式Schema 里可选项默认 None,不自动填充
字段名漂移输出 outerDiameter 而非 outer_dia提示词未固定字段名在提示词里列出所有合法字段名

其中数值幻觉最隐蔽,因为生成的模型看起来“很正常”,但多了用户没要求的特征。我的做法是在校验阶段对比用户原文和抽取结果,如果某个可选字段被填充了但原文里找不到对应关键词,就标记为可疑并询问用户确认。

5.2 STL 导出破面与修复

STL 破面是 3D 打印玩家的老朋友了。text-to-cad 生成的模型破面,通常有三个原因:壁厚太薄、三角化偏差太大、实体本身有自相交。

壁厚问题最好解决,在校验阶段加一条规则:任何特征的最小尺寸不小于 0.8mm。三角化偏差问题通过调小 tolerance 解决,但要注意文件体积会膨胀。自相交问题最麻烦,通常是布尔运算的数值误差导致的,解法是在 CadQuery 里对实体做一次clean()操作,它会合并重合的面和边,消除微小间隙。

如果 STL 已经导出且发现破面,可以用 trimesh 的修复功能补救:

import trimesh mesh = trimesh.load("broken.stl") mesh.fill_holes() mesh.fix_normals() mesh.export("fixed.stl")

fill_holes补洞,fix_normals修正法线方向。这两个操作能解决 80% 的常见破面问题。剩下 20% 需要手动在 MeshLab 里修,那就超出自动化范畴了。

5.3 不同 CAD 软件的 STEP 兼容性

STEP 虽然是国际标准,但不同软件的实现有差异。我实测下来,CadQuery 导出的 STEP 在 FreeCAD、中望 CAD、SolidWorks 里都能正常打开,但在某些老版本软件里可能出现曲面丢失。

如果你遇到 STEP 导入后曲面变平面,大概率是导出时的精度设置问题。CadQuery 的 STEP 导出默认精度是 1e-6,一般够用。如果目标软件对精度敏感,可以在导出时显式指定:

cq.exporters.export(model, "part.step", exportType="STEP", tolerance=1e-7, angularTolerance=1e-6)

另外提醒一句:STEP 文件里不包含材质和颜色信息,如果你需要带颜色的模型,得用 GLB 或者专门的渲染格式。这是格式本身的限制,不是导出代码的问题。

5.4 性能优化:批量生成时的瓶颈

单次生成一个零件,耗时主要在 LLM 调用上,大概 2 到 5 秒。但如果你要批量生成几百个零件,瓶颈就转移到几何生成和导出上了。

我的优化经验是:LLM 调用可以并发,几何生成必须串行。因为 CadQuery 底层的 OpenCASCADE 不是线程安全的,多线程同时建模会崩溃。所以架构上我用一个线程池处理 LLM 调用,把解析结果放进队列,另一个单线程消费者从队列取参数、建模、导出。这样 LLM 的等待时间被重叠掉了,整体吞吐量提升三到四倍。

导出环节,STL 的三角化最耗时。如果批量生成的是同一类零件只是尺寸不同,可以考虑缓存三角化模板,只对变化的部分重新计算。不过这个优化比较复杂,除非你的批量规模上千,否则不值得做。

6. 扩展方向:从单零件到装配体与参数化模板库

6.1 多零件装配的描述方式

单零件跑通之后,自然会想生成装配体。比如“一个盒子,底盖厚 3mm,侧壁厚 2mm,内部空腔 80×60×40mm,盖子可分离”。这涉及多个实体的定位和配合。

我的做法是在 Schema 里引入assembly类型,包含一个parts数组,每个零件有自己的参数和变换矩阵。LLM 负责把描述拆成多个零件,几何生成模块逐个建模,最后用 CadQuery 的Assembly类组装。

assy = cq.Assembly() assy.add(base, name="base", loc=cq.Location((0, 0, 0))) assy.add(lid, name="lid", loc=cq.Location((0, 0, 43))) assy.save("box.step")

Location控制零件的位置,(0, 0, 43)表示盖子放在底盖上方 43mm 处,正好是底盖高度加壁厚。装配体的 STEP 导出后,在 CAD 软件里会显示为多个独立实体,可以单独选中和移动。

6.2 参数化模板库的积累思路

text-to-cad 的长期价值不在于单次生成,而在于模板库的积累。每跑通一种零件类型,就把它固化成模板,后续同类需求直接调用,不再依赖 LLM 抽取。

我的模板库目前覆盖了法兰、支架、齿轮、盒子、轴套五类。每类模板有独立的参数 dataclass 和建模函数,注册到一个字典里,根据 LLM 输出的type字段分发。新增模板只需要写一个 dataclass 和一个 build 函数,注册进去即可,扩展成本很低。

模板库还有一个好处:可以脱离 LLM 独立使用。如果你已经知道参数,直接构造 dataclass 调用 build 函数就行,不需要联网调模型。这在批量生成标准件时特别有用,速度快且完全可控。

6.3 与现有 CAD 工作流的衔接

生成的 STEP 文件怎么融入现有工作流?我的经验是把它当作“初稿”而不是“终稿”。text-to-cad 负责快速生成 80% 的几何,剩下的 20% 细节——比如特殊倒角、螺纹特征、工程标注——在 CAD 软件里手动补。

具体操作上,我会把生成的 STEP 导入中望 CAD 或 SolidWorks,然后在此基础上添加工程图、标注尺寸、设置公差。这样比从零建模快得多,而且参数化模板保证了基础几何的准确性。

对于 3D 打印场景,STL 直接进切片软件,调整打印参数即可。我通常会把 text-to-cad 生成的 STL 和手动修复的版本对比,如果尺寸偏差在 0.1mm 以内,就直接用生成的版本,省去手动建模的时间。

6.4 精度与效率的平衡点

最后聊一个实操中反复权衡的问题:精度和效率怎么平衡。我的经验值是,对于大多数工程零件,STL 的线性偏差设 0.02mm 是个甜点。再小,文件体积翻倍但视觉和打印质量提升不明显;再大,圆弧面会出现肉眼可见的多边形棱角。

STEP 导出没有这个权衡,因为它本身就是精确的,文件大小主要取决于几何复杂度,和精度设置关系不大。所以我的策略是:STEP 永远导出最高精度,STL 按用途调偏差,GLB 用中等偏差加材质。这样既保证工程可用性,又控制文件体积。

我在实际使用中发现,text-to-cad 最大的价值不是替代 CAD 软件,而是把“想法到初稿”的时间从半小时压缩到十秒。这个压缩带来的迭代速度提升,才是它真正改变工作方式的地方。你可以在一分钟内试十个不同的尺寸组合,快速找到最优解,然后再用传统工具精修。这种“快速试错 + 精细打磨”的组合,比单纯依赖任何一方都高效。

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

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

立即咨询