做电机驱动和运动控制的工程师,应该都见过这种场面:转速稳态波形不是平的,而是像心电图一样规律地抖,FFT一分析,基频峰值明晃晃地立在那儿。我最早是在带周期负载的永磁同步电机上撞上这个问题的,压缩机曲轴每转一圈,负载转矩就狠狠波动一次,PI参数调到临界稳定,转速波动依然压在头上。后来换成“PI+重复控制”的复合结构,几个周期内波形就安稳下来,效果立竿见影。这篇文章就把这件事从头到尾拆开讲:扰动为什么压不住、重复控制凭什么能压住、Simulink里怎么落地、以及我在调参和排坑过程中攒下的经验。
具体会覆盖这些内容:周期性负载转矩扰动在哪些场景最常见;重复控制的内模原理,以及怎么从离散角度理解“延迟N拍就是记住上一周期的误差”;在Simulink里搭建从被控对象、扰动源到重复控制器的完整仿真模型;核心参数Kr、Q、N各自的作用与调参顺序;还有我踩过的几个实打实的坑,包括延迟周期不匹配、初值漂移和变步长求解器引发的锯齿振荡。适合谁看?正在做伺服驱动、机器人关节、机床主轴、压缩机等转动系统的控制工程师,以及对重复控制感兴趣、想在Simulink里快速复现的初学者。
1. 为什么普通反馈压不住周期性负载转矩扰动
1.1 扰动长什么样:周期性负载转矩的工程来源
周期性负载转矩在转动系统里几乎是无法回避的存在。最常见的几个来源:
- 偏心负载:比如洗衣机脱水桶、曲柄连杆机构,负载转矩随转角变化,大致呈现正弦或者多阶谐波叠加的形态。
- 电机本体转矩脉动:永磁同步电机的齿槽转矩随转子位置周期性波动,频率通常是电机极对数的整数倍。
- 齿轮啮合冲击:齿轮箱每转一圈,齿与齿的啮合产生一个固定频率的转矩扰动,频率等于轴转速乘以齿数。
- 压缩机、泵类负载:活塞或叶轮在每个工作周期内受力变化,转矩波动频率和旋转周期直接绑定。
这类扰动的共性是:变化规律确定、周期固定,但频率往往远高于转速闭环带宽,或者正好落在闭环截止频率附近。PI控制器治理这类扰动,本质上是在用比例积分环节去反复“追赶”一个不停变化的扰动信号,结果就是相位滞后和幅值不足,最终体现为稳态转速波动。我习惯用一句话总结:只要扰动频率和电机转速有整数倍关系,并且能被看成“周期已知”的信号,那就非常适合用重复控制来处理。换句话说,这种扰动有规律地在折磨你,但它也因此有规律可循。
1.2 反馈控制为何无力:从误差到输出只有一条迟滞路径
常规反馈控制能不能抑制周期性扰动?能,但有上限。对于一个周期为Tp的扰动,把它展开成傅里叶级数,它就是一簇基波加谐波。反馈控制系统对扰动的抑制能力,取决于该频率处的开环增益。拉高增益确实能压低扰动,但增益是有上限的:机械谐振、相位延迟、测量噪声、采样离散化,这些因素都会限制你最终能加多大的增益。
更直观的理解是,PID对“下一次出现在同一个位置的扰动”没有任何预判,它只能在扰动已经表现为误差之后再去反应。误差先发生,控制器后调节,相位滞后必然存在。到了周期性扰动面前,这种“事后反应”的缺陷被放大:你刚把上一周期这一瞬间的误差压下去,到下一周期的同一时刻,扰动又原样扑过来了。
我在仿真里做过一个很直观的对比:一个以25Hz周期变化的负载转矩叠加在转速环上,PI参数已经调到临界稳定边缘,转速波动仍然有±30r/min。这不是PI参数没调好,而是它的工作方式决定了它只能做到这个程度。要彻底压低这类稳态波动,必须从“误差发生前”做文章,这也就自然引出了重复控制。
2. 重复控制的内模原理:把一个“信号发生器”搬进闭环
2.1 内模原理:闭环里包含信号的模型,才能对它有零稳态误差
重复控制的底层逻辑来自内模原理,听起来高大上,其实一句话就能讲明白:如果一个控制器内部“内嵌”了目标信号的数学模型,那么这个信号在闭环里就不会产生稳态误差。
拿阶跃信号类比。为什么PI控制器的积分环节能消除阶跃误差?因为阶跃的模型是1/s,而积分器本身就自带这个模型。当被控对象无法直接等于阶跃时,积分器会不断调整输出,直到误差消失。同样道理,正弦信号的模型是s²+ω²,如果你把等价形式放进控制器,你就能对固定频率正弦扰动做到零误差抑制。重复控制就是内模原理在周期信号上的推广:它把“周期重复”这个性质本身作为模型放进了控制器,所以它对任意波形、任意谐波成分的周期扰动,理论上都有全频次的抑制能力。
从离散域看更清楚。周期为Tp的信号离散化后用N拍表示,它一定满足:任意第k拍的值等于第k-N拍的值。因此周期信号的离散模型就带有一个1/(1-z^{-N})的分母。把1/(1-z^{-N})放进闭环,就是构造了一个周期内模。这也是重复控制器在Simulink里总是绕不开延迟模块的根本原因,它要直译的就是这个数学关系。
2.2 离散延迟N:重复控制器的“记忆体”与误差累计机制
既然周期信号满足x(k)=x(k-N),那么控制器内部只需要部署一个N拍的延迟环路,就能让信号“自动重放”上一周期的波形,再配合误差信号逐步修正,形成迭代学习的效果。
以最简单的并联型结构为例,当误差e出现在输入端时,重复控制器的输出u_rc会经历这样的过程:
- 第一周期:误差第一次出现,重复控制器输出基本来自当前误差经增益Kr放大后的值,延迟环路里还没存下足够信息,修正有限;
- 第二周期:记忆单元里已经有上一周期同一时刻的输出值,它会用“上周期此刻的值”加上“上周期此刻误差的修正量”,作为本周期此刻的输出;
- 循环往复:每个周期都更新记忆值,直到误差的各次谐波被逐步抵消,记忆单元里的波形稳定下来,就等于生成了一个与扰动反相、幅值匹配的“对抗波形”。
这里N是灵魂参数:N等于扰动基频周期对应的采样拍数,即N=fs/fp。比如采样频率10kHz,扰动频率25Hz,那N=10000/25=400。只要N精确,延迟单元就能在相位上与扰动严格对齐,这是整篇文章里最容易出错、也最关键的一个数字。纯内模1/(1-z^{-N})是临界稳定系统,对模型不确定性非常敏感,工程上几乎不会直接用它,而是会在反馈支路里串联一个低通Q(z),把极点从单位圆上拉回圆内。这个Q(z)就是搭建模型时重点处理的一个环节。
3. Simulink模型搭建:从被控对象到重复控制器一体成型
3.1 顶层框架:转速环、转矩环简化与被控对象建模
先明确我要搭的仿真对象:一个典型的永磁同步电机速度控制环。电流环动态相对较快,为了聚焦重复控制本身,我对电流环做了一定简化:直接用比例增益Kt表示电磁转矩常数;机械部分用转动惯量J的积分环节表示;负载转矩T_L作为扰动输入叠加在电机轴上。
在Simulink里,我把模型分成四块:
- 参考转速与PI控制器:给定速度ref,PI输出作为转矩指令的基准部分;
- 重复控制器:输入转速误差e,输出u_rc叠加到PI输出上,共同构成转矩指令;
- 被控对象:从转矩指令到电磁转矩,乘以1/J后积分得到转速;周期扰动源T_L通过Gain(1/J)叠加进同一个积分节点;
- 测量与记录:转速经To Workspace导出,方便后续做FFT分析。
使用“PI+重复控制”的复合结构而不是纯重复控制,是工程上很重要的取舍。重复控制对周期信号抑制能力强,但动态响应慢,因为它的修正要等满一个周期才能看出效果;PI则相反,对阶跃和突变响应快。两者并联,一个管动态,一个管周期稳态误差,任务分离互不干扰。刚开始学重复控制的人如果直接上纯重复控制器,会发现负载突变时转速动态很拖沓,反而容易误判方案不可用。
顶层信号流向可以这样描述:速度误差分成两条支路,一条进PI,一条进重复控制器的误差通道;PI输出加重复控制输出,得到总转矩给定Te*;Te*经饱和限幅后乘Kt,得到电磁转矩Te;Te减去T_L,乘1/J,积分得到转速omega;omega负反馈回给定端,形成闭环误差。
3.2 重复控制器内部:用Delay、Gain和Filter把内模搭出来
重复控制器的内部结构,我搭成最经典的“历史记忆+误差修正+低通滤波”三段式,模块选择如下:
- 记忆回路:使用Discrete Delay模块,延迟步数设为N,用来存住上一周期的输出值;
- 误差修正:当前误差e(k)与N拍前的误差e(k-N)做差,乘以增益Kr;
- 低通滤波Q(z):常见做法是用常数Q=0.95放在延迟反馈支路里,也可以用一阶低通Discrete Filter实现,进一步提高鲁棒性。
并联型重复控制的数学形式如下:
u_rc(k) = u_rc(k-N) * Q + Kr * ( e(k) - e(k-N) * Q )
这里Q是0.95或者低通滤波器的输出,Kr是重复控制增益。e(k)-e(k-N)的意义是:只有当本周期误差和上一周期同一时刻不一致时,才做修正;如果完全一致,说明记忆波形已经和扰动匹配,保持原样输出即可。这就是“误差累计”的直观写法。
在Simulink里具体接线:
- 拉一个Discrete Delay模块,延迟步数填delayN,初始条件填0;
- 拉一个Gain或者Discrete Filter作为Q,这里先用常数Gain=0.95举例;
- 误差e经过一个Sum模块,实现 e(k) - DelayN(e)*Q;
- 上述结果乘以Kr;
- 再用一个Sum,把修正量和DelayN(u_rc)*Q相加,得到u_rc;
- 在u_rc输出端加Saturation限幅模块,限幅值跟随转矩指令限幅设置。
这个结构在Simulink里看起来像一个自环,很多人第一次搭会担心代数环问题。实际不必担心:因为有延迟模块隔开,k时刻的输出只依赖k-N时刻的量,闭环在时间上被延迟切断,不存在代数环,直接跑即可。
3.3 初始化脚本与参数设置:用脚本而不是手填,才能复现
我强烈建议模型所有关键参数都通过Matlab脚本设置,而不是在GUI模块里手填数值。原因很简单:采样周期、延迟周期、PI参数、Q系数、扰动频率之间存在耦合关系,手填一次就等于埋一个雷,改起来极易漏改。我的脚本大概长这样:
%% 基本参数 Ts = 1e-4; % 采样周期 100us fs = 1/Ts; % 采样频率 10kHz fp = 25; % 负载转矩扰动基频 Hz delayN = round(fs/fp); % 延迟拍数 400 %% 被控对象参数 J = 0.01; % 转动惯量 kg·m^2 Kt = 1.0; % 电磁转矩常数 Nm/A Kp = 2.0; % PI比例 Ki = 10; % PI积分 %% 重复控制参数 Kr = 0.8; Q = 0.95; %% 扰动参数 tOn = 0.3; % 扰动开始时间 TL_amp = 0.5; % 扰动幅值 Nm把脚本跑一遍,Simulink模块参数位置直接填变量名。比如Discrete Delay的Delay length填delayN,Gain填Kr,PI的P填Kp,I填Ki。这样一旦扰动频率变化,只需要改fp,重复控制支路的延迟周期自动跟随。求解器建议选择discrete或fixed-step discrete,固定步长等于Ts,具体原因等第5节详细说。仿真时长建议设为0.8到1.2秒,保证重复控制经历30到50个扰动周期,稳态效果才看得清楚。
4. 调参与仿真结果分析:三个关键参数决定控制品质
4.1 Kr、Q、N各自的作用:先懂原理,再动手调
重复控制的三个核心参数作用完全不同,不能只盯着一个调。
- 周期延迟N:决定内模频率是否准确。N必须是扰动周期的整数拍数,原则上越准越好。一旦N有偏差,相位对齐就失效,谐波压不下去。工程中若转速变化导致扰动频率漂移,就需要变周期重复控制或自适应方案。
- 增益Kr:决定每个周期误差被“消化多少”。Kr太大会引发振荡甚至发散,太小收敛慢。经验工作范围在0.3到1.0之间,一般从0.5起步试探。
- 低通Q(z):决定鲁棒性与稳态精度的折中。Q=1时是理想内模,稳态能力强但鲁棒性差;常数Q=0.95左右足以限制高频增益、提高稳定性,代价是极低频外会有极小残差;用低通滤波器更真实但实现更复杂。
一个实用的调参顺序是:先固定Q=0.95,Kr从0.3开始,每次增加0.1,观察转速误差波形,直到出现振荡就退回上一个值,然后回头微调Q,在0.9到0.98之间测试。我个人的经验是,只要Kr不超过1就能收敛,就没有必要在Q上追求复杂的滤波器结构。常数Q胜在简单、直观、好复现,工程调试时能少出很多幺蛾子。
4.2 仿真结果怎么看:从时域波形到FFT量化
仿真的目的不是“跑出一条好看的线”,而是量化抑制效果。我建议用To Workspace把转速误差e保存下来,扰动加入后取最后若干个周期做FFT。对比项设置两个:
- 场景A:只有PI控制,重复控制支路增益设为0;
- 场景B:PI+重复控制全部启用。
同样施加25Hz、0.5Nm的周期扰动,结果通常是这样:
| 指标 | 纯PI | PI+重复控制 |
|---|---|---|
| 转速波动峰峰值 | ±30 r/min左右 | ±3 r/min以内 |
| 25Hz基波误差幅值(FFT) | 较高 | 下降一个数量级以上 |
| 50Hz/75Hz谐波 | 尚可见 | 同步大幅下降 |
| 收敛时间 | 瞬时 | 约0.3到0.5s |
不要被“收敛时间”吓到,这里的收敛慢不是动态响应慢,而是“学习”需要时间。前几个周期里,重复控制输出还没被修正到合适波形,相当于还在离线学习阶段;但它不影响PI处理阶跃突变,因为PI仍然负责快速动态。这就是复合结构最舒服的地方:两者任务分离,互不干扰。
4.3 稳态精度与鲁棒性的折中:限幅和饱和处理不能忘
还有一个容易被忽视的点,是重复控制输出限幅后的积分饱和问题。由于重复控制器内部存在延迟环路,相当于一个慢积分器,一旦输出被限幅截断,记忆值会继续往饱和方向发展,最终导致退出饱和后的大幅过冲。
解决办法是在重复控制输出处加限幅,同时把限幅值设得比总转矩指令限幅更紧一些。比如总限幅±5Nm,重复控制输出限幅±3Nm。这样即使重复控制器饱和,它也只是转矩指令的一部分,不会让整个环路陷入深度饱和。工程里还可以对u_rc做“记忆值冻结”:当误差方向与修正方向长期不一致时,暂时关闭误差修正项。初学者先在Simulink里做限幅对比实验,就能直观看到差异。
5. 这些坑我踩过:周期延迟不匹配、初值漂移与仿真步长
5.1 周期延迟N算错了,谐波抑制直接失效
这是重复控制仿真里最容易出现的翻车现场。我在给一个压缩机负载建模时,一开始把扰动频率当成“转速×50%”来算N,结果谐波抑制一事无成,误差波形从平滑正弦变成了毛刺抖动。
排查过程其实很简单:先对扰动信号自己做一次FFT,确定它的真实基频fp,再用delayN=round(fs/fp)重新计算。但要注意,如果扰动频率不是转速基频的整倍数,比如齿轮齿数带来的啮合频率,这个fp可能是转速的17倍或31倍,那就老老实实按啮合频率来设N。仿真的第一步永远是确认“周期到底是多少”,而不是“我觉得大概是”。
另外,如果fp很低导致N很大,比如fp=5Hz、fs=10kHz,N=2000,延迟模块会占用较多内存,但Simulink完全可以处理,不用特别担心。更需要注意N的浮点舍入:round之后要回代检验一下实际对应的频率偏差。偏差小于扰动频率的0.1%基本无感,偏差大了就要用更高的采样率或者考虑小数延迟补偿。
5.2 初值漂移:启动瞬间的“学习冲击”怎么压下去
重复控制器的记忆数组初值默认是0。模型一运行,误差刚出现时,重复控制器会按“误差修正”逻辑在第一个周期内快速输出一个较大的修正量,如果Kr偏大,转速波形会出现一个尖峰。这不是理论问题,是初值处理问题。
我的处理习惯有两个:
- 在扰动源上加开关,让扰动在第0.3秒后才接入。这样前0.3秒内系统已经稳定在给定转速附近,误差为零,重复控制器的记忆值安稳地保持0,不会产生冲击;
- 如果扰动必须在0时刻就存在,就把重复控制器的使能信号接到一个延迟触发逻辑上,等系统进入稳态后再使能。Simulink里用Timer模块或者Step模块做使能条件即可。
这两个技巧看着不起眼,能省去大量排查“为什么启动瞬间转速过冲”的时间。
5.3 求解器与采样率:变步长连续仿真为什么会莫名其妙振荡
最后一个高频踩坑点是求解器设置。我在初版模型里用了ode45变步长连续求解器,跑出来的波形在加入重复控制后出现锯齿状振荡,而纯PI完全正常。排查到最后发现,问题不在算法,而在求解器与离散延迟模块之间的步长失配。
Discrete Delay模块的延迟拍数N是基于固定采样周期Ts定义的,但连续变步长求解器在动态过程中实际步长会变化,模块内部“一拍”对应的真实时间并不恒定,导致延迟时间长度在仿真过程中漂移。重复控制对相位异常敏感,延迟误差一点点就能破坏抑制效果。
解决方案很明确:把求解器设为discrete或fixed-step discrete,固定步长等于Ts。这样每个采样步严格等间隔,延迟模块一拍就是Ts,N拍的物理时长才恒定等于周期。如果你必须用连续模型,也可以用定步长ode4或ode5配合固定步长Ts,效果等同。这一点务必在仿真开始时一次设好,不要等跑出怪波形才回头查。
6. 从仿真到工程应用:可以往哪里继续深入
6.1 电流环和转速环中重复控制的差异:带宽位置决定效果上限
仿真模型里我把重复控制加在了转速环,这是初学者比较直观的理解方式。但在实际电机驱动里,重复控制的安放位置很有讲究。
放在转速环,优点是误差信号直接反映转速波动,物理意义明确;缺点是转速环带宽低,重复控制能覆盖的频率范围有限,对中高频转矩脉动的抑制效果会受到机械滤波作用的遮挡。
放在电流环则不一样。电流环响应快,重复控制可以覆盖更宽的频率范围,对电机本体转矩脉动(比如较高的齿槽谐波)效果更好。代价是电流环里的扰动数据量更大,对延迟周期N的计算要求更高。我的建议是:如果是负载周期扰动,来自机械负载,放转速环;如果是电机自身转矩脉动,放电流环。仿真时可以把同样的重复控制器复制到两个位置对比一下波形,这个对比过程本身就是学习重复控制最好的练习题。
6.2 变周期问题:当扰动频率随转速变化时怎么办
重复控制的内在逻辑要求扰动频率基本恒定,但工程中转速往往不是恒定值,负载扰动频率会跟着转速变。这个问题有两条工程路径:
- 转速前馈同步:当转速由指定指令决定时,扰动频率可以写成转速的函数,实时计算N并同步更新延迟模块。Simulink里可以用Variable Delay模块实现变延迟,参数为当前转速下的周期拍数;
- 自适应重复控制:引入自适应律在线调整N或Q,学术文献里很多,工程实现稍复杂。对多数应用来说,先把前馈同步做好就能解决90%的问题。
我自己的建议是,初学者先在定转速工况下跑通重复控制,理解N的物理含义后,再考虑转速变化。一上来就挑战变周期,学习曲线陡到容易放弃。
6.3 推荐学习路径:从仿真复现,到单参数扫描
如果你准备完整复现这个仿真,我推荐一个带有梯度的学习路径:
- 从第3章的模型开始,跑通PI+重复控制的完整闭环;
- 分别设置Kr=0.1、0.5、1.0,用Scope观察误差波形,对比收敛快慢与过振荡点;
- 把Q改为0.9、0.95、1.0,观察稳态误差与鲁棒性变化;
- 把fp改成30Hz、50Hz,注意N必须同步更新,看基波抑制是否依旧有效;
- 尝试把扰动源改成“方波周期扰动”,验证重复控制对非正弦周期扰动的抑制能力。
每一步都要做FFT量化分析,不要只看时域。这个流程走下来,你对重复控制的理解会比看十篇论文更扎实。
关于重复控制仿真,我想再多说一句体会。很多人第一次看到波形被压平时,会觉得“参数对了就完事”;但真正能迁移到工程上的能力,其实是判断“这个扰动到底是不是周期的、周期是多少、应该在哪一层闭环里去治理它”。我在实际项目里踩过的坑远比仿真多,比如把负载转矩脉动误当成阶跃扰动调PI,调了几天没效果;也见过有人把重复控制输出限幅忘掉,结果一启动就饱和顶到限幅值。这些都说明,仿真模型不只是一种工具,更是一种训练判断力的方式。希望这篇经验分享能帮你少走点弯路。