先讲个真实经历。上个月我需要一个带法兰边的异形管道接头,第一反应是让 AI 直接生成 3D 模型。折腾了半小时,模型倒是“像那么回事”,但一导入工程软件就露馅:面是碎的、尺寸标注全是小数、布尔运算之后甚至出现悬空点。我换了个思路,让 AI 写一段生成这个零件的 Python 代码,结果十分钟出了一版参数化模型,改内径、改法兰厚度只需要改两个数字。这篇文章就是想把整个思路和实操过程拆开讲清楚,说说为什么“让 AI 写代码”比“让 AI 吐模型”靠谱得多,以及你该怎么把这套方法落地。
这个主题适合三类人:被 AI 直出模型坑过的设计师、想给建模流程引入程序化思路的工程师、以及正在学 AI 编程但不知道能用在什么实际场景的开发者。前 80% 的内容是思路和避坑,后 20% 是可直接抄走的代码。
1. 别让 AI 直接“倒模”:直出模型在真实工期的四个翻车现场
我先不急着夸“写代码”的路线,而是把“AI 直接吐模型”这件事按场景拆开,你才能理解后面所有方法为什么要那样设计。
1.1 游戏资产:拓扑和面数是硬伤
游戏行业对模型的要求不是“像”,而是“省”。一个场景里几百个物件,每个物件几千个三角面已经是极限,次世代资产也得控制在几万面以内。AI 直接生成的模型,尤其是通过图片重建或者文生模型管线出来的 Mesh,普遍存在两个问题:
- 拓扑结构混乱。三角面走向完全没有规律,贴图的时候 UV 拉伸得一塌糊涂。
- 面数不可控。同一个茶壶,AI 生成出来可能是 80 万面,也可能是 8 万面,你没法让它“按预算布线”。
这两个问题在程序化建模里根本不存在。代码生成的模型,每一个顶点、每一条边都是逻辑推导出来的,你完全知道它长什么样、有多少面。
1.2 机械件和工程件:尺寸公差根本对不上
有一次我需要一个 M8 螺母的模型做装配演示。AI 给了一个“看起来很像”的六角体,但内径画成了 7.2mm,而不是螺纹中径对应的小径;对边距离也不是标准值。在游戏里这无所谓,但如果是做 3D 打印或者 CNC 加工,这种模型直接废掉。
AI 直出模型最大的问题就是:它不懂工程语义,不知道什么是公差、什么是配合间隙、什么是基准面。它只是把训练数据里的“螺母图片”或“螺母模型”的统计特征模仿出来了。
1.3 3D 打印:破面与封闭性问题排查到怀疑人生
3D 打印软件切片时要求模型必须是封闭的流形,也就是“水密”的。AI 生成的模型经常有开口边、重叠面、反向法线。你用软件自动修复,有时候越修越乱。上回一个朋友让我帮看一个 AI 生成的摆件模型,光修复破面就花了两个小时,最后发现缺口处有十七个重合顶点,这已经不是“修”能解决的事,只能重新拓扑。
1.4 非标准几何:AI 只能“画得像”,没法“算得对”
你可以让 AI 直出一个“一朵抽象化的花”,这个它擅长。但你要是让它生成“一个半径为 30 的圆弧过度到半径为 8 的孔,再绕中心轴阵列为 12 个”的零件,它就傻眼了。因为这种需求本质上是参数约束,不是像素和网格层面的拟合。AI 直出模型是“画得像”,程序化建模是“算得对”,这是两种完全不同的建模哲学。
2. “写代码生成模型”到底是怎么工作的:把建模变成可执行的逻辑
既然 AI 直出模型不靠谱,那“让 AI 写代码”具体又是什么?这里需要把程序化建模(Procedural Modeling)这个概念讲透。
2.1 从“手工捏形”到“程序生成”:换个视角看建模
传统手工建模,你的工具是鼠标、键盘、数位板,你的原材料是点、线、面。程序化建模,你的工具是代码,你的原材料是数字——长度、角度、数量、半径、偏移量。你告诉程序“画一个半径 20 的圆,拉伸 5 毫米,在圆周上阵列 8 个直径 4 的孔”,它就精确地生成这样一个实体。
很多设计师一听“编程”两个字就头大,但其实你天天用的 SolidWorks、Fusion 360、Blender,底层都是这么干的。SolidWorks 里改一个草图尺寸,整个零件跟着变,这就是参数化思路。代码只是把这个思路彻底开放了——你不受界面按钮的限制,任何几何关系都可以用数学表达。
2.2 程序化建模适合什么场景,不适合什么场景
程序化建模不是万能的。它擅长有规律、可复用的几何:机械零件、建筑构件、管道系统、齿形、螺纹、散热片阵列、编织结构。它不擅长有机形态:人脸、动物、自然景观。但这些不擅长的领域,AI 直出模型其实也做不好看——当然那是另一个话题。
最适合程序化建模的场景有一个共性:你需要的不是一个模型,而是一族模型。比如同样一款法兰,内径要适配 DN15、DN20、DN32 三种管径。手工建模你要建三个,程序化建模你只需要改一个变量,运行三次。
2.3 技术选型对比:Blender Python、CadQuery、OpenSCAD、Trimesh
“让 AI 写代码生成 3D 模型”的关键是选对执行环境。我试过几个主流方案,分别说下感受:
| 方案 | 擅长领域 | 适合人群 | 上手成本 |
|---|---|---|---|
| Blender + Python (bpy) | 多边形建模、做动画资产 | 游戏美术、设计师 | 中 |
| CadQuery | 精确机械建模、STEP 导出 | 机械工程师 | 中高 |
| OpenSCAD | 纯代码建模,极客风格 | 创客、3D 打印玩家 | 低 |
| Trimesh | 网格分析和处理 | 开发者、算法工程师 | 中 |
我个人最常用的是 CadQuery,因为它的建模逻辑是“布尔 + 拉伸 + 扫掠”,和机械设计直觉一致,而且能直接导出 STEP 这种带精确曲面数据的工业格式。Blender Python 更适合做视觉效果类的东西,导出的 STL/OBJ 在工程软件里往往还需要再做处理。OpenSCAD 语法很简单,但几何能力偏基础,复杂的圆角过渡不好做。Trimesh 不是建模工具,更多是加载、处理、分析模型用的库,适合做自动化流程里的中间环节。
3. 实测一条可行的落地链路:AI 写 Python 代码,程序化生成参数化零件
这一节是全文的实操核心。我会用一个真实的参数化法兰盘例子,完整演示“让 AI 写代码生成 3D 模型”的链路。
3.1 给 AI 下需求:怎么写提示词,它才能产出可运行代码
先给大家看看我第一次是怎么写的,以及为什么被坑了:
“帮我生成一个法兰盘”
这种描述给到 AI,它可能会给你一段看起来很优美但实际上跑不起来的代码,或者一堆不存在的 API。原因是:建模代码必须精确描述几何参数和操作顺序,你没给参数,它就只能瞎猜。
经过反复试错,我整理出一套好用的提示词结构:
- 明确建模目标:要生成什么零件。
- 给出全部关键参数:外径、内径、厚度、孔的数量、孔的位置。
- 明确单位:毫米还是英寸。
- 指定输出格式:导入 STEP 还是 STL。
- 说明约束条件:比如“所有孔必须均匀分布在分度圆上”。
按这个结构,我写给 AI 的提示词是这样的:
使用 CadQuery 生成一个法兰盘: - 外径 100mm,法兰厚度 12mm - 中心孔直径 34mm - 分度圆直径 80mm,上有 4 个螺栓孔,孔径 9mm - 单位是毫米 - 最后导出为 STEP 文件,文件名为 flange.step这段描述只占屏幕三行,但信息量已经足够让 AI 生成一段基本可用的代码。
3.2 核心代码拆解:以参数化法兰盘为例
下面这段代码是 AI 生成的,我做了少量修正后实测通过:
import cadquery as cq # 参数定义 flange_od = 100 # 法兰外径,mm flange_id = 34 # 中心孔直径,mm flange_th = 12 # 法兰厚度,mm bolt_circle_d = 80 # 分度圆直径,mm bolt_hole_d = 9 # 螺栓孔径,mm bolt_count = 4 # 螺栓孔数量 # 创建法兰主体 result = ( cq.Workplane("XY") .circle(flange_od / 2) .extrude(flange_th) .faces(">Z") .workplane() .hole(flange_id) ) # 在分度圆上生成螺栓孔 for angle in range(0, 360, int(360 / bolt_count)): x = bolt_circle_d / 2 * cos(radians(angle)) y = bolt_circle_d / 2 * sin(radians(angle)) result = ( result.faces(">Z") .workplane() .center(x, y) .hole(bolt_hole_d) ) # 导出 STEP cq.exporters.export(result, "flange.step")里面有几个容易踩坑的地方需要特别说明:
faces(">Z")表示选中顶面作为下一个操作基准面,这是 CadQuery 里非常核心的链式操作逻辑。很多人写代码时漏了这一句,导致孔打在底面或者侧面。range(0, 360, int(360 / bolt_count))这种写法其实有隐患,当螺栓孔数量不是整数时会导致角度不均。正确的做法是直接用range(bolt_count),然后通过angle = 360 / bolt_count * i计算角度。后来我把这个坑反馈给 AI,它自己也承认写法不够严谨。hole()方法默认是从当前工作平面向下打孔,如果你选中的是顶面,打出来的孔会贯穿整个实体。这一步没问题,但新手容易在.center(x, y)之后忘记重新选中面,导致孔的位置叠加在之前的位置上。
跑通这段代码之后,你会发现一个特别有价值的特性:参数化。把bolt_count改成 8,把bolt_circle_d改成 95,重新运行,一个新的法兰就出来了。传统手工建模你可能要改一堆草图的约束,代码这边就是改两个数字的事。
3.3 验证与修改:让 AI 自己修 bug 的方法
代码能跑只是一个开始,更常见的情况是代码跑通了,但模型跟预期不完全一样。这时候很多人会自己去读代码、改代码,但我给你一个提效思路:把报错信息和实际现象贴回给 AI,让它改代码,而不是自己硬改。
有一次我把法兰厚度改成 20mm 之后,螺栓孔不见了。我把模型截图和以下描述扔给 AI:
法兰厚度改成 20 之后,螺栓孔没有穿透,只在顶面形成了一个凹坑。请检查代码,修正打孔逻辑,确保螺栓孔穿透整个法兰厚度。
AI 很快就发现,hole()的默认深度只与当前工作平面相关,厚度过大时孔没有穿透底面。它给出的修正方案是给hole()加一个深度参数,或者使用cut()+ 圆柱体的方式做贯穿切割。这种“现象描述 → 让 AI 改代码”的循环,通常两三轮就能把模型修到可用状态。
这比让 AI 直接生成模型再手工修复 Mesh 靠谱多了。为什么?因为代码出错你能定位到具体某一行,而 Mesh 出错你连“哪一步导致的”都说不清。
3.4 进阶:组合多个零件,批量生成装配体
单个零件跑通之后,你可以进一步让 AI 生成一组配套零件,然后组装。比如除了法兰盘,再生成一个配套的密封垫圈:
gasket = ( cq.Workplane("XY") .circle(flange_od / 2 + 2) # 外圈加 2mm .circle(flange_id / 2 - 1) # 内圈缩小 1mm .extrude(2) # 垫圈厚度 2mm )因为两个零件共享同一组参数,你只需要改一次上限参数,装配体里的所有零件都会同步更新。这种联动式修改,手工建模时非常痛苦,代码方案里却是天然行为。
4. 从“能出图”到“能交付”:格式、尺寸、性能的踩坑记录
代码跑通只是第一步,真正把一个模型变成可交付的资产,中间全是细节。这一节把我在真实项目里踩过的坑集中盘一遍。
4.1 单位与比例:最容易翻车的第一关
CadQuery 默认使用毫米,这没问题。但如果你在 Blender Python 里写代码,Blender 的默认单位是米。一个常见的坑是:你让 AI 生成一个“直径 100”的圆柱,它默认你在 Blender 里用的单位是米,结果导出的模型实际尺寸是 100 米。
所以写提示词的时候一定要强制声明“单位是毫米”,并且在代码里显式设置单位。如果是 Blender,bpy.context.scene.unit_settings.scale_length = 0.001这一行就能把单位从米换算成毫米。
4.2 导出格式选哪种:STEP、STL、OBJ、GLB 各有各的坑
选格式不是一个“随便”的事情,我列一张表说明建议:
| 格式 | 优点 | 缺点 | 建议使用场景 |
|---|---|---|---|
| STEP | 精确曲面,带几何邻接关系 | 文件较大 | 机械加工、工程装配 |
| STL | 通用性好,3D 打印都认 | 只有三角面,无拓扑信息 | 3D 打印 |
| OBJ | 带 UV 和材质信息 | 几何精度低 | 游戏、视觉效果 |
| GLB/glTF | Web 友好,体积小 | 建模精度有限 | 网页 3D 展示 |
工程交付首选 STEP,因为 CNC 和 CAE 软件对 STEP 的支持最完整。3D 打印用 STL 没问题,但记得导出前检查模型是封闭的。CadQuery 导出的 STL 一般不会出现破面,这一点可比 AI 直出模型省心太多了。
4.3 性能观察:布尔运算、阵列数量与内存上涨
程序化建模有一个隐藏成本:运算时间。尤其是涉及大量布尔运算(比如给一个大平面上打几百个孔)的时候,代码会跑得很慢。我做一个测试,在一个 200x200mm 的铝板上阵列 144 个直径 6mm 的孔(12x12 阵列),CadQuery 普通的布尔打孔耗时约 8 秒,内存峰值约 1.2GB。
优化思路有两条:
- 用
cq.workplane().rarray(x_count, y_count, x_spacing, y_spacing)批量打孔,比循环调用一次一次打要快很多。 - 尽量减少布尔操作次数。同一个平面上规律排列的孔,尽量合并成一个“多体布尔”,而不是一个孔一个孔地切。
如果还是慢,可以考虑先用代码生成 2D 轮廓再用 CAD 软件拉伸,但这已经偏离本文主题了。
4.4 排查链路实例:从“导出的模型是空的”说起
有朋友跑了一段 AI 生成的代码,导出后屏幕上什么都没有,但文件确实生成了。这种问题典型的排查链路是这样的:
- 先确认代码里模型是否存在于变量中:在导出前加一行
print(result.volume()),如果体积是 0,说明模型本来就是空的。 - 再看是不是坐标系建错了:比如模型被生成在
X=100000的位置,导入软件后视野里根本看不到。 - 然后再排查导出参数:CadQuery 导出 STEP 用的
exporters.export,第二个参数是文件路径,这个没错,但如果你export了一个尚未把它赋给任何实体的变量,自然导出的是空气。
这个排查过程里,我最推荐的思路是:让问题通过代码暴露出来,而不是盯着界面瞎猜。手工建模排查破面,你无从下手;代码建模排查形状,你可以一句一句打印中间结果,每一步都清清楚楚。
5. 基于个人经验再补充:让 AI 持续写建模代码的工作流与下篇预告
最后这部分不写教程,聊一聊怎么把这套方法融入真实工作流,以及还有哪些坑值得提前规避。
5.1 用 git 给模型代码做版本管理
模型代码也是代码,那就该用代码的方式管理。我在本地给每个项目建一个 git 仓库,所有.py文件和导出的.step文件都放进去。每次改完代码,生成新模型,提交一次 commit。
这个习惯的好处在于:当客户说“还是上一版好看”的时候,你不用翻半天历史文件。git log看记录,git checkout切回去,重新导出就是之前那版模型。AI 直出的模型做不到这一点——它对你的“上一版”毫无概念。
5.2 给 AI 一个“上下文包”:项目规范、单位约定、命名规则
同一个 AI,你跟它聊天时它可能会忘记你之前说的单位约定。一个很实用的做法是:在项目根目录放一个spec.md说明文件,每次让 AI 生成代码之前,先把这份规范粘贴给它:
# 项目建模规范 - 单位:毫米 - 坐标系:Z 轴朝上 - 命名:零件文件名必须包含版本号 - 导出格式:STEP, 需检查 volume() 大于 0这看起来是小事,但能极大减少来回纠偏的沟通成本。AI 有它自己的“习惯”,但你用规范约束它,它就能稳定产出符合你要求的代码,而不是每次生成一个“看起来对但细节全错”的版本。
5.3 后续还能怎么玩:AI Agent 自动建模、参数化组件库
我已经在实际环境里测试了“AI Agent 自动建模”的工作方式:给 Agent 指定一个任务描述,比如“生成一个带 6 个螺栓孔的 DN50 法兰,外径遵循 GB/T 标准”,它会自动查阅标准、生成代码、导出模型,然后自我验证尺寸是否符合标准。这一步目前还需要人工复核,但已经能省掉 80% 的重复建模时间。
另外,把常用的零件都做成参数化代码,积累成组件库,也是一个很值得投入的方向。眼下是法兰、齿轮、固件,未来整个设备的非标件都可以走这条路。这样一来,新项目启动时,你手上不是一堆光秃秃的模型文件,而是一套可以随时“重新长出来”的生成规则。
就我个人这段时间的实践来看,把 AI 用在生成代码而不是直接生成模型上,前期看起来绕了弯路,实际上省下了大量后期返工的时间。如果你正在被 AI 直出模型的“好看但没法用”折磨,建议找个简单的机械零件,照这篇文章的方法试一次,你会马上体会到“改数字而非改模型”的乐趣。
这篇先写到这。下一篇我会详细拆解怎么把 AI 生成代码的流程做成一个可复用的自动化工具链,包括我怎么处理布尔运算失败、怎么用脚本批量验证模型尺寸、以及怎么对接现有的 3D 打印切片流程。如果你在复现的过程中遇到什么奇怪的问题,欢迎在评论区带上你的代码段和报错信息,我看到了都会回。