简介:这份资源是一套基于自抗扰控制(ADRC)理论编写的MATLAB程序,参考韩京清教授的自抗扰思想,面向自动化、车辆工程等方向的学习者与研究人员,重点解决汽车引擎控制中模型不确定、内外扰动难以精确建模的问题。压缩包内共1个文件,为m脚本类型,整体约563B,核心围绕扩展状态观测器(ESO)的实现展开,用于在线估计系统状态与未知扰动,为控制器提供补偿依据。目前已有1929人学习下载,说明其在ADRC入门与工程验证中具有一定参考价值。读者可借此理解ADRC相较传统PID无需精确模型、抗干扰能力更强的特点,掌握ESO的设计思路与MATLAB实现方式,并参考其仿真验证流程,将理论转化为可运行的控制器代码,为汽车引擎控制等实际场景提供可复用的案例与排错思路。
1. 从一条发散曲线说起:adrc自抗扰程序到底在解决什么
如果你调过 PID,大概率见过这种场面:阶跃给定下去,超调一大截,积分饱和后回调慢半拍,负载一扰动,输出又开始抖。参数改小响应肉,改大直接振荡,最后只能折中。adrc自抗扰程序(仿真成功)这个标题背后,真正被反复搜索的诉求就一句话——有没有一套不靠精确模型、能把扰动当场估出来再补掉的控制器,而且能在仿真里先跑通、看到波形。
ADRC(Active Disturbance Rejection Control,自抗扰控制)的思路和 PID 不一样。它不要求你知道被控对象是几阶、增益多少,而是把对象里所有说不清的部分——未建模动态、参数摄动、外部扰动——统统打包成一个“总扰动”,用一个扩张状态观测器(ESO)实时估它,再在控制量里减掉。剩下的部分被补偿成一个近似积分器串联,控制器就好设计了。这套东西适合谁?适合手里有对象但模型不准、工况会变、又不想每次重头辨识的人。仿真成功只是起点,关键是你能不能把 ESO 带宽、控制器带宽、跟踪微分器这几组参数调明白。
2. 把 ADRC 拆成三块:TD、ESO、SEF 各自在干什么
2.1 跟踪微分器 TD:给阶跃信号安排一条能走的过渡曲线
TD 的作用是安排过渡过程。给定一个阶跃,直接砸进控制器,误差瞬间很大,容易激发超调。TD 把阶跃变成一条有加速度上限的曲线,同时给出它的微分。对二阶对象,常用的是离散最速跟踪微分器:
import numpy as np def td_track(v, y, h, r): """ 离散跟踪微分器(最速控制综合函数 fhan 简化实现) v: 当前跟踪值 y: 当前跟踪值的微分 h: 采样步长 r: 速度因子,越大跟踪越快,但过大易振荡 返回: 下一时刻的 (v, y) """ d = r * h d0 = d * h # 误差 e = v - 0 # 这里 0 代表目标输入,实际调用时传入 setpoint # 简化 fhan,工程上常用形式 y_ = y + h * (-r * np.sign(v - 0)) # 占位,实际按 fhan 公式展开 return v + h * y_, y_上面这段是骨架,真正落地时 fhan 函数要按公式展开,包含对误差和微分的分段判断。参数上,r 决定过渡过程快慢,h 是采样步长,一般取仿真步长或稍大。r 太小过渡慢,系统响应被拖;r 太大 TD 自己先振荡,等于把噪声引进来。我一般先把 r 设到让过渡时间约为期望调节时间的 1/3 到 1/2,再微调。
2.2 扩张状态观测器 ESO:把“说不清的部分”估出来
ESO 是 ADRC 的核心。以二阶对象为例,状态是 x1(位置)、x2(速度),扩张出 x3 代表总扰动。离散线性 ESO 常见形式:
def eso_update(z1, z2, z3, y, u, b0, beta1, beta2, beta3, h): """ 三阶线性扩张状态观测器 z1,z2,z3: 观测器状态估计 y: 实际输出 u: 控制量 b0: 控制增益估计 beta1,beta2,beta3: 观测器增益 h: 步长 """ e = z1 - y z1_new = z1 + h * (z2 - beta1 * e) z2_new = z2 + h * (z3 - beta2 * e + b0 * u) z3_new = z3 + h * (-beta3 * e) return z1_new, z2_new, z3_new三个增益通常按带宽整定:把 ESO 看成三阶系统,配置成 (s+wo)^3 形式,则 beta1=3wo,beta2=3wo^2,beta3=wo^3。wo 是观测器带宽,是这套程序里最关键的参数之一。wo 越大估计越快,但对噪声越敏感;wo 太小估计滞后,扰动补不干净。仿真里可以先取 wo 为控制器带宽的 3 到 5 倍,再根据波形调。
2.3 状态误差反馈 SEF:补偿完再控
有了 z1、z2、z3,控制律就简单了。误差 e1 = v1 - z1,e2 = v2 - z2,控制量 u = kpe1 + kde2 - z3/b0。最后一项就是把总扰动估出来直接抵消。kp、kd 按闭环带宽 wc 配置,二阶常见 kp=wc^2,kd=2*wc。b0 是控制增益,估不准会直接影响扰动补偿效果,仿真里可以先用标称值,再扫一遍看鲁棒性。
3. 在仿真里跑通 ADRC:从零搭一个最小可复现例子
3.1 选对象与仿真框架
为了能复现,我一般用一个二阶对象:y'' = -ay' - by + c*u + d(t),其中 a、b、c 是标称参数,d(t) 是外部扰动。仿真用 Python 手写循环就够,不依赖额外工具箱。步长 h 取 0.001s,仿真时长 5s,给定阶跃在 0.5s 加入,扰动在 2.5s 加入。
import numpy as np import matplotlib.pyplot as plt h = 0.001 T = 5.0 N = int(T / h) # 对象标称参数 a, b, c = 1.0, 2.0, 1.0 # ADRC 参数 wc = 10.0 # 控制器带宽 wo = 40.0 # 观测器带宽 b0 = c # 控制增益估计 kp = wc**2 kd = 2 * wc beta1, beta2, beta3 = 3*wo, 3*wo**2, wo**3 # 状态 y, dy = 0.0, 0.0 z1, z2, z3 = 0.0, 0.0, 0.0 v1, v2 = 0.0, 0.0 r_td = 100.0 setpoint = 1.0 log = [] for k in range(N): t = k * h ref = setpoint if t >= 0.5 else 0.0 # TD 简化:一阶惯性逼近,工程上可替换为 fhan v1 += h * v2 v2 += h * (-r_td * (v1 - ref) - 2*np.sqrt(r_td)*v2) # ESO e = z1 - y z1 += h * (z2 - beta1 * e) z2 += h * (z3 - beta2 * e + b0 * u if k > 0 else 0) z3 += h * (-beta3 * e) # 控制律 e1 = v1 - z1 e2 = v2 - z2 u = kp * e1 + kd * e2 - z3 / b0 # 对象积分(欧拉) d = 0.5 if t >= 2.5 else 0.0 ddy = -a*dy - b*y + c*u + d dy += h * ddy y += h * dy log.append((t, ref, y, z3, u)) log = np.array(log) plt.plot(log[:,0], log[:,1], label='ref') plt.plot(log[:,0], log[:,2], label='y') plt.plot(log[:,0], log[:,3], label='z3') plt.legend(); plt.show()这段代码里 u 在第一次循环时还没定义,实际写的时候要把 ESO 更新放到控制律之后,或者给 u 初值 0。逻辑顺序是:先算 TD,再算 ESO(用上一拍 u),再算控制律,最后积分对象。参数说明:wc 决定闭环响应速度,wo 决定扰动估计速度,b0 要和实际控制通道增益接近。跑出来应该看到 y 跟踪 ref,2.5s 扰动加入后 z3 抬起,u 相应增大把 y 拉回。
3.2 参数整定的顺序与经验值
整定顺序我一般按这个来:先定 b0,用标称增益或做一次开环扫频估;再定 wc,让闭环带宽满足响应要求;最后定 wo,从 3wc 起步往上加,看 z3 的噪声和估计滞后。表格里给一组仿真里常用的起点:
| 参数 | 含义 | 起点建议 | 调整方向 |
|---|---|---|---|
| b0 | 控制增益估计 | 标称 c | 偏大补偿弱,偏小易振荡 |
| wc | 控制器带宽 | 期望调节时间倒数 | 越大越快,受噪声限制 |
| wo | 观测器带宽 | 3~5 倍 wc | 越大估计快,噪声敏感 |
| r_td | TD 速度因子 | 让过渡时间约 1/3 调节时间 | 过大 TD 自振 |
3.3 看波形判断问题出在哪
仿真跑完不要只看 y 跟没跟上。三条线要一起看:ref 和 y 的跟踪误差、z3 的估计波形、u 的控制量。如果 y 有稳态误差,先查 z3 有没有把扰动估出来,再查 b0 是否偏大。如果 y 高频抖,多半是 wo 太大把噪声放大了,或者 TD 的 r 太大。如果超调大,先看 TD 过渡是不是太陡,再看 kp 是否偏大。这套排查逻辑比盲调参数快得多。
4. 避坑与常见问题:仿真能跑不等于参数能用
4.1 现象:波形发散,数值越来越大
原因通常是 ESO 增益和步长不匹配。wo 很大时,beta3=wo^3 会非常大,显式欧拉积分在固定步长下容易数值不稳定。解决:要么减小 h,要么改用更稳的离散化方法,要么先把 wo 降下来。仿真里 h=0.001、wo=40 一般没事,wo 上到 200 就要小心。
4.2 现象:扰动加入后 z3 有反应但 y 回不来
原因多半是 b0 估得偏大,导致 -z3/b0 补偿量不够。解决:把 b0 调小一点试,或者直接检查控制通道实际增益。另一个可能是 wo 太小,z3 滞后,补偿总是慢半拍。
4.3 现象:稳态时 u 一直在抖
这是 ESO 把测量噪声或数值噪声估进 z3 了。解决:降低 wo,或者在 y 进入 ESO 前加一个低通滤波。注意滤波会引入相位滞后,wo 要相应再调。仿真里如果对象输出干净,抖动多半来自步长和增益组合,先查数值稳定性。
4.4 现象:TD 输出自己就振荡
r_td 给太大,TD 的最速控制综合函数进入切换频繁区。解决:把 r_td 降到过渡时间满足要求即可,不要追求“越快越好”。TD 是安排过渡的,不是用来提速的。
4.5 现象:换一组对象参数就崩
ADRC 的鲁棒性有边界。如果对象增益变化超过 b0 的容忍范围,或者延迟大到 wo 估不过来,照样翻车。解决:仿真里做参数扫描,把对象增益、时间常数、延迟各拉偏 30%~50%,看还能不能稳住。这一步不做,仿真成功只是假象。
5. 进阶:把 ADRC 从仿真推到能用的程度
仿真跑通之后,真正决定这套 adrc自抗扰程序能不能用的是三件事:离散化方式、带宽约束、以及和实际采样周期的匹配。我一般会做一轮“降采样”测试:把 h 从 0.001 逐步放大到 0.01、0.02,看波形什么时候开始恶化。这个临界步长就是你对实际控制器算力的最低要求。如果实际采样只能到 0.01,而仿真在 0.005 就开始抖,那这套参数上机大概率不行。
另一个进阶点是 b0 的在线估计。固定 b0 在工况变化大时不够用,常见做法是用递推最小二乘或简单的增益自适应,把 b0 慢慢往真实值修。但要注意,b0 一动,z3 的含义就变了,补偿项要同步处理,否则会引入新的振荡。我自己的习惯是:先固定 b0 跑通,再开自适应,且自适应增益给得很小,宁可慢不可抖。
验证方法上,除了看阶跃响应,我还会做三组测试:第一组,给定正弦跟踪,看相位滞后;第二组,对象增益突变 50%,看恢复时间;第三组,输出加 1% 幅值的白噪声,看 u 的抖动幅度。这三组过了,才敢说这套 ADRC 参数有上机价值。血泪经验是,仿真里干干净净的波形最容易骗人,加噪声、加延迟、加参数摄动之后还能稳,才算数。希望帮到你。
本文还有配套的精品资源,点击获取