Text-to-CAD 实战:从自然语言到可制造CAD模型的技术路线与踩坑指南
不用一上来就急着搜“text-to-cad 工具”先想清楚一个问题你到底是想要一个能生成模型的新玩具还是想要一个能真正融入工作流、替代重复劳动的生产力工具这两件事的难度差距比从“输入一段文字”到“拿到一张合格图纸”之间的差距还要大。我在接触这个方向的初期就在这两者之间反复拉扯最后才慢慢摸清楚它真正能干什么、不能干什么、以及上手的时候哪些环节最容易翻车。简单说text-to-cad 要解决的核心问题是把自然语言描述转换成可编辑、可制造、可复用的 CAD 模型。它和你平时用的那些文生图工具完全是两码事——图像生成出来就算结束了而 CAD 模型生成出来才只是开始后面还要编辑、装配、出工程图、做仿真、进加工。所以这个方向的难点从来不在“生成得像不像”而在“生成的东西能不能进下游流程”。这篇文章主要写给两类人一类是做机械结构、产品设计、非标自动化想评估这个技术能不能给自己省时间的工程师另一类是做 AI 应用开发想在这个方向上找落地场景的技术人。我会把技术路线、核心难点、实操流程和踩坑经验一起讲清楚尽量不说废话。1. 先搞清楚 text-to-cad 到底在解决什么问题1.1 从需求到模型中间隔着多少繁琐步骤你得先理解传统建模的工作流到底有多繁琐才能明白 text-to-cad 的价值点在哪。比如客户说“我要一个 L 型的支架两侧各开四个 M6 的腰形孔长度 80宽度 40板厚 3”一个熟练的工程师在 SolidWorks 或 FreeCAD 里操作从新建草图、标注尺寸、拉伸、打孔到导出文件至少也要十几分钟。如果是系列化的变体设计比如同一个支架要出 5 个长度版本那就得重复做五遍低水平的劳动。text-to-cad 想做的事情就是把“语言转参数”和“参数转模型”这两步合并掉。换句话说它不是一个画图辅助工具而是一个需求解析器。它要能从自然语言里提取出几何约束、尺寸、特征类型、装配关系然后输出一个能被 CAD 软件识别和继续编辑的模型文件。这就带出了一个关键认知这个方向的技术难点不在“生成”而在“语义理解”和“格式保真”。生成一个看起来像支架的网格体很容易但生成一个参数化特征树完整、尺寸约束正确、能回到 CAD 里继续修改的实体模型难度会高好几个数量级。1.2 技术路线各有各的打法目前在 text-to-cad 方向上公开的研究和应用方案大致可以分成三条路线各有各的适用场景和天花板。第一条路线把 CAD 建模过程当作代码生成任务。核心思路是让大语言模型输出一段程序化建模脚本比如 OpenSCAD 脚本、CadQuery 脚本或者 FreeCAD 的 Python API 调用序列。然后由本地的建模内核去执行这段代码真正建立起实体模型。这条路线最简单也最容易落地因为开源软件天然支持脚本化而且 LLM 在生成代码这件事上已经被验证过是靠谱的。缺点是表达能力受限于脚本库的封装程度复杂曲面、自由造型、拓扑变化多的零件会很难描述。第二条路线LLM 直接输出原生 CAD 格式如 STEP、BREP。这条路线最优雅因为 STEP 是工业界的交换标准下游生态完整。但难点也最突出——STEP 文件里存储的是精确的几何和拓扑信息是一大堆面、边、顶点之间的拓扑关联和 NURBS 参数。LLM 需要学习这种极其结构化的格式生成时只要有任何一个拓扑引用关系写错整个文件就废了。当前这条路线还在论文阶段居多实际能用的产品非常少。第三条路线先用生成模型产出体素、点云或深度图再拟合成曲面和实体。这条路线视觉效果好生成自由度高适合概念设计和快速原型。但问题在于拟合出来的结果通常是 T-Spline 或网格实体回到传统 CAD 后很难参数化修改一个尺寸可能比重新建模还费劲。所以它更适合做“表达意图”不适合做“生产交付”。从我的实践体验来说第一条路线是目前性价比最高的也是普通工程师能快速上手验证的方向。后面我会重点讲这条路线的实操细节。1.3 谁来用、用在哪text-to-cad 现阶段最适合的场景是标准件选型、系列化变体、简单结构件的快速生成以及没有 CAD 基础的产品人员做前期结构验证。比如你想确认一个传感器支架大概占多大空间、能不能装进现有的机箱直接打一句话生成一个模型丢进装配体里看干涉比开软件学一小时草图高效得多。不太适合的场景也很明确精密配合面、涉及 GDT 标注的零件、需要严格遵循企业内部制图标准的交付件这些短期内还得靠人。把 text-to-cad 理解成一个“把需求快速变为初步几何”的工具而不是“替代 CAD 建模”的工具你用它的心态会健康很多。2. 核心细节解析与实操要点2.1 数据集是这行的“兵工厂”任何 text-to-cad 项目第一步都是搞数据。你可能觉得模型架构最重要但以我自己调参和复现的经验来看数据质量对最终效果的影响通常超过模型结构。原因是自然语言到几何的映射非常敏感同一个描述在不同数据集里对应完全不同的建模习惯模型学不到统一规律就会产生幻觉。当前公开可用的数据集有几个值得关注。一个是 ABC Dataset它包含了超过一百万条 CAD 模型格式是 STEP来源是广泛搜集的公开模型库特点就是量大、覆盖面广适合做预训练。另一个是 Fusion 360 Gallery Dataset里面有带参数化特征历史记录的模型适合训练“从语言到特征树”的映射因为它保留了建模过程模型可以学习到先画草图再拉伸再打孔这些操作序列。还有一个是 Text2CAD 相关工作里发布的文本标注数据集它把 CAD 模型和自然语言描述对齐了一般是从 OnShape 或 Fusion 360 的公开模型库里爬取再用模板加人工的方式生成描述文本。一个很现实的问题是大部分公开数据集里的文本描述都偏“列举式”比如“a rectangular base with four holes at corners”很少会有“用于固定在铝型材上的安装板考虑到强度需要在中间加两条加强筋”这种真实工程语言。这就是为什么很多模型在论文里跑 ROUGE-L 指标很好看一拿到真实需求就崩。实践里最简单的补法是用 LLM 批量改写现有描述把它从“形状描述”改写成“功能描述”再用改写后的文本做二次训练或检索增强。2.2 模型架构与训练思路解决 text-to-cad 问题模型架构本质上是从通用语言模型出发加上对目标格式的编码约束。以代码生成路线为例基础方案通常是 CodeLlama、DeepSeek-Coder 这类代码大模型。训练时分两种情况如果只是做零样本或少样本推理那不需要训练直接设计好 Prompt 模板把任务说清楚就行如果要做微调那就需要构造“文本描述 - 建模脚本”的平行语料。这里我要强调一个容易被忽略的点建模脚本的中间表示设计直接影响模型的学习难度。举个例子同样的一个 L 型支架用 OpenSCAD 写可能长这样module bracket(length80, width40, thickness3) { difference() { union() { cube([length, thickness, width]); cube([thickness, 40, width - thickness]); } // 加孔、倒角等细节 } } bracket();用 CadQuery 写则是另一套面向对象的写法背后是 B-rep 建模内核在驱动。模型生成前者只需要学会几个基础几何原语和布尔运算生成后者则要理解“面、线、约束、草图和拉伸”这些概念。所以我的建议是早期验证用 OpenSCAD 作为目标语言它的语法简单、表达紧凑、还能直接渲染预览是非常适合做研究和 MVP 的靶子。等流程跑通、确定要进生产了再切换到 CadQuery 或者 FreeCAD Python API把精细度和特征表达能力提升一个台阶。2.3 输出格式决定了落地难度很多人容易忽略的一个关键点是你输出的格式与你后续使用工具的兼容性直接决定这活儿能不能干完。如果模型输出的是 OpenSCAD 脚本那你需要本机装 OpenSCAD 命令行工具去执行渲染最后导出 STL 或者 STEP。OpenSCAD 本身对 STEP 的支持有限导出 STL 没问题但如果下游要 CNC 加工STEP 才是正道。如果模型输出 CadQuery 脚本执行后可以直接导出 STEP也可以推送到 FreeCAD 里继续编辑。CadQuery 底层是 OpenCascade 内核这个内核和 FreeCAD 同源所以格式兼容性非常稳。我的做法是统一用 CadQuery 作为中间语言所有生成结果都先落地成 STEP再按需转换。如果模型直接输出原生 STEP 的 B-rep 数据那对推理精度要求极高而且下游可编辑性也很差。你拿到一个 STEP 文件在 SolidWorks 里打开多半是一个“死模型”没有特征树想改尺寸得用直接编辑功能效率和从零建模差不多。所以这类方案只能用在“一次性生成、不做二次修改”的场景。2.4 评估指标别只看视觉效果text-to-cad 的评估指标是个老大难问题。如果你只看渲染出来的图片那你看到的只是三角网格的显示效果完全看不出来这个模型能不能制造也看不出来它在 CAD 里是不是一个可编辑实体。我在评估生成质量时一般会组合用这几类指标几何指标包括交并比IoU、倒角距离Chamfer Distance、表面误差这些指标可以衡量生成形状与目标形状的重合程度比如体素化后算 IoU数值高说明几何层面对得比较准。可制造性指标上最简单的检查必须包括模型是否封闭。在 CAD 内核里检查模型是水密的实体而不是开放的曲面壳这个问题不解决切片或加工的第一步就过不去。另外底部与平台的接触面积也很重要涉及 3D 打印时的支撑策略。参数化一致性是更严格的一层它的思路是检查生成脚本里的参数声明和实际几何结果是否一致比如脚本声明板厚 3mm但实际用内核去量测模型厚度是不是 3mm。我在实际测试中这一步能筛掉大量“看起来很好、量起来全错”的生成结果。我在实操中用了一套前置检查脚本先生成 STEP 或 STL再用 Python 的trimesh库检查水密性用cadquery加载后检查实体数量最后再用 OpenSCAD 渲染对比图。三步全过才叫“模型合格”只要有一项挂掉就直接打回重生成。3. 实操过程与核心环节实现3.1 选型自己训练还是用开源模型先回答一个最常见的问题我需要自己训练一个模型吗答案是大概率不需要。除非你要做的是某个极垂直的领域比如“只能生成特定类型的法兰和阀体”否则你直接用通用代码模型加精心设计的数据组织方式已经能应付大部分需求。真正要判断的点在另外两个维度你输入的自然语言有多自由以及你需要输出的建模语言有多复杂。如果你输入的文本比较标准只是像“生成一个 80x40x3 的 L 型支架带 4 个直径 6 的孔”这种那直接走零样本推理用 Prompt 把格式要求和示例写好就够了。如果你输入文本是长篇大论的自由描述比如“设备外壳底部需要留走线孔顶部需要有一个稍微凸起的斜面用于贴标签侧面考虑散热格栅”这种就必须微调因为通用模型对“散热格栅”这种隐含结构语义的表达太弱了。我在做的项目里采用的方式是先用带示例的 Prompt 做一轮粗筛把明显不合理的输出过滤掉然后人工收集 100 到 200 条高价值的失败样本把“错误描述-修正后的正确脚本”整理成纠错对做一次 LoRA 微调。成本不高但能把最具挑战的那部分场景补上。3.2 Prompt 设计的一些经验Prompt 设计在这个场景里重要性不亚于模型训练因为模型对建模指令的理解完全是靠 Prompt 牵出来的。我试过几次之后总结出几个有效的写法这里直接给出来。第一明确告诉模型输出什么格式。这一步看似简单但很多人会漏模型经常会在思考里夹带解释性文字导致后面解析脚本失败所以 Prompt 里要明确写“只输出 CadQuery Python 代码不要输出任何解释”。第二给出目标脚本的结构骨架。比如你先定义一个make_bracket(length, width, thickness)函数再让模型填空式的完成return part这就比让它从零生成一整段脚本稳定得多因为模型不用去猜程序的顶层逻辑只需要专注于生成特征操作序列。第三给出输入描述中的关键尺寸抽取规则。比如你告诉模型先列出文本中出现的所有数值标注它对应的特征长度、宽度、孔径、孔距再进入建模。这一步本质上是让模型先做一次“信息结构化”而不是直接让文本到编码。第四放两个成功的 few-shot 示例。Note 里不放示例的零样本模式生成质量的波动非常大。而放上示例之后哪怕示例和你的目标零件在几何上毫无关系输出的稳定程度也会有明显提升。一个我在实际中调好的 Prompt 模板长这样你是机械设计工程师负责把用户自然语言需求转成 CadQuery 脚本。 步骤 1列出所有尺寸参数标注用途。 步骤 2定义建模顺序按草图-拉伸-布尔-圆角顺序执行。 步骤 3输出可导入 CadQuery 的 Python 代码导出为 STEP。 只输出代码不输出解释。 示例 [这里放两个 few-shot 例子]这个模板跑下来最简单的零件成功率能到七成以上复杂零件掉到三成左右剩下那些失败案例基本都出在隐含工程常识的部分例如“注意不要干涉”“留出扳手空间”这类描述。3.3 完整跑通一个 text-to-cad 工作流我在这套流程上已经跑了挺长时间现在把完整流程写下来给你一个可以直接抄作业的参考。前提是你电脑上已经装好 Python 3.10 以上版本、CadQuery 和 OpenSCAD。搭建环境时我用了一个独立的 conda 环境避免把依赖装乱conda create -n t2cad python3.10 conda activate t2cad pip install cadquery trimesh openai推理这一步我用的是 OpenAI 的 API 来调用 GPT-4oPrompt 按上面那个模板走返回的文本就是一段 CadQuery 脚本。这里有一个需要特别注意的点返回的脚本可能不是一个可以直接运行的完整文件里面可能缺少 import 或者包含多余的解释文字所以我在保存之前会做一次静态检查先抓有没有 import 语句没有就自动补上。脚本落地之后用 CadQuery 去执行并导出 STEP这是我们所有下游操作的主文件import cadquery as cq from pathlib import Path code Path(generated_bracket.py).read_text() namespace {} exec(code, namespace) part namespace.get(bracket) if part is None: raise ValueError(脚本中未找到 bracket() 对象) cq.exporters.export(part, bracket.step) exported cq.exporters.export(part, bracket.stl) print(f导出 STEP 和 STL 完成)导出完成后会进入几何质量检查阶段这个步骤我用的是 trimesh 库来跑水密性检查import trimesh mesh trimesh.load(bracket.stl, forcemesh) print(水密性:, mesh.is_watertight) print(主体体积:, mesh.volume) if not mesh.is_watertight: print(找到开放边:, mesh.self_intersections.count())这一步能筛掉一大半问题。不水密的文件进不了切片软件也进不了 CAM后续全都白做。3.4 一个完整的生成示例为了让你直观感受整个工作流我用一段接近真实需求的文本来跑一遍“一个安装板轮廓大致是一个 100mm x 60mm 的矩形四个角倒 R5 圆角中心有一个直径 20mm 的通孔四角各有一个直径 6mm 的安装孔孔中心距离边缘 10mm板厚 4mm”。这段文本直接交给模型后生成出来的 CadQuery 代码大致逻辑如下import cadquery as cq result ( cq.Workplane(XY) .rect(100, 60) .vertices().fillet(5) .extrude(4) .faces(Z) .workplane() .hole(20) .faces(Z) .workplane() .rect(80, 40, forConstructionTrue) .vertices() .hole(6) )这段脚本的执行过程很清晰先在 XY 平面画一个 100x60 的矩形四个顶点倒 R5 圆角然后向 Z 方向拉伸 4mm接着在上表面中心打一个直径 20 的通孔再在上表面建立一个用于定位的构造矩形四角打四个直径 6 的孔位置自动由定位矩形约束。最后导出 STEP 后模型在 FreeCAD 里直接打开可以看到它是一个完全的实体具备一个可识别的步骤历史。如果你想改板厚从 4 改成 5只需改extrude(4)为extrude(5)再重新执行。这个例子看起来简单但它验证了一件很重要的事一个完全不懂 CAD 的人只要能把需求说清楚就能在几分钟内得到一个可编辑的实体模型文件这才是 text-to-cad 真正的价值拐点。4. 常见问题与排查技巧实录4.1 生成的模型不闭合这是最常见的问题没有之一。表现是生成出来的 STEP 文件在打开时是片体而不是实体或者 STL 导入切片软件时报“模型有漏洞”的错误。原因通常有两个。一是建模脚本里用了不恰当的布尔运算比如把一个体切成两半之后没有重新缝合CAD 内核实际处理是两个独立实体而不是一个整体。二是倒角或圆角操作在几何上生成了退化面比如在非常薄的壁厚上做 R 值过大的圆角导致面自相交。排查的思路很简单不要相信渲染效果直接跑水密性检查。我发现很多生成脚本能渲染出漂亮的图像但导出 STL 后它就是不完全闭合的因为默认渲染器会把开放面也显示成封闭的样子。建议在生成代码之后立刻用 CadQuery 的val().isValid()方法判断实体有效性print(result.val().isValid())如果返回False那就需要检查布尔运算的表达式以及圆角值是不是超出了壁厚允许的范围。4.2 尺寸语义丢失模型生成的脚本里尺寸数据经常对不上原始描述。典型的情况是“直径 20mm 的孔”变成了半径 20mm 的孔或者“板厚 4mm”被翻译成了 40mm精度单位直接错了一位。这个问题的根源在于 LLM 对工程语义中“直径/半径”的区别不够敏感而且对单位制缺少常识性的约束。解决思路是在 Prompt 里加入单位校验和二次确认步骤让模型在输出代码之前先用注释把抽取出来的所有尺寸列出来且写明每个尺寸对应的是直径还是半径。我在实践中还让模型把单位系统固定为公制毫米减少转换出错的可能。另一个兜底方案是用正则表达式或者 LLM 二次调用做尺寸抽取与建模脚本交叉比对import re matches re.findall(r(?:直径|半径|长度|宽度|厚度)[:]?\s*(\d(?:\.\d)?), text) print(抽取尺寸清单:, matches)如果生成脚本里出现的数值和抽取清单对不上直接标红重跑。虽然有点土但确实是稳定性的最后防线。4.3 基准面选择与建模顺序混乱CadQuery 这类库的建模逻辑是从某个基准面出发通过workplane设置当前工作面再按顺序执行特征操作。如果基准面选错零件整体就会歪掉。比如描述要求“板面水平放置孔垂直于板面”模型可能画出竖直的板或者孔的方向和板面平行。这个问题没有捷径靠 Prompt 里的建模顺序约束能缓解一部分但核心还是要做好后处理的自动校验。我在脚本后面会加一段检查代码用 CadQuery 查询生成实体的面的法向量然后和预期方向做对比。如果孔轴线和板面法线不平行就打回重做。另外建模顺序也常常导致问题很多人会先把底板画出来再在底板上放凸台最后才打孔这里如果顺序乱了后续特征的定位基准就会断掉。我建议在 Prompt 里显式规定顺序——先主轮廓、再次要特征、再孔、最后倒角——这样能让模型的输出失误率下降不少。4.4 输出与下游 CAD 平台对接的坑最后说说和 CAD 平台对接时最容易踩的坑。CadQuery 导出 STEP 文件本身没问题但这个文件导入到 SolidWorks、NX 或 Fusion 360 时一般不会带参数特征树它是“无历史记录”的实体。也就是说你能对它做移动、旋转、布尔运算但没法回到特征树里去改某个孔的大小。如果你需要的是参数化编辑能力最好的办法不是导出 STEP而是把 CadQuery 脚本转换成对应平台的脚本。比如 Fusion 360 可以用它的 Python API 重新执行一遍建模流程SolidWorks 可以用 API 或者宏把特征重建出来。只有用原生脚本重建才能得到完整的特征树。另一个容易踩的坑是 STEP 格式版本不兼容。CadQuery 默认导出的 STEP 遵循 AP214 标准大部分现代 CAD 都能读。但如果你用 OpenSCAD 导出的文件再转一次 STEP中间经过的转换器可能丢拓扑信息到了下游平台步骤历史一样是空的。实操里比较稳妥的做法是源文件保留脚本交换文件用 STEP最终交付才用平台原生格式。也就是把脚本当成“设计源文件”来管理每一次修改都从脚本重新生成而不是在黑盒模型上直接改。这套习惯让我在几个项目里省下了大量重复建模的时间。4.5 一个意外但重要的坑幻觉特征最后补一个很有意思的问题模型会生成描述里完全没有提过的特征。比如你只说了“四个安装孔”它自作主张给你加了加强筋或者中心定位台。从视觉上可能觉得多此一举甚至会美化外观但对工程师来说这就是灾难——孔位、重量、干涉全都会受影响。这种幻觉特征很难用规则检查出来因为几何上它是完全合法的实体。我的做法是在 Prompt 里加一条约束“不允许创建描述中未提及的特征。”同时在文本解析阶段让模型先抽取描述里出现的特征清单建模完成后核对生成脚本里的操作命令是否都在清单范围内例如统一用特征关键词做过滤。这套办法不能 100% 杀干净幻觉毕竟模型输出具有一定随机性但加上去之后至少能拦住九成以上。我个人的体会是text-to-cad 在当下更像一个“需求到几何的快速翻译器”而不是一个“设计代替者”。真正好用的场景是把它放在前期概念验证、标准件库检索、非标方案快速比选这些环节里让机器把那部分低水平重复的建模劳动吃掉。你省下来的时间应该花在更值得花的事情上——想清楚约束条件、优化结构方案、处理那些文字表达不出来的隐性工程经验。最后分享一个我在实际项目里的小技巧把生成过的所有“文本-脚本-STEP”三元组积累下来按零件类型归档。用不了几个星期你手里就会有一个覆盖自己产品线的小型私有数据集。下一次再遇到类似需求直接拿历史样本做 few-shot 示例比什么模型都靠谱。这个习惯我认为是这个方向目前最值得投入的一件事。