ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

实测text-to-cad:自然语言生成可编辑CAD模型的原理与避坑指南

实测text-to-cad:自然语言生成可编辑CAD模型的原理与避坑指南

我花了不少时间把几个text-to-cad相关的开源工具拉下来实测,发现这东西确实不是噱头。简单来说,text-to-cad就是让你用一句自然语言描述,直接生成可编辑的CAD模型。比如"做一个外径30mm、内径20mm、高度5mm的垫片",系统能直接给你丢出一个可用的三维模型文件。这篇文章不谈玄学,只说实测经验:它背后的原理是什么,市面上有哪些工具能真跑起来,以及我在实操里踩过的坑和解法。如果你是做结构设计的工程师、3D打印爱好者,或者想给非专业人员配一个快速建模入口,这篇内容应该对你有用。

1. 内容整体设计与思路拆解

1.1 text-to-cad到底在解决什么痛点

传统CAD建模的门槛在于"要想清楚再画"。你脑子里有一个零件轮廓,但落到软件里得先选基准面、画草图、标约束、定尺寸、再拉伸或切除,中间任何一步操作错位,模型就瑕疵百出。text-to-cad的思路是把这个过程反着来——你的需求本身是自然语言,系统负责把语言解析成几何语义,再自动生成参数化操作序列。

我接触到的第一个直接需求来自3D打印圈子。群里经常有人问"谁帮我画个能卡住树莓派的小盒子",这就是典型的text-to-cad场景。传统流程下要打开Fusion 360画半天,但用文本生成的话,先描述清楚外形尺寸和开口位置,能省下大半时间。所以它的核心价值不是替代专业CAD软件,而是把"描述需求"和"实现建模"之间的翻译成本压下来,让非专业用户也能参与设计。

1.2 为什么这个方向现在才真正落地

自然语言转CAD不是新概念,早期受制于两件事:语义理解的准确性太低,以及参数化建模的适配粒度太粗。现在情况变了,大语言模型把自然语言到结构化参数的映射能力拉高了一大截。比如你输入"带四个安装孔的方形底板,孔间距25mm",模型能提取出"方形""25mm""四个孔"这些关键语义,而不是再靠穷举规则匹配。

另一个变化是CAD内核开始拥抱脚本化接口。OpenSCAD、CadQuery这些工具本来就是代码生成模型,正好给文本转CAD提供了落点。LLM生成的是Python脚本或者SCAD代码,再让脚本驱动内核出模型,这样既保留了参数化可编辑性,又避开了让AI直接操作GUI的复杂路径。实测下来,这种"文本转代码、代码转模型"的链路比直接输出网格文件更稳,因为模型还能回炉改尺寸。

1.3 整体技术栈与方案选型

我实测下来比较顺手的路线是:LLM做语义提取和代码生成,CadQuery或OpenSCAD做几何内核,最后统一导出STEP或STL。选CadQuery的主要原因是它基于Python,和LLM的代码生成天然契合,而且它的建模逻辑是"shown-entity"式,能直接用代码描述特征。

OpenSCAD则更适合纯CSG(构造实体几何)场景,比如组合圆柱、立方体做布尔运算。如果你要做的零件以拉伸、旋转和布尔操作为主,OpenSCAD更简洁;如果涉及复杂倒角、圆角、放样这类特征,CadQuery的底层依赖OpenCASCADE,更占优势。我建议先根据自己的输出格式需求选型——需要直接给机械工程师用,就选STEP输出强的CadQuery,只是快速看外观,OpenSCAD足够。

2. 核心细节解析与实操要点

2.1 把自然语言拆成几何参数的系统方法

文本转CAD的关键一步是从描述里提取量化参数。比如"一个30x25x4的U型槽",你需要让LLM明白x、y、z分别对应长、宽、高,同时识别出"U型"这个特征。实践里最有效的方法是给LLM设计一个中间表示层,不直接让它生成CAD代码,而是先让它生成一个结构化的JSON,包含类型、尺寸、位置、特征列表。

举个例子,输入"做一个直径20mm的圆盘,中心有直径为8mm的通孔",中间表示就是{type: disc, diameter: 20, features: [{type: hole, diameter: 8, position: center}]}。让LLM先输出JSON,再根据JSON生成CAD代码,出错率会显著低于直接生成的方案。我自己踩过的坑是跳过了中间表示,结果LLM把通孔错误地解释成了盲孔,导致后期干涉检查才发现问题。

参数单位是另一个必须约定的细节。我建议在提示词里直接固定单位体系,比如"所有尺寸默认mm,插入文本时如果出现英寸要主动换算"。因为LLM训练数据里混合了公制和英制,你不锁定它就容易混用。还有一个实用技巧:在系统提示里要求小数和整数都保留两位有效数字,避免生成一些浮点精度差异导致布尔运算失败。

2.2 参数化建模的特征与约束表达

