text-to-cad实战:从自然语言到参数化三维模型生成
1. 从一句话到三维实体text-to-cad 到底在解决什么问题第一次听到 “text-to-cad” 这个词我脑子里蹦出来的画面是对着电脑敲一行字比如“一个外径 60mm、内孔 20mm、厚度 10mm 的法兰盘”然后软件自动吐出一个可以打开、可以编辑、可以直接丢进切片软件的三维模型。这个画面在几年前还属于科幻范畴但现在已经有一批工具把它做成了现实。所谓 text-to-cad本质上是把自然语言描述转换成 CAD 可识别的几何数据最终输出 STEP、GLB、STL 这类通用三维格式。它要解决的核心痛点非常明确大量非专业建模人员有明确的几何需求却卡在“不会用 CAD 软件”这道门槛上。我接触这个方向最初是因为帮一个做机械配件的小团队做自动化改造。他们每天要处理几十个来自客户的定制需求很多需求其实就是“改个孔径”“加个倒角”“把长度从 80 改成 120”这种简单变更但每次都要人工打开 CAD 软件重新画一遍效率极低还容易出错。那时候我就在想如果能把客户的自然语言描述直接转成参数化的三维模型哪怕只覆盖 60% 的常见需求也能省下大量重复劳动。text-to-cad 正好切中了这个场景。这篇文章适合几类人看一是做机械设计、产品设计想了解如何用自然语言驱动建模的从业者二是做 3D 打印、手办制作需要快速生成基础模型的玩家三是做自动化工具、低代码平台的开发者想把三维生成能力集成到自己的系统里。我会从整体设计思路讲到具体实操把踩过的坑和验证过的方案都摊开来说尽量让不同基础的人都能拿走能用的东西。2. 整体设计思路为什么不是“直接生成网格”而是“先生成参数再建模”2.1 两条技术路线的取舍逻辑text-to-cad 目前主流有两条路线。第一条是端到端生成网格用大模型直接输出顶点和面片数据或者输出隐式场再提取等值面。第二条是自然语言转参数化脚本让模型输出一段建模代码或参数化指令再由 CAD 内核执行生成实体。我两种都试过最后坚定地站在第二条路线上原因有三个。第一可编辑性。端到端生成的网格是一坨“死”的几何你想改个孔径只能重新生成或者手动修网格非常痛苦。而参数化脚本生成的是带特征的实体孔径、壁厚、倒角都是独立参数改一个数字就能重新出图。对于工程场景来说这一点几乎是决定性的。第二精度可控。网格生成的分辨率受限于模型输出圆孔可能变成多边形平面可能有微小起伏。参数化建模调用的是 CAD 内核的精确几何运算圆就是圆平面就是平面尺寸精度可以做到微米级。做机械配合件的时候这个差别直接决定能不能装配。第三格式兼容。参数化实体可以导出 STEP这是工业界通用的交换格式几乎所有 CAD 软件都能打开和继续编辑。网格格式如 STL、GLB 虽然也能用但在专业设计流程里往往只是最终交付格式不是中间编辑格式。注意如果你的目标只是做视觉展示、游戏资产或者简单的 3D 打印摆件端到端网格生成也能用而且速度可能更快。但只要涉及尺寸配合、后续修改、工程交付参数化路线是唯一靠谱的选择。2.2 核心架构拆解从文本到实体的四层结构我把整个系统拆成四层每一层都有明确的输入输出和职责边界。第一层是意图理解层。输入是一句自然语言输出是结构化的几何意图。比如“一个长 100、宽 50、高 30 的长方体四个角倒 R5 圆角”这一层要识别出基本体是长方体尺寸参数是 100/50/30特征是倒圆角圆角半径是 5作用位置是四个竖直边。这一层通常用大语言模型做 few-shot 抽取配合一套预定义的 schema 来约束输出格式。第二层是参数校验与补全层。大模型抽取的参数经常有缺失或矛盾。比如用户说“做一个法兰盘”没给尺寸这时候需要根据常见工程惯例补全默认值或者主动向用户追问。又比如用户说“内孔比外径大”这是逻辑矛盾需要拦截并提示。这一层的存在感很低但少了它后面会频繁报错。第三层是建模脚本生成层。把结构化意图翻译成具体的建模指令。如果用 CadQuery就是生成一段 Python 代码如果用 OpenSCAD就是生成对应的脚本。这一层的关键是模板化常见几何特征都有对应的代码模板模型只需要做参数填充和组合。第四层是执行与导出层。调用 CAD 内核执行脚本生成实体然后按需导出 STEP、GLB、STL 等格式。这一层要处理内核报错、几何有效性检查、单位换算等脏活累活。2.3 为什么选 CadQuery 作为主力内核市面上参数化建模方案不少我最终选 CadQuery 作为主力理由很实际。它是基于 OpenCASCADE 的 Python 库而 OpenCASCADE 是工业级的三维几何内核精度和稳定性经过大量工程验证。CadQuery 的 API 设计比较符合直觉写起来像在描述几何特征而不是在操作底层数据结构。另外它的社区活跃遇到问题容易找到参考。对比 OpenSCADCadQuery 的优势在于它操作的是 B-rep 实体支持真正的圆角、倒角、布尔运算而 OpenSCAD 基于 CSG圆角这类特征实现起来很别扭。对比直接调用 FreeCAD 的 APICadQuery 的抽象层次更高代码量更少更适合做自动化生成。当然 CadQuery 也有坑比如某些复杂布尔运算会失败圆角半径过大会导致几何无效这些后面会详细讲。3. 核心细节解析意图抽取、参数映射与脚本模板3.1 意图抽取的 schema 设计与提示词技巧意图抽取是整个流程的入口抽错了后面全错。我设计了一套 JSON schema 来约束输出核心字段包括primitive基本体类型、dimensions尺寸字典、features特征列表、units单位、constraints约束条件。基本体类型我预定义了长方体、圆柱、圆环、球体、棱柱、法兰、支架等常见类别。特征列表里每个特征包含type如 fillet、chamfer、hole、pocket、params特征参数、target作用对象。提示词方面我踩过的最大坑是不要指望模型一次抽对所有参数。早期我写了一个很长的提示词试图让模型一次性输出完整结构结果经常出现字段缺失、类型错误、单位混乱。后来改成两步走第一步只让模型判断“这是什么类型的零件”和“有哪些关键尺寸”第二步再针对具体类型做详细参数抽取。准确率明显提升。另一个技巧是给模型提供单位换算的显式规则。用户可能说“10 公分”“两寸”“M8 的孔”这些都要在提示词里给出换算表。M8 孔意味着直径 8mm两寸管的外径约 60.3mm这些工程常识要提前喂给模型否则它会瞎猜。# 意图抽取的 schema 示例简化版 intent_schema { part_type: flange | box | cylinder | bracket | ..., dimensions: { outer_diameter: float (mm), inner_diameter: float (mm), thickness: float (mm) }, features: [ {type: hole, diameter: float, count: int, pattern: bolt_circle} ], units: mm, confidence: float 0-1 }3.2 参数补全与冲突检测的实操规则参数补全这块我的原则是能推断就推断不能推断就追问绝不瞎编。比如用户说“做一个 M8 螺栓孔的法兰”没给法兰外径我会根据螺栓孔数量和常见法兰标准推断一个合理的外径范围并在输出里标注“外径为推断值请确认”。如果用户说“做一个很大的板”这个“很大”无法量化就必须追问具体尺寸。冲突检测我总结了几个高频场景。一是尺寸矛盾比如内孔直径大于外径或者壁厚为负。二是特征冲突比如在同一个位置既要求倒圆角又要求倒直角。三是单位冲突比如同时出现“mm”和“inch”且没有明确换算关系。这些都要在生成脚本之前拦截掉否则 CAD 内核执行时会报一堆难以理解的错误。实操心得我习惯在参数补全后加一个“确认回显”步骤把最终采用的参数用自然语言复述给用户比如“我将生成一个外径 80mm、内孔 30mm、厚度 10mm 的法兰带 6 个直径 8mm 的螺栓孔均布在直径 60mm 的圆上”。这一步能拦掉大量因为理解偏差导致的返工。3.3 CadQuery 脚本模板的编写要点脚本模板的质量直接决定生成成功率。我的做法是按零件类型建立模板库每个模板是一个参数化的函数接收标准化的参数字典返回 CadQuery 的实体对象。模板内部处理常见的几何操作比如螺栓孔阵列、圆角、倒角、抽壳等。写模板有几个关键点。第一所有尺寸参数必须显式传入不在模板内部硬编码。这样同一个模板可以覆盖不同规格的零件。第二布尔运算的顺序要固定。先做主体再做减材特征孔、槽最后做倒角和圆角。顺序错了可能导致圆角失败。第三加异常捕获和几何有效性检查。CadQuery 的val().isValid()可以检查生成的实体是否有效无效时返回明确的错误信息而不是直接崩溃。import cadquery as cq def make_flange(outer_d, inner_d, thickness, bolt_hole_d, bolt_circle_d, bolt_count): # 主体圆盘 result cq.Workplane(XY).circle(outer_d / 2).extrude(thickness) # 中心孔 result result.faces(Z).workplane().hole(inner_d) # 螺栓孔阵列 result ( result.faces(Z).workplane() .polarArray(bolt_circle_d / 2, 0, 360, bolt_count) .hole(bolt_hole_d) ) # 上下边缘倒角 result result.edges(%CIRCLE).chamfer(1.0) return result这个模板看起来简单但实际写的时候要考虑很多边界情况。比如螺栓孔数量为 1 时polarArray的行为是否正常倒角半径大于厚度时会不会失败内孔直径接近外径时壁厚是否足够。这些都要在模板里做校验。4. 实操过程从零搭一套可用的 text-to-cad 流水线4.1 环境准备与依赖安装先把环境搭起来。我用的组合是 Python 3.10 CadQuery 2.4 一个大模型 API。CadQuery 的安装推荐用 conda因为它的依赖里有 OpenCASCADE 的二进制包pip 安装有时候会遇到编译问题。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery2.4 pip install openai pydantic装完之后跑一个最小验证确认 CadQuery 能正常工作import cadquery as cq box cq.Workplane(XY).box(10, 10, 10) cq.exporters.export(box, test.step) print(CadQuery OK)如果这一步报错大概率是 OpenCASCADE 的动态库没找到检查 conda 环境是否激活正确。Windows 上偶尔需要手动把 conda 环境的 Library/bin 加到 PATH 里。4.2 完整流水线的代码实现整个流水线我写成一个主函数输入是自然语言字符串输出是 STEP 文件路径和生成日志。核心步骤包括调用大模型抽取意图、校验和补全参数、选择模板、执行建模、导出文件。import json from openai import OpenAI import cadquery as cq client OpenAI(api_keyyour-key) def text_to_cad(user_input: str, output_path: str output.step): # 第一步意图抽取 intent extract_intent(user_input) # 第二步参数校验与补全 params validate_and_complete(intent) # 第三步选择模板并执行 part build_part(params) # 第四步有效性检查与导出 if not part.val().isValid(): raise ValueError(生成的几何无效请检查参数) cq.exporters.export(part, output_path) return output_path, params def extract_intent(user_input): prompt f从下面的描述中抽取三维零件的几何参数输出 JSON。 描述{user_input} 要求尺寸单位统一为 mm缺失的参数标记为 null。 resp client.chat.completions.create( modelgpt-4o, messages[{role: user, content: prompt}], response_format{type: json_object} ) return json.loads(resp.choices[0].message.content)build_part函数根据part_type分发到不同的模板。我目前维护了法兰、长方体板、圆柱、L 型支架、齿轮毛坯等十几个模板覆盖了大部分常见需求。4.3 参数计算实例一个法兰的完整生成过程拿一个具体例子走一遍。用户输入“做一个外径 100mm、内孔 40mm、厚度 12mm 的法兰用 6 个 M8 螺栓孔螺栓孔中心圆直径 75mm。”意图抽取结果part_typeflangeouter_d100inner_d40thickness12bolt_hole_d8M8 对应 8mmbolt_count6bolt_circle_d75。参数校验外径 100 大于内孔 40壁厚 (100-40)/2 30mm足够。螺栓孔中心圆直径 75 介于内孔 40 和外径 100 之间合理。螺栓孔直径 8mm6 个孔均布在直径 75 的圆上相邻孔中心角 60 度弧长 π × 75 / 6 ≈ 39.3mm孔间距足够不会干涉。执行建模调用make_flange(100, 40, 12, 8, 75, 6)生成实体检查有效性导出 STEP。整个过程从输入到输出大约 8 到 15 秒主要时间花在大模型调用上。如果本地部署小模型做意图抽取可以压缩到 2 秒以内但抽取准确率会下降需要权衡。4.4 导出格式的选择与转换STEP 是首选导出格式因为它是工业标准保留了 B-rep 实体信息可以被 SolidWorks、Fusion 360、FreeCAD 等软件直接打开和编辑。GLB 适合做可视化展示和网页预览文件小、加载快但丢失了精确几何信息。STL 适合 3D 打印是网格格式精度取决于细分程度。我通常同时导出 STEP 和 GLB。STEP 给设计人员做后续修改GLB 给非技术人员做快速预览。导出 GLB 需要额外装cadquery-ocp的导出模块或者用trimesh做格式转换。# 同时导出 STEP 和 GLB cq.exporters.export(part, output.step) # GLB 导出需要先转成网格 from cadquery import exporters exporters.export(part, output.glb, exportTypeGLB)注意GLB 导出时如果模型有非常小的特征比如 0.5mm 的倒角可能会因为网格细分不足而丢失。做可视化预览没问题但不要用 GLB 做精度验证。5. 常见问题与排查技巧实录5.1 几何生成失败的典型原因与解法CadQuery 报错最常见的是布尔运算失败和圆角失败。布尔运算失败通常是因为两个实体没有正确相交或者相交面有微小间隙。解法是检查实体的位置和尺寸确保减材特征完全穿透或正确嵌入主体。圆角失败通常是因为圆角半径大于相邻边的长度或者圆角作用在非凸边上。解法是减小圆角半径或者先做圆角再做其他特征。还有一个隐蔽的坑是单位问题。CadQuery 默认单位是毫米但如果从外部导入的模型是英寸混用会导致尺寸差 25.4 倍。我在流水线里强制所有参数在进入模板前统一转成毫米避免这个问题。5.2 大模型抽取错误的兜底策略大模型不是万能的抽取错误在所难免。我的兜底策略分三层。第一层是schema 校验用 Pydantic 定义严格的参数类型和范围不符合的直接拒绝。第二层是几何合理性检查比如尺寸是否为正、比例是否合理、特征是否冲突。第三层是生成后验证检查实体是否有效、体积是否在合理范围、包围盒尺寸是否符合预期。如果三层都过了但结果还是不对那就需要人工介入。我会把原始输入、抽取结果、生成参数、预览图一起展示给用户让用户确认或修正。这个反馈数据收集起来可以用来微调抽取模型形成正向循环。5.3 性能优化与批量生成单次生成十几秒可以接受但批量生成几百个零件时就需要优化。我的做法是把大模型调用做成异步并发同时用缓存避免重复抽取相同或相似的描述。CadQuery 的建模本身很快瓶颈主要在网络请求上。另外如果需求集中在少数几种零件类型可以考虑本地部署一个小模型专门做意图抽取牺牲一点准确率换取速度。我实测下来7B 级别的模型在法兰、板件、圆柱这几类上抽取准确率能到 85% 左右配合规则校验可以提到 95% 以上速度比调用云端 API 快一个数量级。问题现象可能原因排查方法解决方案布尔运算失败实体未相交或有间隙检查减材特征位置和尺寸调整位置或加大穿透深度圆角失败半径过大或作用在非凸边减小半径或换边先圆角后其他特征导出 STEP 为空实体无效或未生成检查 isValid()修正参数重新生成尺寸偏差 25.4 倍单位混用检查输入单位统一转毫米大模型抽取字段缺失提示词不够明确查看原始输出加 few-shot 示例5.4 几个我踩过的坑和对应技巧第一个坑是中文描述里的模糊量词。“稍微倒个角”“孔开大一点”这种描述大模型会瞎猜一个值。我的处理是维护一个模糊量词映射表“稍微”对应 0.5 到 1mm“大一点”对应增加 10% 到 20%并在输出里标注这是推断值。第二个坑是多零件装配的描述。“做一个轴和一个套轴能插进套里”这种需求涉及两个零件和配合关系。我目前的方案是拆成两次生成分别输出两个 STEP 文件配合关系用注释说明暂不做自动装配。自动装配的复杂度太高投入产出比不划算。第三个坑是特殊字符和单位符号。用户输入里可能有“Φ”“×”“°”这些符号直接传给大模型有时候会乱码。我在预处理阶段做统一替换“Φ”转“直径”“×”转“x”“°”转“度”效果稳定很多。6. 工具选型与扩展方向6.1 CadQuery、OpenSCAD、FreeCAD 的对比这三个是我实际用过的方案各有适用场景。CadQuery 适合做自动化生成Python 生态好API 简洁B-rep 精度高。OpenSCAD 适合做参数化设计语法简单但 CSG 内核在圆角和复杂布尔运算上能力有限。FreeCAD 功能最全有完整的 GUI 和 Python API但 API 比较底层写自动化脚本代码量大。如果你的场景是“批量生成简单零件”CadQuery 是最优解。如果是“交互式参数化设计”OpenSCAD 更直观。如果是“需要完整 CAD 功能且要自动化”FreeCAD 更合适但开发成本高。6.2 后续可以扩展的能力目前这套流水线覆盖的是单零件生成。往后再走有几个方向值得投入。一是装配体生成支持多个零件的相对位置和配合关系。二是工程图输出自动生成三视图和尺寸标注。三是参数优化根据受力或工艺约束自动调整尺寸。四是与 3D 打印切片软件打通生成模型后直接切片并估算打印时间。我个人最看好的是装配体方向因为实际工程需求里单零件很少大部分是多个零件的组合。但装配的几何约束和配合关系比单零件复杂得多需要引入更多的工程知识库。6.3 给不同基础读者的上手建议如果你是完全新手建议先从 CadQuery 的官方示例入手手动写几个零件脚本理解参数化建模的思路。然后接一个大模型 API从最简单的“生成长方体”开始逐步增加特征复杂度。如果你有 CAD 基础但不会编程可以把 text-to-cad 当成一个“快速原型”工具用它生成基础形状再导入熟悉的 CAD 软件做精细调整。这样既能享受自动化的效率又不用完全依赖生成结果。如果你是开发者建议把重点放在意图抽取的准确率和模板库的覆盖率上。这两个指标直接决定用户体验。几何内核的调用反而是最稳定的部分CadQuery 已经帮你处理了大部分底层细节。最后分享一个我在实际使用中总结的小技巧把常用的零件描述存成模板短语。比如“标准法兰 100/40/12”对应一组固定参数用户直接说短语就能生成比每次描述完整参数快得多也减少了抽取错误。这个做法在团队内部推广后生成成功率从 70% 左右提到了 90% 以上。