
简介Fusion360Exporter-master是一个面向Fusion 360用户的导出功能增强项目旨在弥补软件默认导出格式有限、跨软件协作不便的短板。压缩包共3个文件由Python导出脚本、Markdown说明文档和开源许可证构成整体仅6KB轻量易用。脚本基于Fusion 360 API编写可扩展STL、OBJ、STEP等常见格式输出并允许调节网格质量、分辨率等参数从而适配3D打印、CNC加工、渲染等下游环节同时优化导出流程以保留更多设计细节文档详述了安装与自定义方法许可证保证合规使用。项目适用于产品设计、机械工程、工业设计等场景已有347人学习尤其适合需要跨平台数据交换的中高级用户。对具备Python基础的开发者来说可直接参考脚本逻辑二次开发或将其集成进Fusion 360实现批量导出与精细化控制减少重复操作有效提升协作效率。1. 为什么我最后用脚本改写 Fusion 360 的导出流程打开 Fusion 360 的导出面板选格式、选单位、点确定这个动作看起来只有十几秒但当你的项目里有几十个零件、需要按固定的命名规则整理模型、或者要把同一个模型出成不同精度的网格时手动导出就成了纯粹的体力劳动。Fusion360Exporter 这类工具解决的就是这个问题把 Fusion 360 模型导出从手工操作变成可重复执行的脚本让参数统一、命名统一、路径统一。我最早做这件事是在一个跨平台协作的项目里三维模型要从 Fusion 360 端定期同步给下游做渲染和仿真。当时人手一个格式、一个单位习惯导出的模型有的放大十倍有的缩小十倍最后发现根本原因只是导出时单位选错了。后来我把导出流程写成脚本固定了单位和坐标系问题才彻底消失。这篇笔记不是某个现成插件的使用说明书而是我基于 Fusion 360 的建模 API 自己搭导出器时走通的路径从原理到代码再到踩坑照着做就能用。适合读这篇的人你在用 Fusion 360 做参数化建模需要把模型批量导出给其他工具或者你维护的模型每周都要出多个版本的网格文件再或者你已经被手工导出的命名混乱、单位错乱、装配体丢零件折磨过。这篇的内容围绕一个目标展开——用脚本把 Fusion 360 模型的导出过程变成可复现、可批量的工程动作。2. 导出器到底在导出什么Fusion 360 的数据组织和 API 入口2.1 Fusion 360 模型数据结构的三个层次写导出器之前先得搞清楚你要导出的东西在 Fusion 360 内部是怎么组织的。Fusion 360 的文档模型不是简单的「一个文件里装一个零件」它的结构大致分三层应用层是 Document就是你在界面上打开的那个文件往下是 Design代表参数化设计环境再往下一层是发生在设计环境里的各种实体比如 BRepBody边界表示的实体体、MeshBody网格体、Component组件和 Occurrence装配中的实例。对于导出器来说最常打交道的对象是 Component 和 BRepBody。一个 Component 可以包含多个 BRepBody对应到界面上就是「一个零件里有几个实体」。在装配体场景里每个被插入的零件都是一个 Occurrence它指向一个 Component 定义。这里有个容易绕晕的点同一个 Component 被多次插入时多个 Occurrence 共享同一个 Body 定义导出时如果按 Occurrence 遍历同一个几何会被导出多次。选导出对象时我一般分两种情况处理。单个零件文件直接遍历根 Component 的 BRepBody 列表装配体文件则遍历 Occurrence因为每个 Occurrence 有独立的变换矩阵导出时要乘上这个矩阵才能保证装配位置正确。Fusion 360 的 API 里Occurrence.transform 是 Matrix3D 类型脚本里拿到的几何坐标是局部坐标必须经过变换才能得到世界坐标。2.2 ExportManager 和 ExportOptions官方 API 的两种导出姿势Fusion 360 的 API 里提供了 ExportManager 对象它是所有导出操作的总入口。ExportManager 可以创建不同类型的 ExportOptions比如 OBJExportOptions、FBXExportOptions、STLExportOptions每种 Options 里定义了对应的格式参数。这是官方支持的做法写起来规范而且不会出现坐标错乱的问题。用法上有个关键区别。对于网格类格式STL、OBJ你可以先通过网格转换把实体转成 MeshBody再走网格导出也可以直接把 BRepBody 交给 ExportOptions让 API 内部完成网格化。直接交 BRepBody 的方式更省事但精度控制没有手动网格化那么细。对于需要控制网格密度的场景我习惯先自己用 MeshManager 创建网格再导出这样导出的模型面数可预测。另一种导出姿势是直接写文件。用 API 里的 exportToFile 方法配合自定义的几何遍历这种写法底层但灵活性最高适合做非标准格式的导出。我在一个图像处理 Demo 里用过这种姿势导出自定义的点云格式Fusion 360 官方的 Options 只支持标准格式遇到不支持的格式就只能自己遍历几何、自己写文件头。2.3 三种导出路线的取舍导出选项、网格转换、直接写文件三种方式放一起对比各有各的使用场景。用 ExportOptions 导出的好处是省心官方帮你处理坐标系、单位和格式细节坏处是 Options 支持的参数有限比如 OBJExportOptions 里能调的只有单位、是否导出外观网格密度这类参数管不了。用 MeshManager 先转网格再导出好处是网格质量完全由你控制建网格时可以传入弦高偏差和表面细分参数坏处是多了一步网格化而且网格一旦生成原始的 BRep 参数精度信息就丢了。直接写文件最灵活坏处是所有东西都要自己处理。我的建议是只导出标准格式给下游用直接上 ExportOptions别折腾导出前需要固定网格精度用 MeshManager 先网格化要导出自定义格式认命直接遍历 BRepBody 拿三角形数据自己写。下面的章节会重点展开前两种因为这是大多数人做 Fusion360Exporter 的真实需求。3. 用脚本在本地跑通最小导出器环境准备与第一个可用的导出命令3.1 运行环境Fusion 360 脚本和插件的加载位置Fusion 360 的 Python 脚本运行在软件内置的解释器里不需要本地单独装 Python。打开 Fusion 360 后切到「工具」菜单下的「脚本和附加模块」面板就能看到一个脚本管理器。新建脚本时Fusion 360 会在用户目录下生成一个文件夹里面包含一个 .py 文件和一个 .manifest 文件。manifest 文件声明这个脚本的元数据py 文件是入口代码。开发导出器这类工具我一般建议做成「附加模块」而不是「一次性脚本」。附加模块可以常驻带 UI 面板适合反复操作脚本则是点一下跑一次适合验证逻辑。初次写导出器先用脚本形式把导出逻辑跑通验证输出文件没问题后再决定要不要改成附加模块加界面。环境这块有个坑Fusion 360 的 Python 环境里不是所有标准库都可用requests 这类网络库就别想了但 os、json、csv 这些基础库没问题。3.2 最小可用代码把当前文档保存成 STL写第一个脚本时目标只定一个把当前打开的文档里的所有 BRepBody 合并导出成一个 STL 文件。核心逻辑分四步拿当前文档找根组件下的所有 BRepBody构造 STLExportOptions执行导出。代码里最关键的是拿到 ExportManager 实例它是从 Design 对象上取下来的。import adsk.core import adsk.fusion import traceback import os def run(context): ui None try: app adsk.core.Application.get() ui app.userInterface design adsk.fusion.Design.cast(app.activeProduct) if not design: ui.messageBox(没有处于激活状态的设计) return # 获取导出管理器 exportMgr design.exportManager # 指定输出路径 outputDir os.path.expanduser(~) /fusion_export if not os.path.exists(outputDir): os.makedirs(outputDir) outputFile outputDir /demo_output.stl # 构造 STL 导出选项 stlOptions exportMgr.createSTLExportOptions(design.rootComponent, outputFile) stlOptions.meshRefinement adsk.fusion.MeshRefinementSettings.MeshRefinementMedium # 执行导出 exportMgr.execute(stlOptions) ui.messageBox(导出成功: outputFile) except: if ui: ui.messageBox(导出失败:\n traceback.format_exc())createSTLExportOptions 的第一个参数可以传 Component也可以传 BRepBody。传 Component 时导出该组件下所有实体传单个 Body 时只导出那个实体。MeshRefinement 有三个档位MeshRefinementLow、MeshRefinementMedium、MeshRefinementHigh对应粗糙、适中、精细三种网格密度分别适合预览、常规使用、精细渲染。导出完成后execute 会在指定路径生成 STL 文件。3.3 把导出扩展到 OBJ单位和坐标系的参数设置STL 只存几何不存颜色和材质信息很多场景不够用。要导 OBJ 时Options 换成 OBJExportOptions它多了一个关键参数——单位。这里就是前面说的坑Fusion 360 默认的文档单位可能是厘米但下游工具期望的是毫米单位不对模型大小直接差十倍。import adsk.core import adsk.fusion import traceback import os def run(context): ui None try: app adsk.core.Application.get() ui app.userInterface design adsk.fusion.Design.cast(app.activeProduct) exportMgr design.exportManager outputDir os.path.expanduser(~) /fusion_export if not os.path.exists(outputDir): os.makedirs(outputDir) outputFile outputDir /demo_output.obj # 创建 OBJ 导出选项设置单位为毫米 objOptions exportMgr.createOBJExportOptions(design.rootComponent, outputFile) objOptions.unit adsk.fusion.ExportFileUnits.MillimeterExportUnits exportMgr.execute(objOptions) ui.messageBox(OBJ 导出成功: outputFile) except: if ui: ui.messageBox(导出失败:\n traceback.format_exc())ExportFileUnits 枚举里包含的是文档里能见到的那些单位选项CentimeterExportUnits、MillimeterExportUnits、InchExportUnits 等。这里有个容易忽略的事Fusion 360 的文档单位制会影响默认导出单位。如果你的文档本身是英寸而你导出时没显式指定单位出来的模型尺寸就会按英寸解释下游如果默认按毫米处理尺寸直接错乱。所以导出时永远显式指定单位不要依赖默认值这是我在这个项目里总结出的第一条铁律。3.4 你的第一个批量导出脚本遍历所有组件分别输出单文件导出跑通后下一步就是批量。常见的需求是一个装配体文档里有多个 Component每个 Component 要单独导出一个 OBJ 文件文件名用 Component 的名字。遍历组件的 API 是 rootComponent.allComponents它会递归取出所有层级的子组件。要注意的是allComponents 返回的是组件的定义不是装配实例所以名字可以直接用不会出现同一个组件名字出现多次的问题。import adsk.core import adsk.fusion import traceback import os def run(context): ui None try: app adsk.core.Application.get() ui app.userInterface design adsk.fusion.Design.cast(app.activeProduct) exportMgr design.exportManager root design.rootComponent outputDir os.path.expanduser(~) /fusion_export_batch if not os.path.exists(outputDir): os.makedirs(outputDir) # 递归获取所有子组件 comps root.allComponents index 1 for comp in comps: # 用组件名称作为文件名处理名称带空格的情况 safeName comp.name.replace( , _) outputFile outputDir / str(index).zfill(3) _ safeName .obj objOptions exportMgr.createOBJExportOptions(comp, outputFile) objOptions.unit adsk.fusion.ExportFileUnits.MillimeterExportUnits exportMgr.execute(objOptions) index 1 ui.messageBox(批量导出完成共导出 str(index - 1) 个组件) except: if ui: ui.messageBox(导出失败:\n traceback.format_exc())文件名前的序号 zfill(3) 是为了保持文件排序稳定方便后面用脚本批量处理时按顺序对应。组件名里的空格替换成下划线是为了避免路径解析工具和后续处理脚本出现不必要的麻烦。allComponents 包含根组件本身如果你不想导出根组件遍历时判断 comp root 然后跳过即可。这个脚本跑完后每个子组件对应一个 OBJ 文件装配关系不保留但每个零件的几何是独立的适合下游逐零件处理。4. 把装配体导成一个整体坐标变换和网格导出的隐藏细节4.1 装配体里 Occurence 和 Component 的关系为什么直接导会丢位置装配体文档里根组件下挂着子组件子组件被插入到装配里时产生 Occurrence。Occurrence 有位置信息Component 没有。直接传 Component 给导出选项时Fusion 360 导出的是该组件的零件几何不包含它在装配里的位置。所以如果你想导出一个装配体的整体模型比如把整个机器导成一个 STL 用于 3D 打印预览直接遍历 Component 导出是错的出来的零件全都在原点附近堆着。正确的做法是遍历装配体里的 Occurrence。每个 Occurrence 的 transform 属性是一个 Matrix3D记录了这个实例相对父级的平移、旋转和缩放。要得到世界坐标需要把这个矩阵一直乘到根。Fusion 360 的 API 里提供一个便捷接口occurrence.getWorldMatrix()可以直接拿到世界矩阵省去了手写递归乘法的麻烦。4.2 遍历 Occurrence 并合并导出一个整体 STL把装配体整体导出的思路是遍历根组件下的所有 Occurrence获取每个 Occurrence 对应的 BRepBody 列表然后把世界矩阵应用到手性变换上最后把所有三角形数据收集到一起写入一个新的网格。但直接用 ExportManager 做不到合并——它只能导出 Component 或 Body不能导出一个自定义的三角形集合。所以这个场景必须换一条路先给所有 Occurrence 生成网格再把网格数据提取出来统一变换后合并。import adsk.core import adsk.fusion import traceback import os import struct def run(context): ui None try: app adsk.core.Application.get() ui app.userInterface design adsk.fusion.Design.cast(app.activeProduct) root design.rootComponent # 收集所有 occurrence occs root.allOccurrences meshMgr design.meshManager allTriangles [] for occ in occs: # 拿到该 occurrence 下的所有实体体 bodies occ.component.bRepBodies worldMat occ.getWorldMatrix() for body in bodies: # 创建网格控制弦高偏差 mesh meshMgr.createMesh(body, adsk.fusion.MeshCalculationOptions()) # 获取网格数据和索引 nodeCoords mesh.nodeCoordinates nodeIndices mesh.nodeIndices # 遍历每个三角形应用世界矩阵变换 for i in range(0, len(nodeIndices), 3): idx1 nodeIndices[i] * 3 idx2 nodeIndices[i 1] * 3 idx3 nodeIndices[i 2] * 3 p1 adsk.core.Vector3D.create(nodeCoords[idx1], nodeCoords[idx1 1], nodeCoords[idx1 2]) p2 adsk.core.Vector3D.create(nodeCoords[idx2], nodeCoords[idx2 1], nodeCoords[idx2 2]) p3 adsk.core.Vector3D.create(nodeCoords[idx3], nodeCoords[idx3 1], nodeCoords[idx3 2]) # 应用变换 p1.transformBy(worldMat) p2.transformBy(worldMat) p3.transformBy(worldMat) allTriangles.append((p1, p2, p3)) # 写自定义 STL二进制格式 outputPath os.path.expanduser(~) /assembly.stl writeBinaryStl(outputPath, allTriangles) ui.messageBox(装配体整体导出完成三角形数量: str(len(allTriangles))) except: if ui: ui.messageBox(导出失败:\n traceback.format_exc()) def writeBinaryStl(path, triangles): with open(path, wb) as f: # 80 字节文件头 f.write(b0 * 80) # 三角形数量 f.write(struct.pack(I, len(triangles))) for tri in triangles: # 法向量这里简化处理为 0 f.write(struct.pack(3f, 0, 0, 0)) for p in tri: f.write(struct.pack(3f, p.x, p.y, p.z)) # 属性字节数 f.write(struct.pack(H, 0))这段代码里最值钱的地方是 createMesh 这一步。MeshCalculationOptions 如果不设置任何参数网格会按照文档默认的弦高偏差生成精度常常不够圆角位置会出现明显的棱边。下面专门说网格参数的调法。另外writeBinaryStl 里法向量写 0 是偷懒做法大多数 STL 读取软件会自行计算法向量不影响显示但如果下游工具严格依赖法向量做布尔运算这里需要补用法向量计算逻辑。4.3 MeshCalculationOptions 的参数弦高偏差和表面细分的直觉理解MeshCalculationOptions 有两个关键参数chordTolerance 和 maxEdgeLength。chordTolerance 控制的是「弦高偏差」即网格边缘到真实曲面之间的最大允许距离这个值越小网格越贴合曲面三角形数量越多。maxEdgeLength 控制的是三角形边的最大长度用来限制大平面上出现超大三角形。这两个值的设置要按模型的实际尺寸来。如果你的模型整体尺寸是 100mmchordTolerance 设 0.1mm 可以接受面数可能在几十万设 0.01mm 面数可能直接破百万文件写出来几百 MB下游加载卡死。maxEdgeLength 我一般设成模型典型边长的 1/50 到 1/100太细的没意义只增加文件体积。调参的顺序也有讲究先调 chordTolerance 解决曲面精度问题如果某些平面上的三角形依然太大再降 maxEdgeLength。不要一上来两个参数都往极限调不然导出时间会指数级上升。我自己的血泪经验是在某个模拟项目X里为了追求视觉效果把 chordTolerance 设成了 0.001mm结果一个模型生成了 2000 多万个三角形脚本跑了一个多小时最后文件还打不开。后来把参数调到 0.05mm面数少了 90%渲染效果几乎没差别。5. Fusion360Exporter 使用避坑坐标系、单位、命名和性能的四个真实翻车现场5.1 单位翻车文档单位是英寸导出却按毫米解释现象从 Fusion 360 导出的 OBJ 加载到别的软件后模型整体放大了 25.4 倍或者缩小了 25.4 倍。原因Fusion 360 的文档单位是英寸时OBJExportOptions 如果没有显式指定单位默认会按照文档单位导出。下游工具如果按毫米读取 OBJ而 OBJ 文件内部的数据是按英寸数值写的尺寸必然对不上。解决创建导出选项后显式设置 unit 属性为期望的目标单位不要留默认值。同时检查文档单位制在脚本开头读取 design.unitsManager 的默认长度单位打印出来看一眼。三个地方的单位要保持一致Fusion 360 文档单位、导出选项单位、下游工具的导入单位。5.2 坐标系翻车镜头前的模型在渲染器里底朝天现象从 Fusion 360 导出的模型导入渲染软件后模型是倒的或者旋转了 90 度与 Fusion 360 里的视角不一致。原因Fusion 360 默认的坐标系是 Z 轴向上的建模坐标系但很多渲染引擎和游戏引擎使用 Y 轴向上。导出时 OBJ 格式本身记录的是原始坐标数据Fusion 360 不会主动做轴系转换坐标系不匹配时模型方向就跟着变。解决导出的数据里做一个轴系映射比如 Z-up 转 Y-up把坐标的 Y 和 Z 互换。多数情况下还要额外绕 X 轴旋转 90 度才能在目标场景里正确显示。这个问题没有银弹必须在导出脚本里写死一条转换规则然后结合目标软件的实际表现调整。5.3 命名翻车文件名里的空格和特殊字符在下游脚本里崩掉现象导出的文件名带空格或中文字符下游一个批处理脚本在处理到带空格的文件时直接退出。原因通用批处理脚本里空格会被当作参数分隔符。文件名如果是 C:\export\part 1.obj脚本解析时就断成两截。解决写导出器时建议做一层文件名净化把空格换成下划线去掉括号和特殊符号只保留字母、数字、下划线和连字符。这是个小改动但能省掉下游无数麻烦。我在实际项目里吃过这个亏之后把文件名净化做成了导出器的固定动作之后再没出过这类问题。5.4 性能翻车导出 200 万三角形只需几秒但脚本卡死半小时现象脚本在遍历 Occurrence 生成网格时卡住CPU 占用率很高但导出进度不动最后等了半小时才完成。原因网格生成是计算密集型操作而且 createMesh 是同步调用。遇到大装配体、多个高精度实体累积的时间会很长。另一个隐藏原因是网格对象在 Python 层持有大量数据引用内存不停增长GC 频繁触发。解决临时调低网格精度参数把弦高偏差放宽到视觉可接受范围实现分块导出先导出零件再合并而不是一个循环里全部做完导完一个 Occurrence 后手动释放引用。必要时把网格生成丢到独立线程或者干脆按组件拆分导出任务分多次执行脚本。高精度网格生成确实是玄学参数差一点时间差十倍先对齐目标再开跑。6. 进阶导出技巧自定义命名规则与自动记录导出日志导出器做到能用的程度之后真正拉开差距的是细节把命名规则做成可配置的模板把每一次导出的参数记录成日志。命名规则这事我用的是类似 printf 的模板方案定义一个模板字符串里面有 {name}、{index}、{date} 这些占位符运行脚本时动态替换。这样同一个脚本在不同项目里用不同的命名规则不需要改代码。import datetime def buildFileName(template, compName, index): now datetime.datetime.now() dateStr now.strftime(%Y%m%d) replacements { {name}: compName, {index}: str(index).zfill(3), {date}: dateStr, {time}: now.strftime(%H%M%S) } result template for key, value in replacements.items(): result result.replace(key, value) return result # 使用示例 template {date}_{index}_{name} fileName buildFileName(template, bracket_v2, 5) # 输出: 20250623_005_bracket_v2日志记录的逻辑更简单但很重要。导出的单位、网格参数、坐标系是否翻转、输出路径、执行时间这些信息如果不记录下次出问题的时候根本无从查起。我在脚本里加了一个 log 函数把每次导出的参数写进 CSV 文件字段包括时间、组件名、目标格式、单位、弦高偏差、三角形数量。排查问题的时候打开 CSV 一眼就能看出哪次导出用了错误参数。import csv import os def appendLog(logPath, rowData): fileExists os.path.exists(logPath) with open(logPath, a, newline, encodingutf-8) as f: writer csv.writer(f) if not fileExists: writer.writerow([time, component, format, unit, chordTol, triangles]) writer.writerow(rowData)这两个技巧合在一起导出器就从一个只会出文件的脚本变成了一个可以追溯、可以复现、可以交接给其他人的工程工具。我自己的习惯是导出器写完后至少要在三个不同文档上跑一遍——单个零件、多零件装配体、带约束的复杂装配体确认三种场景下的输出都正确。每次调完参数留一条日志下次遇到类似问题直接翻日志就行不用重新试错。这套习惯帮我省了很多回头路希望也能帮到你。本文还有配套的精品资源点击获取