生成CAD代码时,特征和约束的表达决定了模型的可编辑性。CadQuery里通过Workplane对象来定义建模平面,然后依次用box、cylinder、hole等方法来添加特征,最后用布尔运算组合。你需要引导LLM遵循这样的顺序:先建立基准坐标系,再搭建基底特征,然后逐层添加阵列、孔、倒角。

约束关系也很重要。比如"四个螺栓孔对称分布在四角"意味着孔的位置必须考虑板宽和板厚的比例,而不是硬编码坐标。在实测里,我让LLM输出参数化变量,比如boss_pad = 10mm,后续特征都引用变量而不是写死数值,这样模型后期改尺寸更容易。另外,对于圆形阵列特征,我要求LLM生成循环代码而不是手动列坐标点,避免出现阵列后模型塌陷的问题。

2.3 从脚本到标准CAD格式的导出链路

文本生成脚本只是第一步,最终交付需要标准CAD格式。CadQuery可以直接用cq.exporters.export()导成STEP、STL或者DXF。STEP格式适合传统CAD软件编辑,STL适合3D打印,DXF用于二维图纸复用。实测发现,STL导出时需要注意网格分辨率,默认值可能让曲面看起来有明显棱角,需要手动调整tolerance参数。

有一点很容易被忽略:导出的模型文件命名规范。如果你的生成器每次输出固定文件名,后面迭代就很乱。我自己的做法是让LLM在生成脚本的同时,根据用户输入提取一个短标识符作为文件名前缀,比如bearing_cover_30x25x4.step,这样后续整理项目目录能省不少事。另外,在导出STEP前跑一回checkGeometry()来验证模型的几何连续性,能提前发现自相交或开放壳这类的bug。

3. 实操过程与核心环节实现

3.1 环境准备与工具链安装

我用的是本机Python环境,配合CadQuery 2.x版本。安装很简单:pip install cadquery,但需要明确你的Python版本必须低于3.12,实测3.11最稳。OpenSCAD可选,如果你要走CSG路线,直接用系统包管理器安装即可。

LLM层面,我推荐用支持代码生成的模型,比如GPT-4或者本地的CodeLlama。如果你有隐私顾虑(很多工程图纸确实不能上传云端),本地部署可以用Ollama跑qwen2.5-coder或者CodeLlama 13B,精度稍低但在可控范围内。我实测时搭了个本地API端点,用Python的requests库直接调用,避免再过一层框架,延迟低很多。

3.2 构建一个最简单的text-to-cad管道

这里写一个完整的核心逻辑,用CadQuery作为后端,LLM负责生成脚本。关键代码如下:

import json import requests import cadquery as cq def llm_to_json(prompt: str, llm_url: str) -> dict: payload = { "prompt": prompt, "context": "你是一个机械设计助手。将所有尺寸解释为毫米。" } resp = requests.post(llm_url, json=payload) resp.raise_for_status() return json.loads(resp.json()["response"]) def json_to_cadquery(description: dict) -> cq.Workplane: w = cq.Workplane("XY") base_type = description["type"] if base_type == "box": w = w.box(description["length"], description["width"], description["height"]) elif base_type == "cylinder": w = w.cylinder(description["height"], description["radius"]) else: raise ValueError(f"Unsupported base type: {base_type}") # add features from list for feature in description["features"]: if feature["type"] == "hole": w = w.hole(feature["diameter"]) elif feature["type"] == "chamfer": w = w.chamfer(feature["distance"]) return w def export_model(w: cq.Workplane, path: str): cq.exporters.export(w, path, exportType="STEP")

这段逻辑不长,但能跑通一个完整的"文本描述→JSON→CadQuery对象→STEP文件"流程。实测中最大的收益是,只要LLM端给出的JSON符合这个schema,后续建模块几乎是零改动。

3.3 一个实际零件:带孔垫片

让管道处理"外径30mm,内径20mm,厚度5mm的垫片"这个描述。LLM转出来的JSON是:

{ "type": "cylinder", "height": 5, "radius": 15, "features": [ { "type": "hole", "diameter": 20 } ] }

注意半径是15,不是30。LLM默认先把直径换算成了半径。CadQuery代码里,cylinder()接口接受半径参数,所以这样的JSON可以直接生成模型。导出STEP后用FreeCAD打开检查,关键尺寸测量无误。这个案例说明,只要中间表示设计得合理,LLM能完成语义到几何参数的准确映射。

然后我试着输入"四个M4螺丝孔均匀分布在一个直径60mm的圆法兰盘的90度位置",生成的JSON里用了polarArray特征描述。CadQuery里有对应的polarArray方法,LLM生成的代码直接创建了一个基础圆柱,然后通过循环打孔,运行结果完全符合预期。到这里你能明显感觉到,中间表示加上特征词约束,比让LLM自由生成脚本要可靠得多。

3.4 流程运行时的参数调优

运行管道时,有两个参数我建议重点关注。第一个是LLM的temperature,调低到0.2以下,确保输出稳定,不会每次生成的JSON结构都不同。第二个是max_tokens,要设置得足够高,否则长描述容易截断,生成出来的脚本不完整。实测里max_tokens=2048对中等复杂度的零件已经够用,复杂装配体要2628以上。

