Магнитное поле и электромеханика · Плоскопараллельная
Дроссель с зазором: индуктивность, рассеяние и запасённая энергия
Воздушный зазор делает индуктивность дросселя менее чувствительной к проницаемости сердечника и позволяет запасать энергию. При этом поле у краёв зазора перестаёт быть одномерным.
Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.
Допущения и устройство учебной модели
| Учебная модель FEMM 5 | Причина |
|---|---|
| Параметры и диапазон исследования выбраны для последовательного изучения в нашем курсе. | Учебная постановка FEMM 5 |
| Геометрия, источники, русские свойства, управление серией и проверки объединены в самостоятельную программу встроенного Python. | Учебная постановка FEMM 5 |
| Сеточная чувствительность и физические контроли показаны отдельно от основной параметрической характеристики. | Учебная постановка FEMM 5 |
Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.
Физическая и математическая постановка
Принцип действия и расчётные соотношения
При заданном токе магнитодвижущая сила равна NI, поток приближённо определяется суммой сопротивлений стали и зазора. Эта цепная оценка объясняет знак изменения индуктивности, но не включает выпучивание поля и рассеяние. В поле индуктивность вычисляется через потокосцепление, а независимый интеграл энергии должен дать половину произведения тока на потокосцепление для линейных материалов. При увеличении зазора энергия при том же токе уменьшается; при заданном потоке направление изменения было бы иным.
Численная проверка и новая серия расчётов
Сначала выполняются три независимых расчёта одной постановки с базовыми размерами 1,2; 0,8; 0,5 мм. Фактический размер в узких местах задаётся программой отдельно и может быть меньше этого масштаба. Сравниваются наблюдаемая величина и число элементов, а не только вид поля. После этого на мелкой сетке выполняется параметрическая серия и независимые физические контроли. Каждый файл модели перед расчётом закрывается и открывается повторно; этим проверяется сохранение русских свойств в точной решаемой копии.
Для нового опыта скачайте файлы со страницы работы в отдельный каталог и откройте reproduce.py в редакторе встроенного Python FEMM 5. Программа использует непосредственные команды mi/ mo и не требует внешнего управления Python. Она создаёт новую папку calculated-NN рядом с программой. Прежние решения не используются для получения новых чисел. Результат и все входы сохраняются вместе, поэтому найденное число всегда можно связать с параметрами, сеткой и точным файлом.
Исходные данные
Плоскопараллельная
| Параметр | Значение | Единица | Происхождение |
|---|---|---|---|
| Геометрия и источники | Сердечник 70×60 мм, толщина стержня 10 мм, глубина 40 мм, μr=1000, обмотка 100 витков и 1 А; зазор 0,25–2 мм. | СИ и мм | Выбранные учебные параметры |
| Рабочая координата | Зазор, мм | см. подписи результатов | Параметрическая серия |
Порядок воспроизведения
- Разберите физическую модель и соотношения до запуска программы. Найдите каждое число из таблицы в CONFIG и геометрических командах.
- Откройте reproduce.py во встроенном Python FEMM 5. Сохраните пользовательские документы и запустите программу.
- Сопоставьте три сетки одной постановки и независимые физические проверки. Не подменяйте сравнение сеток разными рабочими режимами.
- Изучите параметрическую серию: объясните знак, размерность и тенденцию каждой величины. Измените один параметр и повторите опыт в новой папке.
- Оцените, какие выводы требуют трёхмерного, теплового или динамического расчёта. Сохраните модель и результат новой серии вместе.
Скачайте нужные файлы по отдельным ссылкам ниже в одну рабочую папку, затем откройте reproduce.py во встроенном Python FEMM 5 и выполните его. Сохраните имена файлов и структуру подпапок. Условия конкретного запуска и путь новых результатов указаны в комментариях программы.
Программа встроенного Python FEMM 5
"""Самостоятельное полевое исследование во встроенном Python FEMM 5.
Все размеры геометрии указаны в миллиметрах, результаты — в СИ. Значения
гармонических токов являются амплитудами. Материалы и условия имеют русские
имена; номера групп описаны в README. Программа создаёт новую calculated-NN
рядом с собой, не заменяя предшествующих расчётов. Каждая входная модель
сохраняется, закрывается, открывается повторно и только затем решается.
Открытые пользовательские документы не изменяются. Параметры — в CONFIG.
"""
import os
import sys
import json
import math
MU0 = 4e-7 * math.pi
script = globals().get("__file__", "") or (sys.argv[0] if sys.argv else "")
HERE = os.path.dirname(os.path.abspath(script)) if script else os.getcwd()
number = 1
while True:
OUT = HERE + "/calculated-%02d" % number
try:
os.stat(OUT)
number += 1
except OSError:
os.mkdir(OUT)
break
def scalar(value):
if isinstance(value, (list, tuple)):
return scalar(value[0])
return complex(value).real
def complex_parts(value):
value = complex(value)
return [value.real, value.imag]
def point(radius, angle):
angle = angle * math.pi / 180
return (radius * math.cos(angle), radius * math.sin(angle))
def line(a, b, group=0, size=0):
mi_addnode(a[0], a[1])
mi_addnode(b[0], b[1])
mi_addsegment(a[0], a[1], b[0], b[1])
mi_selectsegment((a[0] + b[0]) / 2, (a[1] + b[1]) / 2)
boundary = (
"Внешняя граница A=0"
if globals().get("AXIS", False) and abs(a[0]) + abs(b[0]) < 1e-9
else "<None>"
)
mi_setsegmentprop(boundary, size, 0 if size else 1, 0, group)
mi_clearselected()
def polygon(points, group=0, size=0):
for index in range(len(points)):
line(points[index], points[(index + 1) % len(points)], group, size)
def rectangle(x1, y1, x2, y2, group=0, size=0):
polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)], group, size)
def arc(radius, start, end, group=0, step=2):
a, b = point(radius, start), point(radius, end)
mi_addnode(a[0], a[1])
mi_addnode(b[0], b[1])
mi_addarc(a[0], a[1], b[0], b[1], end - start, step)
middle = point(radius, (start + end) / 2)
mi_selectarcsegment(middle[0], middle[1])
mi_setarcsegmentprop(step, "<None>", 0, group)
mi_clearselected()
def circle(radius, group=0, step=2):
for start in (0, 90, 180, 270):
arc(radius, start, start + 90, group, step)
def label(x, y, material, mesh, group=0, circuit="<None>", turns=1, angle=0):
mi_addblocklabel(x, y)
mi_selectlabel(x, y)
mi_setblockprop(material, 0, mesh, circuit, angle, group, turns)
mi_clearselected()
def begin(mesh, kind="planar", frequency=0, depth=40, radius=100, br=1.1, mur=1.05):
global AXIS
AXIS = kind == "axi"
newdocument(0)
mi_probdef(frequency, "millimeters", kind, 1e-9, depth, 25)
mi_addmaterial("Воздух", 1, 1, 0, 0, 0)
mi_addmaterial("Обмотка без вытеснения тока", 1, 1, 0, 0, 0)
mi_addmaterial("Медь 58 МСм на метр", 1, 1, 0, 0, 58)
mi_addmaterial("Сталь с линейной проницаемостью", 1000, 1000, 0, 0, 0)
mi_addmaterial("Сталь с учебной кривой насыщения", 1, 1, 0, 0, 0)
for b, h in (
(0, 0),
(0.3, 100),
(0.6, 200),
(1, 350),
(1.3, 700),
(1.5, 1800),
(1.65, 5000),
(1.8, 15000),
(2, 60000),
(2.2, 200000),
):
mi_addbhpoint("Сталь с учебной кривой насыщения", b, h)
mi_addmaterial("Постоянный магнит учебный NdFeB", mur, mur, br / (MU0 * mur), 0, 0)
mi_addboundprop("Внешняя граница A=0", 0, 0, 0, 0, 0, 0, 0, 0, 0)
if kind == "axi":
rectangle(0, -radius, radius, radius)
for x, y in ((0, 0), (radius, 0), (radius / 2, -radius), (radius / 2, radius)):
mi_selectsegment(x, y)
mi_setsegmentprop("Внешняя граница A=0", 0, 1, 0, 0)
mi_clearselected()
else:
circle(radius, step=4)
for angle in (45, 135, 225, 315):
x, y = point(radius, angle)
mi_selectarcsegment(x, y)
mi_setarcsegmentprop(4, "Внешняя граница A=0", 0, 0)
mi_clearselected()
def band(r1, r2, count, fraction, group, mesh, phase=0, mode="radial"):
"""Кольцевая сборка: заданные магнитные секторы и воздушные промежутки."""
pitch = 360 / count
for index in range(count):
start = index * pitch + phase
stop = start + pitch * fraction
for radius in (r1, r2):
arc(radius, start, stop, group, step=2)
if fraction < 0.999999:
arc(radius, stop, start + pitch, group, step=2)
line(point(r1, start), point(r2, start), group)
line(point(r1, stop), point(r2, stop), group)
angle = (start + stop) / 2
x, y = point((r1 + r2) / 2, angle)
if mode == "steel":
label(x, y, "Сталь с линейной проницаемостью", mesh, group)
else:
direction = 2 * angle if mode == "halbach" else angle + 180 * (index % 2)
label(x, y, "Постоянный магнит учебный NdFeB", mesh, group, angle=direction)
if fraction < 0.999999:
x, y = point((r1 + r2) / 2, (stop + start + pitch) / 2)
label(x, y, "Воздух", mesh, group)
def energy():
mo_clearblock()
mo_groupselectblock()
values = [scalar(mo_blockintegral(k)) for k in (2, 17, 4)]
mo_clearblock()
return values
def solve(tag, group=2, probe=(1, 0), circuits=()):
path = OUT + "/" + tag + ".fem"
mi_saveas(path)
mi_close()
opendocument(path)
mi_analyze(1)
mi_loadsolution()
e, w, p = energy()
row = {
"state": tag,
"elements": mo_numelements(),
"energy_J": e,
"coenergy_J": w,
"loss_W": p,
}
if group:
mo_groupselectblock(group)
if CONFIG.get("kind") != "course":
row["Fx_N"] = scalar(mo_blockintegral(18))
row["Fy_N"] = scalar(mo_blockintegral(19))
row["torque_Nm"] = scalar(mo_blockintegral(22))
if CONFIG.get("kind") != "course":
row["lorentz_x_N"] = scalar(mo_blockintegral(11))
row["lorentz_y_N"] = scalar(mo_blockintegral(12))
mo_clearblock()
if CONFIG.get("kind") == "gear":
for part in (2, 3, 4):
mo_groupselectblock(part)
row["T%d_Nm" % part] = scalar(mo_blockintegral(22))
mo_clearblock()
values = mo_getpointvalues(probe[0], probe[1])
row["Bx_T"] = scalar(values[1])
row["By_T"] = scalar(values[2])
row["Hx_Am"] = scalar(values[5])
row["Hy_Am"] = scalar(values[6])
for circuit in circuits:
i, v, psi = mo_getcircuitproperties(circuit)
row[circuit + "_I_A"] = scalar(i)
row[circuit + "_Psi_Wb"] = scalar(psi)
row[circuit + "_Psi_im_Wb"] = complex(psi).imag
row[circuit + "_V_V"] = scalar(v)
row[circuit + "_V_im_V"] = complex(v).imag
mo_close()
mi_close()
return row
def check(name, value, limit, passed):
CHECKS.append(
{"name": name, "value": value, "limit": limit, "passed": bool(passed)}
)
def relative(a, b, minimum=1e-15):
return abs(a - b) / max(abs(a), abs(b), minimum)
def finish(plot=None):
result = {
"title": CONFIG["title"],
"records": ROWS,
"checks": CHECKS,
"units": "Геометрия мм; магнитное поле Тл, А/м; энергия Дж; сила Н; момент Н·м; потокосцепление Вб.",
"passed": bool(CHECKS) and all(row["passed"] for row in CHECKS),
}
if plot:
result["plot"] = plot
with open(OUT + "/result.json", "w") as stream:
json.dump(result, stream)
print(CONFIG["id"], result["passed"], len(ROWS), "решений", OUT)
assert result["passed"], "Не пройдена независимая проверка физической модели."
ROWS = []
CHECKS = []
CONFIG = {
"id": "gapped-reactor-energy",
"kind": "reactor",
"title": "Дроссель с зазором: индуктивность, рассеяние и запасённая энергия",
"baseline": 0.5,
"metric": "energy_J",
"sweep": [0.25, 0.5, 1, 2],
"parameter_name": "Зазор, мм",
"metric_label": "Энергия поля, Дж",
"dimensions": "Сердечник 70×60 мм, толщина стержня 10 мм, глубина 40 мм, μr=1000, обмотка 100 витков и 1 А; зазор 0,25–2 мм.",
}
def build(mesh, parameter, current=1):
kind = CONFIG["kind"]
axis = kind in (
"plunger",
"coil-magnet",
"pm-bearing",
"sphere-temperature",
"clutch",
"levitation",
)
frequency = parameter if kind == "levitation" else 0
br = 1.1 * (1 - 0.001 * (parameter - 20)) if kind == "sphere-temperature" else 1.1
begin(
mesh,
"axi" if axis else "planar",
frequency,
40,
150 if kind == "halbach" else 100,
br,
1 if kind == "halbach" else 1.05,
)
coil = "Обмотка без вытеснения тока"
steel = "Сталь с линейной проницаемостью"
magnet = "Постоянный магнит учебный NdFeB"
circuits = []
if kind == "halbach":
band(15, 30, int(parameter), 1, 2, mesh, mode="halbach")
label(0, 0, "Воздух", mesh)
label(70, 0, "Воздух", 6 * mesh)
probe = (0, 0)
elif kind == "gear":
circle(10, 2)
label(0, 0, "Воздух", 4 * mesh, 2)
label(13, 0, steel, mesh, 2)
band(16, 22, 4, 0.94, 2, mesh, parameter)
band(24, 28, 8, 0.5, 4, mesh, mode="steel")
band(30, 36, 12, 0.94, 3, mesh)
circle(42, 3)
label(39, 0, steel, mesh, 3)
label(23, 0, "Воздух", mesh)
label(29, 0, "Воздух", mesh)
label(70, 0, "Воздух", 6 * mesh)
probe = (23, 0)
elif kind == "reluctance-rotor":
points = []
for index in range(360):
theta = index * 2 * math.pi / 360
x, y = 35 * math.cos(theta), 20 * math.sin(theta)
turn = parameter * math.pi / 180
points.append(
(
x * math.cos(turn) - y * math.sin(turn),
x * math.sin(turn) + y * math.cos(turn),
)
)
polygon(points, 2)
label(0, 0, steel, mesh, 2)
circle(42)
label(40, 0, "Воздух", 0.6 * mesh)
mi_addcircprop("Эквивалентная фазная обмотка", current, 1)
for x1, x2, sign in ((-60, -50, 1), (50, 60, -1)):
rectangle(x1, -25, x2, 25)
label(
(x1 + x2) / 2,
0,
coil,
1.5 * mesh,
1,
"Эквивалентная фазная обмотка",
200 * sign,
)
label(80, 0, "Воздух", 5 * mesh)
circuits = ["Эквивалентная фазная обмотка"]
probe = (0, 0)
elif kind in ("reactor", "nonlinear-reactor", "transformer-matrix"):
gap = parameter if kind == "reactor" else 0.5
material = (
"Сталь с учебной кривой насыщения" if kind == "nonlinear-reactor" else steel
)
polygon(
[
(-35, -30),
(35, -30),
(35, -gap / 2),
(25, -gap / 2),
(25, -20),
(-25, -20),
(-25, 20),
(25, 20),
(25, gap / 2),
(35, gap / 2),
(35, 30),
(-35, 30),
],
size=min(0.25, gap / 3) * mesh,
)
label(-30, 0, material, 2 * mesh, 1)
label(0, 0, "Воздух", 5 * mesh)
mi_addcircprop("Первичная обмотка", current, 1)
for x1, x2, sign in ((-43, -37, 1), (-23, -17, -1)):
rectangle(x1, -14, x2, 14)
label(
(x1 + x2) / 2, 0, coil, 1.5 * mesh, 0, "Первичная обмотка", 100 * sign
)
circuits = ["Первичная обмотка"]
if kind == "transformer-matrix":
mi_addcircprop("Вторичная обмотка", parameter, 1)
for x1, x2, sign in ((17, 23, -1), (37, 43, 1)):
rectangle(x1, -14, x2, 14)
label(
(x1 + x2) / 2,
0,
coil,
1.5 * mesh,
0,
"Вторичная обмотка",
50 * sign,
)
circuits.append("Вторичная обмотка")
probe = (30, 0)
elif kind == "plunger":
mi_addcircprop("Катушка привода", current, 1)
rectangle(10, -10, 16, 10)
label(13, 0, coil, mesh, 1, "Катушка привода", 200)
rectangle(0, parameter - 8, 8, parameter + 8, 2)
label(4, parameter, steel, mesh, 2)
label(35, 0, "Воздух", 5 * mesh)
circuits = ["Катушка привода"]
probe = (4, parameter)
elif kind == "coil-magnet":
mi_addcircprop("Катушка привода", current, 1)
rectangle(10, -6, 14, 6)
label(12, 0, coil, mesh, 1, "Катушка привода", 100)
rectangle(0, parameter - 3, 6, parameter + 3, 2)
label(3, parameter, magnet, 0.2 * mesh, 2, angle=90)
rectangle(0, parameter - 7, 9, parameter + 7)
label(8, parameter, "Воздух", 0.25 * mesh)
label(35, 0, "Воздух", 5 * mesh)
circuits = ["Катушка привода"]
probe = (3, parameter)
elif kind == "pm-bearing":
for center, angle, group in ((0, 90, 1), (parameter + 4, 270, 2)):
rectangle(5, center - 2, 12, center + 2, group)
label(8, center, magnet, 0.3 * mesh, group, angle=angle)
rectangle(0, -5, 16, parameter + 9)
label(14, 0, "Воздух", 0.35 * mesh)
label(40, 0, "Воздух", 5 * mesh)
probe = (8, 2 + parameter / 2)
elif kind == "sphere-temperature":
mi_addnode(0, -5)
mi_addnode(0, 5)
mi_addarc(0, -5, 0, 5, 180, max(0.25, mesh))
line((0, -5), (0, 5), 2)
label(2, 0, magnet, mesh / 5, 2, angle=90)
mi_addnode(0, -15)
mi_addnode(0, 15)
mi_addarc(0, -15, 0, 15, 180, max(0.25, mesh))
label(10, 0, "Воздух", 0.4 * mesh)
label(30, 0, "Воздух", 5 * mesh)
probe = (1, 0)
elif kind == "clutch":
polygon(
[
(0, -15),
(30, -15),
(30, 0),
(25, 0),
(25, -10),
(10, -10),
(10, 0),
(0, 0),
],
1,
size=mesh,
)
label(5, -5, steel, 2 * mesh, 1)
rectangle(11, -9, 24, -1)
mi_addcircprop("Катушка муфты", current, 1)
label(17, -5, coil, mesh, 1, "Катушка муфты", 200)
rectangle(
0, parameter, 30, parameter + 5, 2, size=min(0.3, parameter / 3) * mesh
)
label(15, parameter + 2, steel, mesh, 2)
label(40, 5, "Воздух", 5 * mesh)
circuits = ["Катушка муфты"]
probe = (5, parameter / 2)
elif kind == "levitation":
mi_addcircprop("Катушка переменного тока", current, 1)
rectangle(8, -10, 20, -2)
label(14, -6, coil, mesh, 1, "Катушка переменного тока", 100)
rectangle(0, 3, 25, 6, 2, size=mesh)
label(12, 4.5, "Медь 58 МСм на метр", mesh / 2, 2)
label(45, 0, "Воздух", 5 * mesh)
circuits = ["Катушка переменного тока"]
probe = (12, 4.5)
elif kind == "linear-motor":
for index in range(8):
x = -40 + index * 10
rectangle(x, -10, x + 9, -4, 1)
label(x + 4, -7, magnet, mesh, 1, angle=90 + 180 * (index % 2))
mi_addcircprop("Подвижная катушка", current, 1)
for x1, x2, sign in (
(parameter - 11, parameter - 6, 1),
(parameter - 1, parameter + 4, -1),
):
rectangle(x1, 1, x2, 5, 2)
label((x1 + x2) / 2, 3, coil, 0.2 * mesh, 2, "Подвижная катушка", 50 * sign)
rectangle(parameter - 13, 0, parameter + 6, 8)
label(parameter - 12, 0.5, "Воздух", 0.25 * mesh)
label(60, 20, "Воздух", 5 * mesh)
circuits = ["Подвижная катушка"]
probe = (parameter, 0)
return probe, circuits
kind = CONFIG["kind"]
baseline = CONFIG["baseline"]
metric = CONFIG["metric"]
mesh_rows = []
meshes = CONFIG.get("meshes", (1.2, 0.8, 0.5))
fine_h = meshes[-1]
for index, mesh in enumerate(meshes):
parameter = 0 if kind == "transformer-matrix" else baseline
probe, circuits = build(mesh, parameter, CONFIG.get("current", 1))
row = solve(
"сетка-%d" % (index + 1),
0
if kind
in (
"reactor",
"nonlinear-reactor",
"transformer-matrix",
"halbach",
"sphere-temperature",
)
else 2,
probe,
circuits,
)
row["mesh_mm"] = mesh
row["parameter"] = (
CONFIG.get("current", 1) if kind == "nonlinear-reactor" else parameter
)
mesh_rows.append(row)
ROWS.append(row)
fine = mesh_rows[-1]
error = relative(mesh_rows[-2][metric], fine[metric])
check(
"Стабилизация наблюдаемой величины на двух мелких сетках",
error,
0.025,
error < 0.025,
)
check(
"Действительное уточнение сетки",
fine["elements"] / mesh_rows[0]["elements"],
1.2,
fine["elements"] > 1.2 * mesh_rows[0]["elements"],
)
if kind == "halbach":
count = int(baseline)
exact = 1.1 * math.log(2) * math.sin(2 * math.pi / count) / (2 * math.pi / count)
err = relative(abs(fine["Bx_T"]), exact)
check(
"Поле сегментированного цилиндра и аналитическая гармоника",
err,
0.025,
err < 0.025,
)
elif kind == "sphere-temperature":
exact = 2 * 1.1 * (1 - 0.001 * (baseline - 20)) / (1.05 + 2)
err = relative(fine["By_T"], exact)
check("Внутреннее поле равномерно намагниченного шара", err, 0.005, err < 0.005)
elif kind in (
"reactor",
"nonlinear-reactor",
"transformer-matrix",
"plunger",
"clutch",
"reluctance-rotor",
):
name = circuits[0]
expected = 0.5 * CONFIG.get("current", 1) * fine[name + "_Psi_Wb"]
if kind != "nonlinear-reactor":
err = relative(expected, fine["energy_J"])
check("Энергия поля и работа токового источника", err, 0.002, err < 0.002)
else:
check(
"Положительная энергия при насыщении",
fine["energy_J"],
0,
fine["energy_J"] > 0,
)
for value in CONFIG.get("sweep", []):
current = CONFIG.get("current", 1)
parameter = value
if kind == "nonlinear-reactor":
current = value
parameter = 0.5
probe, circuits = build(fine_h, parameter, current)
row = solve(
"параметр-%02d" % len(ROWS),
0
if kind
in (
"reactor",
"nonlinear-reactor",
"transformer-matrix",
"halbach",
"sphere-temperature",
)
else 2,
probe,
circuits,
)
row["parameter"] = value
row["mesh_mm"] = fine_h
if kind == "nonlinear-reactor":
row["static_inductance_H"] = row["Первичная обмотка_Psi_Wb"] / current
ROWS.append(row)
if kind == "reactor":
rows = ROWS[3:]
check(
"Индуктивность уменьшается при увеличении зазора",
rows[-1]["energy_J"] / rows[0]["energy_J"],
1,
rows[-1]["energy_J"] < rows[0]["energy_J"],
)
elif kind == "nonlinear-reactor":
rows = ROWS[3:]
check(
"Насыщение снижает статическую индуктивность",
rows[-1]["static_inductance_H"] / rows[0]["static_inductance_H"],
1,
rows[-1]["static_inductance_H"] < rows[0]["static_inductance_H"],
)
elif kind == "transformer-matrix":
probe, circuits = build(fine_h, 1, 0)
secondary = solve("вторичное-возбуждение", 0, probe, circuits)
ROWS.append(secondary)
m12 = secondary["Первичная обмотка_Psi_Wb"]
m21 = fine["Вторичная обмотка_Psi_Wb"] / CONFIG.get("current", 1)
err = relative(m12, m21)
check("Взаимность индуктивностей", err, 0.005, err < 0.005)
l1 = fine["Первичная обмотка_Psi_Wb"] / CONFIG.get("current", 1)
l2 = secondary["Вторичная обмотка_Psi_Wb"]
k = abs(m12) / math.sqrt(l1 * l2)
check("Пассивность матрицы индуктивности", k, 1, k < 1 and k > 0)
elif kind in ("plunger", "clutch"):
controls = []
for value in (baseline - 0.2, baseline + 0.2):
probe, circuits = build(fine_h, value, CONFIG.get("current", 1))
r = solve("виртуальное-смещение-%d" % len(ROWS), 2, probe, circuits)
ROWS.append(r)
controls.append(r)
derivative = (controls[1]["coenergy_J"] - controls[0]["coenergy_J"]) / 0.0004
err = relative(derivative, fine["Fy_N"])
check("Сила по коэнергии и взвешенному тензору напряжений", err, 0.06, err < 0.06)
elif kind == "reluctance-rotor":
controls = []
for value in (baseline - 0.5, baseline + 0.5):
probe, circuits = build(fine_h, value, CONFIG.get("current", 1))
r = solve("виртуальный-поворот-%d" % len(ROWS), 2, probe, circuits)
ROWS.append(r)
controls.append(r)
derivative = (controls[1]["coenergy_J"] - controls[0]["coenergy_J"]) / (
math.pi / 180
)
err = relative(derivative, fine["torque_Nm"])
check("Момент и угловая производная коэнергии", err, 0.03, err < 0.03)
elif kind in ("coil-magnet", "linear-motor"):
probe, circuits = build(fine_h, baseline, -CONFIG.get("current", 1))
reversed_row = solve("обратный-ток", 2, probe, circuits)
ROWS.append(reversed_row)
field = "Fx_N" if kind == "linear-motor" else "Fy_N"
err = relative(fine[field], -reversed_row[field])
check("Смена направления силы при обращении тока", err, 0.01, err < 0.01)
if kind == "linear-motor":
err = relative(fine["Fx_N"], fine["lorentz_x_N"])
check("Сила Лоренца и взвешенный тензор напряжений", err, 0.04, err < 0.04)
elif kind == "pm-bearing":
rows = ROWS[3:]
check(
"Репульсивная осевая сила",
min(r["Fy_N"] for r in rows),
0,
all(r["Fy_N"] > 0 for r in rows),
)
check(
"Спад силы при увеличении зазора",
rows[-1]["Fy_N"] / rows[0]["Fy_N"],
1,
rows[-1]["Fy_N"] < rows[0]["Fy_N"],
)
elif kind == "levitation":
rows = ROWS[3:]
check(
"Отсутствие вихревых потерь при постоянном токе",
rows[0]["loss_W"],
1e-8,
abs(rows[0]["loss_W"]) < 1e-8,
)
check(
"Положительные вихревые потери",
min(r["loss_W"] for r in rows[1:]),
0,
all(r["loss_W"] > 0 for r in rows[1:]),
)
check("Сила отталкивания проводящего диска", fine["Fy_N"], 0, fine["Fy_N"] > 0)
elif kind == "gear":
balance = abs(fine["T2_Nm"] + fine["T3_Nm"] + fine["T4_Nm"]) / max(
abs(fine["T2_Nm"]), abs(fine["T3_Nm"])
)
check("Баланс моментов трёх магнитных тел", balance, 0.05, balance < 0.05)
a = 0j
b = 0j
for row in ROWS[3:]:
phase = 2 * row["parameter"] * math.pi / 180
factor = complex(math.cos(phase), -math.sin(phase))
a += row["T2_Nm"] * factor
b += row["T3_Nm"] * factor
ratio = abs(a / b)
check(
"Отношение основных гармоник моментов после отделения зубцовых составляющих",
ratio,
1 / 3,
relative(ratio, 1 / 3) < 0.05,
)
finish(
{
"x": "parameter",
"y": metric,
"x_label": CONFIG.get("parameter_name", "Параметр"),
"y_label": CONFIG.get("metric_label", metric),
}
)
Картина рассчитанного поля
Окна FEMM 5
Снимки интерфейса FEMM показывают геометрию, настройки и работу с результатами в FEMM 5. Откройте изображение, чтобы рассмотреть подписи и значения в полном размере.
Результаты и численные проверки
Сохранено решений FEMM: 3. Выполнено проверок: 4. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.
| Ток, А: Первичная обмотка | Напряжённость Hy, А/м | Напряжение, В: Первичная обмотка | Потокосцепление, Вб: Первичная обмотка | Индукция By, Тл | Мнимая часть напряжения, В: Первичная обмотка | Состояние | Масштаб сетки, мм | Мнимая часть потокосцепления, Вб: Первичная обмотка | Параметр серии | Коэнергия, Дж | Элементов | Индукция Bx, Тл | Потери, Вт | Напряжённость Hx, А/м | Энергия, Дж |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | -135100 | 0 | 0,008574 | -0,1697 | 0 | сетка-1 | 1,2 | 0 | 0,5 | 0,004287 | 38240 | -224,3 × 10⁻⁹ | 0 | -0,1785 | 0,004287 |
| 1 | -135100 | 0 | 0,008582 | -0,1697 | 0 | сетка-2 | 0,8 | 0 | 0,5 | 0,004291 | 57304 | -76,1 × 10⁻⁹ | 0 | -0,06056 | 0,004291 |
| 1 | -135000 | 0 | 0,008597 | -0,1696 | 0 | сетка-3 | 0,5 | 0 | 0,5 | 0,004299 | 90205 | -57,22 × 10⁻⁹ | 0 | -0,04554 | 0,004299 |
| 1 | -210900 | 0 | 0,01224 | -0,265 | 0 | параметр-03 | 0,5 | 0 | 0,25 | 0,006118 | 171776 | -2,514 × 10⁻⁹ | 0 | -0,002 | 0,006118 |
| 1 | -135000 | 0 | 0,008597 | -0,1696 | 0 | параметр-04 | 0,5 | 0 | 0,5 | 0,004299 | 90205 | -57,22 × 10⁻⁹ | 0 | -0,04554 | 0,004299 |
| 1 | -78770 | 0 | 0,00582 | -0,09898 | 0 | параметр-05 | 0,5 | 0 | 1 | 0,00291 | 65896 | 3,501 × 10⁻⁹ | 0 | 0,002786 | 0,00291 |
| 1 | -43130 | 0 | 0,003972 | -0,0542 | 0 | параметр-06 | 0,5 | 0 | 2 | 0,001986 | 65659 | 75,12 × 10⁻⁹ | 0 | 0,05978 | 0,001986 |
| Критерий | Получено | Допуск или ожидаемое значение | Итог |
|---|---|---|---|
| Стабилизация наблюдаемой величины на двух мелких сетках | 0,001797 | 0,025 | Выполнен |
| Действительное уточнение сетки | 2,359 | 1,2 | Выполнен |
| Энергия поля и работа токового источника | 2,252 × 10⁻¹² | 0,002 | Выполнен |
| Индуктивность уменьшается при увеличении зазора | 0,3246 | 1 | Выполнен |
Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.
Модели, решения и программа
Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.
- Файл результатов: README.md4418 байт
- Программа встроенного Python: reproduce.py26779 байт
- Файл результатов: verification.json1037 байт
- Магнитное решение: сетка-1.ans2313057 байт
- Магнитная модель: сетка-1.fem4544 байт
- Магнитное решение: сетка-2.ans3506710 байт
- Магнитная модель: сетка-2.fem4544 байт
- Магнитное решение: сетка-3.ans5583087 байт
- Магнитная модель: сетка-3.fem4513 байт
Границы применимости
- Сталь линейная и без потерь. Величина, полученная при постоянном токе, не является автоматически индуктивностью реального дросселя на рабочей частоте.
- Это результат численного исследования выбранной модели; физический макет и заявленное изготовителем изделие не проверялись.