1. 从一句话到三维模型:text-to-cad 到底在解决什么问题
第一次听到 text-to-cad 这个词,我脑子里蹦出来的画面是:对着电脑说一句“给我画一个带四个安装孔的方形法兰盘”,屏幕上就自动生成一个可以导出加工的 3D 模型。这个场景放在几年前还像是科幻,但这两年随着大模型能力的提升,它已经变成了一个真实可落地的工程方向。text-to-cad,顾名思义,就是把自然语言文本转换成 CAD(计算机辅助设计)模型的技术方案。它的核心价值在于:让不懂 CAD 软件操作的人,也能通过描述需求直接得到可用的三维几何体。
我之所以对这个方向特别感兴趣,是因为在实际工作中见过太多这样的场景:一个机械工程师想快速验证某个结构想法,却要花半小时在软件里画草图、拉伸、打孔;一个产品经理拿到客户的口头需求,想先出个粗略的三维示意,却因为不会用专业工具而卡住。text-to-cad 要解决的,正是这种“想法到模型”之间的效率断层。它适合的人群很明确:有一定工程背景但想提升建模效率的从业者、做快速原型验证的开发者、以及想探索 AI 辅助设计边界的技术爱好者。
需要先说明的是,text-to-cad 目前并不是要替代专业 CAD 软件,而是作为一种“前置快速生成”的手段存在。它生成的模型通常需要后续在专业软件里做细节修正和工程标注。理解这一点很关键,否则容易对它产生不切实际的期待。接下来我会从整体设计思路、核心技术细节、实操流程和常见问题几个维度,把这个方向拆开讲透。
2. 整体设计思路:为什么这样搭架构
2.1 三种主流技术路线的取舍逻辑
做 text-to-cad,摆在面前的第一道选择题就是技术路线。我梳理下来,目前主流方案大致分三类,每类都有自己的适用边界。
第一类是基于代码生成的方式。核心思路是让大模型输出一段参数化建模脚本,比如 OpenSCAD 或 CadQuery 的代码,然后由这些脚本引擎执行生成三维模型。这条路线的优势非常明显:模型是参数化的,尺寸精确可控,生成结果可复现,而且代码本身就是一种“设计意图的文档”。缺点是模型的表现力受限于脚本引擎的能力,复杂曲面造型会比较吃力。
第二类是基于序列化建模操作的方式。把 CAD 操作抽象成一系列指令序列,比如“创建草图-绘制矩形-拉伸-打孔”,让模型预测这个操作序列,再在 CAD 内核里回放执行。这种方式更贴近真实建模流程,但对操作序列的表示和预测精度要求很高,训练数据也更难获取。
第三类是基于隐式表示或网格生成的方式。直接输出点云、体素或网格模型,绕开参数化建模的复杂性。这种方式生成速度快、造型自由度高,但缺点是模型不可参数化编辑,精度也往往达不到工程要求。
我在实际选型时的判断标准是这样的:如果你的目标是做功能性零件,比如支架、法兰、连接件这类以尺寸精度为核心的模型,优先选代码生成路线;如果目标是做概念造型,比如外观曲面、艺术化结构,网格生成路线更合适;序列化操作路线则适合那些需要完整保留建模历史、后续要频繁修改参数的场景。对大多数入门者来说,我建议从代码生成路线切入,因为它的技术栈最清晰,调试也最直观。
2.2 为什么参数化是绕不开的核心
很多人一开始会想:直接生成一个网格模型不就行了吗,为什么要费劲搞参数化?这个问题我在项目初期也纠结过。后来想明白了:CAD 的本质不是“画出一个形状”,而是“定义一组约束关系”。一个孔的位置不是固定的坐标,而是相对于某个基准面的偏移量;一个圆角的大小不是绝对值,而是和板厚关联的参数。这种参数化能力,才是 CAD 区别于普通三维建模的地方。
text-to-cad 如果只生成一个静态网格,那它本质上只是个“三维画图工具”,价值有限。只有当它生成的模型带有参数化结构,用户后续能改尺寸、调位置、加特征,它才真正融入了设计工作流。这也是我坚持选择代码生成路线的根本原因——OpenSCAD 和 CadQuery 这类工具天生就是参数化的,模型里的每个尺寸都可以是变量,改一个数字整个模型自动更新。
提示:如果你只是想快速看个形状,网格生成路线确实更快。但只要涉及后续修改和工程出图,参数化路线是唯一选择,前期多花的时间会在后期成倍省回来。
2.3 大模型在其中的角色定位
搞清楚大模型在 text-to-cad 里到底干什么,能避免很多方向性错误。它不是“理解三维空间”的,它做的是自然语言到结构化代码的翻译。也就是说,模型需要把“一个长宽各 50 毫米、厚 5 毫米的方板,四角各有一个直径 4 毫米的孔”这样的描述,翻译成一段带有正确变量和函数调用的建模代码。
这个定位决定了几个关键设计点。首先,提示词工程极其重要,因为模型需要足够的上下文才能生成正确的代码结构。其次,需要给模型提供清晰的代码模板和示例,让它知道目标代码长什么样。最后,必须有一套验证机制,因为模型生成的代码不一定能跑通,也不一定符合几何逻辑。我在实践中发现,把大模型当成一个“懂建模语法的翻译官”而不是“懂设计的工程师”,心态会稳很多,对它的输出预期也更合理。
3. 核心细节解析:从文本到模型的关键环节
3.1 提示词设计:怎么把话说清楚
提示词的质量直接决定生成结果的质量。我踩过的最大坑就是:以为随便说一句“画个盒子”就能得到想要的东西。实际上,模型需要的是结构化、带尺寸、带约束关系的描述。经过反复试验,我总结出一个比较稳的提示词模板,包含以下几个要素。
- 几何类型:明确是立方体、圆柱、法兰、支架还是其他基本体或组合体。
- 关键尺寸:长宽高、直径、厚度,单位统一用毫米。
- 特征描述:孔、槽、圆角、倒角的位置和尺寸。
- 约束关系:哪些特征是对称的,哪些是相对于某个面定位的。
- 输出格式:明确要求输出 OpenSCAD 或 CadQuery 代码。
举个例子,与其说“做一个安装板”,不如说“创建一个长 80 毫米、宽 60 毫米、厚 6 毫米的矩形板,四个角各有一个直径 5 毫米的安装孔,孔中心距离板边缘各 10 毫米,输出 OpenSCAD 代码”。后者的生成成功率比前者高出好几倍。这个道理其实很朴素:你给的信息越精确,模型需要“猜”的东西就越少,出错概率自然就低。
3.2 代码模板与约束注入
光靠提示词还不够,我习惯在系统提示里注入一套代码模板和硬性约束。这套模板定义了变量命名规范、单位约定、以及常用的建模函数封装。比如我会预先定义好plate_width、plate_length、hole_diameter这类变量名,让模型生成的代码风格统一,后续维护起来也方便。
约束注入则是把一些工程常识写进提示里。比如“所有尺寸单位为毫米”“孔必须完全贯穿实体”“圆角半径不得超过最小板厚的一半”。这些约束看起来琐碎,但能挡掉大量低级错误。我印象很深的一次,模型生成了一个圆角半径 10 毫米、板厚只有 6 毫米的模型,结果圆角直接把板“吃穿”了。自从加了圆角约束,这类问题再没出现过。
3.3 几何有效性校验
模型生成的代码能跑通,不代表生成的几何体是有效的。我遇到过好几次代码执行成功,但导出的模型是空心的、自相交的、或者根本就是个空文件。所以必须加一层几何校验。我的做法是:代码执行后,检查生成实体的包围盒尺寸是否和预期一致,检查体积是否大于零,检查是否存在非流形边。
这套校验逻辑用 Python 写起来不复杂,核心就是调用建模引擎的 API 读取模型属性,然后和预期值做比对。如果校验不通过,就把错误信息反馈给大模型,让它重新生成。这个“生成-校验-反馈-重生成”的循环,是整个系统稳定性的关键。我一般设置最多重试三次,三次还不行就报错让用户介入,避免无限循环浪费资源。
| 校验项 | 检查方法 | 不通过的常见原因 |
|---|---|---|
| 包围盒尺寸 | 读取模型 bounding box 与预期比对 | 尺寸单位理解错误 |
| 实体体积 | 计算体积是否大于零 | 布尔运算失败导致空实体 |
| 流形性 | 检查是否存在非流形边 | 特征重叠或自相交 |
| 孔特征 | 统计圆柱面数量 | 孔被误删或位置错误 |
3.4 参数提取与变量化处理
这一步是让模型真正“可用”的关键。模型生成的代码里,尺寸往往是硬编码的数字。我会在生成后做一次参数提取,把关键尺寸抽成变量,放在代码顶部集中管理。这样用户拿到代码后,改一个变量就能调整整个模型,不用去代码里到处找数字。
参数提取的实现方式有两种。一种是让大模型在生成时就使用变量,另一种是生成后做静态分析自动提取。我倾向于前者,因为在提示词里明确要求“所有关键尺寸必须定义为顶部变量”,模型基本都能遵守。后一种方式作为兜底,用正则匹配数字然后人工确认。两种方式结合,参数化覆盖率能到九成以上。
4. 实操过程:手把手搭建一个可用的生成流程
4.1 环境准备与依赖安装
先把基础环境搭起来。我用的技术栈是 Python 加 OpenSCAD,原因是 OpenSCAD 的脚本语法简单,大模型生成起来准确率高,而且它自带命令行执行能力,方便自动化。CadQuery 功能更强但语法复杂,适合进阶后再切换。
# 创建虚拟环境 python -m venv text2cad_env source text2cad_env/bin/activate # 安装核心依赖 pip install openai trimesh numpyOpenSCAD 需要单独安装,装完后确认命令行可用。trimesh用来做几何校验,numpy处理数值计算。大模型接口这里用通用的调用方式,具体用哪家根据自己情况定,关键是保证能稳定返回代码文本。
注意:OpenSCAD 的命令行参数在不同版本间有差异,装完后先用一个简单脚本测试
openscad -o output.stl input.scad能否正常执行,确认无误再往下走。
4.2 完整生成流程的代码实现
整个流程分五步:接收文本、调用模型生成代码、执行代码、校验几何、返回结果。我把核心逻辑写成一个函数,方便复用。
import subprocess import trimesh import os def generate_cad_from_text(user_input, max_retries=3): system_prompt = """你是一个 OpenSCAD 代码生成助手。 要求: 1. 所有尺寸单位为毫米 2. 关键尺寸必须定义为顶部变量 3. 只输出代码,不要解释 4. 圆角半径不超过最小板厚的一半 """ for attempt in range(max_retries): code = call_llm(system_prompt, user_input) scad_path = "temp_model.scad" stl_path = "temp_model.stl" with open(scad_path, "w") as f: f.write(code) result = subprocess.run( ["openscad", "-o", stl_path, scad_path], capture_output=True, text=True ) if result.returncode != 0: user_input = f"上次代码执行报错:{result.stderr},请修正" continue mesh = trimesh.load(stl_path) if mesh.volume <= 0: user_input = "生成的模型体积为零,请检查布尔运算" continue return code, stl_path raise RuntimeError("多次生成均失败,请检查输入描述")这段代码的核心在于那个重试循环。每次失败都把错误信息拼回输入,让模型带着“上次错在哪”的上下文重新生成。实测下来,第一次成功率大概六成,加上重试能到九成以上。call_llm是调用大模型的封装函数,根据自己用的接口实现即可。
4.3 参数计算的实际案例
拿一个具体例子走一遍。用户输入:“做一个 100x80x8 毫米的底板,四角各有一个 M6 的沉头孔,孔中心距边缘 12 毫米。”
模型生成的代码大致是这样:
// 参数定义 plate_length = 100; plate_width = 80; plate_thickness = 8; hole_dia = 6.5; // M6 通孔标准间隙 counterbore_dia = 11; // 沉头直径 counterbore_depth = 4; edge_offset = 12; difference() { cube([plate_length, plate_width, plate_thickness], center=true); for (x = [-1, 1], y = [-1, 1]) { translate([ x * (plate_length/2 - edge_offset), y * (plate_width/2 - edge_offset), 0 ]) { cylinder(h=plate_thickness+2, d=hole_dia, center=true); translate([0, 0, plate_thickness/2 - counterbore_depth/2]) cylinder(h=counterbore_depth+1, d=counterbore_dia, center=true); } } }这里有几个细节值得说。M6 螺栓的通孔直径取 6.5 毫米而不是 6 毫米,这是机械设计的常规做法,留出装配间隙。沉头直径 11 毫米对应 M6 沉头螺钉的标准头部尺寸。孔的位置用for循环加正负号生成四个对称位置,比写四遍translate简洁得多。这些细节模型不一定每次都能处理对,所以我在提示词里会补充“M6 通孔按 6.5 毫米生成”这类工程约定。
4.4 结果导出与后续处理
生成 STL 后,如果只是看形状,直接扔进任意三维查看器就行。但如果要进入正式设计流程,我建议把 OpenSCAD 源码也一并保留,因为 STL 是网格格式,丢失了参数信息。后续要改尺寸,改源码重新导出即可。
如果需要更高精度的工程模型,可以把 OpenSCAD 代码转成 STEP 格式。这需要借助 FreeCAD 的命令行工具做格式转换。转换后的 STEP 文件可以导入主流 CAD 软件,继续做工程标注和装配设计。这一步我一般只在模型确认可用后才做,避免在中间产物上浪费时间。
5. 常见问题与排查技巧实录
5.1 生成代码报错怎么快速定位
代码报错是最常见的问题,我整理了一张速查表,覆盖八成以上的报错场景。
| 报错信息关键词 | 根本原因 | 解决方向 |
|---|---|---|
| syntax error | 括号或分号缺失 | 检查代码块配对 |
| unknown variable | 变量未定义就使用 | 确认变量声明顺序 |
| child object required | 布尔运算参数为空 | 检查 difference 内是否有实体 |
| CGAL error | 几何运算失败 | 简化特征或调整尺寸 |
| empty geometry | 结果为空实体 | 检查减运算是否减没了 |
排查时我的习惯是:先把生成的代码单独在 OpenSCAD 图形界面里跑一遍,看报错行号,比在命令行里盲猜快得多。图形界面会高亮出错位置,定位效率高很多。
5.2 模型尺寸不对的排查思路
尺寸偏差通常有三个来源。第一是单位理解错误,模型把毫米当成了厘米。第二是坐标系理解偏差,center=true和默认居中的区别会导致位置偏移。第三是特征叠加时的尺寸累积误差。
我的排查方法是:生成后先量包围盒,和预期尺寸比对。如果整体差 10 倍,基本是单位问题。如果位置偏了但尺寸对,检查translate的基准点。如果局部特征尺寸不对,单独把那个特征的代码拎出来测试。这个“先整体后局部”的排查顺序,能快速缩小问题范围。
5.3 复杂模型生成失败的应对策略
描述越复杂,生成成功率越低,这是必然的。我的应对策略是分而治之:把复杂模型拆成几个简单部件,分别生成,最后用布尔运算组合。比如一个带支架的盒子,先单独生成盒子,再单独生成支架,最后合并。
另一个技巧是逐步细化。先让模型生成一个粗略版本,确认整体结构对了,再在提示词里逐步加细节。一次加一个特征,每次生成后验证。这样虽然步骤多了,但每步都可控,总体成功率反而更高。我试过一次性描述一个带十几个特征的复杂零件,失败率极高;拆成五步后,每步都顺利通过。
提示:复杂模型不要指望一次生成到位。把大模型当成一个需要明确指令的助手,而不是能读心的大师,分步骤引导效果最好。
5.4 提升生成质量的几个实操心得
第一个心得是给示例。在系统提示里放一两个高质量的代码示例,模型生成的质量会明显提升。这相当于给它打了个样,它知道该往什么风格上靠。
第二个心得是固定变量命名。我要求所有生成的代码都用统一的变量命名规范,比如plate_length、hole_dia这种。命名统一后,后续做参数提取和批量修改都方便很多。
第三个心得是保留生成历史。每次生成的代码和对应的输入描述都存下来,积累一段时间后就是一份高质量的微调数据集。我攒了几百条后,用这些数据做了简单的提示词优化,生成成功率又上了一个台阶。
第四个心得是人工兜底。再好的自动化也有失败的时候,所以流程里一定要留人工介入的口子。我的做法是重试三次失败后,把最后一次的代码和错误信息展示给用户,让用户决定是修改描述重试,还是手动改代码。这个兜底机制让整个流程的可用性提升了很多。
6. 这个方向后续还能怎么玩
把基础流程跑通后,我陆续尝试了几个扩展方向,效果都还不错。一个是批量生成,把一份参数表读进来,批量生成一系列尺寸不同的零件,适合做标准件库。另一个是反向解析,给一个现有模型,让模型反推出生成它的代码,用于理解别人的设计。还有一个是多轮对话式修改,生成初版后,用户继续说“把孔改大一点”“加个圆角”,模型在上一版代码基础上增量修改,体验比每次重新描述好很多。
这些扩展方向的共同点是:都建立在基础流程稳定可靠的前提上。所以我的建议是,先把单次生成的准确率打磨到位,再考虑这些花活。基础不牢,扩展越多问题越多。我在实际项目里的体会是,text-to-cad 这个方向技术门槛不算特别高,但工程细节特别多,每一个环节的打磨都需要耐心。把提示词、校验、重试这三件事做扎实,基本就能得到一个可用的系统了。