ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

text-to-cad:用自然语言直接生成可编辑CAD模型的核心原理与实践

text-to-cad:用自然语言直接生成可编辑CAD模型的核心原理与实践

开头直接写正文。

text-to-cad这个项目名一出来,经常画图的朋友应该就知道是什么意思了:直接输入一句“圆孔均布的100x50x3法兰板”,让程序自己生成对应CAD模型。这几年围绕“文字转三维模型”的探索不少,但大部分关注的还是游戏、影视里的卡通角色,真正落到工程设计、机械加工、建筑构件这类场景的产品并不多。text-to-cad就是冲着工程语义去的,它想解决的是一个很直接的问题:把设计师脑子里的结构意图,用自然语言“说”出来,然后让模型生成可编辑、可导入CAD软件继续修改的几何体,而不是一个只能看的三角网格。这篇内容我会从项目背景、核心原理、实用操作、踩坑记录四个角度拆一遍,适合正在研究AI辅助设计、想做CAD自动化或对AIGC落地工业场景感兴趣的工程师。

1. 项目整体设计思路:为什么text-to-cad比通用文生3D更难

1.1 传统CAD建模的痛点

如果你用过SolidWorks、AutoCAD、中望CAD这类工具,肯定有体会:新建一个零件,先选基准面,画草图,标约束,拉伸、切除、倒角,一个复杂模型可能要反复调整草图关系几十次。熟练的工程师做起来还好,但换个角度想,很多需求其实一句话就能说清楚。比如“一个直径8mm、深度10mm的沉头孔”,在软件里却要去画圆、约束、拉伸切除、做拔模角度,过程繁琐且模式固定。更难受的是,这种操作路径完全没法批量化——出一百个孔,你就要画一百遍。这正是text-to-cad这类项目想改变的核心场景:把结构需求用文本表达,自动完成建模命令序列,减少重复劳动。

1.2 与通用文生3D的本质区别

大家熟悉的文字生成3D模型,像DreamFusion、Point-E,更偏向“物体外观”的生成。它们的目标是让生成结果看起来像一只猫、一辆车,足够像就行,不需要精确尺寸,不需要装配关系,面片扭曲一点也无所谓。但CAD领域完全不是这个逻辑。机械图纸上标注一个直径50mm的孔,就得是50mm,偏差0.1mm都可能影响装配;法兰盘上有8个孔,均匀分布就是均匀分布,不能生成7个或者乱序排列。所以text-to-cad不能只输出mesh,它至少需要满足三点:几何上的精确性、语义上的参数对应、输出结果能被传统CAD工具继续编辑。

1.3 这类项目的能力边界

我得先说清楚,目前的text-to-cad项目不是让你用中文一句话就出一整套精密装配体。我接触过的实现里,能做到比较稳定的是单零件、中等复杂度的结构件,比如支架、法兰、带槽轴、简单箱体。复杂的型腔、复杂的曲面过渡、多体装配,效果还不太行。这个边界主要受限于训练数据和模型表达方式。但这并不影响它的工程价值——只要能快速出初版结构,让工程师在软件里微调,效率提升已经非常明显了。

2. 核心技术拆解:文本到CAD是怎么一步步实现的

2.1 文本是输入,CAD命令序列是输出

text-to-cad项目最常见的技术路线,并不是直接生成网格,而是把建模过程建模为“命令序列生成”问题。所谓命令序列,就是CAD软件里画图的每一步:创建草图、画直线、画圆弧、加约束、拉伸、打孔、阵列、倒角等。模型要做的事,是读入一段文本描述,输出一串可执行的CAD操作指令。这个思路有点像代码生成——文本是自然语言,CAD命令就是“方言化的代码”。好处是生成结果天然具备参数化特性:改一个数字,对应命令里的参数就变了,重新执行一遍就得到新模型。而且命令序列可以被CAD软件回放,这在工程上是实实在在可用的。

2.2 自然语言与几何语义的对齐