还有一个小坑:CadQuery的默认坐标系原点在第一个操作的中心,如果你后续要定位孔的位置,需要明确使用moveTo或center方法。一开始我让LLM生成一个F型支架,结果安装孔全部放在原点重合了,就是没有对坐标系加减偏移导致的。后来我在提示词里加了规则:"所有特征坐标必须相对于模型整体坐标中心,而不是默认的原点",这个问题才消失。

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

4.1 单位混乱导致模型离谱

最常见的问题是单位。我输入"2英寸的圆底座"时,LLM直接生成了半径2的圆柱,没有换算。最后导出的模型实测是2mm而不是2英寸。解决方案是在LLM的系统提示中强调所有尺寸必须以mm为单位,并且给出换算示例。我在提示词模板里强制加了一句"英寸换算成mm时乘以25.4,所有浮点数保留到小数点后两位",实测用了几次都避免了再犯。

4.2 布尔运算失败与开放壳问题

CadQuery通过OpenCASCADE内核做几何计算,偶尔出现布尔运算失败,通常是因为两个特征恰好共面或圆角太小。遇到这类问题我会先跑checkGeometry()看具体报错,比如"No solids"或者"Non-manifold edge"就说明网格有问题。排查技巧是临时把特征尺寸放大0.001mm,打破完全共面状态,布尔运算就能通过。这个经验是我在做一个带底部倒角槽时发现的,牺牲0.001mm的公差换来了极高的稳定性。

4.3 特征定位偏移和阵列错误

当描述里出现"分布""阵列"这类词时,LLM容易写错循环变量。我遇到一次孔间距均匀分布需求,LLM生成的循环步长是range(1, 4)而不是[0, 90, 180, 270],结果孔位完全错乱。排查技巧是仔细阅读生成的循环代码,看步长和个数是不是符合预期。更稳妥的办法是不让LLM直接写循环,而是让它输出一个坐标点列表,由我们自己的代码统一处理。这样既保持了灵活性,又减少错误。

4.4 输出格式不被下游工具兼容

导出的STEP文件有时候其他软件打不开,主要原因可能是导出的几何单位是厘米而下游期望毫米。CadQuery有一个units参数,导出时显式指定cq.exporters.export(w, "file.step", exportType="STEP", units="mm")就能解决。我还养成了习惯,每次导出后都再用Python读回模型检查尺寸,比如用w.val().BoundingBox()获取长宽高,与预期比对,低于0.5%的偏差就说明模型是健康的。

5. 进阶应用与后续扩展建议

5.1 批量变型设计与参数化族管理

text-to-cad天然适合做批量变型设计。只要底层用参数化工作流,换一组尺寸参数就是新模型。比如法兰盘从DN50到DN200,只需要在描述里替换直径和孔距。我实操时写了一个批量调用脚本,读Excel表格里的型号列表,循环调用LLM转JSON,再生成STEP文件,一次能产出几十个变体。这套方法比传统手动改模型速度快十倍不止,且不容易漏改尺寸。

5.2 结合生成式AI做概念方案阶段

在概念设计阶段,你往往"说不清楚但大致有想法"。我用text-to-cad跑过几个早期方案,输入"和易拉罐差不多大小的外壳,顶部有防滑纹理"这类模糊描述,LLM会生成一个接近外观的粗模。虽然后续仍需细化,但作为头脑风暴的起点,能给团队一个很直观的讨论基础。比起打开三维建模软件从零画,这个起点成本低很多。

5.3 接入企业内部设计规范与知识库

如果你的团队有自己的设计规范(比如标准件库、材料标准),可以用RAG(检索增强生成)的方式把这些规范注入到LLM的上下文里。例如描述"一个符合GB/T标准的M8螺栓孔间距"时,LLM能正确对应标准数值。实操中我在本地向量库存放了零件库文档,查询时把最相关的文档片段拼进提示词,实测生成结果的合规率提高明显。

6. 我的实操总结与避坑心得

整个流程跑下来最大的体会是,text-to-cad的核心壁垒其实不在生成端,而在把用户输入标准化成结构化几何描述的过程。只要这一步稳了,后面的CAD建模反而是最简单的一环。所以在设计你的生成管道时,务必优先设计一套稳定的JSON schema,再考虑大模型的选型。

第二个体会是,一定不要相信LLM的直接输出。无论它看起来有多合理,都要拿到CadQuery里跑完布尔运算和几何检查再使用。跳过检查环节会导致后期出现大量干涉或破面问题,返工成本极高。

最后一个实用技巧是,在做文件管理时,把生成的STEP文件与原始文本描述放在同一个文件夹,再写个简单的脚本,在导出后把JSON中间表示序列化存入文件。这样任何模型都能追溯回"当初是谁、根据什么描述生成的",对于后期追溯和团队协作非常有用。这套组合用下来,我在项目里交付的模型稳定性和迭代效率都提升了好几个档位。

RELATED READING

延伸阅读

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