一文看懂Text-to-CAD:从自然语言到3D模型
1. 一文看懂text-to-cad:从自然语言到3D模型
做机械设计和3D打印的朋友应该都有过这种经历:脑海里明明有一个非常清晰的零件样子,但打开SolidWorks或者Fusion 360,从画草图、标尺寸、加约束到拉伸切除,一套操作下来少说半小时,遇到曲面或者装配关系更复杂的情况,磨一个下午也是常有的事。
text-to-cad要解决的就是这个痛点——直接把人对零件的语言描述(比如“一个直径50mm、厚度10mm的不锈钢法兰盘,中心开直径8mm的孔,外圈均布6个直径5mm的安装孔”),变成可编辑、可导出、可直接用于制造的CAD模型。这项技术早期主要出现在学术界,最近一两年借助大语言模型(LLM)的能力爆发,逐步形成了几条清晰的落地路线,不管是个人创客还是小型工作室,现在都能在本地搭一套可用的流程。
这篇博文适合几类读者:一直在关注AI辅助设计、但不知道从哪下手的工程师;想给3D打印工作流提速的创客;以及做设计工具产品、想评估这项技术边界的产品经理。我会从技术路线、核心原理、完整实操到问题排查,把我在这个方向上踩过的坑和验证过的方案一次性讲清楚。
2. 核心技术拆解:大模型、程序化建模与数据闭环
2.1 技术路线A:LLM生成程序化建模代码
这是目前个人用户最容易复现、落地效果也最稳定的路线。核心思路很直接:把text-to-cad的问题转化成text-to-code的问题,只是这里的“code”不是普通的业务代码,而是OpenSCAD、CadQuery、Build123d这类程序化CAD建模语言的脚本。
程序化建模语言有一个共同特点:用代码描述几何体,通过布尔运算(并集、差集、交集)组合出最终实体。拿OpenSCAD来说,一个法兰盘的核心代码就这么几行:
difference() { cylinder(h=10, d=50, $fn=100); // 外圆 translate([0, 0, -1]) cylinder(h=12, d=8, $fn=50); // 中心孔 for (angle = [0:60:300]) { rotate([0, 0, angle]) translate([20, 0, 5]) cylinder(h=10, d=5, $fn=30); // 6个安装孔 } }大模型做代码生成这件事已经非常成熟,所以只要模型见过足够多的OpenSCAD或CadQuery脚本,它就能从自然语言里提取关键参数(直径、高度、孔数量、孔的位置分布),生成一段可编译的脚本。我实测下来,GPT-4级别的模型在这里表现不错,代码可编译率在七成以上;如果配合自修复循环,成功率能拉到九十以上。
这路线的核心优势有两个。第一,结果天然是可编辑的参数化模型,用户拿到代码后可以改任何一个尺寸,而不是得到一个无法修改的封闭网格。第二,中间产物是文本,成本低、可缓存、可调试,整个流程非常透明。缺点也很明显:模型的表达能力受限于底层建模语言,复杂曲面造型(比如汽车外覆盖件那种A级曲面)用OpenSCAD描述基本不可能。
2.2 技术路线B:多模态Transformer直接输出B-Rep
学术界走得更激进一些,代表性的工作是2024年初武汉大学、南洋理工大学和Meta联合发布的Text2CAD论文。这个方案不再经过代码这个中间层,而是让模型直接输出CAD的边界表示(B-Rep,Boundary Representation),也就是用拓扑结构(面、边、顶点)和几何信息(曲面方程、曲线方程)直接定义实体。
具体做法是把B-Rep数据离散化成序列。每个面用一个参数化曲面标记符表示,每个环用边界曲线标记符表示,整个模型就是这些标记符按特定顺序排列的序列——建模过程变成了序列生成任务。训练数据来自ABC数据集和Fusion 360画廊里的程序化建模记录,模型学了之后能把自然语言直接映射成B-Rep序列,再重建回实体模型。
这条路线的上限很高,因为B-Rep是CAD工业界的通用底层表示,导出STEP、IGES都顺畅。但落地难度也大:序列长度长、训练数据获取成本高、生成结果的拓扑有效性检查(比如是否闭合、是否自相交)是一个独立的研究问题。现阶段论文成果更多是概念验证,离工程可用还有距离。
2.3 为什么代码生成路线是当前最稳的选择
我在自己的测试环境里把这两条路线都跑过,负责任地说,如果你今天就想在自己的电脑上实现一个能用的text-to-cad,走代码生成路线是唯一现实的选择,原因有三个:
一是工具链成熟。OpenSCAD是开源软件,命令行编译、脚本化导出、参数化驱动都是现成的,CadQuery基于Python,和现有工程脚本生态无缝衔接。二是调试方便。生成结果错了,看代码能定位是逻辑错误还是参数错误,而B-Rep序列错了只能重建、检查拓扑,调试成本高一个量级。三是对硬件要求低。代码生成路线的重活在大模型的推理上,个人电脑用API调用即可,不需要像训练B-Rep模型那样准备昂贵的GPU集群。
当然,选择代码生成路线也意味着放弃一部分几何表达力。你没法用它生成带自由曲面的复杂外观件,但机械结构件、连接件、外壳、支架这类占日常设计需求绝大部分的场景,它完全够用。
3. 从零到一实操:搭建你自己的text-to-cad流水线
3.1 环境准备与工具选型
先列一个我目前用下来最舒服的软件组合,全部是免费或开源方案,没有特殊硬件门槛:
| 组件 | 推荐工具 | 作用 |
|---|---|---|
| 建模语言 | OpenSCAD(最新稳定版) | 最终生成脚本的语言 |
| Python环境 | Python 3.10+ | 编排整个流程 |
| LLM接口 | OpenAI API / 本地Ollama部署 | 自然语言转代码 |
| 执行环境 | OpenSCAD命令行 + solidpython | 编译脚本、导出STL/STEP |
| 预览与检查 | OpenSCAD GUI / 在线查看器 | 快速确认模型是否符合预期 |
关于LLM的选择,我给一个具体建议:如果追求最高的生成准确率,用GPT-4级别,API成本每次调用大概人民币几分钱,配合自修复循环后总成本可控;如果在意数据隐私或想完全离线,用Ollama部署Qwen2.5-Coder-32B或DeepSeek-Coder-V2-Lite,效果略逊但也能用。我本地测试时常用7B量级的模型,简单零件没问题,遇到带装配语义的描述就经常漏尺寸参数。
OpenSCAD安装后,记得把openscad命令行加到系统PATH里,因为后续脚本需要通过subprocess调用它完成编译和导出。在Windows上路径通常是C:\Program Files\OpenSCAD\openscad.exe,Linux直接apt install或者源码编译。
3.2 提示词设计技巧:把需求“说”清楚
text-to-cad的提示词设计,决定了生成结果的上限。我前期测试时走了很多弯路,最后总结出一条黄金法则:让模型像读一份工程图纸那样理解需求,而不是像听日常对话那样。
一份有效提示词应该包含以下五个要素:
- 功能描述:这是一个什么零件?用于什么场景?(“用于连接两根直径40mm水管的法兰盘”比“一个法兰盘”好得多)
- 关键尺寸:明确标注所有关键尺寸和单位(默认毫米,但一定写清楚)
- 特征列表:有哪些孔、槽、倒角、突出结构,分别在哪里
- 位置关系:孔是均布还是特定位置?是对称的还是偏心?
- 精度提示:是否需要倒角?是否需要圆角过渡?螺纹孔还是通孔?
举个例子,这是我在测试中用过的一份效果不错的提示词:
用OpenSCAD生成一个矩形安装底板,尺寸120mm x 80mm x 8mm, 四个角各有一个直径6mm的通孔,孔中心距边缘10mm, 中心位置有一个直径30mm的圆柱凸台,凸台高度12mm, 凸台中心有一个直径12mm的螺纹孔(简化表示即可)。这种描述方式,模型生成的脚本基本一次通过编译,尺寸也和学生思维里的设计一致。反观如果你只说“帮我画一个带孔的板子”,模型会自由发挥,输出什么完全随缘。
还有一个很实用的技巧——加体系约束。OpenSCAD里所有操作都要在同一个坐标系内完成,提示词里最好说明“所有尺寸单位均为毫米,坐标原点设在零件的底面中心”,这样模型生成的脚本在位置关系上会规范很多。
3.3 完整复现:生成一个带孔法兰盘的详细流程
下面这套流程是我在本地跑通的,完整覆盖“自然语言→CAD模型→可制造文件”的链路。
第一步:构建调用脚本
我用Python写了一个编排脚本,负责任务拆分、调用LLM接口、写临时文件、触发OpenSCAD编译。核心逻辑大概是这样:
import subprocess import json import openai def generate_cad(text_prompt): system_prompt = """你是一个OpenSCAD专家。根据用户的描述,生成合法的OpenSCAD代码。 要求: 1. 所有尺寸单位为毫米 2. 只输出纯代码,不要多余解释 3. 使用difference/union/intersection完成布尔运算 4. 圆柱用$fn控制细腻度,默认$fn=100""" response = openai.ChatCompletion.create( model="gpt-4o", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": text_prompt} ], temperature=0.2 ) return response.choices[0].message.content.strip() def compile_and_export(scad_code, output_name): with open(f"{output_name}.scad", "w", encoding="utf-8") as f: f.write(scad_code) result = subprocess.run( ["openscad", "-o", f"{output_name}.stl", f"{output_name}.scad"], capture_output=True, text=True, timeout=60 ) if result.returncode != 0: raise RuntimeError(f"OpenSCAD编译失败: {result.stderr}") print(f"已生成: {output_name}.stl")注意一个细节:temperature参数我固定设置在0.2左右。温度过高会让模型在生成代码时引入不必要的随机性,比如变量名乱变、布尔运算顺序不稳定,温度太低则会让模型过于保守,有时会放弃实现某些描述。0.2是我测试下来生成质量与多样性平衡比较好的值。
第二步:自修复循环
第一次生成的代码不一定能编译通过,遇到这种情况不要慌,写一个自修复函数,把编译错误信息回传给模型重新生成:
def generate_with_repair(text_prompt, max_attempts=3): for i in range(max_attempts): code = generate_cad(text_prompt) try: compile_and_export(code, "flange") print("编译成功") return code except RuntimeError as e: print(f"第{i+1}次编译失败: {e}") text_prompt = f"""你之前的OpenSCAD代码编译出错。 错误信息:{str(e)} 请修正代码,保持设计意图不变。 原需求:{text_prompt}""" raise RuntimeError("多次修复后仍无法编译")我测试了一批机械件描述,单次生成编译成功率约72%,加入自修复后能到91%以上。这个循环是整套流程里性价比最高的一环,强烈建议每个人都在自己的实现里加上。
第三步:后处理与验证
STL导出后,别急着拿去切片或加工,图形界面打开确认三件事:一是外形轮廓是否符合预期,二是孔径和位置是否符合要求,三是是否有穿插面或非流形边缘。如果是制造用途,建议在OpenSCAD里改用polyhedron或其他曲面导出方式,进一步提升B-Rep精度。
如果要做进一步工程化,还可以用CadQuery替代OpenSCAD,它依托Python生态,导出的STEP文件能直接进SolidWorks、NX这类商业软件做下游设计。
4. 常见问题与排查技巧实录
4.1 模型“听不懂”复杂几何约束怎么办
描述里出现“与XX相切”“与XX同轴”“均布”这类几何约束词时,大模型有时会理解偏差。比如“均布”它可能理解为随机分布,“同轴”可能被忽略导致孔位置跑偏。
我的处理经验是:把几何约束翻译成坐标运算。不在提示词里依赖模型对抽象词的理解,而是直接给出可计算的描述。举例,“中心孔与外圆同轴”不如写成“中心孔的圆心坐标为(0,0)”,“6个孔均布”不如写成“for循环从0到360度,步长60度”。后者是OpenSCAD的标准写法,模型照着生成基本不会错。
4.2 生成模型无法导入CAD软件、尺寸不对怎么办
STL文件是三角网格,进入SolidWorks这类参数化建模软件后是“哑实体”,不能编辑特征,这是所有网格格式的天然局限。如果你的下游流程要求可编辑,两个办法:一是改用CadQuery生成STEP格式,STEP是B-Rep格式,能做特征识别;二是用OpenSCAD先把模型重建为CSG实体再导入,OpenSCAD最新版支持直接导出STEP(实验功能)。
尺寸不对是另一个高频问题。原因主要有两类:一是模型把直径当成半径用,二是OpenSCAD的默认单位虽然是毫米,但模型没在提示词里强调单位,自己脑补成了英寸。排查办法很直接——导出前先在OpenSCAD里用echo输出关键尺寸,或者用resize约束强制定位。
4.3 高频问题排查速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 编译报语法错误 | 模型生成了不存在的函数名 | 启用自修复循环,回传错误信息 |
| 圆柱面粗糙有棱角 | $fn值设置过低 | 把$fn提高到100以上 |
| 孔的位置偏移 | 没有明确圆心坐标 | 在提示词中指定原点与偏移量 |
| 导出STL破面 | 布尔运算产生非流形边界 | 增加交集剪切、检查重合面 |
| 模型比例不对 | 单位未明确,模型假设成英寸 | 在提示词和系统提示中双重强调毫米 |
| 生成结果每次不一致 | temperature过高 | 降到0.2以下,必要时设seed |
还有一个容易踩的坑:OpenSCAD默认的render()流程在处理大量布尔运算时,会非常吃内存和时间。复杂模型带几十个孔时,建议把孔的循环用for+union()合并,尽量减少顶层的difference()操作数量,编译速度能快好几倍。我一个有36个散热孔的壳体模型,把这个优化做完,导出时间从45秒降到11秒。
5. 应用场景与影响范围
5.1 制造与3D打印:从“画图”到“描述”
3D打印是text-to-cad最先落地的场景。我自己的一个典型用法是:打印配件时,先用卡尺量出配合件的尺寸,然后直接打字描述需求,生成模型,切片打印。以前这个流程里最耗时的“建模”环节现在被压缩到一分钟内。
对小型机械加工厂和创客空间来说,这项技术还能减少一部分重复劳动。标准件、连接件、支架类零件,本身设计工作量不大,但数量多、种类杂,用text-to-cad批量生成参数化模型,比每次打开软件重新画效率高得多。
5.2 教育、设计与创意领域的新交互范式
教学场景的价值可能被低估了。CAD软件的学习曲线是劝退很多学生的第一道坎,但有了text-to-cad,学生可以先用自己的话描述想法,再观察模型是如何从代码里长出来的,倒过来理解建模逻辑和几何关系,理解速度反而更快。
设计师做前期概念探索时也用得上:描述十个不同形态的方案,对比模型效果,选一个进精细设计阶段。这个流程让创意发散和收敛的节奏快了很多,人不用被工具拖累。
5.3 不可回避的局限性
诚实地说,text-to-cad目前还替代不了专业CAD工程师。复杂机械装置中常见的装配约束、运动仿真、公差标注、材料属性、热处理工艺要求,现在没有任何一条text-to-cad路线能自动处理。生成雕塑级自由曲面、A级曲面这类需要高级曲面建模能力的场景,现阶段的代码生成路线基本无能为力。
此外,大规模工业级应用还受制于数据合规。生产图纸往往涉及公司核心技术,LLM调用云端API意味着设计数据出域,这在很多企业里是不可接受的。本地化部署虽然能解决隐私问题,但小参数模型的能力下降又会让体验大打折扣。
从影响范围来看,text-to-cad真正改变的不是专业设计人员的生产力工具,而是把“三维建模”这件事的门槛拉低到了“会说自然语言”的程度。它让更多没有受过CAD训练的人也能把自己的想法变成实体,这个价值在创客教育、快速原型验证和小批量定制生产场景里会越来越明显。
我目前实际使用中的体会是,不要把text-to-cad当成一个能一步到位的自动设计机器,把它当成一个“听得懂人话的实习生”更准确。你给它说清楚需求,给它必要的尺寸约束,它快速给你一版基础模型,你再在这个基础上修改、迭代,这才是当前技术条件下最务实的用法。做产品的话,可以考虑在这套流水线上加一层“需求结构化”的中间界面,把用户的自由文本先变成结构化参数表单,再交给模型生成,这样生成成功率还能再上一个台阶。