核心难点在于如何让模型理解“十个均布孔”这种话。“十个”对应阵列数量,“均布”对应阵列类型,“孔”对应特征类型,这些词之间有关系,又和最终几何约束绑定。业内一般用两种方式解决:一种是把文本和CAD特征用对比学习方式对齐,先编码文字,再编码对应的CAD模型特征,让两者的向量在空间中相近;另一种是直接用Transformer结构,把文本token和CAD命令token放在一起训练,网络自己学会关注文本的哪个词对应哪个参数。后者在数据量充足的情况下效果更好,因为命令本身的结构化信息也能参与学习。我实际用下来,纯靠对齐方式的模型,复杂命令容易错乱;Transformer端到端生成的,长命令序列容易丢失前后一致性。两者结合才是现阶段的工程正解。

2.3 数据从哪来,是项目成败的关键

这类项目的训练数据不是公开图像,而是“文本-命令序列”对。最理想的数据是已有CAD图纸的建模历史树,再配上一段解释性文字。但实际建模历史通常不公开,很多DWG文件里只有最终几何,没有中间步骤。可行的路子有三条:第一,用自动化脚本批量生成简单零件,每个零件自动标注文本描述;第二,从开源数据集里筛,像Fusion 360 Gallery这类带建模历史的可以当种子;第三,自己做一套规则模板,比如“板类零件生成器”,配合随机参数生成几十万组合。我见过不少开源版本用第三类,虽然多样性差点,但训练起来稳定。要注意,数据质量比数量更重要。命令序列中间如果缺一步,后续特征会全部错位,模型学出来全是脏数据。

2.4 几何表达:从网格到体素再到隐式曲面

有的text-to-cad项目还会走另一条路:先由文本生成隐式曲面场或者体素模型,再通过等值面提取,得到STEP或BREP数据。隐式场方法在生成自由曲面时表现好,但精度仍然不如命令序列方法。做工程落地的话,我建议优先关注命令序列方向,因为CAD软件里最终要的是可编辑特征,而不是一张曲面网。

3. 实操:自己跑通一个text-to-cad流程

3.1 环境准备与模型选型

市面上开源的text-to-cad实现不太多,但也不是没有。找项目时优先看是否基于HuggingFace发布、是否有预训练权重、是否提供推理示例脚本。环境上我建议直接用Linux服务器,Python 3.8以上,装好PyTorch 2.x,显存8GB起步。我自己在16G显存的卡上跑过7B模型,推理一次大概1到3秒,效果还能接受。如果显存不够,可以试试4bit量化。依赖安装时注意几个坑:CUDA版本最好和PyTorch官方文档对应,别因为conda默认源装出旧版本;tokenizers库容易自动升级导致模型加载失败,装完先固定版本。

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install transformers tokenizers sentencepiece

这一步完成后,先跑官方自带的示例脚本,确认模型权重能正常加载。

3.2 推理:从文本到命令序列

加载好模型后,输入文本需要遵循项目预训练时的写法。多数实现会期望一个前缀,比如"create a bracket with 4 holes",你不要自己添加多余标点或全称,最好按照项目README里的示例格式。我来演示一个典型流程:

from transformers import AutoTokenizer, AutoModelForCausalLM model_path = "your_local_text2cad_model" tokenizer = AutoTokenizer.from_pretrained(model_path) model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto") text = "create a rectangular plate 100x60x5 with four holes of diameter 8 at each corner" input_ids = tokenizer(text, return_tensors="pt").input_ids.cuda() output = model.generate(input_ids, max_new_tokens=256, do_sample=False) command_seq = tokenizer.decode(output[0], skip_special_tokens=True) print(command_seq)

