编写Python自动化脚本,使用Autodesk Fusion辅助Ansys HFSS进行建模

前言

最近正在复现一个Vivaldi天线,需要绘制由曲线阵列的圆形构成的开槽。由于HFSS如同大便一般的建模逻辑实在不方便完成这个操作,我决定研究一下使用Autodesk Fusion进行阵列,再将坐标导入到Ansys HFSS中绘制圆柱。

得益于这两款软件提供的自动化API,我们能够分别编写两个Python脚本,实现我们需要的功能。

Fusion 建模

导入参考图后,在草图中绘制阵列对象和曲线,之后进行路径阵列,如下图所示。注意路径起点和圆心重合,程序根据路径起点的坐标计算阵列对象的绝对坐标。

Snipaste_2025-10-13_18-04-45

在“实用程序-附加模块”中,点击加号,选择“创建脚本或附加模块”,在弹出的窗口中选择“脚本”,修改名称,选择编程语言为“Python”,最后点击“创建”。

Snipaste_2025-10-13_18-10-40

勾选左侧“由我创建”,找到刚才创建的脚本,右键,选择“在代码编辑器中编辑”。

编写脚本。脚本内容见附录。

运行脚本,保存阵列对象的坐标到CSV文件。

HFSS操作

在“Automation”菜单栏中,点击“Record Script”,填好文件名和路径。随便新建一点什么,再点击“Stop Recording”。

打开保存的Py,编写脚本。注意IronPython的版本貌似比较久远,不支持一些新语法。脚本内容见附录,注意修改文件路径。

最后点击“Run Script”,选择刚才创建的脚本。可以看到,我们成功完成了建模。成品如下图。

Snipaste_2025-10-13_18-23-52

附录

注意:以下代码由AI辅助编写。

Fusion脚本如下。

"""This file acts as the main module for this script."""

import traceback
import adsk.core
import adsk.fusion
import csv

# import adsk.cam

# Initialize the global variables for the Application and UserInterface objects.
app = adsk.core.Application.get()
ui = app.userInterface


def run(_context: str):
    """This function is called by Fusion when the script is run."""

    try:
        # Your code goes here.
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)

        if not design:
            ui.messageBox("需要激活的Fusion设计")
            return

        # 获取根组件
        rootComp = design.rootComponent

        # 准备导出数据
        positions = []

        # 遍历所有特征
        for feature in rootComp.features:
            # 检查是否是路径阵列特征
            if feature.classType() == adsk.fusion.PathPatternFeature.classType():
                pathPattern = adsk.fusion.PathPatternFeature.cast(feature)
                (returnValue, startPoint, endPoint) = pathPattern.path.item(
                    0
                ).curve.evaluator.getEndPoints()

                base_position = (
                    startPoint.x * 10,
                    startPoint.y * 10,
                    startPoint.z * 10,
                )
                # 获取阵列中的所有实例
                for occurrence in pathPattern.patternElements:
                    # 获取变换矩阵
                    transform = occurrence.transform

                    # 提取位置(矩阵的平移部分)
                    translation = transform.translation
                    positions.append(
                        {
                            "x": translation.x * 10 + base_position[0],
                            "y": translation.y * 10 + base_position[1],
                            "z": translation.z * 10 + base_position[2],
                        }
                    )

        # 导出到CSV文件
        if positions:
            # 让用户选择保存位置
            fileDialog = ui.createFileDialog()
            fileDialog.title = "保存位置数据"
            fileDialog.filter = "CSV文件 (*.csv)"
            fileDialog.isMultiSelectEnabled = False

            if fileDialog.showSave() == adsk.core.DialogResults.DialogOK:
                filename = fileDialog.filename

                with open(filename, "w", newline="") as csvfile:
                    writer = csv.DictWriter(csvfile, fieldnames=["x", "y", "z"])
                    writer.writeheader()
                    writer.writerows(positions)

                ui.messageBox(f"成功导出 {len(positions)} 个位置到:\n{filename}")

    except Exception:  # pylint:disable=bare-except
        # Write the error message to the TEXT COMMANDS window.
        app.log(f"Failed:\n{traceback.format_exc()}")

HFSS脚本如下。

# ----------------------------------------------
# Script Recorded by Ansys Electronics Desktop Version 2022.1.0

# ----------------------------------------------
import ScriptEnv
import csv

