1. 先想清楚PMSM仿真的整体思路与方案选型1.1 我们在仿什么解决什么问题先说结论用MATLAB/Simulink搭PMSM仿真不是为了“跑个波形截图交差”而是为了在写硬件代码之前先把控制算法看清楚。常规的开发流程是需求分析 → 仿真验证 → 快速原型 → 代码生成 → 台架测试。仿真处于第二步但这步做扎实了后面踩坑的概率会小很多。我之前带过几个刚入门做电机控制的同学上来就问我为什么不能直接拿开发板测当然可以但有几个问题很难绕开硬件出了异常不好定位是算法写错还是硬件损坏PI参数需要反复烧录调试费时间费板子有些极端工况堵转、急加速加载在台架上试一次的成本很高甚至可能炸管子。仿真模型可以帮助你把控制逻辑、参数整定思路理顺让你带着明确预期去操作硬件。这篇文章要做的就是一件事从零搭建一个完整的PMSM电流环速度环FOC仿真模型。我会给出能从MATLAB脚本里直接复制运行的初始化代码、坐标变换函数和SVPWM实现并把每一步的为什么讲清楚。适合正在做电机控制课设、毕业设计或者刚刚接手FOC项目、想在 Simulink 里先把算法跑通的同学参考。1.2 为什么选用MATLAB/Simulink来做这件事现在的电机仿真工具其实不少有专门做电磁场有限元的有做系统级机电分析的也有纯手写C代码跑的。PMSM控制仿真这个场景我个人还是推荐MATLAB/Simulink理由有三个。第一Simulink对控制环路的结构化表达非常直观。电流环、速度环、坐标变换、SVPWM用模块连起来后整个逻辑一目了然方便自己检查也方便给别人讲。第二MATLAB的脚本环境可以同时做参数计算、数据后处理和可视化不需要额外开工具。第三也是最实际的一点很多做嵌入式电机控制的团队算法验证环节已经习惯用Simulink甚至后续会用Embedded Coder直接生成C代码早一点在Simulink上熟悉这套流程对后续工程化非常有帮助。当然它也有缺点。比如纯Simulink仿真里对逆变器死区、IGBT管压降这些非理想因素建模比较浅和真实硬件的差距需要通过硬件在环测试来补。但这不影响我们用仿真做算法验证的价值关键是我们要清楚模型的边界在哪。1.3 仿真系统的整体框架一个完整的PMSM FOC仿真模型看起来模块很多拆开来看其实就五块电机本体部分接收三相电压输出三相电流、转子位置和转速。坐标变换部分将三相静止坐标系的电流变换到旋转dq坐标系用于控制同时把dq轴的指令电压变换回三相静止坐标系用于调制。逆变器与SVPWM部分把d/q轴电压指令变成实际作用在电机端的PWM脉冲输出等效三相电压。控制器部分外环速度环内环电流环通常都用PI控制器。负载与工况部分用于模拟突加负载、变速等实际运行场景。搭模型的时候按这个逻辑分层去做比直接拿现成demo往里面改要清晰得多。我不会一开始就让你拖一堆Simulink模块库里的元器件出来而是推荐先用数学公式自己把电机模型搭出来理解每个变量的物理含义再对比是否切换到内置模块。这一步做好了后面遇到奇怪的问题你才能知道该往哪一层找原因。2. 数学基础PMSM模型的abc相与dq轴表达2.1 三相绕组模型为什么很难直接用PMSM的定子是三相对称绕组转子是永磁体。直接在三相静止坐标系下写电压方程绕组的互感系数会随转子位置变化方程里的电感矩阵含电角度三角函数导致模型呈非线性、强耦合。比如L_ab不仅取决于A相和B相的相对位置还受到转子角度影响。这种模型拿来理解物理没问题拿来做控制就很麻烦。为了让控制简单必须做坐标变换把变量从三相静止坐标系投影到和转子同步旋转的坐标系里让电感不再随角度变化这时候方程才好解耦控制。这就是“Field Oriented ControlFOC”的数学基础。坐标变换是绕不开的第一个坎也是很多刚接触FOC的同学觉得晕的地方。其实记住一个朴素的道理就行三相绕组产生的合成磁动势可以用一组合成矢量来代替而我们关心的只是这个矢量在某个坐标轴上的分量。2.2 Clark变换从abc到αβClark变换把三相静止坐标映射到两相静止坐标αβ。保持幅值不变时变换矩阵为C_clark [ 1, -1/2, -1/2; 0, sqrt(3)/2, -sqrt(3)/2 ]如果三相电流大小不满足i_a i_b i_c 0那么变换后还会产生一个零轴分量i_0实际电机绕组中点是浮空的星形不接地零轴电流通常不会形成通路工程上可以忽略。Clark变换让三相加成变成了两相正交分量此时i_α和i_β仍然都是交流量频率和电机电频率相同。要做恒转矩控制还需要把这些交流量变成直流量这就是Park变换要做的事。2.3 Park变换从αβ到dqPark变换把αβ坐标逆时针旋转一个角度θ_e得到dq坐标。变换公式很简洁i_d i_alpha * cos(theta_e) i_beta * sin(theta_e); i_q -i_alpha * sin(theta_e) i_beta * cos(theta_e);这里的θ_e是转子电角度也就是转子磁极方向和A相轴线之间的夹角。经过Park变换后i_d和i_q在稳态下都是直流量控制起来就很方便。i_d对应转子磁场方向的电流分量i_q对应力矩方向的电流分量。问为什么i_d默默承担了“磁场分量”的角色因为d轴被定义在转子永磁磁链方向上。如果让i_d等于0定子电流全部用于产生转矩效率最高这就是常见的“id0控制”。在弱磁区需要让i_d为负来抵消一部分转子磁链才能突破电压极限但那是另一个话题本文先把id0场景做扎实。2.4 dq坐标系下的PMSM数学模型做了上述变换后PMSM在dg坐标系下的基本方程如下。电压方程u_d R_s * i_d L_d * di_d/dt - omega_e * L_q * i_q u_q R_s * i_q L_q * di_q/dt omega_e * (L_d * i_d psi_f)磁链方程psi_d L_d * i_d psi_f psi_q L_q * i_q电磁转矩方程T_e 1.5 * p * (psi_f * i_q (L_d - L_q) * i_d * i_q)对于表贴式PMSML_d L_q电磁转矩简化为T_e 1.5 * p * psi_f * i_q。这也是为什么i_q常被称为“转矩电流”。机械运动方程J * d(omega_m)/dt T_e - T_L - B * omega_m这里的omega_e p * omega_mp是极对数omega_m是转子机械角速度。看这些公式要抓重点d、q轴电压都被分成了两部分一部分是电阻压降和电感感应电动势另一部分是运动电动势omega_e项。这个运动电动势项就是交叉耦合的来源。如果i_d0那么u_d公式里还剩一项-omega_e * L_q * i_q这就是为什么高速时如果不做前馈解耦q轴电流的变化会扰动d轴。对于大多数初学仿真PI参数整得合适、不太追求极限动态响应可以先不引入前馈解耦直接调闭环PI也能工作。但你要知道这个耦合是存在的后面遇到高速工况动态变差时就得回头加解耦前馈。3. Simulink模型搭建全流程3.1 仿真步长、求解器与整体布局打开Simulink先别急着拖模块。第一步是配置仿真求解器。仿真时长先设0.5秒左右便于快速观察启动过程后续可以延长到2秒。求解器类型用变步长ode45默认足够PMSM模型里存在高频开关控制如果后续加了SVPWM载波比较建议切换到固定步长步长取开关周期的1/10到1/20比如10kHz开关频率步长1e-6秒。误差控制相对误差设1e-4甚至更严防止电流波形失真。模型整体布局按信号流来指令速度 → 速度PI → iq指令 → 电流PI → 反Park变换 → SVPWM → 逆变器 → PMSM → 传感器反馈。我的习惯是把坐标变换、SVPWM、控制器分别打包成子系统这样后面调试时只看某个子系统内部即可不会淹没在一堆连线里。注意在Simulink中搭建电机模型时要特别注意角度单位。Simulink里面三角函数默认单位是弧度如果从外面拿到的是“度”要自己转换不然波形会非常诡异。3.2 电机本体模块直接建模还是用内置模块搭建电机本体有两种常见做法。第一种是直接用Simscape Electrical库里的PMSM模块。这个模块已经封装好了电磁和机械方程参数填进去就能用配合逆变器桥臂模块仿真结果非常接近实际电路。缺点是模块内部是黑盒遇到“为什么电流是这个形状”的问题你得从外部波形去反推内部机理对新手不够友好。第二种是自己用公式搭通过Simulink的数学模块或者MATLAB Function块把2.4节那四个方程直接写成积分器结构。我建议初学者先用这种方式搭一遍哪怕后面换成内置PMSM模块也一定要打过这个底子。自建电机模型的核心思路是输入u_d、u_q中间量通过电压方程积分计算i_d、i_q再计算电磁转矩机械方程积分得到转速和角度在这个结构里用到的关键积分器就是Simulink里的Integrator模块初值设为零即可。角度模块需要把电角度累积起来再用mod(2π)取整周期避免角度无限增长导致浮点问题。3.3 坐标变换与SVPWM实现坐标变换建议用MATLAB Function块或者直接写脚本函数在模型里调用。MATLAB Function块的优点是可以用类似C的语法写逻辑动手改公式非常方便。Clark和Park变换我已经在2.2、2.3节给出写起来就几行代码。SVPWM则是整个仿真里相对麻烦的一块主要是要处理扇区判断、矢量作用时间计算和饱和限幅。我在网上见过很多人直接把SVPWM的一堆公式抄进函数块里结果发生错误后完全不知道怎么排查。这里我建议把SVPWM分成三步来理解先判断参考电压矢量在哪一个扇区。计算该扇区相邻两个基本电压矢量的作用时间以及零矢量时间。转换成三相PWM比较值并做饱和限幅。这些逻辑我后面会给出完整的MATLAB函数代码直接在Simulink里接到逆变器桥臂触发端就能用。如果是用Simscape内置逆变器则可以用PWM Generator模块直接产生门极信号省去手写SVPWM的麻烦但这样你对调制细节的感知会弱很多。我个人推荐这个阶段手写一遍SVPWM哪怕只是仿真用不生成代码你就明白了为什么直流母线电压利用率比正弦PWM高为什么需要那两个零矢量。3.4 电流环与速度环控制回路设计FOC双闭环结构是经典的内外环结构。内环是电流环给定i_d0给定i_q来自速度环输出反馈是Park变换后的i_d、i_q。外环是速度环给定转速反馈来自电机模块的机械角速度。搭建时的连线逻辑三相电流采样 → Clark → Park → 得到i_d、i_qi_d误差过PI → 得到v_di_q误差过PI → 得到v_qv_d、v_q做反Park变换 → 得到v_alpha、v_betav_alpha、v_beta → SVPWM → 逆变器如果暂时不想写SVPWM可以先在这里用一个简单的两电平逆变器模型将v_alpha、v_beta通过电压源逆变器等效为三相电压接到电机上。这样也能跑通但载波效果和实物对不上建议最终还是要补SVPWM。PI控制器可以用Simulink里的PID Controller模块设置成P和I限制输出限幅。也可以自定义两个Gain再加Integrator。自定义的方式更容易看清控制回路逻辑也更方便实现积分限幅、抗积分饱和。3.5 负载阶跃测试怎么加加负载是为了验证系统的抗扰能力。最简单的方法是在机械运动方程模块旁边加一个常值或阶跃信号接到T_L上。比如0到0.25秒空载启动0.25秒突加0.5 N·m的负载观察转速跌落情况和恢复时间。这个测试能暴露很多问题如果速度环PI的积分太强恢复是快但容易出现超调甚至震荡如果积分太弱则稳态会有静差。电流环动态不够快时突加负载瞬间会出现很大的转速跌落这时候你就能感觉到电流环带宽的重要性。4. 完整初始化代码与参数配置4.1 用MATLAB脚本统一管理参数我建议所有仿真参数都放在一个脚本文件里不要直接在Simulink各个对话框里手填数字。为什么因为后期调参数时你需要同时试很多组散落在各对话框里非常容易出错。脚本管理的好处是改一个变量名全模型同步生效方便做参数扫描。下面给出一份可以直接使用的初始化脚本。%% PMSM仿真参数初始化脚本 clear; clc; close all; % 电机额定参数 Rs 0.958; % 定子电阻 [Ohm] Ld 0.00525; % d轴电感 [H] Lq 0.00525; % q轴电感 [H] psi_f 0.1827; % 永磁磁链 [Wb] p 4; % 极对数 J 0.003; % 转动惯量 [kg*m^2] B 0.0001; % 阻尼系数 [N*m*s/rad] % 直流母线电压 Vdc 300; % [V] % 开关频率 fsw 10000; % [Hz] Ts 1/fsw; % 控制周期 [s] % 电流环PI参数 kp_i 10; ki_i 500; % 速度环PI参数 kp_s 0.5; ki_s 10; % 限幅值 i_lim 15; % 电流限幅 [A] T_lim 5; % 转矩/电流指令限幅对应速度环输出 % 仿真控制指令 speed_ref 1000; % 目标转速 [rpm] load_torque 0.3; % 负载转矩 [N*m]这份参数对应一台常见的小功率表贴式PMSM。注意几个单位坑速度环的指令单位我用的是rpm但电机模型内部计算要注意是否转成rad/s。psi_f永磁磁链的单位是Wb不要写成V·s。极对数p在计算电角速度时要乘进去。4.2 关键参数对照表以下是我在调试过程中常用的参数适配表方便你对照自己的电机调整方向。参数物理含义常见取值区间调整影响Rs定子电阻0.1~10 Ω影响低速稳态精度和铜耗Ld/Lqd/q轴电感0.1~50 mH决定电流环响应和弱磁能力psi_f永磁磁链0.01~0.5 Wb直接影响转矩系数p极对数2~8决定电机转速与频率关系J转动惯量0.0001~0.1 kg·m²影响速度环响应Vdc直流母线电压48~600 V限制最高转速和调制度fsw开关频率2k~20k Hz影响电流谐波与损耗表格里这些参数是相互耦合的比如Vdc越低能达到的最高转速就越低J越大速度环越难整定容易产生大超调。不要只看单个变量。4.3 坐标变换与SVPWM的完整MATLAB实现坐标变换函数都很短我会直接写在MATLAB Function块里。function [i_alpha, i_beta] fcn(i_a, i_b, i_c) % 保持幅值不变的Clark变换 i_alpha i_a - 0.5 * i_b - 0.5 * i_c; i_beta (sqrt(3)/2) * (i_b - i_c); endfunction [i_d, i_q] fcn(i_alpha, i_beta, theta_e) % Park变换theta_e为电角度单位弧度 i_d i_alpha * cos(theta_e) i_beta * sin(theta_e); i_q -i_alpha * sin(theta_e) i_beta * cos(theta_e); endSVPWM函数稍微长一点这里给一个适合Simulink测试的版本。输入是v_alpha、v_beta和直流母线电压Vdc输出是马鞍波风格的调制波值可以在PWM Generator里直接使用不需要再处理载波比较。function [ta, tb, tc] svpwm(v_alpha, v_beta, Vdc) % 归一化处理 v1 v_alpha; v2 v_beta; % 扇区判断 if (v2 0) A 1; else A 0; end if (sqrt(3)*v1 - v2 0) B 1; else B 0; end if (-sqrt(3)*v1 - v2 0) C 1; else C 0; end sector A 2*B 4*C; % 根据扇区查表计算作用时间 x sqrt(3) * v2 / Vdc; y (3/2*v1 sqrt(3)/2*v2) / Vdc; z (-3/2*v1 sqrt(3)/2*v2) / Vdc; switch sector case 1 T1 z; T2 y; case 2 T1 y; T2 -x; case 3 T1 -z; T2 x; case 4 T1 -x; T2 z; case 5 T1 x; T2 -y; case 6 T1 -y; T2 -z; otherwise T1 0; T2 0; end % 过调制处理 Tsum T1 T2; if (Tsum 0.866) over Tsum / 0.866; T1 T1 / over; T2 T2 / over; Tsum T1 T2; end % 插入零矢量并生成三相占空比 T0 1 - Tsum; Tcmp1 T0/2; Tcmp2 Tcmp1 T1/2; Tcmp3 Tcmp2 T2/2; switch sector case 1 ta Tcmp2; tb Tcmp1; tc Tcmp3; case 2 ta Tcmp3; tb Tcmp1; tc Tcmp2; case 3 ta Tcmp1; tb Tcmp3; tc Tcmp2; case 4 ta Tcmp1; tb Tcmp2; tc Tcmp3; case 5 ta Tcmp3; tb Tcmp2; tc Tcmp1; case 6 ta Tcmp2; tb Tcmp3; tc Tcmp1; otherwise ta 0; tb 0; tc 0; end end这段代码是按照SVPWM经典的两电平七段式方法实现的。为什么是0.866这个阈值因为三相线电压基波最大可以达到Vdc/sqrt(3)归一化后对应矢量长度为1/√3≈0.577而采用SVPWM后最大调制度可以到0.866归一化到半母线电压的写法不同会略有差异。如果不做限幅T1T2超过周期时实际输出会失真导致电流畸变。4.4 电流环和速度环PI参数整定思路我把参数整定方法完整放在这一节是因为很多人仿真跑不通最终原因基本都是PI参数太离谱。电流环带宽的选择工程上电流环带宽通常取开关频率的1/10到1/5。比如10kHz开关频率电流环你可以预期带宽1kHz左右。在仿真里实用做法是先把电流环单独闭环调试断开速度环给一个阶跃iq指令观察iq跟踪速度。把kp_i调大直到出现明显振荡然后退回来0.6~0.7倍再逐步加ki消除稳态误差。定性地说就是“先P后IP调到临界I补齐精度”。速度环调试同理在电流环工作正常后给一个固定iq比如3A让电机自由加速记录转速上升斜率估算系统机电时间常数然后初定kp_s和ki_s。先让速度环能跟上斜坡而不震荡再考虑加负载看跌落。切记不要一上来就给阶跃1000rpm然后狂调PI那样很难定位问题。5. 常见问题与调试实录5.1 仿真直接发散NaN满天飞这种现象我见得太多了。常见原因有电感值过小模型步长太大导致电流变化在单个步长内翻倍数字积分发散。PI输出没限幅c指令给到了几百安积分一直在累加。角度反Park变换时角度没对齐电流反馈极性反了形成正反馈。排查顺序我通常是先看角度对不对把角度模块直接在示波器里画出来确认是0到2π锯齿波再看电流反馈的极性给一个小iq阶跃确认id、iq能稳定跟随最后再检查PI的限幅让输出限制在电压极限内。5.2 电流波形是标准正弦但电机转不起来这个问题多半出在角度上。旋转坐标系的控制反馈角度和实际磁极位置必须严格对齐哪怕差30度电角度分解出来的id、iq就不是你期望的值。常见的错误是把电角度和机械角度混用或者在PMSM模块里配置的是“角度传感器为转子机械角”但控制端直接用了这个角度忘了乘极对数。5.3 转速有静态误差但看不出震荡出现这个情况先怀疑速度环积分增益太小或积分限幅太紧。仿真不是实物积分限幅可以相对放宽但也要防止深度饱和。我的经验是速度环ki初步可以从kp_s的5~20倍开始试再看阶跃响应的恢复速度调节。5.4 开关频率、采样周期与仿真步长的配合有的同学把控制周期设成1e-3秒同时开关频率也是1kHz但仿真步长又是变步长结果波形乱得一塌糊涂。这里的核心原则是仿真步长要能分辨PWM载波变化控制周期决定算法更新的节奏两者必须分开考虑。固定步长建议取开关周期的1/10比如10kHz开关频率时步长1e-6秒会比较保险。有些demo为了跑得快用1e-5秒也能跑但波形细节会差。5.5 强启切入FOC瞬间电流尖峰仿真中给速度和电流指令是阶跃信号导致起转瞬间电流指令直接被拉到限幅值波形上就是一条竖起来的尖峰。这个是仿真里常见的现象不代表模型错误但反映了一个真实问题实际硬件里必须加软化启动或者斜坡限制否则过流保护很容易触发。你可以在Simulink里加一个Rate Limiter模块来处理转速和电流指令的斜坡也能极大改善起转波形。我个人实际调试中还有一个很笨但很好用的技巧在模型里多接几个示波器分别观察母线电流、三相电流、dq电流和转速。不要觉得图标太乱宁可多花一点排布时间也不要等波形异常时再去临时找数据。做仿真最忌讳的就是黑箱式操作信号流一旦可视化了问题的定位速度会快很多。最后再分享一个小技巧如果你想做参数扫描可以在初始化脚本里写成for循环把每次仿真结果用sim函数跑完存下来最后用hold on把多条转速曲线画在同一张图里比较。这一步看似很简单但在对比不同J或不同PI参数对系统影响时效果好得惊人。希望这份教程能帮你把PMSM仿真从“会抄demo”提升到“会自己搭”的程度。
