做设计的人应该都经历过这样的时刻:脑子里已经构建出完整的零件造型,参数、结构、装配关系清清楚楚,但打开CAD软件对着屏幕却无从下手。要么是草图约束反复报错,要么是圆角倒角顺序搞错,模型怎么都生不出来。我最近几个月一直在死磕一个方向,就是text-to-cad,用自然语言直接生成CAD模型。简单说,你输入一句“设计一个内径20毫米、外径35毫米、高度15毫米的法兰盘,带六个均布安装孔”,它就能直接输出一个可编辑的STEP文件。这套工作流对机械设计、3D打印爱好者、自动化设计工具开发者的价值非常大,今天把我自己的完整实操经验和踩过的坑都整理出来。
text-to-cad的核心逻辑不是让AI凭感觉生成一团面片,而是把大语言模型当作一个会写参数化代码的绘图员,用自然语言描述设计意图,由模型生成脚本,再在本地编译成真正的CAD模型。这个思路和我一开始想的完全不同,我最初以为会是某种端到端神经网络直接输出网格,但深入了解之后发现,沿着代码生成这条路走,才能保证精度、可编辑性和可制造性。
1. 为什么text-to-cad可行:一句话说清技术逻辑
很多人在第一次接触text-to-cad时会问:这不就是把用户输入丢给大模型,让它随便画吗?其实完全不是。要理解这个方向为什么成立,关键要明白CAD模型在计算机内部到底是怎么表达的。
1.1 从“描述到模型”的本质是把设计意图翻译成参数化代码
机械设计里的零件从来不是一堆无序三角面片堆出来的。一个真正的CAD模型,内部存储的是特征树和参数约束:拉伸、旋转、打孔、倒角、阵列,每一步都有明确的几何参数。比如一个法兰盘,在CAD软件里可能是“先在XY平面画外圆,再画内圆草图,拉伸15毫米,再在圆周上阵列六个孔”。这种表达方式天然结构化,每一步都是可参数化的操作。
大语言模型最擅长的恰恰就是这种“结构化生成”。它不直接生成几何体,而是生成一段描述这些操作的代码。目前主流的两条路线,一是生成CadQuery的Python脚本,二是生成OpenSCAD脚本。CadQuery更接近工业CAD的特征建模思路,OpenSCAD更接近程序员做3D打印的CSG布尔建模。本质上都一样:模型输出的是“怎么做”的逻辑,而不是“长什么样”的像素。
这个转变带来一个非常重要的优势:模型不需要自己推理空间几何形状,只需要把问题拆解成一系列可以执行的操作序列。空间计算交给几何内核来完成,比如CadQuery下面跑的是OCCT内核,和FreeCAD、Salome的几何内核同源,精度和稳定性有保障。
1.2 为什么是代码生成,而不是直接生成网格
我见过不少人尝试用AI直接生成STL网格,效果都不理想。原因很直观,大语言模型天生是文本模型,你让它输出一个包含几万到几十万个三角形顶点坐标的二进制文件,本质上是在要求它背诵一串它根本理解不了的长随机数序列。这个方向在数学上就不可能稳定。
更致命的是网格文件的工程价值很低。STL只有表面三角形信息,没有特征树、没有参数约束、没有设计历史。你拿回来一个STL,改不了尺寸,删不掉一个孔,只能当成一块“石头”去加工。这对工业设计场景基本没有用。
代码生成路线解决的正是这个问题。设计意图被固化为Python脚本,脚本里每一行都有明确的工程语义。想改壁厚?改一个数字就行。想增加阵列数量?改一个参数就行。脚本本身还携带了注释和命名,可以作为团队的知识资产沉淀下来。这也是为什么我在很多技术讨论里反复强调:text-to-cad的正确落地路子是“LLM + 参数化脚本 + 几何内核”,而不是“LLM + 三维网格”。
2. 技术路线与工具选型:两条主线如何选
既然核心是代码生成,接下来的问题就是选哪条技术路线来生成代码。我实际试过CadQuery、OpenSCAD和SolidPython三条路线,各有优劣,结合自己的使用场景来选,别盲目跟风。
2.1 基于OpenSCAD的路线:语法简单、上手快、打印友好
OpenSCAD是目前对LLM最友好的CAD脚本语言之一。它的语法极其简洁,没有复杂的类继承和对象模型,核心就是矩形、圆形、圆柱、立方体,然后做差集、并集、交集。这种函数式的CSG建模方式和编程语言初学者写代码差不多,LLM很难“写错”,因为它需要的抽象层次很低。
OpenSCAD生成的模型非常适合3D打印。我测试过很多次,只要布尔运算逻辑没毛病,输出的STL都非常干净,水密性容易保证,切片的成功率很高。但它的缺点同样明显:生成的模型是纯多面体,没有特征历史,也没有B-rep边界表示的精确曲面信息。遇到需要圆弧精度极高的配合面时,OpenSCAD的网格近似可能不满足要求。
还有一个实际坑:OpenSCAD对圆角的处理比较弱,大量使用offset或minkowski会让模型生成速度急剧下降,复杂模型动辄要算几十秒甚至几分钟。LLM如果被要求做很多圆角,生成的代码性能会非常差。
2.2 基于CadQuery的路线:贴近工程制造、精确建模的关键
CadQuery是我在工业级应用中的首选。它构建在OCCT几何内核之上,支持精确的B-rep建模,导出的STEP格式可以直接进入主流CAM软件做数控编程,甚至可以进有限元分析的流程。这意味着真实制造业的精度需求是被完全满足的。
CadQuery的API设计走的是“自顶向下”的特征建模思路:创建二维草图、拉伸、开孔、布尔运算、倒角、阵列。这个思路和SolidWorks、Fusion 360里的特征树是同一个抽象层级的,LLM生成的代码只要逻辑清晰,几乎不会出现非流形实体或者拓扑错误。
当然,CadQuery的语法比OpenSCAD复杂得多。它的API有很多细节,比如Workplane的坐标系变换、.faces(">Z")的选取方式、cutBlind和throughAll的区别,这些细节对LLM来说都是易错点。但好消息是,只要提示词写得好,多轮迭代几次,模型能自我修正大部分问题。
2.3 两条路线对比与选择建议
| 对比维度 | OpenSCAD | CadQuery | 我的选择建议 |
|---|---|---|---|
| 语法复杂度 | 极低,接近伪代码 | 中等,类似Python面向对象 | LLM生成的OpenSCAD更稳定 |
| 几何精度 | 网格近似,有误差 | 精确B-rep边界表示 | 制造业必须用CadQuery |
| 输出格式 | STL/3MF/AMF | STEP/STL/SVG/DXF | 需要STEP选CadQuery |
| 可制造性 | 适合3D打印 | 适合CNC/打印/注塑 | 按工艺选 |
| 生成性能 | 复杂圆角极慢 | 中等,依赖特征数量 | 性能敏感选CadQuery |
| 文件大小 | 网格大 | 参数化脚本极小 | 长期维护选CadQuery |
我现在的固定搭配是:快速打样、验证想法时用OpenSCAD;真正要做零件、出加工文件或者进装配体时用CadQuery。两者都需要和后端构建一套自动化的编译验证链路,这一点没有区别。
2.4 LLM模型怎么选:开源还是闭源,通用还是专用
模型选择上不需要过度纠结,通用大模型其实已经够用了。我实测过GPT-4o系列、Claude的Opus系列,以及几款国内开源模型如DeepSeek-V3和Qwen系列,它们在CadQuery和OpenSCAD两种语言的代码生成上都有不错的表现。关键差异在于代码推理能力,而不在于参数规模。一个能稳定处理“先拉伸再打孔再阵列”这种多步推理链的模型,比单纯参数大的模型要实用得多。
多模态能力是加分项但非必需。如果输入的是手绘草图或者参考图,那么多模态模型能把视觉信息转化为设计参数;如果输入纯粹是文字描述,文本模型足够。我在本地部署场景一般选Qwen系列的代码专用版本,响应速度和离线能力更均衡。在线场景用闭源模型,真要长上下文反复迭代时,闭源模型的窗口管理更省心。
3. 从零搭建text-to-cad工作流:一套可以复用的工程实践
讲完了选型逻辑,下面是整个项目里价值密度最高的部分:一套可复用的text-to-cad工程化工作流。这套流程我踩了很多坑才稳定下来,现在拆开来讲,可以直接照着搭。
3.1 环境准备:只装必要的东西,别铺张
工作流的核心依赖只有三项:Python环境、CadQuery库(可选)、OpenSCAD命令行工具。CadQuery的安装要注意版本匹配,我遇到过Python 3.12下旧版CadQuery装不上依赖的情况,建议直接用官方推荐的Python 3.10或3.11,然后执行:
pip install cadquery这个命令会连带装好OCCT内核绑定。安装完验证一下:
python -c "import cadquery as cq; print(cq.__version__)"能输出版本号就说明内核通了。OpenSCAD从官网下载安装包后,需要把可执行文件加入系统PATH,因为我们要在Python里调用它的命令行接口做编译。这一步很多人会忽略,后面自动化脚本里调用openscad命令报错的时候才意识到。
3.2 提示词模板设计:把约束全部写进上下文
我强烈建议不要直接甩一句“帮我画一个法兰盘”给模型,那样生成的代码大概率不合意。正确做法是把设计约束、单位、输出格式、禁止事项全部写进提示词。我目前的提示词模板大致长这样:
你是一名资深机械设计工程师。请根据以下需求生成CadQuery Python脚本: - 单位:毫米(mm)。 - 模型必须位于原点附近,底面保持在Z=0平面。 - 主要几何尺寸:内径20,外径45,厚度8。 - 六角凸台:六角外接圆半径18,高度6,位于顶面中心。 - 中心通孔直径8。 - 六个安装孔:直径4.5,均布在半径28的圆上。 - 代码必须使用cadquery库,最终通过cq.exporters.export(result, "output.step")导出STEP。 - 不要使用未定义的变量,不要写多余的调试输出,不要使用不存在的CadQuery API。关键点是:把尺寸作为显式数字写清楚,而不是让模型自己决定;明确指定基准面和Z=0底面;给出导出语句;禁止模型发挥不存在的API。这套约束下来,代码的可用率能提高一大截。我在一次测试中,带约束的提示词生成代码一次通过编译的概率接近七成,而裸提示词只有两成左右。
3.3 自动化验证闭环:生成、编译、渲染、审查一条龙
代码生成了不是终点,必须建立一个自动化的验证闭环。我的流程分为四步:
- 模型生成代码后保存为
model.py。 - Python编译检查:
python -c "import ast; ast.parse(open('model.py').read())",这一步做基础语法验证。 - 执行脚本,导出STEP和STL文件。如果执行过程抛出CadQuery异常,把异常信息反馈给模型,让它自己修复。
- 用OpenSCAD命令行导出PNG预览图,或者直接用CadQuery的
exporters把模型转成SVG视图,人工快速判断形状是否合理。
自动化闭环的实质是把“编译、执行、反馈”做成一个循环。我写过一个简单的执行脚本:
import subprocess import sys import json def run_pipeline(code: str, output_prefix: str): with open("generated_model.py", "w", encoding="utf-8") as f: f.write(code) try: result = subprocess.run( [sys.executable, "generated_model.py"], capture_output=True, text=True, timeout=30 ) if result.returncode != 0: return {"success": False, "error": result.stderr[-2000:]} return {"success": True, "info": result.stdout[-1000:]} except subprocess.TimeoutExpired: return {"success": False, "error": "timeout"}推荐把整个流程封装成函数,这样后面接入LLM的多轮迭代循环会非常方便。每轮模型生成代码,脚本执行,把错误信息回传,模型修改,再执行,形成一个稳定的反馈回路。
3.4 多轮迭代策略:别一次求完美,让模型自己改错
text-to-cad和纯文本生成最大的区别在于:代码是可以反复执行的。这意味着你完全可以采用“先出草图,再迭代优化”的策略。我的迭代习惯是:
第一轮只求结构骨架对,尺寸不准没关系。第二轮根据预览图修正尺寸和比例。第三轮做细节收尾:倒角、圆角、螺纹孔、阵列。每轮都把上一轮的报错信息和渲染图传给模型,模型自己会修正。
这里有一个非常重要的经验:不要把错误信息一股脑全塞给模型。CadQuery的报错信息有时非常长,包含大量堆栈追踪信息,占满上下文窗口反而会干扰模型判断。我通常只截取最后500到1000个字符的错误摘要回传,同时附上一句指令:“根据错误信息修复代码,确保能一次通过编译”。效果比用完整报错提升明显。
4. 常见问题与排查技巧实录:坐标、单位、布尔运算的四个坑
实操跑题多了,发现text-to-cad看起来门槛不高,但真正稳定输出能用的模型,要翻过不少暗坑。我把最常见的几类问题整理出来,按频率排序,每条都附上排查思路。
4.1 单位与尺寸不匹配:毫米被当成英寸
大模型的训练数据里,英制单位出现的频率非常高。我一开始跑一个300毫米的长条零件,模型生成出来只有7.62毫米。一看代码,模型把输入的“300 mm”理解成了英寸,内部转换了25.4倍。这个错误非常隐蔽,因为编译不会报错,模型也能正常生成,但尺寸就是不对。
排查办法是强制在提示词里声明“所有数值均为毫米,不要转换单位”,同时在自动验证脚本里加一个体积或体积包围盒检查:如果生成的模型体积和预期差两个数量级以上,直接判定尺寸异常,让模型重生成。这个检查我用过很多次,能拦住一大半离谱结果。
4.2 坐标基准错误:零件飞到空中或插进地面
还有一次我要求模型“圆柱体站立在Z=0平面上”,它生成的圆柱中心在原点,一半位于Z轴负方向。原因是CadQuery默认的圆柱构造方式是两端拉伸,如果不指定起始平面,模型就会跨越原点对称分布。输出STEP后,导入其他CAD软件一看,零件整体悬空。
这类问题的根源在于CAD坐标系的“基准面”和“拉伸方向”概念,LLM有时候会搞混。解决办法是在提示词里明确写一句“确保模型最低点位于Z=0平面”,然后生成后用形状包围盒检查:
shape = result.val() bb = shape.BoundingBox() print("zmin:", bb.zmin, "zmax:", bb.zmax)如果zmin偏离0超过1毫米,就反馈给模型:“模型最低点不是Z=0,调整坐标使底面落回Z=0平面”。
4.3 布尔运算失败导致非流形实体
布尔运算是CAD建模里最危险的操作,也是最让LLM头疼的。当两个特征恰好在某个点上相切,或者完全共面时,OCCT内核会报“non-manifold”错误,整个脚本直接崩溃。我遇到过一个非常经典的案例:要求法兰盘的圆柱体和底座完全对齐,两个面完全重合,布尔求交时直接报错。
工程实践上的解法有两个。第一个是避免精确共面:把圆柱体底座稍微高出0.01毫米,然后用“切掉多余部分”的方式修正。第二个是引导模型使用“从底面向上拉伸”的操作模式,减少隐式布尔。CadQuery的Workplane操作之间本身有的就是链式特征累加,有的则是布尔差集,提示词里明确告诉模型“优先使用工作平面上的草图拉伸,少用对象之间的并集布尔”,代码稳定性会有质的提升。
4.4 特征嵌套太深导致性能爆炸
有一次我让模型生成一个布满散热鳍片的外壳,它直接用for循环生成了一百多个重复特征。编译没有报错,但导出STEP花了十几分钟,文件大小超过200兆,直接把内存吃满。这不是逻辑错误,而是算法复杂度失控。
排查方法:在验证脚本里对导出的STEP文件大小做限制,超过50MB直接判定失败;同时检查生成的代码里是否有可疑的for循环嵌套。更优的解法是提示词里对这类情况做约束:“阵列和重复特征请使用CadQuery的Array操作或者polar系列方法,不要使用Python的for循环逐个建模”。这条约束能几倍地缩短生成时间,也让模型更符合CAD软件的建模习惯。
5. 质量评估:怎么知道模型“造得好不好”
很多人做text-to-cad只关注到“代码能不能跑通”就结束了,但一个真正有价值的环节是质量评估。能不能筛选出高质量生成结果,决定了这套工作流能否在日常设计中真正顶用。
5.1 客观尺寸指标与参数检查
最直接的评估手段是让生成模型和预期设计参数做对比。以法兰盘为例,预期是外径60、内径20、高度15,生成模型可以直接读取几何数据来检验:
import cadquery as cq result = cq.importers.importStep("output.step") bb = result.val().BoundingBox() outer_cyl = result.faces(">Z").val().Area()更合理的做法是检查模型的包围盒尺寸和关键特征的数量。比如安装孔的数量可以用面数统计来核对。这类指标可以自动化执行,适合批量筛选模型。我给工作流加了一个简单规则:尺寸误差超过5%,直接判定失败,不进入人工审核环节。
5.2 可制造性与建模规范性检查
生成模型是否“能造出来”,比是否好看重要得多。3D打印场景要检查最小壁厚、悬垂角度;CNC加工场景要检查最小圆角半径;注塑场景要检查拔模斜度。这些检查在CadQuery里都能通过分析面的曲率和法线方向来做,但工程上更省事的方法是直接导入切片软件或CAM软件里做一次仿真。
还有一个规范性检查值得做:确认模型是否全部是闭合实体,没有自由边、没有薄壳结构。用shape.isValid()和shape.isWatertight()做布尔判断,非常方便。我在实际项目里遇到过模型从表面看挺像样,但其实是几个分离的薄壳拼在一起的情况,这种模型到了切片阶段就废了。提前用自动检查拦住,是这套流程最有价值的收益。
5.3 典型质量评估表格
| 检查项 | 检查方法 | 合格标准 | 我的执行建议 |
|---|---|---|---|
| 编译通过 | 执行Python脚本 | 返回码为0 | 每次生成后必查 |
| 几何有效 | Shape.isValid() | 返回True | 在导出STEP前检查 |
| 水密实体 | Shape.isWatertight() | 返回True | 3D打印前必查 |
| 尺寸正确 | 对比包围盒与预期 | 误差<5% | 自动化对比 |
| 特征完整 | 统计孔数、阵列数量 | 与预期一致 | 人工抽检或写断言 |
| STEP可读性 | 换软件导入验证 | 无报错无破面 | 至少选一款专业CAD验证 |
这套表我直接固化在自动验证脚本里,每次跑完自动打分。评分太低的自动触发重新生成,评分中等的人工决定是否保留。这个评估意识会让text-to-cad真正变成可信赖的设计工具,而不只是一个玩具级Demo。
6. 我在实际项目里的一段完整样例
前面讲了很多方法论,可能有点抽象。这里给大家展示一个完整的典型样例:用自然语言生成一个带凸台的电机安装座。从输入到最终STEP,我把整个流转过程拆开来看。
我给的描述是:一个正方形底座,边长100毫米,高10毫米,四个角各有直径8毫米的安装通孔,中心有一个直径30毫米高20毫米的圆柱凸台,凸台中心有一个直径12毫米贯穿的通孔。
首轮生成的CadQuery代码大致长这样:
import cadquery as cq base = ( cq.Workplane("XY") .rect(100, 100) .extrude(10) ) mounting_holes = ( base.faces(">Z").workplane() .rect(80, 80) .vertices() .hole(8) ) boss = ( mounting_holes.faces(">Z").workplane() .circle(15) .extrude(20) ) center_hole = ( boss.faces(">Z").workplane() .circle(6) .cutBlind(-20) ) cq.exporters.export(center_hole, "motor_mount.step")这一版第一次跑就编译通过了,但做包围盒检查时发现zmin是0而zmax是30,底座10毫米加上凸台20毫米,C是30,这是符合预期的。不过盲检发现中心孔没有贯穿到底座,只切了凸台的20毫米,底座的10毫米还是实心。反馈给模型:“中心通孔需要贯穿整个模型,从凸台顶面一直穿出底座底面”,模型很快修正为cutBlind(-30)或在底面上单独开孔。
整个过程耗时不到两分钟,其中大头是模型生成时间,本地编译和检查基本秒级完成。这个效率对比传统手工建模,优势非常明显,尤其是在出方案初期“要快速确认这零件长什么样、能不能装”的场合,价值极高。
7. 最后再分享一个小经验
text-to-cad这个方向我玩到现在,最大的体会是:别把它当成“AI自动画图”,一定要把它当成“AI辅助下的参数化建模流程”。它不是为了替代设计师,而是替代那些重复的、机械的、描述成本高于建模成本的操作——标准件建模、相似零件变体、对外协方案的快速验证。你描述得越清晰,它输出得越准确。
如果要做整套流程的产品化部署,我建议优先把“提示词模板库”做起来。每次生成完一个成功的模型,就把对应的描述和代码沉淀下来,作为后续任务的标准用例。这套模板库积累得越多,模型在特定领域里的表现就越稳定,最终会形成你个人或团队专属的text-to-cad知识资产。这和当年大家对“提示词工程”的热情是一样的,只是这里的回报更加可量化——直接体现在从需求到STEP文件的转化率上。