生成出来的是一串类似sketch circle at (10,10) diameter 8 ... extrude 5 ...的伪代码,并不直接是DXF或STEP文件。大多数人走到这里会愣一下——其实这就是核心中间表达。你还需要一个解释器,把这串命令转换成真正的几何文件。有些项目自带转STEP的脚本,没有的话,可以自己写一个简单的解释器规则:按行读命令,识别关键字,调用CadQuery或FreeCAD API完成建模。CadQuery是Python库,直接支持circle,rect,extrude,hole,linearPattern等方法,映射很方便。

3.3 从命令序列生成可编辑的STEP模型

用CadQuery做映射时,我习惯把模型输出切成两步:第一步根据命令构造草图截面,第二步执行拉伸、旋转或打孔、阵列。下面是一个把命令序列转成实体的最小示例:

import cadquery as cq def build_from_commands(cmds): result = None for cmd in cmds: if cmd.startswith("rect"): _, w, h = cmd.split() result = cq.Workplane("XY").rect(float(w), float(h)) elif cmd.startswith("extrude"): _, depth = cmd.split() result = result.extrude(float(depth)) elif cmd.startswith("hole"): _, x, y, dia = cmd.split() result = result.faces(">Z").workplane().pushPoint([ (float(x), float(y)) ]).hole(float(dia)) return result model = build_from_commands(command_seq.strip().splitlines()) cq.exporters.export(model, "output.step")

这样最终得到的STEP文件就能在FreeCAD、中望CAD、SolidWorks里随便打开修改了。整个过程看起来不难,但实际跑的时候问题很多,下面单独开一节讲我踩过的坑。

4. 常见问题与排查技巧实录

4.1 生成结果和描述完全对不上

这是最打击人的情况。输入“四个角的孔”,模型生成出六个孔,或者孔跑到中间。我排查的顺序是这样:

第一,检查输入文本是否用了项目训练集里的表述方式。很多开源模型对“every corner”学得好,对“边角处均布”这种偏中文直译的短语不认识。回到英文模板式的描述,效果会改善很多。第二,检查是不是采样参数选了do_sample=True。生成式模型一旦随机采样,工程语义就会漂移,能固定解码就尽量固定。第三,看训练数据的覆盖范围。如果项目本身只训练过板类零件,你却输入轴套描述,生成不对是正常的,说明特征超出了模型能力。

4.2 几何严重破面或重合

命令序列生成对了,但转换出的STEP文件在导入CAD软件时破面,大概率是解释器执行顺序有问题。典型情况是“先打孔后拉伸”和“先拉伸后打孔”会得到不同的特征树。如果命令序列里孔在拉伸之前,而解释器按顺序在二维草图阶段就打孔,后面的拉伸会把孔直接带过去,结果没问题;但如果把打孔定义成了独立特征,又没有指定面,就容易在错误平面上产生垃圾特征。我的解决方法是让解释器维护一个当前工作平面状态,遇到hole不再新建草图,而是基于最新实体面创建。

4.3 模型推理结果不错,但接入企业流程时卡壳

工程化落地时,很多人忽视了一个事儿:模型生成的命令序列不是标准参数化特征树,只是“像命令”的文本。企业要的是和MBD(基于模型的定义)体系对接、带PMI标注、能进PDM系统的模型。这个目前没法完全自动化。我在实际项目中采用的办法是“半自动模板方案”:把模型生成的命令序列用作模板实例化参数,而不是直接进系统。也就是让text-to-cad充当“参数提取器”,人审核一遍,再把参数填进企业自定义的特征模板里,这样模型输出的不准确性就能被模板限制住。

4.4 和现有CAD生态怎么融合

很多朋友问,是不是能直接做成AutoCAD或中望CAD插件。从技术上讲可行,但我建议不要一步到位做全自动。更稳的做法是做一个中间文件服务:用户文本请求进来,生成STEP,再通过CAD软件的批处理接口自动导入布置好。AutoCAD有脚本和.NET API,中望CAD也提供二次开发接口。实际做的时候,有个坑要注意:STEP文件要统一用AP214标准导出,不然不同软件间会出现单位错乱和颜色丢失。我踩过单位错乱的坑,项目配置文件里默认值设成了英寸,导入毫米项目的图纸后模型放大25.4倍,还好在审查阶段发现了。

