做永磁同步电机(PMSM)调速的工程师,十有八九被同一件事烦过:PI参数在一组工况下调得顺顺当当,换个负载、加点扰动,性能立刻垮掉。这几年ADRC(自抗扰控制)在电机控制圈子的热度越来越高,原因很直接——它不依赖精确的电机模型,而是把系统里的“总扰动”当作一个状态实时估计、实时补偿,天然比PI扛造。我先后在Simulink里把PMSM的ADRC方案完整搭过很多轮,从建模、调参到波形诊断踩了一堆坑,这篇就来手把手拆解整个流程,顺便把几个文档里不会明说的“骚操作”一并摊开。适合刚接触ADRC、想在仿真里快速跑通原理的人,也适合那些PI调到怀疑人生、想换条路试试的控制工程师。
1. 为什么PMSM调速要上ADRC
1.1 传统PI控制的三道坎
先说清楚问题在哪。PMSM矢量控制的标配是“电流环PI+速度环PI”,这套东西成熟到教科书都不愿意多废话,但它有几个绕不开的先天弱点。
第一,PI本质上是线性控制律,它默认被控对象是“参数固定、线性、时不变”的。可电机不是这样:绕组电阻Rs随温度漂移,电感Ld、Lq随电流饱和变化,转子磁链ψf受温度影响,负载转矩更是随时跳变。对象变了,PI参数没变,性能自然往下掉。
第二,PI靠误差驱动。负载突变的那一刻,转速误差还没建立起来,PI输出来不及反应,等到误差变大了再使劲,转速已经跌了一大截。这就是“扰动后知后觉”的致命伤。
第三,快速性和超调这对矛盾,靠一组固定PI参数很难两全。把比例增益调大,动态响应快,但超调、噪声、振荡一起冒出来;调保守一点,扰动来了半天回不到稳态。
这三道坎不是PI不能用,而是它的模型假设天花板太低。ADRC的思路完全不同:不去精确建模,不指望“误差出现后再纠偏”,而是把系统内部参数变化、外部负载变化统统打包成“总扰动”,在它还没造成严重后果之前就补偿掉。
1.2 ADRC的三个核心角色与整体架构
ADRC之所以听起来玄,是因为它引进了一套新的概念体系。拆开看其实就三个模块,各管一件事。
跟踪微分器(TD)管“怎么去”:参考转速不能瞬间砸进系统,否则必然激起超调和冲击。TD把阶跃的转速指令变成一条平滑的过渡轨迹,同时给出它的“速度趋势”,相当于给控制系统安排了刹车和油门。
扩张状态观测器(ESO)管“怎么看”:这是ADRC的心脏。它不满足于只观测转速,而是把系统里乱七八糟的扰动——负载突变、参数摄动、未建模摩擦——统统归并成一个新的状态变量,在观测转速的同时把它一起估出来。
非线性状态误差反馈(NLSEF)管“怎么打”:把当前状态和参考轨迹的误差用非线性组合算出一个初步控制量,再把ESO报上来的总扰动前馈抵消掉。三个模块合在一起,控制量既响应误差,又提前干掉扰动。
这套架构最迷人的地方在于:它不需要你知道扰动具体是怎么来的、方程长什么样,ESO会在运行中自己把它“看出来”。
1.3 为什么选Simulink而不是手写代码
有人会问,ADRC本身就是一套差分方程,直接写C或者Python不香吗?仿真阶段真不建议。
Simulink的优势在于“半实物”建模:PMSM模型、三相逆变器、PWM调制、测量反馈这些环节,都有现成模块,你不用从电机方程开始敲。算法部分用MATLAB Function把差分方程封装进去,既保留了对细节的掌控,又不需要搭几十个积分器和增益模块。后期要换参数、加扰动、看波形,改一行脚本比手动重建整个模型快得多。
更重要的是,Simulink的Scope和To Workspace能让你瞬间对比转速指令、实际转速、ESO估计扰动、负载转矩这些信号。做控制最怕“黑箱”,ADRC调参时你特别需要透视观测器内部状态,这一点Simulink天生合适。
2. 模型搭建前的基础准备
2.1 PMSM的数学模型与坐标变换关系
不管用什么模块库,原理都得先落在纸面上。表贴式永磁同步电机在dq同步旋转坐标系下的电压方程是:
ud = Rs·id + Ld·(d id/dt) - ωe·Lq·iq uq = Rs·iq + Lq·(d iq/dt) + ωe·(Ld·id + ψf)
转矩方程:
Te = 1.5·pn·(ψf·iq + (Ld - Lq)·id·iq)
表贴式电机Ld等于Lq,括号里第二项归零,转矩完全由iq决定。这就是id=0控制的根源——iq直接对应电磁转矩,控制iq就控制了转矩,简洁得让人放心。
坐标变换是从abc三相到αβ静止坐标(Clarke变换),再从αβ到dq旋转坐标(Park变换)。Simulink自带的PMSM模块内部会处理这些变换,但你自己心里要有一本账:电流环的输出是ud和uq,要经过逆Park变换和SVPWM才能变成逆变器的开关信号。ADRC算法运行在dq坐标系里,反馈回来的转速是机械转速或电气转速,这些单位换算一旦错位,后面b0参数必炸。
2.2 仿真参数与求解器配置
我用一组常见的仿真参数给你做参考,这套数据属于小功率表贴式PMSM,非常适合首轮跑通:
| 参数 | 数值 | 说明 |
|---|---|---|
| 定子电阻 Rs | 2.875 Ω | 绕组相电阻 |
| dq轴电感 Ld / Lq | 0.0085 H | 表贴式,两者相等 |
| 永磁磁链 ψf | 0.175 Wb | 转子磁链幅值 |
| 极对数 pn | 4 | 决定电气转速与机械转速关系 |
| 转动惯量 J | 0.0008 kg·m² | 折算到电机轴的惯量 |
| 阻尼系数 B | 0.0001 N·m·s | 摩擦阻尼,常被忽略 |
| 直流母线电压 | 311 V | 三相220V整流输出 |
| 开关频率 | 10 kHz | PWM载波 |
仿真步长建议设固定步长,离散求解器,步长取1e-5秒或更小。10kHz开关频率意味着PWM周期100微秒,步长1e-5秒让每个开关周期内有10个计算点,波形足够细腻。很多人图省事用变步长连续求解器,结果模型在高频开关切换处步长疯狂收缩,仿真龟速爬行,还容易报代数环错误。固定步长在这里反而稳。
2.3 双闭环拓扑怎么选:速度环ADRC+电流环PI
第一次做ADRC的人最容易犯的错:一上来电流环速度环全都上ADRC,参数一大把,调到头昏脑涨还不知道是哪个环在捣乱。我的建议非常明确,首轮仿真用“速度环ADRC+电流环PI”的混合架构。
理由不复杂。电流环动态快、模型相对清楚,两个PI参数用传统整定法十分钟就能搞定。速度环才是对抗负载扰动、参数变化的主战场,ADRC最值钱的能力放在这里才能体现出来。等这套混合方案跑顺了、你对ESO的脾气摸透了,再回头试着把电流环也换成ADRC不迟。
这样分层还有个额外好处:问题隔离。波形不对的时候,你只需要怀疑速度环ADRC的参数,电流环PI那边稳如老狗,不会互相甩锅。
3. 手把手拆解ADRC的Simulink实现
3.1 总体框架:模型里到底有哪些模块
一个完整的速度环ADRC仿真模型,信号流从前往后大概是:参考转速模块 → ADRC控制器(内部含TD、ESO、NLSEF)→ q轴电流给定 → 电流环PI → 逆Park变换 → SVPWM → 三相逆变器 → PMSM → 电流、转速测量 → 坐标变换反馈回控制器。
看着环节多,Simulink里其实就是三大块:电力电子主电路、坐标变换与PWM调制、ADRC控制算法。电力电子和PWM部分用模块库里的现成组件,自己重点写的是ADRC算法和电流环PI。
ADRC控制器的输入有三个:参考转速v、反馈转速y(来自电机的测量输出)、电流环反馈iq(用于ESO里的控制量u)。输出只有一个:q轴电流参考值iq_ref。整个控制器封装成一个子系统,内部再分TD、ESO、NLSEF三个子模块,层次清楚,后续改参数也方便。
3.2 TD模块:让参考输入“软着陆”
速度环ADRC里,TD的任务是把阶跃转速指令平滑化。对一阶速度对象,TD可以简化成一阶惯性跟踪,离散递推式:
x1(k+1) = x1(k) + h · (-r · (x1(k) - v(k)))
这里的r就是速度因子,越大过渡越快,但太大就失去了“安排过渡过程”的意义。x1是对参考轨迹的跟踪值,后续NLSEF用的是x1与实测转速的误差,而不是直接用原始指令和实测转速的误差。这个细节很关键——用了TD,阶跃起步那一下就不会把误差一下子拉满,超调自然被压住。
Simulink里一个离散传递函数或一个MATLAB Function就能实现上式。我给的是工程简化版,好调好用。如果你追求论文级效果,可以上完整的二阶最速控制综合函数fhan,但在速度环这种一阶主对象上,简化的收益比远超复杂度付出。
3.3 ESO模块:全场最核心的观测器实现
ESO是ADRC的灵魂,写差一步整个控制就是空中楼阁。针对PMSM速度环的一阶模型:
dω/dt = b0 · iq + f(t)
其中f(t)就是负载扰动、参数变化、未建模动态的总和。二阶ESO的离散递推式如下:
e = z1(k) - y(k) z1(k+1) = z1(k) + h · (z2(k) - β1 · e + b0 · u(k)) z2(k+1) = z2(k) - h · β2 · fal(e, α, δ)
这里z1观测转速,z2观测总扰动f。u是控制量,这里取q轴电流iq。
fal非线性函数:
fal(e, α, δ) = e / δ^(1-α)(当|e| ≤ δ) fal(e, α, δ) = |e|^α · sign(e)(当|e| > δ)
α断电控制在0.5附近,δ可取0.01量级。fal的用意是“小误差时高增益、大误差时低增益”,兼顾收敛速度和抗噪声。
在Simulink里,ESO必须用离散状态更新实现,不能拖一堆连续积分器——连续积分器在固定步长仿真里也能跑,但数字实现落不到单片机里,失去仿真意义。我自己写MATLAB Function实现ESO,上面那组递推式直接搬进去,非常干净。
3.4 “骚操作”一:MATLAB Function替代几十个积木模块
这是我最想强调的实战技巧。网上教程里常见做法是用积分器、增益、求和器把ESO搭成积木图,一个观测器少说十几个模块,连线密到眼睛发花,改个β参数要去增益模块里翻半天。
我的做法:整个ADRC算法内核封装进一个MATLAB Function子系统。
function [iq_ref, z1, z2, x1] = ADRC_Core(v, y, iq, params) % params 包含 r, b0, kp, beta1, beta2, alpha, delta, Ts % TD x1_next = x1 + Ts * (-params.r * (x1 - v)); % ESO e = z1 - y; z1_next = z1 + Ts * (z2 - params.beta1 * e + params.b0 * iq); z2_next = z2 - Ts * params.beta2 * fal(e, params.alpha, params.delta); % NLSEF + 扰动补偿 u0 = params.kp * (x1 - z1); iq_ref = (u0 - z2) / params.b0; % 更新状态 x1 = x1_next; z1 = z1_next; z2 = z2_next; end
外部只用两个接口接线,内部参数全部放在结构体params里,在初始化脚本里赋值。改参数就是改一行脚本,波形不对立刻重跑,效率高出一个数量级。正因为ADRC的差分结构如此清晰,摆在MATLAB Function里远远比积木图更好维护、更好理解、更好移植。
3.5 控制律与扰动补偿接线
NLSEF部分从简:对一阶对象,比例控制律足够,kp就是控制器带宽ωc。加法层面的操作是:先用误差算出u0,再用u0减去z2,最后除以b0得到iq_ref。这个“减z2除以b0”的动作就是扰动补偿——ESO说“我估计干扰是某个值”,控制量就反向抵消它。
注意接线里的单位。z2估计的扰动单位是“转速变化率”(rad/s²),除以b0之后才变成电流单位A。很多第一次搭的人忘记除以b0,结果扰动补偿幅度差了几十倍,ESO再准也白搭。
4. 参数整定:从能跑跑到跑得好的关键一跃
4.1 带宽法把参数从6个降到2个
ADRC的堆头不小,TD有r,ESO有β1、β2、α、δ,控制律有kp,一数六七个参数,手动乱试等于大海捞针。好在有高志强提出的带宽参数化方法,把ESO参数全部归约到观测器带宽ωo上。
对二阶ESO,带宽法给出:
β1 = 2·ωo β2 = ωo²
控制器带宽ωc直接取kp。经验上让ωo等于ωc的3到5倍,保证观测器收敛速度显著快于控制器响应速度。这样实际要调的参数缩减为两个:ωc和ωo,外加一个b0。这三个数定下来,整个ADRC就基本定形。
这个“骚操作”的核心价值:把玄学调参变成工程调参。你先按物理直觉给ωc一个值,ωo跟上去,系统就能稳定,之后再迭代优化,而不是每个参数盲试。
4.2 b0这一步算错全盘皆输
b0是控制量增益的估计值,在速度环里对应电磁转矩系数与转动惯量的比值。从机械运动方程:
J·dω/dt = Kt·iq - TL - B·ω
其中Kt = 1.5·pn·ψf,于是:
b0 = Kt / J
代入前面那组仿真参数:Kt = 1.5×4×0.175 = 1.05 N·m/A,J = 0.0008 kg·m²,所以b0 = 1.05 / 0.0008 = 1312.5。
这个数一定要算准吗?也不完全是。ADRC的ESO会把b0不准造成的模型误差当作扰动的一部分估计出来并补偿掉,所以小幅偏差系统扛得住。但如果你b0差出一个数量级,或者正负号错了,ESO的补偿方向直接反掉,仿真当场发散。
给个实在提醒:仿真里b0错一倍以内还能救,错十倍以上建议直接查单位换算。尤其是转速单位——用rad/s计算得到的b0,和你用rpm当反馈时的b0不是一个数,中间差60/2π倍。
4.3 分步整定的具体操作顺序
调参切忌一步到位,我推荐的顺序是:
- 电流环PI先整定完毕,iq给定到实际电流的响应带宽调到数百赫兹以上,确保电流环不拖后腿。
- ADRC里的TD先屏蔽掉(或者给一个很大的r,让它几乎不滤波),此时系统退化成“P控制+ESO补偿”。
- 从ωc=20开始往上加,观察转速阶跃响应。响应太慢就翻倍,出现振荡就回退到上一个稳定值。
- ωo保持ωc的3~5倍,观察ESO的z2是否跟随实际扰动。如果z2毛刺大、噪声多,适当把ωo往回压。
- 系统稳定后再把TD接回来,r从100开始调,让转速指令变化不产生超调。
每一步只改一个参数,改完跑一次阶跃响应,存好波形再动下一个。这比你同时拧三个旋钮高效得多,出了问题也知道赖谁。
4.4 “骚操作”二:用在线数据辨识快速初估参数
有些时候b0算不准,比如电机铭牌数据和实际不符。这时别傻乎乎地在Simulink里盲猜,直接用仿真数据做离线拟合。
方法不复杂:模型跑起来后,用To Workspace把iq和dω/dt录下来,在MATLAB工作区里对J·dω/dt = Kt·iq做最小二乘拟合,b0的初值一秒估出来。
仿真里估的b0虽然不完全代表实机工况,但至少帮你把量级锁死。等真到了台架阶段,再用阶跃响应或正弦辨识进一步校准。这个招数很适合那些“手里电机参数残缺不齐”的情况,比翻手册慢慢凑数效率高很多。
5. 仿真现场常见问题与排查实录
5.1 一跑就发散,先别急着改参数
我见过太多人模型一炸就疯狂改参数,越改越乱。发散问题先按顺序排查:
第一,看步长和求解器。固定步长1e-5,离散求解器,PWM和逆变器模型别用连续变步长,这是最稳的组合。
第二,查代数环。MATLAB Function里如果出现“以前一时刻的输出直接参与本时刻输入计算”的环,Simulink会报错或发散。解决办法是在递推式里强制用上一拍的状态量来计算本拍输出。
第三,查符号。b0正负错、反馈极性反、ESO的u符号不对,任何一种都会瞬间击穿系统。跑之前拿几个关键数值手算一遍,比如给定iq=1时,估算一下转速增长方向和幅度,和Scope里看到的对一对。
第四,查限幅。iq_ref不加饱和限幅,仿真里控制量可以冲到几千安培,必然导致数值爆炸。输出一定要接Saturation,上下限按电机额定电流的1.5~2倍给。
5.2 ESO观测值跟实际转速对不上
z1本应紧跟实测转速y,如果两者差得老远,问题大多出在ESO参数或输入信号上。
β1、β2太小,观测器收敛太慢,z1就像被胶水粘住一样跟不上。解决:把ωo往上提,β2是平方关系,提升效果特别明显。
但如果z1依然差的很大且稳态时仍有固定偏差,要怀疑模型阶数不匹配——你把二阶对象强行套一阶ESO,或者反过来。PMSM速度环在电流环带宽不够高时,确实会显现二阶特性,此时可以把ESO升到三阶,或者先提高电流环带宽再做决定。
5.3 稳态转速抖振、电流毛刺多
现象是转速在给定值附近抖个不停,电流波形上有明显的高频毛刺。多半是这几个原因:
fal函数的δ设得太小,小误差区增益过高,控制量在稳态附近不稳定。把δ从0.001调到0.01量级,抖振通常会明显缓解。
ωo拉得太高,观测器把测量噪声放大以后当成了“扰动”,控制量跟着噪声狂跳。把ωo从“能压住多大扰动”这个目标降回“观测器不发毛”这个水平。
步长太大导致离散误差明显。电流环这种高带宽回路,仿真步长压缩到1e-5量级,离散化误差带来的相位滞后能被控制在可接受范围内。
5.4 “骚操作”三:把扰动估计拖到Scope里一起对比
这是我每次调试的保留项目:把ESO的z2(总扰动估计值)和实际负载转矩TL画在同一张Scope里,双曲线对比。
道理很简单,ESO估得准不准,直接看两条线是否重合。如果负载在0.5秒时从零阶跃到额定值,z2应该在同一时刻快速跟进到对应水平的转速变化率数值。如果z2的反应慢半拍,说明ωo不够高;如果z2到达后大幅振荡,说明观测器环路不稳。
这个操作让ESO不再“黑箱化”,每次调参都有数据依据,而不是对着一条转速曲线瞎猜。比单纯看转速响应下结论要科学得多。
6. 从仿真到电机的几条实在建议
6.1 离散化移植与运算量评估
Simulink里的ADRC差分方程,直接翻译成C代码毫无障碍,一个控制周期内就是几十行乘加运算。以二阶ESO加一阶TD为例,一个周期内浮点运算量几百次量级,主流MCU跑5kHz到10kHz控制周期绰绰有余。运算量从来不是ADRC上实机的瓶颈,真正的门槛在参数标定和抗噪设计。
移植时的最大坑是时序:实机代码里ADRC必须在电流采样完成之后、PWM占空比更新之前执行完毕,所有状态量更新一次,不能仿真那样连续刷新。把Simulink模型转为C代码时,要特别注意离散模块的采样时间设置和控制周期完全对齐。
6.2 实机环境下的噪声与限幅处理
仿真里信号干干净净,实机里电流传感器有偏置和噪声,编码器测速有量化误差。ESO把这些测量噪声当成扰动的一部分估计出来,会让z2高频抖动,然后通过补偿链路污染控制量。
应对策略有三条:ωo别按仿真值硬拉,实机里通常降到仿真值的二分之一甚至更低;速度反馈自己做低通滤波或采用测速机处理,别拿原始脉冲直接怼给ESO;控制量输出必须限幅,并且要加抗积分饱和机制,防止ESO内部状态被大误差冲到飞。
6.3 我个人踩过的坑与习惯做法
最后说几句实在话。ADRC不是魔法,它解决的痛点很明确,但带来的参数敏感性和调试负担也真实存在。我在实机调试中最深的一个感受是:b0这个参数比ωc和ωo都重要。b0一旦严重失准,ESO花多大力气都补不回来;b0准了,剩下两个带宽参数怎么拧都有下限兜底。
另一个习惯是每次调参都留数据。Simulink的Scope截图只是辅助,把关键信号用To Workspace整段存下来,参数每次调整都做一次记录,过几天回头翻,你才能看清参数和性能变化之间的因果关系。做控制这一行,经验是攒出来的,不是靠感觉硬调出来的。
如果后续想往深了走,可以试试把ESO和TD改成自适应参数版本,或者加入模型参考自适应做在线b0辨识,这些都是ADRC在电机控制里很活跃的扩展方向。先把基础版本完整跑通、调平稳,再考虑这些锦上添花的东西,路才走得踏实。