ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

text-to-cad实战:从自然语言到可编辑三维模型的工程链路

text-to-cad实战:从自然语言到可编辑三维模型的工程链路 1. text-to-cad 到底在“生成”什么最近总有人问我text-to-cad 是不是像那些“输入一句话生成3D模型”的网页一样敲一句“一个好看的杯子”它就能吐出一个可以打印的杯子其实差得远。text-to-cad 的核心不是做“像”而是做“准”你给我一句“M5螺栓固定的L型支架壁厚3毫米边长40毫米”它要能变成一个尺寸明确、能继续编辑、能直接送去加工或切片的三维实体。这个区别决定了整个技术路线的走向。先说清楚一个问题文生3D模型和 text-to-cad 不是一回事。前者生成的往往是网格模型比如OBJ、STL视觉效果可以很惊艳但拓扑乱七八糟面朝向不统一很多模型甚至不是水密的。拿进SolidWorks或者Fusion 360想改一下孔的位置你会发现根本没有特征树也没有参数改一处等于重新建模。而工程上要的是B-rep实体有草图、有约束、有特征历史修改尺寸会联动更新。所以 text-to-cad 的最终产出应该是一种“可编辑的数学描述”而不只是一堆三角形。从这个角度看目前主流的产出形态大概有三种第一种是程序化脚本也就是生成 OpenSCAD、CadQuery、FreeCAD Python 宏这些代码第二种是中间表示类似“草图约束特征树”的数据结构这也是很多学术模型喜欢输出的东西第三种才是直接生成网格文件。我个人在工程里几乎只用第一种因为代码本身就是参数化的后面无论是批量调整尺寸还是接入自动化流水线都极其方便。1.1 文本到模型不等于文本到图片如果你用“文字生成一张图片”的思路去做 text-to-cad大概率会卡在半路。图片是像素网格错几个像素没人看得出来但CAD模型错0.1毫米就可能装配不上错1毫米可能直接报废一件料。所以 text-to-cad 的评估标准也完全不一样不只是“像不像”还要看尺寸正不正确、特征是否完整、能不能被下游工具识别。我见过一些早期方案直接把自然语言丢给神经网络生成体素或者点云再通过算法拟合成网格。这种思路的问题是输出结果很难落到正常设计流程里。你拿到的是一坨“形状差不多”的网格没有明确的圆孔可以提取没有平面约束可以定位甚至边缘都是锯齿。做做概念预览还行真要进PDM、出工程图、做CAM全部白搭。所以我会把 text-to-cad 看作一项“约束翻译”任务把用户语言中的尺寸、公差、装配关系、加工约束翻译成CAD内核能够执行的特征操作。大模型在这里扮演的是翻译官OpenSCAD/CadQuery 是执行器校验脚本是质检员。这个链条比“端到端生成几何体”靠谱得多也更容易在真实项目中落地。1.2 为什么现在才爆发LLM程序化CAD 是关键早几年就有人做“文本生成CAD”方向但效果一般。原因很简单那时候没有大模型或者说模型对代码的理解远没有现在强。而 OpenSCAD、CadQuery 这类工具本质上是“用代码描述几何”天然适合语言模型处理。既然 LLM 能把一句需求变成 Python 代码那它同样有机会把一句“做一个带凸台的底座”变成一段 OpenSCAD 脚本。这个转变很重要。它把 text-to-cad 从“最难的三维几何生成问题”变成了“相对成熟的代码生成问题”。模型不需要凭空想象三角形的位置只需要学会组合cylinder、cube、difference这些基础几何运算并把参数写对。而参数是否合理、代码能否运行我们完全可以用命令行快速验证。说句不客气的话这是目前把大模型用在三维设计上最扎实的一条路线比“生成一张好看的渲染图”更有工程价值。1.3 一个最小闭环长什么样我实测下来的最小闭环只有六步。第一步准备一段足够结构化的需求描述第二步把需求发给大模型让它生成 OpenSCAD 或 CadQuery 代码第三步把代码存成脚本文件第四步用命令行工具执行并导出模型文件第五步用自动化脚本检查包围盒、水密性、体积这些硬指标第六步不满足要求就把报错信息和检查结果回填给大模型让它自己修。循环这几步直到模型通过校验。这套闭环可以小到在一台没有图形界面的服务器上跑也可以大到接入公司的PDM系统。后续所有的进阶玩法比如生成装配体、自动排料、批量改型都是在这条主线上加齿轮。所以这篇博客后面所有内容都是围绕这个最小闭环展开的怎么搭怎么调有什么坑。2. 本地搭一个最小闭环LLM OpenSCAD 生成参数化零件2.1 选型为什么我不用CAD软件而是用代码化建模第一次做 text-to-cad 的时候我差点去走控制SolidWorks、Fusion 360宏的路子。后来放弃了。原因很现实这些软件虽然有API但不是每个环境都有授权何况在服务器上跑GUI软件简直是一种折磨。OpenSCAD 就没有这个问题它是完全脚本化的语法简单得像在写伪代码一个-o参数就能把.scad转成.stl放到Crontab里都能跑。当然如果你做机械设计早晚要面对 STEP 格式。OpenSCAD 导出 STEP 的能力很弱这是底层机制决定的。所以我另外准备了 CadQuery 作为“输出STEP”的路线。CadQuery 是 Python 库底层基于 OpenCascade能生成真正的 B-rep 实体导出的 STEP 可以被 FreeCAD、SolidWorks 直接打开编辑。下面这个最小示例我用 OpenSCAD因为它最适合解释逻辑如果你明确需要 STEP请直接跳到后面看 CadQuery 的方案。2.2 首个示例一句需求变成一个可打印的支撑座为了不空谈我用一个非常典型的场景做测试我要一个“M5螺栓支撑座”底座长40毫米、宽30毫米、厚4毫米中心有一个外径14毫米、高12毫米的凸台凸台中间开直径5.2毫米的通孔也就是M5的过孔留了0.2毫米间隙底座四角还要有直径3.2毫米的安装孔孔心距边缘5毫米。我给大模型的提示词是这样写的你是OpenSCAD专家。请为下面的零件生成完整OpenSCAD代码 - 所有尺寸单位mm - 底座outer_width40, outer_depth30, base_thickness4 - 凸台boss_outer_diameter14, boss_height12 - 凸台通孔through_hole_diameter5.2 - 4个底座安装孔mount_hole_diameter3.2孔心距边缘5mm - 使用$fn64保证圆柱光滑 - 输出只有代码不要解释一个合格的模型返回的代码大致长下面这样$fn 64; outer_width 40; outer_depth 30; base_thickness 4; boss_outer_diameter 14; boss_height 12; through_hole_diameter 5.2; mount_hole_diameter 3.2; edge_margin 5; module support_base() { difference() { // 主体 底座 凸台 union() { cube([outer_width, outer_depth, base_thickness], centertrue); translate([0, 0, base_thickness]) cylinder(h boss_height, d boss_outer_diameter); } // 中心通孔穿透整个底座和凸台 translate([0, 0, -1]) cylinder(h base_thickness boss_height 2, d through_hole_diameter); // 四个安装孔 for (dx [-1, 1], dy [-1, 1]) { translate([ dx * (outer_width / 2 - edge_margin), dy * (outer_depth / 2 - edge_margin), -1 ]) cylinder(h base_thickness 2, d mount_hole_diameter); } } } support_base();注意中心通孔的高度我特意多留了上下各1毫米也就是从z-1一直穿透到z20而不是刚刚好和实体一样高。这个细节后面第三部分会专门讲现在先记住布尔差集里切割体一定要比被切体多伸出去一点。2.3 用命令行一键出STL自动进入切片流程把上面的代码存成support_base.scad然后在项目目录执行openscad -o support_base.stl support_base.scad会得到一个STL文件直接用PrusaSlicer或Cura打开就能开始切片。如果参数需要改动比如孔径要改成5.4毫米我通常会写成命令行变量传入openscad -o support_base.stl -D through_hole_diameter5.4 support_base.scad但不要急着肉眼去看模型。肉眼很容易骗自己正确的做法是用程序做一次包围盒检查。下面这个脚本我放在所有生成任务后面自动执行import trimesh mesh trimesh.load(support_base.stl) print(包围盒尺寸:, mesh.bounding_box.extents) print(体积:, mesh.volume) print(是否水密:, mesh.is_watertight)第一次跑这个示例时如果包围盒长度不是40、30、16说明模型尺寸理解有问题后面就不用往下看了。这个“先跑自动检查、再打开软件人工看”的顺序是我做 text-to-cad 以来最重要的习惯。3. 从“能出图”到“能用”四个关键环节决定成败很多人试过一次“模型生成了STL”之后就以为成功了其实那只是能出图离能用还差得远。我在实际项目中把卡片一翻真正决定 text-to-cad 能不能投入生产的永远是下面这四件事。3.1 把需求翻译成结构化Prompt你如果直接跟大模型说“给我生成一个支架”它大概率会给你一个看起来像支架但尺寸完全随机的东西。大模型最擅长的是“接话”你给的信息越明确它发挥的余地就越小。所以我把Prompt分成角色、任务、规格、输出要求四段每一段都把它逼到墙角。一个我目前很常用的模板是这样的[角色] 你是一名机械设计工程师和OpenSCAD专家。 [任务] 根据以下需求生成OpenSCAD代码。 [规格] - 单位mm - 关键尺寸必须以变量形式定义在代码顶部 - 螺钉过孔直径 螺钉直径 0.2mm - 所有圆柱体使用 $fn64 - 底座安装孔位置使用矩形四角对称布局 [输出要求] - 只输出纯代码不要解释 - 代码中不得使用未定义的变量 - 使用模块化写法主模块名与文件一致这样写的好处是大模型的“想象力”被限制在了公差和结构约束之内。比如孔的公差你不写它可能写3.0写了“螺钉直径0.2”它就知道M5对应5.2。结构化Prompt看起来很简单却是让 text-to-cad 从玩具走向实用的第一道门槛。3.2 参数化命名留好“后门”我见过很多LLM生成的OpenSCAD脚本里面全是硬编码数字cylinder(h12, d14)cube([40,30,4])。这种代码单独看没问题但一旦想改其中一个尺寸就得全文搜索改完还不一定记得哪里改过。更麻烦的是自动化流程里每改一次参数都要重新调一遍大模型既慢又不稳定。所以我拿到代码的第一件事就是把关键尺寸提成文件顶部的变量。这不仅是“代码洁癖”更是实用主义同一套CAD模型换个孔距就是新零件成本几乎为零。我还会额外加一个clearance变量专门控制所有过孔的公差screw_d 5; clearance 0.2; through_hole_diameter screw_d clearance;这样产品工程师说要改公差我只需要改clearance全部孔都会联动。大模型要做的只是“生成一次对的骨架”后续的参数调整全交给自动化脚本完全不需要再跑LLM。这是 text-to-cad 项目里最被低估的收益。3.3 几何校验不要相信眼睛要相信脚本从模型生成到真正能加工中间隔着一个“校验”动作。我见过有人兴致勃勃把STL切片结果打出来的零件薄得像纸也有人导出的STEP根本没法约束装配。这些都可以通过程序提前发现。我的校验脚本一般检查四样。第一STL或者STEP能不能正常加载第二模型是否水密水密性不好切片会出悬空面或者内部空洞第三体积大于0排除布尔差集把实体全减光的情况第四包围盒尺寸和输入规格的误差在可接受范围内。用 trimesh 写一个函数几十行就够了import trimesh def check_model(path, expected_sizeNone): m trimesh.load(path) results { watertight: m.is_watertight, volume: m.volume, bbox: m.bounding_box.extents, } if expected_size: for got, exp in zip(results[bbox], expected_size): if abs(got - exp) 0.1: print(f尺寸偏差{got:.2f} vs {exp:.2f}) return results这个函数放在生成命令后面每次跑完都自动执行失败就报警。你要相信大模型生成几何的错误千奇百怪人眼根本盯不过来。把校验交给脚本把精力放在设计上。3.4 错误自动修复把OpenSCAD的报错喂回模型LLM写的代码不是每次都能一次通过。OpenSCAD 报错信息虽然不像编译器那么友好但我们只需要把它原封不动地丢回给模型让它自己修。这个“生成-校验-反馈”循环是整个自动化流程的核心。我在脚本里会用subprocess调用 openscad拿到 stderr。如果 stderr 非空就把当前代码和报错信息组合成一条新的Prompt发给大模型要求它输出修改后的完整代码并且不要解释。最多重试三次再不行才转人工。这个流程跑起来之后成功率会从“碰运气”变成“稳定可用”。一句话总结模型不知道自己的代码有没有运行成功你必须把外部世界的反馈喂给它它才知道错在哪。4. 我踩过的坑与排查技巧实录下面这些坑不是从文档里读来的是实打实踩出来的。每一条都曾经让我浪费过半天甚至一天的时间写出来希望你能绕开。4.1 单位错乱模型整体大10倍/25.4倍我遇到过最离谱的一次是要求底座长度40毫米结果生成出来101.6毫米。一开始我还以为是渲染缩放问题查了半天才发现模型把40毫米当成了4英寸来算101.6毫米正好是4英寸。大模型训练语料里英制和公制混着来你不在Prompt里反复强调“单位mm”它就可能给你来一套自由混搭。解决这个问题不能只靠提示词还要靠自动校验。因为10毫米和25.4毫米的偏差在预览图里肉眼几乎是看不出来的只有和真实尺子对比或者看包围盒才醒过来。自那以后所有生成的STL都会被我的检查脚本先量一遍包围盒偏差超过0.1毫米就自动打回重做。4.2 OpenSCAD导出不了STEP这是机制问题不是bug很多人做完OpenSCAD版本的 text-to-cad兴冲冲想导出STEP进SolidWorks装配结果发现根本找不到导出STEP的选项。这不是软件残缺而是OpenSCAD底子是CSG构造实体几何它内部维护的是“用哪些基本体做了哪些布尔运算”的操作序列而不是一个带拓扑连接的B-rep实体。输出网格时可以临时三角化但输出STEP就没有这个能力。如果你的下游需要CNC编程或者真正的装配体建议直接把核心引擎换成 CadQuery。它同样可以用自然语言生成Python代码但底层是OpenCascade导出STEP是原生支持。一个简单例子import cadquery as cq result ( cq.Workplane(XY) .box(40, 30, 4) .faces(Z) .workplane() .circle(14 / 2) .extrude(12) .faces(Z) .workplane() .hole(5.2) ) cq.exporters.export(result, support_base.step)这段代码生成的就是一个带通孔的凸台底座用FreeCAD打开STEP特征树是完整可编辑的。把 text-to-cad 的主要输出从“网格”改成“STEP”很多企业才愿意真正买单。4.3 差集共面导致的退化薄壳OpenSCAD里挖孔很多人会写一个和实体底面完全重合的圆柱来做差集。从数学上讲没问题从浮点运算上讲就是灾难。真实的布尔运算不是理想平面两个曲面完全重合时计算容易产生退化边最终渲染出来的模型可能有烂面切片软件里会出现零厚度区域。解决办法是让切割体“穿透”被切体。前面示例里我把通孔高度写成base_thickness boss_height 2并且translate([0,0,-1])目的就是让孔洞上下各超出实体1毫米。同理安装孔高度也要写成base_thickness 2。这个经验我建议你直接固化到代码规范里不要每次都想一遍。4.4 千万别让模型直接输出STL/网格文件有些刚上手的朋友会走捷径让大模型直接输出一个ASCII格式的STL。第一轮对话如果你只要一个小方块可能还行第二轮要一个带圆角、带多个孔的复杂零件模型生成的STL能写出上万行三角形token直接爆掉。而且这个输出是一次性的尺寸不对想改只能重新生成整个文件毫无可维护性。正确的边界是模型只负责输出“生成模型”的代码或参数化脚本shell脚本负责执行并生成网格。把渲染和重试的活儿交给工具让模型专心做设计翻译。这看起来只是分工问题实际上是 text-to-cad 能不能规模化的分水岭。常见问题典型表现排查思路单位错乱包围盒尺寸完全不对先跑包围盒脚本再问模型是否混用英寸共面差集切片软件出现烂面检查布尔切割体是否穿透实体代码报错stderr抛出未知语句把报错原样回传给模型修复导出格式不对没有STEP选项换CadQuery/FreeCAD脚本引擎水密性检查失败STL入水测试有洞低评细看是否建模时留了缝隙5. 进阶路线从单件走向装配体与自动化如果你的 text-to-cad 已经能稳定生成单个零件那恭喜接下来可以往更复杂的方向走了。但别急着让模型一步生成整个装配体那会是另一个坑。5.1 先单件后装配别让模型一步到位一次Prompt要求“生成一个带轴承座的轴装配体”模型往往会输出一个所有零件叠在一起的鬼东西。原因很简单装配体不仅是多个零件还包含零件之间的坐标系关系、配合约束、以及可能存在的干涉检查。这些信息放在一段自然语言里模型根本消化不了。我的做法是分两步。第一步让模型分别生成每个零件的独立脚本每个零件都以自己的原点为基准建模第二步用一个总装脚本把这些零件按照预设的装配矩阵平移、旋转然后统一做干涉检查。这样做的好处是单个零件脚本可以反复修改重新生成总装脚本不需要跟着变。拆分问题永远比让模型一口吃成胖子靠谱。5.2 搭建自动化pipeline文本-STEP-3D打印现在我的工作环境里text-to-cad 已经不只是“聊天生成模型”了而是一个自动pipeline。用户提交一行描述比如“一个60x40x2的铝合金底板四角M4沉头孔中间开30x20方孔”pipeline会调用代码模型生成CadQuery脚本pipeline执行脚本导出STL和STEP同时用校验脚本检查尺寸、水密性和干涉所有日志和模型预览图自动归档如果生成失败日志会原样返回给模型让它连续修复。这套流水线的价值在于把“设计经验”和“模型生成”解耦了。我需要调整公差不需要重新生成文本直接改脚本变量我需要换材料也不需要重跑模型只改加工工艺标注。text-to-cad 最终目的不是替代设计师而是把从“需求”到“可用模型”之间的反复劳动自动化。5.3 想训练自己的text-to-cad模型从哪里下手如果你的数据敏感不想用外部大模型API那就得考虑自己训练。我的建议是别一上来就设计新的网络结构先做数据闭环。text-to-cad 任务里最值钱的不是模型而是“自然语言描述-程序化建模脚本”的配对数据。OpenSCAD、CadQuery 的开源脚本库以及一些带标注的CAD数据集都可以作为起点。然后在这个数据上微调一个代码模型比如基于 Qwen2.5-Coder 之类的开源模型用指令微调让模型学会“从需求生成脚本”。但这需要投入大量标注和清洗精力数据版权也要提前理清。如果还在验证业务阶段我更推荐用现成LLM把流程跑通等到确认这个功能真的有业务价值再考虑训练自己的模型。很多项目死在“想训练一个通用模型”这一步而不是死在“流程没有价值”上。我在这个项目里最大的体会是text-to-cad 的终点不是“一行字变成一个模型”而是“一条可以被反复验证、自动修改的工程链路”。如果你也想动手第一件事不是去研究神经网络而是先把你手里最熟悉的一个小零件用这里说的最小闭环完整跑一遍。把手弄脏比什么都重要。
返回列表