以下是我在多次测试里总结的一个排查速查表:

现象常见原因处理办法
文本越复杂,生成效果越差模型上下文长度限制,命令序列后半段被截断拆分成多个单特征命令,分步生成再合并
孔位与文本描述不一致训练数据里没有对应槽位改用模板参数填充,不让模型自由排列特征
STEP导入后实体变成曲面片用网格转STEP,丢失拓扑信息确保用CadQuery等BRep内核输出真实体
模型生成速度慢显存不足,或没有用KV Cache优化推理引擎,改用TensorRT或vLLM
命令序列语法正确,但软件不认关键字和CAD脚本API不匹配增加一层适配器,把通用命令转为软件专用脚本

4.5 一个小技巧:利用“反向文本增强”提升生成质量

训练数据不够时,可以把文本和几何互相映射。比如手工建一百个法兰盘,然后用程序自动生成描述:“outer diameter 80, inner diameter 30, six holes uniform distribution”,这就构成一个反向数据对。这种数据扩充方式,对模型理解数量词、位置词特别有效。我自己试过,纯规则生成的数据训练出来的模型,在“均布”“阵列”“沉孔”这类工程词上的准确率明显高于通用文本模型。

5. 后续还能怎么玩:text-to-cad的扩展方向

5.1 从单零件走向装配体

现在多数text-to-cad只能做单零件。扩展思路是分两步:先让模型生成零件,再让模型生成装配关系,比如“A零件插入B零件的孔中”。这一步难点在于空间关系的表达和约束求解。目前有一些实验性项目开始用图网络描述装配结构,节点是零件,边是配合关系。我认为这条路今年会有新突破,因为相关的数据合成管线已经有人打通了。

5.2 与参数化设计平台结合

另一个实用的方向是把text-to-cad当作参数化设计的前置模块。企业内部有标准零件库,每种零件带一套参数,比如法兰的直径、孔数、板厚。传统做法是设计师查手册,手工填参数。现在可以先用自然语言描述,让text-to-cad抽取出参数,再自动匹配到标准库的模板。这个玩法不需要模型具备强推演能力,准确率能到八九成,非常值得先上。

5.3 在电气CAD和钣金CAD等细分领域的应用

很多人问我电气CAD能不能用这个思路。电气图纸的核心是符号、连线和端子排布,它本质上也是命令序列,甚至比机械CAD更有规律。我看到有团队正在做“文字转电气原理图”的项目,原理就是把这套方法迁移到二维电气图元上。钣金也类似,“L型折弯板、三处切口、四角倒圆”文字描述到钣金加工参数,中间只要做一层折弯表映射。细分领域的文本-命令数据比通用机械数据更容易整理,我认为这是下一波落地机会。

6. 给新手的建议和我的体会

别一上来就想做全自动生成完整产品图,那会挫败感很强。我建议按这个顺序练:先用现成模型跑通流程;然后人工审核生成结果,只保留几何正确、命令完整的样例,攒够一百条;再用这些高质量样本做微调;最后才去尝试企业内部复杂零件。

我在实际使用中还有一个感受:不要过度追逐模型参数量。text-to-cad更吃数据和命令表达的确定性,而不是语言的多样性。一个7B模型做得好,可能比70B更实用,因为工程语义是强约束的,不需要那么多发散能力。

最后再分享一个小技巧,把模型输出的命令序列整理成JSON格式,每个特征一个对象,包含feature_type, params, operation_order。这样做有两个好处:一是排查问题的时候可以直接查看特征顺序;二是后续换CAD软件,只需要改最后一层的导出适配器,不需要重新训练模型。我之前为了从FreeCAD迁移到中望CAD,只改了不到三百行导出代码,整条链路就通了。这个经验,能帮你省掉大量返工时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进