尧图精选

AI写代码生成3D模型:参数化建模与CadQuery实操指南

🕒 发布时间:2026/9/14 18:04:01 📁 来源:尧图网络
从“AI直接生成3D模型”转向“让AI写代码来生成3D模型”这个话题我上一篇聊了整体思路和基础工具链。今天这篇“下”重点放在怎么把这条路真正走通走稳走到能交付、能复用、能进生产环境的程度。还是那句话AI直接吐模型文件是个黑盒你拿到的是一堆无法修改的三角面片AI写代码生成模型你拿到的是全套参数、逻辑和可重复执行的构建流程。这篇我会围绕一个完整的实操案例把参数化建模的架构、代码实现的细节、以及我自己踩过的坑全部摊开来讲。1. 从“想要一个模型”到“定义一组规则”1.1 为什么“AI写代码”不是绕远路而是换引擎先说一个最容易劝退人的点很多人觉得我让AI生成3D模型一句话的事为什么要绕一大圈让AI写代码原因其实很现实。直接生成模型的路径有几个致命的短板第一不可控。你提示词里写“一个圆角矩形的外壳壁厚2毫米带四个安装孔”生成器大概率会给你一个看着像、但孔的位置差半毫米、圆角弧度不对、底部是空心的模型。打印或者装配的时候这种“看着像”就是灾难。第二不可迭代。模型生成出来是一堆网格你想改一个孔的位置在Mesh编辑里手动拖吧拖完还可能破面。第三不可复用。今天生成一个盒子明天改成两个孔你得重新生成一次运气好生成器给了个随机种子运气不好它给你形状完全跑偏的东西。代码路线则完全不一样。AI写出来的是一套程序它定义了模型的生成规则壁厚是变量、孔的位置是函数、圆角半径是参数。改动一个变量整个模型跟着变。这就是参数化。和直接生成相比代码路线不是“一张图渲染出模型”而是“用可执行的几何规则来构建模型”前者是黑盒后者是白盒。我在实际项目里最看重的就是白盒性质每一步生成逻辑都能被检查、被调试、被单步执行。出错了不是“返工重试”而是“定位到某行代码改掉它”。这种体验一旦适应了就再也回不去了。1.2 代码生成模型的适用场景边界不过也得说实话代码路线不是万能的。我在项目里会先做一个判断这个模型到底适不适合走代码生成。适合走代码路线的通常具备这些特征有明确的几何约束比如“外径50毫米内径42毫米”、有重复性结构孔阵、散热鳍片、卡扣阵列、有装配需求需要精确配合、或者后期要变体衍生同族产品的不同尺寸。不适合的我建议还是让AI直接生成完全自由的雕塑形态类似有机造型、无法用数学规则描述的艺术曲面、需要高度随机纹理或细节的模型。这类需求硬上代码路线会把自己折磨疯。写一整套噪声函数和各种曲面变形效率远低于手工雕刻。所以在开始之前先用上面这把尺子量一下自己的需求。这篇文章讲的所有内容都默认落在“适合代码路线”的范围内。2. 核心架构设计意图、代码生成、参数化校验2.1 三层结构意图层、代码层、校验层我自己在用的这套流程分为三层。第一层是意图层。你把设计需求用结构化的方式描述出来比如“我想做一个树莓派Zero的外壳尺寸65mmx30mm厚度方向留出8mm给排线一侧开Micro USB孔另外一侧开GPIO引出窗口”。这一层的关键是你的描述必须能被转换成参数约束而不是形容词。第二层是代码生成层。把意图层的内容交给大模型让它用CadQuery或者OpenSCAD写出参数化建模脚本。这里面的技巧我后面会详细讲核心是让AI明白“几何约束优先于视觉美观”。第三层是校验层。用代码本身去生成模型、检查尺寸、甚至自动做布尔运算验证装配间隙。这一步是很多人忽略的。AI生成的代码不一定对但代码生成的模型可以由代码来验证这就形成了一个可闭环的校验链。2.2 为什么我选择 CadQuery 而不是 OpenSCAD市面上的代码建模工具不少我重点比较过 CadQuery、OpenSCAD、以及用 Python 直接写三角网格三种路线。OpenSCAD 是函数式描述语言门槛低但复杂逻辑写起来很痛苦尤其是圆角、倒角、抽壳这些操作语法绕到怀疑人生。Python 直接操作三角网格比如用 Trimesh 库灵活性最高但等于什么都得自己算开发效率太低。CadQuery 是我目前的主力工具。它是一个基于 Python 的库最大的特点是“自顶向下构建实体”先定义2D草图然后挤出、旋转、布尔运算过程非常接近工业建模软件的操作逻辑。而且它对圆角、倒角、抽壳这类操作支持得非常好一行代码搞定OpenSCAD 里可能要写一堆变换矩阵。选型的时候我列过一个表格对比项CadQueryOpenSCAD直接操作网格语法难度中等Python 基础即可低但函数式逻辑绕高一切自己算几何操作能力强支持圆角/倒角/抽壳弱复杂操作需绕行取决于自己写的代码参数化支持原生支持变量即参数原生支持但不灵活需自建框架AI生成友好度高代码结构清晰中语法细节多低没有高层APIAI 生成代码这件事最怕的是目标库语法冷门、API 复杂大模型训练数据不足。CadQuery 在 GitHub 上有大量示例Stack Overflow 也有不少讨论大模型对它的“理解”明显比 OpenSCAD 好很多。实际测试下来同样的需求描述AI 写 CadQuery 的准确率比 OpenSCAD 高不少。2.3 提示词怎么写才不翻车让AI写参数化建模代码提示词的水平直接决定生成质量。我自己总结了一套模板目标是把“模糊的美感需求”翻译成“精确的几何约束”。先说反面教材。你一上来就说“帮我写个好看的手机支架”AI 大概率给你一坨不知道怎么运行的代码或者生成一个完全不符合3D打印约束的模型。我的写法是这样的明确你要建什么、核心尺寸参数有哪些、哪里需要倒角圆角、哪个方向是Z轴、单位是毫米还是英寸。帮我用CadQuery生成一个手机支架。要求整体高度80mm底座宽度60mm底座纵深70mm手机倾斜角度为65度屏幕面离桌面垂直高度120mm底座厚度3mm支撑板厚度3mm与手机接触的顶部挡板厚4mm底部开减重孔直径10mm四角布局孔心距边缘15mm所有可见外缘做2mm圆角圆角R2输出格式为STL单位为毫米请将关键尺寸定义为变量方便后续修改你看这里没有“好看”“简约”“有质感”这类词全部是数字和几何关系。要把设计意图落实成参数这是代码生成3D模型的前提条件。2.4 变量命名与参数化设计思维AI生成代码之后第一步做的事情不是运行而是检查变量名和参数结构。CadQuery 代码可维护性的核心在变量命名是否清晰。比如说一个手机支架好的变量结构应该是phone_angle 65 # 手机倾斜角度单位度 stand_height 80 # 支架总高度单位毫米 base_width 60 # 底座宽度 base_depth 70 # 底座纵深 base_thickness 3 # 底座厚度 leg_thickness 3 # 支撑板厚度 top_lip_height 12 # 顶部挡板高度 ...这一堆变量定义是整个参数化脚本的地基。AI 生成的代码第一次可能有不合理的硬编码比如圆角半径直接写了2而不是定义为变量实测改几版之后发现1.5更合适那你就要全文件搜“2”再替换。我在实际操作中每次拿到AI代码第一件事就是“参数重构”把所有魔法数字提取成具名变量并且加上注释。3. 实操演示做一个带散热孔的传感器盒子3.1 需求描述与设计参数理论讲多了容易飘来一个完整的实操案例。我最近做的一个项目是给家里的温湿度传感器设计一个壁挂式保护盒。需求是这样的传感器板卡尺寸为 40mm x 20mm x 8mm需要留出20cm的排线出口壁挂安装需要两个螺丝孔同时顶部要开散热孔阵列防止夏季高温让传感器读数漂移。设计参数定下来盒子内腔46mm x 26mm x 12mm板卡尺寸加3mm余量壁厚2mmPLA打印1.6mm壁厚足够2mm更稳外尺寸50mm x 30mm x 14mm螺丝孔直径3.2mmM3螺丝加1mm余量孔心距边缘8mm排线出口宽8mm高5mm位于底部一侧散热孔直径4mm阵列间距8mm覆盖顶面中央区域安装结构盒子分为上下盖上盖用卡扣固定这个后面单独写先做整体3.2 CadQuery 脚本生成与解析我把上一节的参数和需求按照提示词模板喂给AI。这里最关键的一点是我没有让它“设计一个盒子”而是给它明确的实体生成步骤。AI 返回的核心代码如下import cadquery as cq # 参数定义 inner_w 46.0 inner_h 26.0 inner_d 12.0 wall_t 2.0 outer_w inner_w 2 * wall_t outer_h inner_h 2 * wall_t outer_d inner_d wall_t # 下盖深度只算底板壁上盖另外覆盖 screw_r 1.6 # 半径 screw_offset 8.0 vent_r 2.0 vent_spacing 8.0 # 底板四壁 base ( cq.Workplane(XY) .box(outer_w, outer_d, outer_h, centered(True, True, False)) .edges(|Z) .fillet(2.0) ) # 掏空内腔 box ( base.faces(Z) .workplane() .rect(inner_w, inner_d) .cutBlind(-inner_h) .faces(Z) .workplane() .rect(inner_w, inner_d) .cutBlind(-(wall_t 0.5), taginner_cut) # 预留上盖槽 )这段代码的逻辑是先做一个实心方块再从上表面向下挖一个盲腔。关键是centered(True, True, False)这个参数让盒子在Z方向以底面为原点向上生长后续定位螺丝孔、散热孔都以底面为基准省去一大堆手动偏移。3.3 增加散热孔阵列散热孔阵列是典型的重复性几何用代码处理几乎零成本。AI 生成这部分的核心代码# 散热孔区域以顶面中心为基准避开边缘5mm vent_box_w inner_w - 10.0 vent_box_d inner_d - 10.0 # 在顶面下一层生成一排排的散热孔 vented ( box.faces(Z) .workplane() .rect(vent_box_w, vent_box_d) .vertices() .hole(vent_r * 2) .edges(Z) .fillet(0.5) )等下这段代码其实有个坑vertices().hole()只会打四个角的孔不会生成阵列。AI 生成这一步的时候踩坑了我需要手动让它改成矩形阵列。正确的做法是使用rarray方法# 顶面散热孔阵列 vent_workplane ( box.faces(Z) .workplane() .center(0, 0) .rect(vent_box_w, vent_box_d) ) # 计算 x 和 y 方向的孔数量 import math num_x int((vent_box_w - vent_r) // vent_spacing) 1 num_y int((vent_box_d - vent_r) // vent_spacing) 1 vented ( vent_workplane .rarray(vent_spacing, vent_spacing, num_x, num_y) .hole(vent_r * 2) )这一步是关键差异点。直接生成模型的AI工具在这里只能“按感觉”打孔而代码方案是精确计算孔间距和数量还能把num_x、num_y变成变量跑批量试验。3.4 布尔运算与实体合并盒子主体有了下一步是螺丝孔和底部排线口。这些操作本质上都是“在实体上减去一个形状”CadQuery 里叫布尔运算。# 螺丝孔在盒子底部平面对称分布 screw_holes ( vented.faces(Z) .workplane() .center(0, 0) .rect(outer_w - 2 * screw_offset, outer_d - 2 * screw_offset) .vertices() .hole(screw_r * 2) ) # 排线出口在盒子底部一侧 cable_cut ( screw_holes.faces(Y) .workplane() .center(0, inner_h / 2) .rect(8.0, 5.0) .cutBlind(2.1) )这里要特别留意faces(Y)的方向选择。CadQuery 在定位平面上非常灵活但也非常容易踩坑。我在第一次运行时排线口开到了顶面就是因为Y的方向判定和我的直觉不一样。排查方式是把模型导出STL后用切片软件逐层查看或者退回faces(Z)逐步打印每个面。3.5 导出 STL 与切片验证模型构建完成后导出 STL 就一行代码cq.exporters.export(screw_holes, sensor_box_v1.stl)导出之后我习惯立刻丢进判断工具我用 BW 和 PrusaSlicer 都会看一眼主要检查三件事第一内腔是否封闭。如果有破洞切片时会看到内部填充乱飞。第二悬垂结构是否需要支撑。Cable出口那里如果直接横跨打印时大概率会拉丝我习惯加一个45度倒角或者调整朝向。第三壁厚是否满足打印机最小要求。这一轮检查下来第一版脚本通常会暴露两三个问题但我只需要改代码再重新运行一次整个流程不到30秒。相比之下手工建模软件里改一个孔位置可能就要重新推演一大轮。4. 核心细节尺寸验证布尔运算的坑与应对4.1 尺寸校验一定要用代码别用眼睛模型生成之后怎么确认尺寸对了用测距工具手动量太慢了而且容易漏。我的做法是在 CadQuery 脚本里直接输出关键尺寸的验证信息。# 打印关键尺寸用于验证 print(f外部尺寸: {outer_w} x {outer_d} x {outer_h} mm) print(f内腔尺寸: {inner_w} x {inner_d} x {inner_h} mm) print(f壁厚: {wall_t:.2f} mm) print(f螺丝孔直径: {screw_r * 2:.1f} mm, 位置偏移: {screw_offset} mm) print(f散热孔数量: {num_x} x {num_y} {num_x * num_y} 个)我在脚本末尾固定放这一段每次生成都自动输出。对比设计参数一眼就知道有没有弄错。另外还可以用 CadQuery 的计算几何接口来检查装配间隙比如验证两个零件之间的最小距离是否满足0.2mm的装配余量。(这个通用方法在压配合件时极其好用比纯眼观靠谱一百倍。)4.2 布尔运算失败实体必须完全闭合我在做深一些的模型时遇到过一个经典问题CadQuery 执行cut或intersect布尔运算时偶尔会报“could not compute boolean operation”之类的错误。排查下来原因绝大多数是实体本身有开放面、退化面或者自相交。怎么预防我的经验是尽可能让每次操作基于同一个Workplane链不要频繁在不同对象之间跳转。比如切割用的刀具实体应当用close()确保它是一个完全封闭的实体而不是一个开口曲面。另外cutBlind的深度要足够大宁可多切1毫米也不要只切到临界值。如果布尔运算还是失败还有一个笨但有效的办法把两个实体分别导出为 STL用 MeshLab 等工具检查法线方向和网格完整性找到具体的破面位置再回去修改生成该特征的代码段。这听起来繁琐但定位到的代码问题通常是一个参数错误或者方向选择错误修完就彻底好了。4.3 圆角操作顺序的坑CadQuery 的 fillet 看起来简单但顺序错了会出幺蛾子。我踩过的坑是实体上先打了孔、后做圆角结果圆角把孔口边缘“吃掉”了尺寸直接失效。正确做法是先把所有需要倒角的边一次性处理完最后再进行打孔操作。如果打孔之后还必须倒角那就得选择性地.edges(tag)来指定某条边倒角不要把整块实体一锅端。4.4 输出格式的隐藏坑单位与坐标系CadQuery 默认单位是毫米这点没问题。但在导出给不同工具链时问题就来了。比如有些渲染软件默认单位为米有些切片软件默认单位为厘米。我习惯在导出时显式指定单位同时将模型沿Z轴向上平移避免打印时贴床面有问题。# 将模型底面放到 z0 平面 shifted screw_holes.translate((0, 0, -screw_holes.val().BoundingBox().zmin)) cq.exporters.export(shifted, sensor_box_v1.stl, exportTypeSTL)这一步看着不起眼但能省掉你在切片软件里各种调整位置的麻烦。5. 工程化落地从“能跑”到“可交付”5.1 建立参数配置文件单次运行成功只是开始真正工程化要把参数和脚本分离。我的做法是建一个params.yaml文件专门放全部设计参数outer_w: 50.0 outer_d: 30.0 outer_h: 14.0 wall_t: 2.0 screw_r: 1.6 screw_offset: 8.0 vent_r: 2.0 vent_spacing: 8.0 cable_outlet_w: 8.0 cable_outlet_h: 5.0然后脚本里用 PyYAML 读取生成不同的版本。想出一个变了尺寸的型号改 yaml 里的两个数字跑一遍脚本一个新STL就出来了。这比在 AI 对话框里来回折腾效率高太多。import yaml with open(params.yaml, r) as f: config yaml.safe_load(f) inner_w config[outer_w] - 2 * config[wall_t] ...5.2 自动化批量生成与格式转换有了参数文件和脚本批量生成模型就顺理成章了。我写了一个简单的 Python 脚本遍历同一目录下的多个 yaml 文件循环执行建模函数输出对应文件名的 STL。一套传感器外壳从 40mm 到 60mm 宽度分四档十几秒就全部生成完毕。如果下游需要的是 STEP 文件用于数控加工或者与机械工程师协作CadQuery 原生支持导出 STEP直接改一行导出格式就行。这一能力直接让我从玩具级的3D打印跨到了可以外发给CNC加工中心生产的程度。5.3 版本管理与模型存档代码建模还有一个隐藏福利版本管理。手工建模软件里模型文件一改名就是新版本但无法看到新旧之间到底改了哪些几何特征。代码建模则天然支持 Git。我维护了一个仓库每个模型的params.yaml和generate.py都在里面每条 Git 记录对应一次设计变更。翻历史和比对差异就像审阅代码一样清清楚楚。客户问“上次那个孔是多大来着”一条命令查到旧版本参数直接重新生成一次STL快速又准确。我自己的习惯是每次跑出新版本顺手在 commit message 里标一个“- v3 修改顶部散热孔数量为 4x3”之类的说明时间久了这个仓库本身就是设计变更史对多版本产品迭代极有意义。5.4 AI辅助整个流程的效率评估用AI写代码建模整体效率到底什么水平我这段时间的实际对比是这样的传统手工建模做这个盒子包括画草图、拉体、挖腔、开孔、倒角算上返工时间大概需要40分钟。直接用AI生成提示词生产模型第一版5分钟出图但遇到需要修改尺寸的环节如果重新生成前后大概15分钟而且每次生成结果都不唯一不可预期。而用“AI写代码建模”的方式第一次让AI写脚本加上排除各种坑大概20分钟但之后所有修改都只是改参数重新运行一次5到10秒。长期下来凡是模型需求重复出现代码路线效率碾压另外两方案。就算是一次性需求只要结构的“参数化价值”高比如尺寸变化后形态逻辑不变代码路线仍然性价比更高。6. 常见问题与排查技巧实录6.1 普适性常见问题速查我在交流群里整理了大家碰到的典型问题这里列个速查表。症状可能原因解决方法STL 打印时悬垂严重设计中未考虑45度规则特征悬空增加倒角或改变成型方向螺丝孔偏小装不进去未考虑打印收缩与层纹精度设计值等于打印值孔直径加0.2~0.4mm余量模型无法切片网格不封闭存在破面用MeshLab修复或检查脚本中是否有未闭合特征布尔运算报错刀具或目标实体有开放面/退化面确认每个2D草图闭合用调大切割深度规避临界值打印件尺寸整体偏大/小切片软件缩放单位错误检查导入时的单位设置CadQuery输出为毫米确认切片软件同样用毫米圆角后孔变形圆角与打孔顺序不对先倒角后打孔或限定edge tag逐个处理6.2 打印装配件的配合余量经验在实际打印配合件时比如盒子上盖和下盖我的默认设置是间隙不小于0.2mm。然后呢每次打完还要用卡尺量一下实际壁厚。不同打印机、不同层高、不同材料收缩率差得很大。PLA 和 ABS 差别尤其明显。我有一套自己的经验框架按下盖的外壁尺寸做上盖的内槽尺寸间隙给0.25mm是松配合要比较紧的卡扣效果间隙给0.15mm。但同一个打印机换了耗材品牌这个数就可能要重调。所以这部分的最终校准建议一定要根据你自己的设备实测别照抄网上的数值。每次校准后把参数更新到设计中重新生成一次脚本这才是闭环。6.3 调试AI生成代码的思维方法AI 给的代码运行报错我的第一反应不是重跑一遍或者换提示词而是让 AI 先“解释这段代码”。我会问它这段代码里faces(Z)选中的是哪个面cutBlind的深度是从哪个基准面开始计算让它自己讲清楚逻辑很多时候错误就在解释里暴露出来了。这算是一个用语言模型来调试语言模型的小技巧实测效率非常高。6.4 从单一模型到产品族代码建模的优势在从“做一个盒子”升级到“做一个产品族”时全面爆发。我最近做的一个项目需要在四种不同规格的外壳中分别集成不同的传感器和走线方案。手工建模或AI生成模型的话相当于重复劳动四遍代码建模则是在同一条generate.py里增加几个条件参数循环跑一遍就完事。更进一步在外观上给不同型号增加一个“凸起条纹”或者“螺丝孔偏移”在代码里就是多一段条件判断。但这里有个教训宁可把所有可能的参数全部放在params.yaml里也不要让脚本内部出现含义不明的魔数。否则过了两三个月你自己回来看代码也要抓瞎半天。7. 做完整流程后的心得与扩展思路这一套流程玩下来已经小半年了我现在手头新项目的默认起点几乎都从“用 AI 生产模型”切换成了“用 AI 生产建模脚本”。整个过程里最直接的一个感受是代码建模虽然在“开头”的前5到10分钟里感觉比直接生成慢但它几乎每步都可控、可查、可复用。你不再是被模型生成器牵着鼻子走而是真正在“设计规则”并且让计算机替你按规则批量执行。还有一个扩展思路特别值得提代码生成模型和生成式AI并不是对立关系它们可以合作。比如你用一个AI工具先给出概念设计然后用CadQuery把其中的关键结构重写为参数化代码再人为加入装配约束和优化调整。事实上我最近在跑的一个批量变体项目思路就是从概念方案提取设计意图再转成生成代码最终一次输出了十几个不同比例的变体且每个都能直接打印、直接组装。工具链上我目前的推荐搭配是AI大模型负责生成CadQuery/python代码的“初稿”CadQuery负责精确几何构建Trimesh用于网格后处理和检测PrusaSlicer负责打印前的切片验证。这套组合经得起反复打磨和实际打印的检验。如果你身边的人还在纠结“让AI出一个模型”和“让AI写代码出模型”怎么选我建议直接让他拿上面这个传感器盒子的例子试一试感受一次“改参数重新运行30秒拿到新模型”的体验。那种掌控感是用过就回不去的。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →