最近我给自己搭了一条“用大白话生成CAD模型”的流水线,起因是团队里总有人甩一句“帮我画个支架,上面留几个孔”就消失。这类需求说难不难,但每次手动建模、调参、改约束,折腾下来至少半小时。text-to-cad 要解决的正是这个问题:让自然语言直接变成可编辑、可参数化、能进生产流程的CAD模型,而不是一张看着像那么回事的渲染图。
这套东西现在还在快速迭代阶段,但已经不再是论文里的demo。数据集的成熟、LLM对程序化建模代码的生成能力、以及CadQuery/OpenSCAD这类“代码即模型”工具的普及,三者碰到一起,让“一句话出模型”变成了可以落地的日常效率工具。如果你做过机械设计、结构件打样,或者经常被“就一个小东西,很快的”这种需求砸脸,这篇内容值得看完。
1. text-to-cad 是什么:从一句需求到三维实体的完整链路
1.1 核心需求解析
先给没接触过这个概念的朋友一个准确定位。text-to-cad 不是“文生图”,它生成的不是图片、不是网格表面,而是真正意义上的CAD模型——带特征树、带参数、带约束、可以继续编辑和出工程图的那种。
举个具体例子。你输入:
生成一个长60、宽40、高20的长方体底座,四个角做R5圆角,中心挖一个直径12的通孔,底座底面再挖一个深度2的沉台。
文生图模型会给你一张好看的示意图,但尺寸是猜的、孔不是真的孔、圆角只是视觉圆角。而text-to-cad系统会把它转成一段程序化建模代码,跑完之后得到的是一个实体模型:圆角可调、孔可改位置、沉台深度可以参数化驱动。这个区别对制造业来说就是“能不能用”的分水岭。
整条链路拆开看,包含四个核心环节:
- 语义理解:把自然语言里的尺寸、形状、空间关系、特征操作(圆角、倒角、挖孔、拉伸、旋转)识别成结构化意图。
- 几何生成:根据意图构造实体几何,涉及裁剪、布尔运算、扫掠、放样等底层操作。
- 参数化建模:把几何生成过程映射到带参特征,而不是一次性固化一个死模型。
- 约束求解:处理平行、垂直、相切、同心、固定尺寸等约束关系,保证后续编辑不塌。
这四个环节里,前三环在2024年前后已经有不少工具能跑通大半,第四环是最大的硬骨头,后面我会展开讲。
1.2 为什么是现在火起来
text-to-cad 概念其实很早就有人提,但之前基本处于“能做demo、不能干活”的状态。我总结下来有三个关键变化让它在最近一两年变得真正可用。
第一个变化是LLM对程序化建模代码的生成能力质变。早期模型生成CadQuery脚本基本属于碰运气,十条能跑通两三条就算惊喜。现在的主流模型配合好的提示词和上下文约束,成功率可以做到七八成。原因在于程序化建模语言的语义空间比自由自然语言窄得多,API是固定的、几何操作是有穷的,LLM在这种受限空间里的表现远比开放式创作稳定。
第二个变化是训练数据从“有”到“足”。Text2CAD数据集的公开以及一批合成数据管线的成熟,让模型见过足够多的“描述-建模代码-实体结果”三元组。模型不再只是靠代码仓库里零散的开源脚本泛化,而是真正见过“人类怎么描述一个零件,以及对应的建模过程”。
第三个变化是CAD内核与脚本语言之间的鸿沟被填平。CadQuery、build123d、OpenSCAD这些工具把Parasolid/ACIS级别的内核能力封装成了可编程接口,让LLM只需要生成逻辑代码,而不用去处理底层网格、B-Rep和拓扑修复。你让模型直接写C++去调内核API,它基本无能为力;你让它写CadQuery,它就轻松很多。工具链把复杂度的天花板降下来了,这是路线胜利。
2. 实现路线的选型分析:三条主流技术方案的取舍
2.1 端到端生成、程序化脚本、参数化API:谁更适合真实生产
目前公认的text-to-cad实现路线大致有三条,我用一个表格先横向对比,再逐个展开说。
| 路线 | 输出形态 | 可编辑性 | 精度 | 对硬件要求 | 典型工具/代表 |
|---|---|---|---|---|---|
| 端到端生成(点云/隐式场) | 网格模型或SDF | 差,需重拓扑 | 中低,细节丢失 | 高,通常需GPU推理 | 学术界较多,Text2CAD等实验性工作 |
| LLM + 程序化脚本 | 参数化实体 | 好,代码即参数 | 高,受代码逻辑控制 | 低,普通CPU即可 | CadQuery/build123d/OpenSCAD |
| LLM + 参数化API | 原生特征树+约束 | 最好,完全可继续编辑 | 最高 | 低,依赖CAD内核环境 | FreeCAD Python API、Fusion 360 API、Rhino/Grasshopper |
端到端生成路线是学术界最爱做的方向:直接把文字描述丢进一个大模型,输出体素、点云或者带符号距离场,再Marching Cubes提取网格。这条路从视觉上看很性感,一张图一个模型,呼啦一下出来。但落到实际生产就露馅了:输出的网格模型没有特征树、没有参数,孔是“画上去”的凹陷而不是贯穿的圆柱孔,倒角位置一旦需要调整,整个模型就得重新生成。更麻烦的是精度,端到端输出的尺寸误差经常在几个百分点左右,看似不多,但对于配合公差0.05毫米的机械件来说,等于完全不可用。所以我个人对这条路的定位是:适合早期概念预览,不适合实际交付。
LLM + 程序化脚本路线是目前社区体验最好的组合。核心逻辑是让模型生成CadQuery或OpenSCAD代码,本地执行后得到实体模型。CadQuery的好处是它是真正的Python库,直接映射了内核级布尔运算、扫掠、放样、圆角等操作,代码风格清晰。OpenSCAD则强在纯几何描述和极低的运行环境要求。这个路线的可编辑性体现在“代码即参数”——想改尺寸,改代码里的变量再重跑就行。配合CadQuery的cq-editor热重载,体验已经逼近商业CAD软件。
LLM + 参数化API路线是最接近“完全体”的方案。模型直接调用FreeCAD或Fusion 360的底层API,生成的是原生特征树和参数约束。好处不用多说:用户打开软件就能看到左侧特征历史、双击尺寸就能编辑,和人工建模产物完全一致。问题在于API调用链太长,一个简单的Pad操作背后有大量对象创建、坐标系转换、文档刷新逻辑,LLM在这种场景下的错误率明显高于写CadQuery代码。目前我见过能稳定跑通的也基本局限在拉伸、旋转、开孔等基础特征组合,复杂装配、多实体布尔、曲线驱动阵列还是得人工兜底。
2.2 三条路线的取舍建议
给不同需求的读者一个可以直接抄的选型方向。
如果你只是自己画些快速概念件、外壳、底座,不在乎特征树是否标准,首选LLM + CadQuery路线,上手成本低、成功率最高、出错好排查。
如果你需要交付给下游工程师继续精修,且他们用的是FreeCAD或Fusion 360,那值得投入精力调优LLM + 参数化API路线。但建议先把生成范围限定在“拉伸+孔+圆角”这类基础特征,不要一上来就让模型自由发挥。
如果你纯粹想体验“一句话变模型”的爽感,或者做前期外观探索,可以玩玩端到端生成的工具。别把它当CAD用,当快速草模生成器就好。
3. 实操演示:用LLM + CadQuery把一句话变成可编辑实体
3.1 环境准备与工具链选择
我日常使用的组合是CadQuery + 任意一款主流的代码生成LLM。CadQuery的安装非常简单,Python 3.9以上环境里直接执行:
pip install cadquery为了验证生成结果,建议再装一个可视化模块:
pip install jupyter-cadqueryJupyter环境下它能以内嵌3D视图的方式实时展示模型,对排查几何问题特别有用。
LLM这边,我两种方式都在用:本地部署Ollama跑开源模型,网络请求走API调用闭源模型。实际体验下来的结论是:只要代码能力不是特别弱,两者都能产出可用的CadQuery代码。区别主要在复杂几何的推理能力上,遇到需要多特征组合才能实现的描述,闭源模型的成功率明显更高。如果你手头没有API预算,先用开源模型把基础流程跑通,再考虑升级。
提示:不要直接在宿主机上执行LLM生成的代码。这不是信不过模型,而是它偶尔会生成删除文件、调用网络、读取环境变量的危险操作,概率不高但后果难料。用Docker容器或至少隔离开的沙箱环境执行,这是一条底线。
3.2 从文本到代码再到实体的完整流程
下面是整个流程的骨架。我用一个非常典型的零件需求走一遍。
需求文本:
创建一个长度80、宽度50、高度15的平板。四角做R8圆角。板面上均匀布置5个直径6.5的安装孔,沿长度方向等距排列。在板的一端做一个10x10的凸台,带M6螺纹孔。
这个需求包含四个特征:基础拉伸、圆角、阵列孔、凸台与螺纹孔。我用一段稍作引导的提示词让模型输出CadQuery代码:
你是CadQuery建模专家。请根据以下需求生成完整的CadQuery Python代码: 创建一个长度80、宽度50、高度15的平板。四角做R8圆角。 板面上均匀布置5个直径6.5的安装孔,沿长度方向等距排列。 在板的一端做一个10x10的凸台,带M6螺纹孔。 不要使用导入后直接执行的方式,把代码封装在build函数中。模型给出的代码大致长这样:
import cadquery as cq def build(): # 基础平板 plate = cq.Workplane("XY").box(80, 50, 15, centered=(True, True, False)) # 四角圆角 plate = plate.edges("|Z").fillet(8) # 5个安装孔,沿长度方向等距 plate = plate.faces(">Z").workplane() \ .transformed(offset=(0, 0, 0)) \ .rect(80, 50, forConstruction=True) \ .vertices() \ .holes(6.5) plate = plate.faces(">Z").workplane() \ .center(-30, 0) \ .rect(10, 10, forConstruction=True) \ .vertices() \ .hole(6.5) return plate result = build() # 导出STEP用于生产 cq.exporters.export(result, "plate.step")等等,这段代码是有问题的。box(80, 50, 15, centered=(True, True, False))之后,底面的Z坐标在0,顶面在15。直接faces(">Z")选顶面没问题。但安装孔用rect(80, 50, forConstruction=True).vertices().holes(6.5)会在四角生成孔,而不是“均匀布置5个”。凸台也没有正确建模,只是一个孔。这正是我要演示的核心问题——LLM输出代码经常“看起来对,细节全错”,所以必须建立校验习惯。
3.3 修正与参数化:从“能跑”到“正确”
上面的代码能跑通,但需求里的“5个等距孔”和“凸台带螺纹孔”都没实现。这时不能回去重新生成,而是跟模型对话修正,或者手动调整。我更推荐先手动把参数逻辑理清楚,再让模型基于修正后的模板学习。
等距5个孔,沿80长度方向分布,可以放在中线上,从一端偏移8开始,间隔(80-16)/(5-1)=16。用CadQuery实现:
import cadquery as cq def build(): plate = cq.Workplane("XY").box(80, 50, 15, centered=(True, True, False)) plate = plate.edges("|Z").fillet(8) # 等距5个通孔,沿长度方向 for i, x in enumerate([-32, -16, 0, 16, 32]): plate = plate.faces(">Z").workplane().center(x, 0).hole(6.5) # 端部凸台:先画10x10x10的立方体,叠加在平板端部 boss = cq.Workplane("XY").box(10, 10, 10, centered=(True, True, False)) \ .translate((40, 0, 15)) plate = plate.union(boss) # 螺纹孔:用tap参数表示M6螺纹底孔 plate = plate.faces(">Z").workplane().center(40, 0).hole(5.0, tap=6) return plate result = build() cq.exporters.export(result, "plate.step")这段代码把“平板+圆角+5孔+凸台+螺纹孔”完整做出来了。注意hole(5.0, tap=6)是CadQuery创建M6螺纹孔的标准方式,先钻5.0底孔,再模拟攻丝。M6的标准底径是5.0毫米,这是机械设计手册的数据,不是拍脑袋。
3.4 提示词工程的关键参数
实操下来,提示词里有几个参数直接决定生成质量,值得单独说。
单位必须显式声明。不声明单位,模型可能给你输出“长度80”但当成英寸处理,也可能默认毫米。我在提示词里固定加上一句话:“除非特殊说明,所有尺寸均以毫米为单位。”
坐标系基准要绑定到“凸台/特征”。让模型自己选择基准面时容易飘,正确做法是提示“以Z=0为底面,所有拉伸方向沿Z正方向”。这能把Z轴方向约束死。
特征操作顺序要前置限制。先基础体,再布尔/切除,最后做圆角倒角。真实建模逻辑里圆角通常放最后,因为先圆角再挖孔会导致特征依赖关系混乱。在提示词里写明“请在最后执行圆角和倒角”,模型生成的代码可维护性立刻上一个台阶。
让模型封装函数而不是顶层脚本。def build()这种封装能避免变量污染,也方便后续参数化改写。我试过直接让模型输出顶层脚本,十个里有三四个会出现重复变量名导致的重定义。
4. 避坑指南:基于真实项目的六大常见问题
4.1 生成代码跑不通的排查策略
LLM生成的CadQuery代码最常见错误是使用不存在的API。CadQuery版本迭代快,网上很多旧教程还在用cq.Workplane("XY").box(...)这种老写法,但新版本有些方法签名变了。我遇到过模型生成circle(6.5).cutThruAll(),在老版本可行,但新版推荐hole(6.5)。排查顺序建议是:
- 先看语法错误,Python本身报错最直观。
- 再看API是否存在,去CadQuery文档或源码里搜方法名。
- 然后看布尔运算结果,用
test视图或导出STEP后检查实体数量。 - 最后检查几何位置,把关键点的坐标打印出来和需求对比。
这里给一个快速验证实体正确性的脚本:
import cadquery as cq result = build() print("实体数量:", len(result.solids().vals())) print("边界范围:", result.val().BoundingBox())4.2 尺寸精度与单位陷阱
LLM在尺寸计算上的错误率不容忽视。比如“5个孔等距分布在80长度上”这类需求,模型经常把端距设为0或者间隔算错。这类问题用“让模型给出计算过程”的方式能显著缓解——在提示词里加一句“如果涉及均布、等距、对称等关系,请明确写出计算步骤”。模型在一步一步推理时,错误率比直接给答案低一个数量级。
单位陷阱更隐蔽。有些模型在预训练数据里见过大量英制单位的CAD代码,你要求“半径6.35”时它可能输出circle(6.35),但从上下文看它可能认为这是英寸的1/4。我的习惯是每次在返回值前做一次“尺寸合理性检查”:如果一个板子只有15高、孔却有50大,十有八九是单位错了。
4.3 特征顺序与依赖问题
模型生成的代码如果特征顺序不合法,CadQuery可能不报错,但结果完全错误。典型问题是先倒角再挖孔——倒角后的边界面改变了,挖孔的参考线失效。还有先做布尔减再去选“顶面”,结果顶面已经不存在了。这些都只能通过人工检查代码顺序来避免,LLM目前还做不到主动规划完整特征依赖树。
我总结的固定顺序是:基础体 -> 加料特征(凸台、筋) -> 减料特征(孔、槽、切除) -> 阵列/镜像 -> 边处理(圆角、倒角) -> 螺纹属性。把这个顺序直接写进系统提示词,能减少大量“逻辑对但顺序乱”的问题。
4.4 复杂零件的一次生成极限
不要指望一次生成带十几个步骤的复杂零件。我实测下来,单次生成能稳定处理的“信息量”大约是5-7个独立特征。超过这个量,模型就开始出现特征漏掉、重复、参数错位等问题。
正确策略是分步生成、渐进合并:
- 先让模型分别生成主体、孔组、凸台、加强筋这几个独立部件。
- 用
union合并前,分别检查每个部件的坐标系和位置参数。 - 合并后再做圆角倒角。
- 最后统一检查和需求逐条比对。
这个“分而治之”的策略把复杂度摊给了多次模型调用,每次保持高成功率,比赌一次大满贯稳太多。
4.5 Latency等待的体验问题
text-to-cad的等待时间目前是个体验短板。模型生成代码要2-5秒,执行代码要1-2秒,预览刷新要1秒,一轮修正又是3-5秒。迭代5轮就是30秒以上,比人工建模还慢。这个问题没有银弹,只能通过“减少迭代轮数”来缓解。把需求描述写得足够丰满、一次性包含所有约束、附带上下文示例代码,把从“提需求到首次出图”控制在10秒以内,体验才勉强可接受。
4.6 从“能生成”到“能生产”的鸿沟
这是最关键的一个认知。text-to-cad生成的模型,和能进生产线的模型之间,还隔着三条鸿沟:
- 制造工艺检查:模型不会自动判断这个特征适不适合铣削、车削、铸造或3D打印。壁厚太薄、悬垂无支撑、内孔与侧壁干涉,这些都要靠人眼或仿真验证。
- 公差与配合:一个孔标了直径6.5,但没有公差等级,加工师傅没法干。text-to-cad目前输出的都是理想几何,公差语义还得靠后续标注。
- 标准件与规格库:M6螺钉实际模型要配合垫圈、螺母、弹簧垫圈使用,模型只会生成孔,不会自动装配标准件。这需要连接到企业标准件库或在线零部件库,是完整落地绕不开的工程。
我做过的几个打样项目里,text-to-cad主要负责把口头需求快速变成“可以参考的三维方案”,节省的是沟通成本而不是设计成本。真正定稿还是要工程师介入调整参数、补全制造语义。把预期放在“加速概念到初稿”而不是“替代设计”上,这个工具就是利器;预期反了,就会觉得它哪哪都不行。
5. 延伸玩法:让text-to-cad融入BOM驱动的正向设计流程
5.1 从文本描述直接驱动API批量建模
text-to-cad的思路不止于“一句一句生成单个零件”。我现在做零部件系列选型时,经常把表格里的参数转成批量建模脚本。比如一个系列的法兰盘有5种尺寸规格,传统做法是建一个模板再手动改参数。现在可以让模型根据规格表生成CadQuery的参数化代码,变量提取到顶层,循环跑一遍,5个模型全部出来,还能同时导出STEP和工程图所需的几何信息。
这种“文本表格 -> 参数代码 -> 批量模型”的模式,在我看来是text-to-cad现阶段性价比最高的落地场景。它不要求LLM有多强的空间推理能力,只需要能把结构化文本准确映射为代码变量,这正是LLM最擅长的事。
5.2 结合PDM或PLM做版本化
既然text-to-cad的输出是代码,那就天然适合放进git仓库管理。模型迭代、需求变更、参数调整,全部走代码review和版本回退。这比传统CAD二进制文件的管理方式干净得多。我最近的做法是:每个零件一个文件夹,包含generation_prompt.md、model.py、params.yaml三个文件。prompt记录需求来源,model是生成代码,params是参数值。后续改动优先改params,需要改结构才动model。这个流程对团队协作极其友好,新成员看三个文件就能完全理解零件演化史。
5.3 常见问题的快速修复模板
最后分享几个我高频使用的问题修复提示模板。当模型输出的代码存在明显问题时,不必重新描述整个需求,直接把错误信息或者期望的修改内容甩给它:
当前代码报错:{错误信息} 请修复,并且在修复后解释你修改了哪里以及为什么。当前结果是实体数量为{n},但需求中应该只有1个实体。请检查布尔运算步骤是否有重复合并或者多余的实体残留。请把以下尺寸改成参数变量: 长度80 -> width = 80 50 -> depth = 50 15 -> height = 15 并在代码顶部生成params.yaml对应的字典。这三个模板覆盖了绝大多数“代码能跑但结果不对”的场景。用“报错信息+输出差异”来驱动模型修正,比重新抛一遍需求,成功率能提升不少。
我个人在实际操作中的体会是:text-to-cad的价值不在于“让AI替你设计”,而在于“把描述、模型、参数三者之间的鸿沟用自然语言填平”。它让设计意图从人脑到三维模型的传送带,第一次有了可以直接跑通的快速通道。虽然现在还要人工校验、调整和兜底,但省下的都是最枯燥的“把话说清楚再翻译成几何”的环节。这个方向后续还可以延伸到拓扑优化结果的自然语言解析、制造工艺自动标注、以及装配关系的语义驱动,我看好它成为未来三年设计和制造衔接处最值得关注的基础工具之一。