axial_force_run.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # -*- coding: utf-8 -*-
  2. """
  3. MARS-12S10P SSSR 轴向磁拉力仿真 (Motor-CAD 前台, PyMotorCAD 驱动)
  4. ================================================================
  5. 参照 pss\\maxcalculator\\motorcad_export.py 的做法: 变量名候选列表逐个尝试,
  6. 探测结果如实记入 results JSON (失败也记, 不掩盖)。
  7. 流程: 载入原 .mot → 另存时间戳副本 → 开力计算开关 → 空载(I=0)求解 →
  8. 负载(原电流)求解 → 探测轴向力输出(变量+波形) → 解析交叉校核 → JSON。
  9. 用法:
  10. python axial_force_run.py [--quit] [--skip-noload]
  11. --quit 完成后关闭 Motor-CAD (默认保持前台打开供人工检查)
  12. --skip-noload 只跑负载工况
  13. """
  14. import json
  15. import math
  16. import os
  17. import sys
  18. import time
  19. BASE = os.path.dirname(os.path.abspath(__file__))
  20. MOT_SRC = os.path.join(BASE, "MARS-12S10P_SSSR_D76-C150_V5.0-0819.mot")
  21. OUT_DIR = os.path.join(BASE, "output_motorcad")
  22. MU0 = 4e-7 * math.pi
  23. # 轴向力输出变量候选 (未实测, 探测式; Units_Force=kN 需留意单位)
  24. FORCE_VARS = [
  25. "AxialForce", "Axial_Force", "ForceAxial", "Force_Axial",
  26. "NetAxialForce", "AFMAxialForce", "RotorAxialForce", "Rotor_Axial_Force",
  27. "StatorAxialForce", "AxialForceMean", "MeanAxialForce",
  28. "AxialForceAverage", "AxialForce_Load", "AxialForce_OC",
  29. ]
  30. # 轴向力波形图候选 (get_magnetic_graph_point)
  31. FORCE_GRAPHS = [
  32. "AxialForceVsAngle", "AxialForce", "ForceAxial", "Axial Force",
  33. "Fz", "ForceZ", "Force (Axial)",
  34. ]
  35. # 气隙磁密波形候选 (解析校核用)
  36. BG_GRAPHS = [
  37. "AirgapFluxDensity", "Airgap Flux Density", "AirgapFluxDensityOC",
  38. "BAirgap", "FluxDensityAirgap",
  39. ]
  40. def probe_var(mc, names):
  41. """按候选名读变量, 返回 (名, 值); 全失败 (None, None)。"""
  42. for n in names:
  43. try:
  44. return n, mc.get_variable(n)
  45. except Exception:
  46. continue
  47. return None, None
  48. def probe_graph(mc, names, npoints):
  49. """按候选名读波形 (逐点), 返回 {name, x, y} 或 None。"""
  50. for n in names:
  51. try:
  52. x0, y0 = mc.get_magnetic_graph_point(n, 0)
  53. except Exception:
  54. continue
  55. xs, ys = [x0], [y0]
  56. for i in range(1, npoints):
  57. try:
  58. x, y = mc.get_magnetic_graph_point(n, i)
  59. except Exception:
  60. break
  61. xs.append(x)
  62. ys.append(y)
  63. return {"name": n, "x": xs, "y": ys}
  64. return None
  65. def stats(ys):
  66. if not ys:
  67. return None
  68. return {"mean": sum(ys) / len(ys), "min": min(ys), "max": max(ys),
  69. "pk2pk": max(ys) - min(ys), "n": len(ys)}
  70. def run_case(mc, tag, results, npoints):
  71. print("== 工况 [%s]: do_magnetic_calculation ..." % tag)
  72. t0 = time.time()
  73. mc.do_magnetic_calculation()
  74. dt = time.time() - t0
  75. print(" 求解耗时 %.1f s" % dt)
  76. case = {"solve_seconds": dt}
  77. n, v = probe_var(mc, ["TorqueValueAveragePerCycle", "AverageTorque",
  78. "MeanTorque", "ShaftTorque"])
  79. case["avg_torque"] = {"variable": n, "value": v}
  80. print(" 平均转矩: %s = %s" % (n, v))
  81. n, v = probe_var(mc, FORCE_VARS)
  82. case["axial_force_var"] = {"variable": n, "value": v}
  83. print(" 轴向力变量: %s = %s" % (n, v))
  84. g = probe_graph(mc, FORCE_GRAPHS, npoints)
  85. if g:
  86. case["axial_force_graph"] = {"name": g["name"], "stats": stats(g["y"]),
  87. "x": g["x"], "y": g["y"]}
  88. print(" 轴向力波形 [%s]: %s" % (g["name"], stats(g["y"])))
  89. else:
  90. case["axial_force_graph"] = None
  91. print(" [警告] 轴向力波形候选全部失败")
  92. g = probe_graph(mc, BG_GRAPHS, npoints)
  93. if g:
  94. ys = g["y"]
  95. b2_mean = sum(b * b for b in ys) / len(ys)
  96. case["airgap_B_graph"] = {"name": g["name"], "stats": stats(ys),
  97. "B2_mean": b2_mean}
  98. print(" 气隙磁密波形 [%s]: %s, mean(B^2)=%.4f"
  99. % (g["name"], stats(ys), b2_mean))
  100. else:
  101. case["airgap_B_graph"] = None
  102. print(" [提示] 气隙磁密波形候选失败, 解析校核转 GUI 人工读数")
  103. results["case_" + tag] = case
  104. return case
  105. def main(argv):
  106. quit_after = "--quit" in argv
  107. skip_noload = "--skip-noload" in argv
  108. os.makedirs(OUT_DIR, exist_ok=True)
  109. ts = time.strftime("%m%d_%H%M%S")
  110. from ansys.motorcad.core import MotorCAD
  111. print("启动 Motor-CAD (前台) ...")
  112. mc = MotorCAD()
  113. results = {"when": ts, "source_mot": os.path.basename(MOT_SRC)}
  114. try:
  115. mc.load_from_file(MOT_SRC)
  116. out_mot = os.path.join(OUT_DIR, "MARS_SSSR_axialforce_%s.mot" % ts)
  117. mc.save_to_file(out_mot)
  118. print("工作副本: %s" % out_mot)
  119. results["work_mot"] = out_mot
  120. # ---- 模型关键参数回读 (如实入档) ----
  121. params = {}
  122. for label, names in [
  123. ("Slot_Number", ["Slot_Number"]),
  124. ("Pole_Number", ["Pole_Number"]),
  125. ("Airgap_mm", ["Airgap"]),
  126. ("Magnet_Thickness_mm", ["Magnet_Thickness"]),
  127. ("ShaftSpeed_rpm", ["ShaftSpeed", "Shaft_Speed"]),
  128. ("PeakCurrent_A", ["PeakCurrent", "Peak_Current"]),
  129. ("PhaseAdvance_deg", ["PhaseAdvance", "Phase_Advance"]),
  130. ("Stator_Lam_Dia_mm", ["Stator_Lam_Dia"]),
  131. ("Stator_Bore_mm", ["Stator_Bore"]),
  132. ("AFM_D_Rotor_mm", ["AFM_D_Rotor"]),
  133. ("TorquePointsPerCycle", ["TorquePointsPerCycle"]),
  134. ("Units_Force", ["Units_Force"]),
  135. ]:
  136. n, v = probe_var(mc, names)
  137. params[label] = v
  138. print(" %s: %s = %s" % (label, n, v))
  139. results["params"] = params
  140. i_load = float(params["PeakCurrent_A"] or 0.0)
  141. npoints = int(params["TorquePointsPerCycle"] or 30) + 1
  142. # ---- 打开电磁力计算开关 ----
  143. for var in ["ElectromagneticForcesCalc_Load",
  144. "ElectromagneticForcesCalc_OC"]:
  145. try:
  146. mc.set_variable(var, True)
  147. print(" [OK] %s = True" % var)
  148. except Exception as e:
  149. print(" [警告] %s 设置失败: %s" % (var, e))
  150. # ---- 工况 A: 空载 (I=0, 磁钢对定子铁芯的静态轴向吸力) ----
  151. if not skip_noload:
  152. name_i, _ = probe_var(mc, ["PeakCurrent", "Peak_Current"])
  153. mc.set_variable(name_i, 0.0)
  154. print(" %s -> 0 (空载)" % name_i)
  155. run_case(mc, "noload", results, npoints)
  156. mc.set_variable(name_i, i_load)
  157. print(" %s 恢复 %.3f A" % (name_i, i_load))
  158. # ---- 工况 B: 负载 (模型自带电流) ----
  159. run_case(mc, "load", results, npoints)
  160. # ---- 解析交叉校核: F ≈ A_gap/(2μ0) · mean(B²) ----
  161. try:
  162. d_out = float(params["AFM_D_Rotor_mm"]) * 1e-3
  163. d_in = float(params["Stator_Bore_mm"]) * 1e-3
  164. area = math.pi / 4.0 * (d_out ** 2 - d_in ** 2)
  165. results["analytic"] = {"area_m2": area,
  166. "note": "F=A/(2mu0)*mean(B^2), B 取仿真气隙磁密"}
  167. for tag in ("noload", "load"):
  168. case = results.get("case_" + tag) or {}
  169. bg = case.get("airgap_B_graph")
  170. if bg and bg.get("B2_mean"):
  171. f_est = area / (2.0 * MU0) * bg["B2_mean"]
  172. results["analytic"]["F_est_%s_N" % tag] = f_est
  173. print(" 解析估算 F_%s ≈ %.1f N (A=%.5f m², mean(B²)=%.4f)"
  174. % (tag, f_est, area, bg["B2_mean"]))
  175. except Exception as e:
  176. results["analytic"] = {"failed": str(e)}
  177. mc.save_to_file(out_mot)
  178. res_path = os.path.join(OUT_DIR, "results_axialforce_%s.json" % ts)
  179. with open(res_path, "w", encoding="utf-8") as f:
  180. json.dump(results, f, ensure_ascii=False, indent=2)
  181. print("RESULTS: %s" % res_path)
  182. return 0
  183. finally:
  184. if quit_after:
  185. try:
  186. mc.quit()
  187. except Exception:
  188. pass
  189. else:
  190. print("[提示] Motor-CAD 保持前台打开供检查; 自动化场景加 --quit。")
  191. if __name__ == "__main__":
  192. sys.exit(main(sys.argv[1:]))