# Edit according to your project and design names
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.SetActiveProject("vivaldi")
oDesign = oProject.SetActiveDesign("AVA2")
oEditor = oDesign.SetActiveEditor("3D Modeler")

# Read coordinates from CSV file
coordinates = []
# Make sure to change the path to your actual CSV file path
with open("D:\\docs\\fusion\\1.csv", "r") as file:
    csv_reader = csv.DictReader(file)
    for row in csv_reader:
        x = float(row["x"])
        y = float(row["y"])
        z = float(row["z"])
        print("read coordinates: x=%f, y=%f, z=%f" % (x, y, z))
        coordinates.append((x, y, z))

# create cylinders at the specified coordinates
for i, (x, y, z) in enumerate(coordinates):
    oEditor.CreateCylinder(
        [
            "NAME:CylinderParameters",
            "XCenter:=",
            "%f mm" % x,
            "YCenter:=",
            "%f mm" % y,
            "ZCenter:=",
            "%f mm" % z,
            "Radius:=",
            "6mm",
            "Height:=",
            "-20mm",
            "WhichAxis:=",
            "Z",
            "NumSides:=",
            "0",
        ],
        [
            "NAME:Attributes",
            "Name:=",
            "Cylinder_From_CSV%d" % (i + 1),
            "Flags:=",
            "NonModel#",
            "Color:=",
            "(143 175 143)",
            "Transparency:=",
            0,
            "PartCoordinateSystem:=",
            "Global",
            "UDMId:=",
            "",
            "MaterialValue:=",
            '"vacuum"',
            "SurfaceMaterialValue:=",
            '""',
            "SolveInside:=",
            True,
            "ShellElement:=",
            False,
            "ShellElementThickness:=",
            "0mm",
            "IsMaterialEditable:=",
            True,
            "UseMaterialAppearance:=",
            False,
            "IsLightweight:=",
            False,
        ],
    )
# Unite all cylinders into one object, then mirror the united object
# and unite the mirrored object with the original united object
UnitedObjects = ",".join(
    ["Cylinder_From_CSV%d" % (i + 1) for i in range(len(coordinates))],
)
oEditor.Unite(
    ["NAME:Selections", "Selections:=", UnitedObjects],
    ["NAME:UniteParameters", "KeepOriginals:=", False],
)

oEditor.DuplicateMirror(
    [
        "NAME:Selections",
        "Selections:=",
        "Cylinder_From_CSV1",
        "NewPartsModelFlag:=",
        "Model",
    ],
    [
        "NAME:DuplicateToMirrorParameters",
        "DuplicateMirrorBaseX:=",
        "0mm",
        "DuplicateMirrorBaseY:=",
        "0mm",
        "DuplicateMirrorBaseZ:=",
        "0mm",
        "DuplicateMirrorNormalX:=",
        "-1mm",
        "DuplicateMirrorNormalY:=",
        "0mm",
        "DuplicateMirrorNormalZ:=",
        "0mm",
    ],
    ["NAME:Options", "DuplicateAssignments:=", True],
    ["CreateGroupsForNewObjects:=", False],
)
oEditor.Unite(
    ["NAME:Selections", "Selections:=", "Cylinder_From_CSV1,Cylinder_From_CSV1_1"],
    ["NAME:UniteParameters", "KeepOriginals:=", False],
)

参考文献

[1] Prof. Halim Boutayeb, Metasurface Design: Theory, Applications & HFSS Simulations with Python | RF & Microwave Engineering, Youtube, https://www.youtube.com/watch?v=To8rcAq12mc, Nov.2024(accessed Oct. 2025).
[2] A. Hossain and A. -V. Pham, "A Novel Gain-Enhanced Miniaturized and Lightweight Vivaldi Antenna," in IEEE Transactions on Antennas and Propagation, vol. 71, no. 12, pp. 9431-9439, Dec. 2023.
[3] Ronghua.LI, [图文笔记]Fusion 创建Python脚本-入门篇01, Autodesk Community, https://forums.autodesk.com/t5/fusion-chan-pin-ji-shu-ying-yong-zhong-wen-lun-tan/tu-wen-bi-ji-fusion-chuang-jianpython-jiao-ben-ru-men-pian01/td-p/10677228, Oct. 2021(accessed Oct. 2025).

posted @ 2025-10-13 18:34  GongYeSUDA  阅读(46)  评论(0)    收藏  举报