简介:这篇发表于《微电机》2011年第11期的技术论文,面向从事新能源汽车电驱动系统建模与仿真的工程师及高校研究者,聚焦纯电动汽车多领域耦合系统的统一建模难题。文中以永磁同步电机为对象,介绍Modelica多领域建模语言与Dymola仿真平台的基本特点,给出永磁同步电机d-q轴数学模型、电压与磁链方程及电磁转矩、机械运动方程的推导思路,并说明如何结合电机及驱动系统台架测试数据校正模型参数,再通过实验纯电动汽车整车仿真验证模型精度,仿真结果与台架测试接近,同时讨论了现有数值模型在整车性能评价中的不足与改进方向。包内为单个PDF文件,约317KB,篇幅紧凑,便于快速检索与引用,适合电机控制、整车仿真方向的读者作为参考文献。目前已有138人学习。
1. 从电机台架上那个「一改参数就炸」的时刻说起
做纯电动汽车电驱仿真的人大概都碰过这类事:把一台 150 kW 永磁同步电机的参数填进某个黑盒电机模块,稳态波形跑得挺好,一旦把母线电压、逆变器和整车质量放进同一张图,转速环开始抖,转矩冒高频毛刺,求解器直接报步长过小停下。问题通常不在电机本身,而在「谁给谁供电压、谁又反过来决定电流」这层耦合被拆到了不同工具里。Modelica 把电机、驱动电路、减速器和整车纵向动力学都写成方程,用连接器自动配平,符号系统统一处理耦合。这篇讲的是怎么从 dq 轴方程一路搭到工况仿真:库组件怎么选、参数怎么填、仿真发散了怎么定位、参数辨识怎么接。
2. Modelica 侧的车用 PMSM 建模:从 dq 方程到库组件参数
2.1 车用 PMSM 的四个方程和那条功率公式
先立方程,不然后面填参数只能靠猜。在转子磁场定向的 dq 坐标系下,车用永磁同步电机的定子电压方程写成:
ud = Rs·id + Ld·did/dt − ωe·Lq·iq uq = Rs·iq + Lq·diq/dt + ωe·(Ld·id + ψf)其中ωe = p·ωm是电角速度,p是极对数,ωm是机械角速度。电磁转矩按
Te = 1.5·p·[ψf·iq + (Ld − Lq)·id·iq]展开,前一项是永磁转矩,后一项是磁阻转矩。内置式转子Lq > Ld,磁阻转矩为正,所以低速大转矩工况常把id往负方向拉;表贴式转子Ld ≈ Lq,磁阻项近似为零,控制上就退化成纯iq给转矩。
机械侧是
J·dωm/dt = Te − TL − B·ωm再看那条常被搜索的「永磁同步电机的功率公式」。电磁功率用 dq 量表示是
Pe = 1.5·(ud·id + uq·iq)稳态下忽略铜耗,它约等于Te·ωm。这两个式子后面会拿来做能量守恒校验——Modelica 的好处正在这里,你可以在模型里同时定义Pe和Te·ωm,让求解器自己暴露出两者对不上的时刻,而不是靠人去推。
需要区分一下:这套 dq 方程适用于正弦波驱动的 PMSM。如果手头模型是六步换相那种方波驱动,反电动势不是正弦,dq 变换的前提就不成立,得换回 abc 三相时域方程来描述。同样一套 dq 模型,做并网仿真时边界条件是电网电压定向和低电压穿越,做车用时边界条件换成了母线电压随 SOC 跌落和宽调速范围,方程一样,外边界完全不同。
2.2 Modelica.Electrical.Machines 里的组件选型和参数表
Modelica 标准库(MSL)里直接有现成的永磁同步电机组件,路径是Modelica.Electrical.Machines.BasicMachines.SynchronousMachines.SM_PermanentMagnet,配套还有Modelica.Electrical.Machines.Utilities.TerminalBox负责星形/三角形接线,以及Modelica.Electrical.PowerConverters里的两电平逆变器组件。选型逻辑很直接:拿库里的 dq 模型当被控对象,把控制算法自己写,这样中间每一步电压、电流、角度都可见。
| 参数 | 含义 | 车用 150 kW 级典型取值 | 填错的后果 |
|---|---|---|---|
p | 极对数 | 4 | 电角速度整体错倍,转矩直接差一个系数 |
fsNominal | 额定电频率 | 300 Hz(对应基速 4500 r/min) | 影响库内部标幺化基准,影响损耗计算 |
Rs | 定子相电阻 | 0.012 Ω @20 °C | 低速铜耗偏小、电流环增益失配 |
Ld | d 轴电感 | 0.25 mH | id响应偏慢,弱磁区算不准 |
Lq | q 轴电感 | 0.55 mH | 磁阻转矩错,峰值转矩对不上台架 |
ψf | 永磁磁链 | 0.09 Wb | 反电动势整体偏差,电压利用率算错 |
Js | 转动惯量 | 0.05 kg·m² | 转速环阶跃响应快慢失真 |
有一点要提醒:不同 MSL 版本对磁链参数的命名可能是phi_f、Phi_f或归到别的组件属性里,useDamperCage这类开关也存在版本差异。
提示:填参数前先打开本地库的组件文档,按你安装的那一版的接口名来写,不要照抄别人博客里的字段名。
再验一下这套参数的合理性。极对数 4、基速 4500 r/min 对应电频率 300 Hz,电角速度ωe = 2π×300 ≈ 1885 rad/s,反电动势幅值ωe·ψf ≈ 169.6 V(相电压峰值)。母线 350 V 左右时基速点还有调节余量,说明这套数不是瞎凑的。峰值转矩 300 N·m 需要iq ≈ 300 / (1.5×4×0.09) ≈ 555 A,属于车用电机正常电流量级。
2.3 整车侧:把车轮阻力折算到电机轴
电机的负载不是恒定的,它来自整车。纵向动力学写成
m·dv/dt = Ftraction − Froll − Faero − Fgrade Froll = m·g·f·cosθ, Faero = 0.5·ρ·Cd·A·v², Fgrade = m·g·sinθ牵引力由电机转矩经减速比i和车轮半径r折算:Ftraction = Te·i·η / r。反过来,折算到电机轴上的负载转矩是TL = (Froll + Faero + Fgrade)·r / (i·η),转子侧惯量按Jeq = Jmotor + m·r² / i²折算。
这一步在 Modelica 里的意义是:你不需要手算折算后的TL曲线再喂给电机,而是把整车方程和电机方程写在同一个模型里,m、Cd、A、坡度这些量改一个,整条链路自动跟着变。这也是从「电机仿真」跨到「纯电动汽车仿真」的分界线。
3. 动手:在 Modelica 里搭一个能跑的 PMSM 驱动仿真
3.1 环境准备与最小可运行模型
环境准备很朴素:Windows 下装 OpenModelica 官方安装包,Linux 下用系统包管理器装,装完确认omc在 PATH 里,omc --version能打出东西就行。Dymola 属于商业工具,命令行风格不同但组件库是同一套 MSL。
最小可运行模型只保留电机本体、端子盒、恒转矩负载三块:
model PMSM_Drive_Minimal "150 kW 车用 PMSM 最小驱动仿真" // ---- 电机本体:MSL 中的永磁同步电机组件 ---- Modelica.Electrical.Machines.BasicMachines.SynchronousMachines.SM_PermanentMagnet motor( p = 4, // 极对数 fsNominal = 300, // 额定电频率 Hz,对应基速 4500 r/min Rs = 0.012, // 20 °C 下定子相电阻 Ω Ld = 0.25e-3, // d 轴电感 H Lq = 0.55e-3, // q 轴电感 H Js = 0.05); // 转动惯量 kg·m2 // ---- 三相端子盒:星形接法 ---- Modelica.Electrical.Machines.Utilities.TerminalBox terminalBox(terminalConnection = "Y"); // ---- 负载侧:恒转矩 + 惯量,对应整车折算后的阻力 ---- Modelica.Mechanics.Rotational.Sources.Torque loadTorque; Modelica.Blocks.Sources.Constant tLoad(k = 50); equation connect(terminalBox.plug_sn, motor.plug_sn); connect(terminalBox.plug_sp, motor.plug_sp); connect(motor.flange, loadTorque.flange); connect(tLoad.y, loadTorque.tau); end PMSM_Drive_Minimal;逻辑说明:motor是被控对象,内部已经用 dq 方程实现了SM_PermanentMagnet;terminalBox把三个绕组端子聚成一组可对接逆变器的接口;loadTorque提供一个恒定的反作用转矩,tLoad的k就是从整车式子里算出来的TL初值。连接器plug_sn/plug_sp/flange都是 MSL 约定的标准接口,只要类型一致,connect就能自动写出守恒方程。
这个模型还没接逆变器和控制,属于开环空载,跑通它只是为了确认参数能编译、初值能给。
3.2 d/q 轴电流环:PI 增益怎么算、解耦怎么加
控制部分自己写一个模型,输入是idRef、iqRef、idMea、iqMea、电角速度,输出是ud、uq:
model FOC_CurrentLoop "d/q 轴电流环,含交叉耦合前馈" parameter Real Ld = 0.25e-3; parameter Real Lq = 0.55e-3; parameter Real Rs = 0.012; parameter Real psi_f = 0.09; parameter Real wBW = 2*Modelica.Constants.pi*500 "电流环带宽 rad/s"; Modelica.Blocks.Interfaces.RealInput idRef; Modelica.Blocks.Interfaces.RealInput iqRef; Modelica.Blocks.Interfaces.RealInput idMea; Modelica.Blocks.Interfaces.RealInput iqMea; Modelica.Blocks.Interfaces.RealInput wElec "电角速度 rad/s"; Modelica.Blocks.Interfaces.RealOutput ud; Modelica.Blocks.Interfaces.RealOutput uq; Real eiD "d 轴积分状态"; Real eiQ "q 轴积分状态"; equation der(eiD) = idRef - idMea; der(eiQ) = iqRef - iqMea; // 前馈项把 -we*Lq*iq 和 +we*(Ld*id + psi_f) 补掉 ud = Ld*wBW*(idRef - idMea) + Rs*wBW*eiD - wElec*Lq*iqMea; uq = Lq*wBW*(iqRef - iqMea) + Rs*wBW*eiQ + wElec*(Ld*idMea + psi_f); end FOC_CurrentLoop;增益不是拍脑袋来的。按内模整定,比例增益取Kp = L·ωbw,积分增益取Ki = Rs·ωbw,其中ωbw是电流环带宽。取带宽 500 Hz,得到Kp_d ≈ 0.785、Ki_d ≈ 37.7(对应 d 轴),Kp_q ≈ 1.73、Ki_q ≈ 37.7(对应 q 轴)。因为Lq比Ld大,q 轴的比例增益必须比 d 轴大,两根轴用同一组 PI 参数是新手最常见的错。
后两项是交叉耦合前馈。电机在高速时ωe·Lq·iq和ωe·Ld·id这两项会很大,纯靠积分去补会拖慢电流响应,把耦合项直接前馈抵掉,电流环就退化成一个简单的一阶系统。注意前馈里的ψf项,它对应反电动势,如果漏掉,q 轴稳态误差会随转速线性增大。
另外要提醒的是,这里的idMea、iqMea需要先通过 Park 变换从三相电流得到,Park 变换用的角度必须是p·θm而不是θm。转子转角直接乘极对数,这是另一个高频错误点。
3.3 工况注入与求解器参数配置
工况注入可以用 MSL 的CombiTimeTable组件,把 NEDC 或 WLTC 的目标车速按时间表填进table,配合 2.3 节的整车方程反算出目标转矩,再经转速环转成iqRef。如果只是想验证控制逻辑,直接给iqRef一个阶跃或斜坡也可以。
求解器配置才是真正影响成败的部分:
# OpenModelica:先编译成可执行文件 omc PMSM_Drive_Minimal.mo # 再按需覆盖运行时参数 ./PMSM_Drive_Minimal -s=dassl -tolerance=1e-6 -stopTime=10 -stepSize=1e-4 -r=pmsm.mat参数含义:-s选求解器,用dassl处理带事件的刚性系统;-tolerance是局部误差容差,电机模型建议从1e-6起试;-stopTime是终止时间;-stepSize是输出步长,不是积分步长;-r是结果文件。OpenModelica 不同版本的命令行开关略有出入,下手前先omc --help确认。
如果模型里带 10 kHz 的 PWM 开关,仿真负债会陡增,这时候可以考虑两种折中:一是用平均值模型替代开关模型,把逆变器输出直接写成等效相电压,牺牲开关纹波换速度;二是保留开关模型但限制最大步长到开关周期的十分之一以内。前者适合做整车级能量核算,后者适合看电流纹波和开关损耗。
4. 仿真发散和精度不对:Modelica 电机仿真的排查顺序
4.1 五类典型症状和对因表
仿真发散在 Modelica 里的表现形式很集中,按症状查比乱改参数快得多:
| 症状 | 常见原因 | 处理方向 |
|---|---|---|
| t=0 就报奇异或无解 | 初始化方程不完整,状态初值互相冲突 | 给initial equation补方程,或固定关键状态初值 |
| 波形出现高频锯齿并快速发散 | PWM 事件密集 + 容差过松 | 收紧tolerance,限制最大步长,或换平均值模型 |
| 转子位置与电流相位明显错位 | Park 变换角度用了机械角 | 改回p·θm,检查角度基准 |
| 转速缓慢漂移不收敛 | 负载转矩或摩擦系数符号方向错 | 核对Torque的正方向约定和B的符号 |
| 波形看着正常但能量不守恒 | 损耗项被忽略或重复计入 | 补铜耗、铁耗定义,核对Pe与Te·ωm |
按这个顺序往下查,比逐行读代码效率高。第一类和第四类通常十分钟能解决,第三类如果模型是自己搭的,八成还会连带发现坐标变换顺序写反了。
4.2 初始化与代数环:从高阶 DAE 降指标的实操
Modelica 模型本质是微分代数方程,求解器启动时要先把代数变量解出来,指标过高或者初值不自洽,求解器就报奇异。车用 PMSM 模型里最容易出问题的是转子静止起动瞬间——转速为零,但反电动势项和耦合项都以转速为系数,形式上退化了。
一个实用的初始化写法:
initial equation // 静止起动:转速初值 0,转子机械角初值 0 // 变量名以本地 MSL 版本中 SM_PermanentMagnet 的接口为准 der(motor.wMechanical) = 0; // 起动瞬间角加速度为 0 motor.wMechanical = 0;逻辑是:先把机械侧锁住,再让电气侧从零电流开始,避免求解器去解一个「转速为零但转矩非零」的矛盾初值。
代数环的处理原则是「能解析化就解析化」。比如电流环里的ud依赖iq,而iq又由ud经电机方程算出来,如果这两者中间没有积分环节隔开,就构成代数环。办法是把电流环的积分器保留在反馈通道里,让ud由积分状态和输入直接写出,而不是反过来解方程。上面FOC_CurrentLoop里der(eiD)、der(eiQ)先出现、ud、uq后写,就是为了保证这个顺序。
注意:改完初始化方程要重新跑一遍零时刻附近的波形,而不要只看稳态结果。初始化的错往往在 t<0.01 s 就暴露完了。
4.3 用仿真数据反推 Rs、Ld、Lq、ψf
参数辨识和仿真是互相喂养的关系:仿真给辨识提供激励充分的数据,辨识给仿真校准模型参数。稳态下 dq 方程里的微分项为零,退化成
ud = Rs·id − ωe·Lq·iq uq = Rs·iq + ωe·(Ld·id + ψf)把Rs、Lq、Ld、ψf当成四个未知量,每个采样点贡献两个方程,做线性最小二乘就能解:
import numpy as np import pandas as pd # 1. 读 Modelica 导出的 csv(列名按实际导出结果调整) df = pd.read_csv("pmsm_log.csv") id_, iq_ = df["id"].to_numpy(), df["iq"].to_numpy() ud_, uq_ = df["ud"].to_numpy(), df["uq"].to_numpy() we_ = df["we"].to_numpy() # 电角速度 rad/s # 2. 组装 A·theta = b,theta = [Rs, Lq, Ld, psi_f] n = len(df) A = np.zeros((2 * n, 4)) b = np.zeros(2 * n) A[:n, 0] = id_ # ud 方程里的 Rs 项 A[:n, 1] = -we_ * iq_ # ud 方程里的 Lq 项 b[:n] = ud_ A[n:, 0] = iq_ # uq 方程里的 Rs 项 A[n:, 2] = we_ * id_ # uq 方程里的 Ld 项 A[n:, 3] = we_ # uq 方程里的 psi_f 项 b[n:] = uq_ # 3. 最小二乘求解 theta, *_ = np.linalg.lstsq(A, b, rcond=None) Rs_hat, Lq_hat, Ld_hat, psi_hat = theta print(f"Rs={Rs_hat:.5f} Ω, Lq={Lq_hat*1e3:.4f} mH, " f"Ld={Ld_hat*1e3:.4f} mH, psi_f={psi_hat:.5f} Wb")几个必须注意的点。第一,这种线性回归只在稳态成立,所以要先从仿真结果里截取转速和电流都稳定的时间段,阶跃瞬间的数据必须剔除,否则did/dt和diq/dt被当成零会带来系统性偏差。第二,id、iq和we之间如果存在共线性(比如一直在同一个转速下跑),Ld和ψf会分不开,激励里必须包含多个转速点。第三,想要动态工况下的参数,得换成递推最小二乘或扩展卡尔曼滤波,那套代码要维护协方差矩阵,复杂度上一个台阶。
5. 进阶:外特性扫描与能量守恒校验
5.1 转矩-转速外特性批量扫描
单个工况点跑通之后,下一步是把工作点铺开。裸手改参数跑几十次不现实,用脚本驱动仿真程序批量覆盖更实际:
import subprocess # 遍历转速点,每点锁定转速、扫电流幅值,找能达到的最大转矩 for n_rpm in [1000, 3000, 4500, 6000, 9000, 12000]: we = 4 * n_rpm * 2 * 3.14159265 / 60 # 电角速度 for id_frac in [-0.3, -0.15, 0.0, 0.15, 0.3]: # 弱磁电流分配比例 tag = f"n{n_rpm}_id{int(id_frac*100)}" subprocess.run([ "./PMSM_Drive_Minimal", f"-override=control.wBW=3141.59,control.idFrac={id_frac}", f"-stopTime=2", "-tolerance=1e-6", f"-r=out_{tag}.mat" ], check=True)说明:-override用来覆盖模型里的参数,路径写成「组件名.参数名」的形式;内层循环扫id分配比例,就是在试不同的弱磁程度。跑完把每个转速点下的最大转矩画出来,得到的就是外特性曲线。这条路比在图形界面里手动点一百次靠谱,也方便进版本管理。
判断曲线是否合理,可以看三个特征点:基速点附近转矩应保持恒定;进入弱磁区后转矩随转速近似按1/ωm衰减;最高转速点的电压利用率接近母线电压上限。
5.2 能量守恒校验:让模型自己证明自己
外特性扫完之后,最有用的一步校验是能量守恒。在模型里把三个功率量都显式定义出来:
pElec = 1.5*(ud*id + uq*iq) // 电磁功率 pMech = Te * wMechanical // 机械功率 pLoss = 1.5*Rs*(id^2 + iq^2) // 定子铜耗然后在结果里核对pElec ≈ pMech + pLoss。稳态下这条式子的残差应该落在数值容差量级,如果残差是百分之几甚至更大,一般说明三件事之一:转动惯量或摩擦系数填错导致机械侧吸收了能量;损耗项漏掉了铁耗,高速时这个缺口会随转速放大;Te的计算里用了机械角速度而不是电角速度去算功率。
| 校验项 | 期望关系 | 残差偏大时的排查 |
|---|---|---|
| 电气-机械功率平衡 | pElec ≈ pMech + pLoss | 先查Te定义,再查摩擦和惯量 |
| 转矩-电流关系 | Te ≈ 1.5·p·(ψf·iq + (Ld−Lq)·id·iq) | 查Ld、Lq是否填反,id符号是否搞错 |
| 反电动势-转速关系 | E ≈ ωe·ψf | 查p和ψf,这两个参数最容易抄错一位 |
一个小技巧:把这些校验关系直接写成模型里的方程,而不是事后用 Python 算。Modelica 的求解器会在时间推进过程中持续检查这些等式,一旦哪一步不满足就报残差,比人翻波形快得多。这也是把 Modelica 用来做车用电驱仿真的核心价值——模型里的每一条物理关系都可以被显式地验证,而不是藏在一个黑盒模块里。
本文还有配套的精品资源,点击获取