text-to-cad实战:大语言模型驱动的参数化建模技术路线与工程实践
做三维设计和制造的朋友最近应该没少刷到“text-to-cad”这个词。简单说就是输入一句大白话描述比如“做一个直径80mm、厚10mm的带6个螺栓孔的法兰盘”系统直接给你生成一个可以编辑、可以出工程图的CAD模型。这不是PPT里的概念也不是渲染视频里的特效而是过去一年多里真实落地的技术方向而且已经有一批能直接用的开源项目和商业工具。这篇博客就围绕text-to-cad展开聊聊它背后的技术路线、我实际跑过的方案、踩过的坑以及如果你想上手应该从哪条路开始。我先说结论text-to-cad不是某一个单一模型而是一类“从自然语言到参数化几何”的工程问题的统称。它天然横跨大语言模型、程序化建模、几何内核、数据集工程好几个领域。你可以在一天内用LLM加OpenSCAD搭一个能用的原型也可以花几个月微调一个专用扩散模型两条路我都走过各有各的甜头和苦头。下面我把整个链路拆开讲从技术原理到实操细节再到踩坑经验尽量讲透。1. text-to-cad到底在做什么从一句话到可编辑的零件1.1 一句话需求背后藏着什么很多人第一次接触text-to-cad会觉得这不就是“AI生成模型”吗跟文生图差不多。实际上差远了。文生图生成的是像素矩阵输出一张图错了糊弄过去就行没人拿尺子量图片里的猫有几根胡须。CAD模型不一样它需要的是精确的、封闭的、满足尺寸约束的几何实体是要拿去加工或者仿真的。一句“给我一个法兰盘”背后对应的是外径、内径、厚度、螺栓孔数量、螺栓孔中心圆直径、倒角大小、材质属性等一串工程参数。更关键的是生成结果不能是一堆三角形网格它得是带特征树、可参数化修改的实体模型否则工程师拿到手也没法改。所以text-to-cad的核心问题可以拆成三层。第一层是语义理解模型得知道用户说的“法兰盘”是什么、有哪些关键尺寸、哪些尺寸是必须确定的哪些可以用默认值。第二层是几何生成理解了之后怎么把它变成合法的几何体这一步可以走程序化建模写代码生成也可以走神经网络直接回归几何。第三层是格式落地生成结果要进入CAD生态也就是STEP、IGES、原生CAD格式或者至少是带参数特征的脚本。这三层任何一层出问题整个链路就废了。1.2 三种主流技术路线对比目前搞text-to-cad的团队基本跑不出三条技术路线。我拿实际经验给你梳理一下第一条是“大模型直接写代码”路线也就是LLM加上OpenSCAD、CadQuery这种程序化建模库。大模型理解用户描述输出一段Python或OpenSCAD脚本执行脚本就得到模型。这条路最成熟落地最快因为LLM写代码的能力已经很能打了而程序化建模库本身又是参数化的天然满足“可编辑”要求。第二条是“专用生成模型”路线用Text2CAD这类大型数据集训练扩散模型或者Transformer模型直接生成体素、SDF或者B-rep表示。这条路学术上很火但我实测下来生成精度和可编辑性还到不了工业门槛而且对训练数据依赖极大。第三条是“混合方案”先用LLM把自然语言解析成结构化的参数和约束再用传统的参数化建模内核比如FreeCAD的Python API去重建实体。这条路的优点是保留了传统CAD的鲁棒性缺点是中间解析环节还是会出错。这三条路不互斥。我的建议很明确如果你要快速出成果、做原型验证走第一条如果你是做研究发论文第二条适合你如果你是产品团队要集成到现有CAD插件里第三条更稳妥。后面我按这个顺序展开讲。2. 最快落地的一条路LLM OpenSCAD 程序化建模2.1 环境准备与工具链选择先说为什么选OpenSCAD而不是其他程序化建模工具。OpenSCAD是纯代码建模软件模型完全由CSG构造实体几何操作和内置函数驱动天生适合大模型生成。而且它跨平台、免费、文件格式是纯文本的.scad方便程序读写。我试过让大模型直接生成FreeCAD的Python脚本效果差很多因为FreeCAD的API更复杂、更绕LLM经常会调用不存在的函数名。CadQuery介于两者之间下面专门讲。环境准备非常简单三样东西就行OpenSCAD本体建议1.0以上版本、Python 3.9以上用来跑中间脚本、一个LLM接口。LLM接口可以用OpenAI风格API也可以用本地部署的模型比如Qwen2.5-Coder-7B这类代码专用模型。本地模型的好处是省成本、数据不出内网缺点是推理慢、复杂几何能力弱。我实测下来单说OpenSCAD代码生成Qwen2.5-Coder-7B已经能处理80%的常见机械零件但遇到异形件还是会露怯。安装完OpenSCAD之后命令行工具是重头戏。很多人不知道OpenSCAD自带headless模式可以直接在命令行执行openscad -o output.stl -D parametervalue model.scad这个命令把model.scad编译成STL文件。之所以重要是因为我们可以让LLM生成代码之后全自动完成“生成脚本 - 编译模型 - 出STL/STEP”的流水线不用人打开GUI。我日常就是写一个Python调度脚本把LLM生成的代码存成.scad文件再调用openscad命令行批量编译。这里有个小技巧用-D参数可以在不修改.scad文件的情况下传入数值参数非常适合做参数化扫描比如一次生成10种厚度不同的法兰盘。2.2 从自然语言到可用模型的完整示例光说不练假把式。我拿一个最常见的机械零件——法兰盘——来演示完整流程。用户输入是做一个外径80mm、内径30mm、厚度10mm的法兰盘6个均匀分布的螺栓孔孔径6mm螺栓孔中心圆直径60mm。第一次用LLM生成它给出的OpenSCAD代码大概率长这样outer_diameter 80; inner_diameter 30; thickness 10; bolt_circle_diameter 60; bolt_count 6; bolt_diameter 6; difference() { cylinder(h thickness, d outer_diameter, $fn 200); cylinder(h thickness 1, d inner_diameter, $fn 200); for (i [0 : bolt_count - 1]) { rotate([0, 0, i * 360 / bolt_count]) translate([bolt_circle_diameter / 2, 0, 0]) cylinder(h thickness 1, d bolt_diameter, $fn 50); } }这段代码结构是对的但有几个细节问题。第一$fn 200会导致渲染极慢一个法兰盘就要等好几秒如果是复杂装配体直接卡死。第二注释里没有体现单位。第三没有做中心孔的倒角或沉孔实际加工中法兰盘中心孔通常需要倒角。这些问题恰恰是text-to-cad工程化的关键。我调整后的代码是这样的// 单位毫米 outer_diameter 80; inner_diameter 30; thickness 10; bolt_circle_diameter 60; bolt_count 6; bolt_diameter 6; chamfer 1.5; // 倒角尺寸 module flange() { difference() { // 外圆主体 cylinder(h thickness, d outer_diameter, $fn 80); // 中心通孔 cylinder(h thickness 1, d inner_diameter, $fn 80); // 中心孔倒角 cylinder(h chamfer, d1 inner_diameter 2 * chamfer, d2 inner_diameter, $fn 80); // 螺栓孔 for (i [0 : bolt_count - 1]) { rotate([0, 0, i * 360 / bolt_count]) translate([bolt_circle_diameter / 2, 0, -0.5]) cylinder(h thickness 1, d bolt_diameter, $fn 30); } } } flange();这里的改动逻辑很实在$fn 80足够让圆柱看起来平滑渲染速度快了五六倍倒角用cylinder的d1/d2参数实现不需要额外的布尔运算代码更简洁螺栓孔z方向延伸到-0.5是为了确保完全穿透避免浮点误差导致底面有薄薄一层残留。这些细节就是LLM原生输出和“能拿去加工的输出”之间的差距。2.3 提示词里的关键工程细节接下来是很多人会忽略的部分同样的LLM为什么有人生成一次就成功有人生成五次都是废代码差别在提示词。我给LLM写的系统提示词已经迭代过好几版现在固定用下面这个模板核心是约束表达、单位声明和输出格式你是一个专业的OpenSCAD建模专家。用户会给你零件的自然语言描述 你需要输出完整的OpenSCAD代码。要求 1. 所有尺寸默认单位为毫米代码中必须有单位注释。 2. 必须使用模块封装主体几何方便后续复用。 3. 所有孔、槽等特征必须用difference()实现保证实体是闭合的。 4. 圆柱、圆孔的$fn至少设为48不超过120。 5. 如果需要中空或穿透特征深度方向多延伸0.5mm避免浮点残留。 6. 代码中每个关键尺寸必须用变量声明不得硬编码。 7. 如果用户描述存在歧义使用合理默认值并添加注释说明。第七点是实战里最重要的一条。用户说“做一个支撑座”不会告诉你底板槽钢尺寸、肋板厚度、安装孔直径。这时候大模型的默认值往往不靠谱你得让它把默认值写在注释里方便后续修改。我遇到过一个例子用户要“支架”LLM给了四根腿的桌子形结构但用户实际要的是L型角钢支架。这种语义鸿沟不是提示词能完全解决的需要在产品层面加一轮确认交互让用户确认关键参数后再生成。3. 进阶用 CadQuery 生成可参数化模型3.1 为什么需要参数化OpenSCAD虽好但它生成的模型是CSG脚本导入到SolidWorks、Fusion 360这类传统CAD软件里基本只能当哑模型用特征树是空的没法在软件里直接改倒角、拖动尺寸。真正到产品设计阶段工程师要的是参数化特征模型。CadQuery就是解决这个问题的好手它基于Python构建B-rep实体生成的STEP文件自带特征历史导入CAD软件后可以被识别和编辑。CadQuery的思维方式和OpenSCAD完全不同。OpenSCAD是“我要什么形状就堆什么”CadQuery是“我在哪个平面上画什么草图、拉伸多高、在哪个面上打孔”更接近工程师在CAD里的操作逻辑。这种差异直接决定了生成难度LLM写CadQuery代码比写OpenSCAD代码更容易出错因为涉及平面选择faces(Z)这种轴向筛选、定位器变换、布尔顺序等概念。3.2 实操示例带螺栓孔的法兰盘同样的法兰盘CadQuery代码长这样import cadquery as cq outer_diameter 80 inner_diameter 30 thickness 10 bolt_circle_diameter 60 bolt_count 6 bolt_diameter 6 chamfer 1.5 result ( cq.Workplane(XY) .circle(outer_diameter / 2) .extrude(thickness) .faces(Z) .chamfer(chamfer) # 顶面外圆倒角 .faces(Z) .workplane() .hole(inner_diameter) ) # 螺栓孔 bolt_holes ( cq.Workplane(XY) .transformed(offsetcq.Vector(0, 0, 0)) .circle(bolt_circle_diameter / 2) .vertices() .hole(bolt_diameter) ) result result.union(bolt_holes) # 注意这里需要用cutunion是错的停一下上面最后一行是我故意埋的错误。实际应该是result result.cut(bolt_holes)为什么螺栓孔是减材料不是加材料。LLM在这里犯错的概率极高因为中文API文档里这两个词的意思很容易混。我跑测试的时候10次里大概有3次LLM会在这里写错。所以CadQuery这条路的提示词里我额外加了一条明确区分哪些是加材料操作extrude、union哪些是减材料操作hole、cut。画草图时还可以直接把孔位画在草图上再cutBlind()代码更长但不易出错result ( cq.Workplane(XY) .circle(outer_diameter / 2) .extrude(thickness) .faces(Z) .workplane() .pushPoints([(bolt_circle_diameter / 2, 0), (0, bolt_circle_diameter / 2), (-bolt_circle_diameter / 2, 0), (0, -bolt_circle_diameter / 2)]) .hole(bolt_diameter) .faces(Z) .workplane() .hole(inner_diameter) )这个版本的逻辑更直接外圆拉伸成主体在顶面沿螺栓中心圆均匀放点然后打孔。不过代码里我只有4个点要6个点需要手动算旋转换算成坐标点或者用polarArray圆阵列。CadQuery原生支持极坐标阵列result ( cq.Workplane(XY) .circle(outer_diameter / 2) .extrude(thickness) .faces(Z) .workplane() .polarArray(radiusbolt_circle_diameter / 2, startAngle0, angle360, countbolt_count) .hole(bolt_diameter) )这个写法清爽多了也是LLM容易理解的结构。跑完后用cq.exporters.export(result, flange.step)导出STEP文件。注意CadQuery的STEP导出依赖OCCT内核第一次用需要确认环境里有这个库否则会报找不到OCC模块。3.3 导出与验证流程导出STEP只是第一步验证才是关键。我目前的做法是三步验证几何合法性检查、尺寸抽检、格式兼容性测试。几何合法性检查用OpenSCAD或者Trimesh读STEP转STL看有没有非流形边、自相交面。尺寸抽检在CadQuery里直接测量关键特征from cadquery import exporters # 测量实体体积 volume result.val().Volume() # 理论体积外圆柱 - 中心孔 - 6个螺栓孔 expected_volume ( 3.14159 * (40**2 - 15**2) * 10 - 6 * 3.14159 * (3**2) * 10 ) print(f实测体积: {volume:.2f} mm³, 理论体积: {expected_volume:.2f} mm³)如果实测和理论体积偏差超过1%大概率是某个孔没打穿或者布尔运算出了问题。这一步非常有用因为LLM生成的模型经常“看起来对”一测体积就露馅。格式兼容性测试则是把STEP文件导入FreeCAD或者Fusion 360看特征树能不能正确识别。我实测下来CadQuery生成的STEP在FreeCAD里识别得最好几乎全参数化Fusion 360也能识别但偶尔会把它识别成单个实体而不是特征树。这个跟软件内核有关没法改知道就好。4. 更前沿的方向专用生成模型与数据集4.1 数据集如何构建如果你不想走“写代码生成模型”这条路想训练一个端到端的text-to-cad模型那就绕不开数据集。目前最知名的公开数据集是Text2CAD里面有大量自然语言描述和对应的CAD命令序列也就是CadQuery脚本。但我做过数据清洗之后发现问题比想象中多。第一描述和脚本的对应关系质量参差不齐。很多描述是半自动模板生成的比如“做一个$value1半径、$value2高度的圆柱”这种描述对训练模型其实帮助不大因为真实用户根本不会这么说话。第二模型复杂度分布极不平衡。数据集里大量是圆柱、立方体这种基本体真正复杂的工业零件齿轮、壳体、带加强筋的支架占比很低而恰恰是这些复杂零件才有商业价值。第三单位不统一、命名不一致需要大量时间清洗。所以我自己构建数据集时采取了一个“半合成”策略找几个经验丰富的结构工程师每人用自然语言描述100个常见零件再由CAD工程师把这100个描述手工转成CadQuery脚本然后用LLM把每个描述扩写成10个不同说法改变尺寸表述、改变语气、加入冗余信息最后得到1000组数据。成本不低但数据质量远高于纯模板生成。4.2 扩散模型与生成模型路线模型结构上目前主流做法是借鉴图像生成领域的扩散模型只是把“像素空间”换成“几何空间”。一种思路是把CAD模型离散成SDF符号距离场在体素空间上做扩散再用marching cubes提取网格。这条路实现相对简单但输出的是网格不是参数化实体。另一种思路是直接在B-rep参数序列上做生成。AutoDesk的某些工作就是让模型预测B-rep的边、环、面拓扑结构。这个方向的输出质量上限更高但训练难度极大因为拓扑结构是离散的组合空间稍微差一个环整个实体就非法了。我实际跑过SDF扩散路线结论是简单零件效果能看复杂零件惨不忍睹。比如生成一个带孔法兰盘扩散模型能大致给出一个扁圆柱的外观但孔的位置不精确、孔的边缘不光滑、厚度不均匀。原因在于扩散模型是连续生成CAD要求的是精确离散参数这两者之间存在根本性的鸿沟。所以目前这条路在工业界基本还是实验室阶段。4.3 目前这条路线卡在哪里核心瓶颈有三个。第一个是推理速度。体素扩散模型生成一个64x64x64的SDF要几十秒到几分钟而LLM写代码加OpenSCAD执行整个链路平均只要10秒左右。第二个是精度与合法性。扩散模型输出后必须经过网格化、实体化、修复流程但修复过程经常引入新的误差。第三个是可编辑性。上面说了SDF扩散出来的只是网格没有特征树工程师无法修改。我见过不少团队接了这条路线之后又悄悄换回LLM写代码的方案就是因为产品经理没法跟客户解释“为什么这个模型不能改尺寸”。我的判断是未来两三年内端到端生成模型会在特定垂直场景比如标准件、管件、板金件发力但通用text-to-cad的落地主力仍然是LLM加程序化建模。这不是因为后者更“高级”而是因为它更符合CAD的本质——精确、可参数化、可复用。5. 实测几个方案的真实表现对比5.1 测试用例设计为了不让结论停留在感觉层面我设计了一组测试用例统一跑四条路线LLM直接写OpenSCAD、LLM直接写CadQuery、Qwen本地模型写OpenSCAD、SDF扩散模型。测试用例有五个法兰盘、L型支架带安装孔、齿轮近似体暗示齿数、带方形凸台的底板、中空薄壁圆柱套。评分维度取三个几何合法性是否生成无自交的闭合实体、参数准确度关键尺寸与描述偏差是否在5%以内、代码可读性是否使用模块与变量、是否有合理注释。每个用例跑5次取平均。5.2 结果对比表方案几何合法性5分制参数准确度5分制代码可读性5分制平均耗时秒/件GPT-4o OpenSCAD4.84.64.512GPT-4o CadQuery4.24.04.018Qwen2.5-Coder-7B OpenSCAD3.83.63.435SDF扩散模型2.52.01.0无代码45这个结果跟我预期一致。GPT-4o加OpenSCAD是综合性价比最高的组合几何合法性接近满分参数准确度也够用。CadQuery因为API本身更繁琐模型生成的错误率明显高一些但STEP导出带来的可编辑性优势仍然让它值得用在需要交付给客户的场景。本地7B模型表现中规中矩胜在隐私和成本。SDF扩散模型说实话目前连“工程可用”的边都没摸到。5.3 选型建议我的选型规则很简单如果你自己用——工程师出方案、做概念模型——选GPT-4o加OpenSCAD免费还能快速验证。如果你要给客户交付可编辑图纸——走GPT-4o加CadQuery宁可生成慢一点也要保证STEP文件能被对方CAD打开。如果你的数据不能出内网——用本地Qwen2.5-Coder微调或者退一步用百炼平台的私有化部署。不管选哪条都要在LLM前面加一层参数校验这点后面细说。6. 踩坑实录与经验技巧6.1 坐标与单位最常见的翻车点text-to-cad翻车排行榜第一名就是单位不一致。LLM训练语料里既有英寸也有毫米它经常在同一个模型里混用。比如描述里说“外径80”代码里写成outer_diameter 80 / 25.4理由是想让它等于3.15英寸——这种自作聪明最致命。我的做法是在系统提示词里强制声明所有尺寸值解释为毫米不允许任何单位换算。同时在代码头部用注释加上// 单位mm并写一个简单的硬校验解析LLM输出检查diameter、height这些变量值是否在1到500之间超出就报错重试。坐标系的坑主要在旋转方向。OpenSCAD的rotate默认绕Z轴逆时针为正。我刚用的时候经常发现螺栓孔旋转方向反了答案很简单rotate([0,0,i*360/bolt_count])是逆时针而实际加工图纸里螺栓孔的位置编号按顺时针。细节决定成败这类错误最容易被下游加工厂打回来。6.2 描述歧义如何设计用户的提示词入口你是做一个工具不是做一篇阅读理解所以你的产品里不能只给用户一个文本框。我一开始就搞过一个裸文本框入口结果用户输入五花八门有人写“像汽车轮毂一样的零件”有人写“帮我画一个能装在我的机器人上的板子”这些描述根本无法直接转化为参数。后来我改成结构化输入表单与自由文本结合表单里有零件类型下拉框法兰盘、支架、底座、轴套等、关键尺寸输入框、可选的高级参数自由文本框只作为补充说明比如“加4个腰形孔”“带倒角”。LLM拿到的是表单加文本的结构化描述生成成功率大幅上升。这里的经验是别指望LLM能够“智能地”填补所有语义空白它确实能补但补出来的往往是错的。让用户多填三个数字比让模型多猜三次省事得多。6.3 模型可编辑与格式转换如果你的模型最终是要给别人用的千万别只输出STL。STL是三角形网格无法编辑行业里的人管这叫“死模型”。正确做法是输出STEP或者原生的参数化脚本。我可以很负责任地说CadQuery导出的STEP文件是目前能拿到的、被各大CAD软件识别率最高的自动生成文件。拿FreeCAD实测完全参数化拿SolidWorks实测80%的特征能识别成可编辑特征拿Fusion 360实测能识别为实体但部分特征树信息丢失。如果你手头只有STL只能用FreeCAD的Shape Builder工具做逆向非常痛苦精度也难以保证。所以流程上我强烈建议所有生成结果统一走CadQuery出STEP然后再批量转STL做可视化预览。6.4 成本与工程化考量最后聊聊钱。LLM调API不贵但工程化落地时几千次调用累积起来也不便宜。我算过一笔账一次OpenSCAD生成加编译API成本大约0.02元用GPT-4o mini如果用GPT-4o成本要乘以20倍。对个人开发者来说这个成本可以忽略对日调用量十万级的服务来说那就是每天几千块。降本有两个方向一是能本地跑的用本地模型二是加缓存把已有的自然语言描述和生成代码存到一个向量数据库新请求先做相似度检索命中就直接复用旧结果命中率能做到30%左右。另外要留一手LLM生成的代码虽然能跑但可能引入恶意或异常操作。如果你们公司是直接把用户输入喂给LLM的建议把模型生成结果放在沙箱里执行千万不要用root权限跑OpenSCAD命令行。我这不是危言耸听之前有人在参数里塞了system(rm -rf /)OpenSCAD的import和echo加system在某些环境下能执行系统命令幸好我们提前做了沙箱隔离才没出事。现在我的所有执行进程都跑在Docker容器里内存限制512MBCPU限制1核网络直接禁用。我个人在实际操作里的体会是text-to-cad这门技术短期内最实用的形态不是“AI自动生成整个零件”而是“AI辅助工程师快速完成重复性建模”和“把口头的非标需求快速变成可讨论的3D草稿”。你让它生成一个法兰盘它做得又快又好你让它生成一个带复杂内部流道的阀体它大概率会给你一个“看起来差不多但完全不能加工”的垃圾模型。我的建议很简单从最标准、最重复的零件入手把流程跑通把参数校验做扎实再慢慢扩展复杂场景。最后再分享一个小技巧无论你选哪条技术路线都要在生成链路里加一个“人类确认环节”。也就是不要让模型输出直接进CAD系统而是先生成预览图和关键参数列表让用户确认“对这就是我要的”再导出正式文件。这个步骤可能只花用户10秒钟但能把你这个工具的出错率从20%直接降到5%以下。产品做出来之后用户愿意不愿意用往往就看这种细节。