news 2026/9/13 13:37:37

跟踪微分器TD详解:从PID误解到自抗扰仿真调参

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟踪微分器TD详解:从PID误解到自抗扰仿真调参

简介:面向自动控制领域学生与工程师,这份资料聚焦跟踪微分器在自抗扰控制中的仿真实现,解决建模、参数调整及干扰估计等实际应用问题。跟踪微分器可平滑提取微分信号、抑制高频噪声,是自抗扰控制器设计中的关键前置环节。包内共五十八个文件,以数据文件、C语言源码、Simulink模型和动态库文件为主,同时包含编译中间文件,覆盖算法设计、S函数编写到仿真验证的完整流程,压缩包约五百四十一KB。已有一千四百零三人学习下载。借助其中的模型与源码,可直观理解跟踪微分器如何提取快速信号并配合自抗扰控制器工作,也可参考C语言实现迁移到嵌入式场景。通过模型仿真与代码注释,读者能快速掌握参数调节技巧,缩短自抗扰控制的学习曲线。适合正在学习韩京清自抗扰理论、需要动手仿真验证或进行控制器调试的读者。

1. 跟踪微分器TD是自抗扰仿真里最先要装上的那个模块

做电机转速闭环时,PID里的微分项一加,示波器上全是毛刺;把微分增益调小,相位又不够,系统像在跟你较劲。问题不在PID参数,而在“从信号里取导数”这件事本身。TD跟踪微分器(Tracking Differentiator)正是为这个动作设计的动态系统:它输出一路v1紧紧跟上输入信号,再输出另一路v2作为导数估计,既不放大高频噪声,也不产生超调,断了工程师硬扛噪声的念想。在自抗扰控制ADRC里,TD通常装在参考输入前面,负责安排过渡过程并生成参考速度,是TD、ESO、控制律三大件中的第一环。标题里的TD不是PID整定单上的微分时间,而是一个算法模块。下面把它的离散递推式、仿真实现、参数规律和排错一次讲透,顺手把“pid中pb ti td”和“安路td”这两类同名不同义的名词也做个了断。

2. TD到底是不是微分时间?先和PID里的TD撇清关系

2.1 从PID参数单上的TD,说到跟踪微分器TD

看PID整定表格时常看到PB、TI、TD三项。PB是比例带,TI是积分时间,TD是微分时间,单位秒。经典PID输出式写为u=Kp(e+1/TI·∫e dt+TD·de/dt),这里的TD是个标量系数,乘的是误差的导数。很多工程师第一次看自抗扰仿真文章里的“TD”,直接套PID思路,认为TD是个微分增益,这是最大的误解来源。

跟踪微分器TD它是一个动态系统,不是一个参数。它的输入是参考信号v,输出两个状态:v1跟踪v,v2跟踪v的导数。结构写作:

x1'=x2 x2'=fhan(x1-v, x2, r, h)

fhan是最速综合函数,r是速度因子,h是滤波因子。x1对信号v的跟踪是渐近的、无超调的,x2则给出x1变化率的估计。内部没有独立的高通微分通道,而是靠两个积分器把导数“逼”出来,所以高频噪声不会被像差分那样直接放大。理解了这一层,再回头看“pid中pb ti td”,就知道两者只是缩写撞车,算法上没有任何继承关系。

2.2 为什么理想微分在自抗扰仿真里撑不住

理想微分器传递函数D(s)=s,幅频特性随频率线性增长,测量噪声在高频段被成倍放大。仿真里一旦给信号加上白噪声,微分输出几乎全是噪声。工程上常搭s/(Ts+1)做近似微分,但这又引入相位滞后,滞后量与噪声抑制直接冲突。

TD不走这个路线。它从轨迹规划的角度求最速跟踪,每步都在算一条受加速度上限约束的跟踪轨迹。由于加速度上限r的存在,TD对高频分量天然不响应,v2携带的是跟踪轨迹中的低频导数。换句话说,滤波能力不是附加的,而是来自加速度限制本身。仿真里这点很直观:输入带噪方波时,差分输出在跳变沿打出一串尖刺,TD输出是一个有界脉冲,高度和宽度随r变化。

TD在自抗扰控制里还有一个绑定任务:对参考输入安排过渡过程。参考输入若是阶跃,直接送进控制器会让初始控制量过大、对象受冲击;经过TD后,v1变成平滑轨迹,v2变成轨迹速度,控制器面对连续给定,执行器不再撞限。三种取导数的方式放一起对比:

方式结构噪声增益相位行为自抗扰仿真里的用途
理想微分 s纯高通频率越高放大越大无滞后基本不用,仿真发散
近似微分 s/(Ts+1)高通+低通组合高频受限T越大滞后越明显只做低位替代
跟踪微分器TD双积分+fhan受r限幅可控折中参考过渡、状态估计组合

2.3 顺带清理重名:安路td和TD有什么关系

搜索“跟踪微分器”时,偶尔会在结果里混进“安路td”。安路TD是FPGA厂商安路科技的一个产品系列代号,和本标题里的跟踪微分器TD没有算法关系。真正把两者联系起来的场景是:把自抗扰控制器部署到FPGA平台时,许多团队会把fhan函数做定点化,放到安路TD系列或同类FPGA芯片上。检索时看到“TD”字样,先看上下文是参数表还是算法图,别把器件选型和控制模块搞混。下面进入仿真实现。

3. 用Python跑通TD的最小离散递推式,先看到第一张曲线

3.1 fhan函数的离散表达式与逐行实现

韩京清提出的二阶最速跟踪微分器,每一步只需要一次fhan计算加两个积分累加。给定采样周期dt和滤波因子h0,离散递推式写为:

x1(k+1)=x1(k)+dt·x2(k) x2(k+1)=x2(k)+dt·fhan(x1(k)-v(k), x2(k), r, h0)

fhan内部按最速控制规律计算加速度,关键是把线性段和最速段拼成连续表达式,避免符号切换引起震荡。完整Python实现如下:

import math def _sign(x): return 1.0 if x > 0 else (-1.0 if x < 0 else 0.0) def fhan(y, x2, r, h0): # y = x1 - v,误差信号;h0 是滤波因子 d = r * h0 * h0 y0 = y + h0 * x2 # 由最速控制终点条件求出的根 a0 = math.sqrt(d * (d + 8.0 * abs(y0))) # 未限幅的目标加速度 a1 = h0 * x2 + (a0 - d) * 0.5 * _sign(y0) # 用 sy 判断 y0 是否落在线性段内 sy = (_sign(y0 + d) - _sign(y0 - d)) / 2.0 a = (h0 * x2 + y0 - a1) * sy + a1 # 用 sa 判断 a 是否进入饱和段 sa = (_sign(a + d) - _sign(a - d)) / 2.0 return -r * (a / d - _sign(a)) * sa - r * _sign(a) class TrackingDifferentiator: def __init__(self, r, h0, x1_0=0.0, x2_0=0.0): self.r = r self.h0 = h0 self.x1 = x1_0 self.x2 = x2_0 def step(self, v, dt): # 先算最速控制量,再做两次积分更新状态 u = fhan(self.x1 - v, self.x2, self.r, self.h0) self.x1 += dt * self.x2 self.x2 += dt * u return self.x1, self.x2

代码说明:d=r·h0²决定最速切换边界;|y0|≤d时,先生成一个线性控制量,避免符号函数在原点抖动;a0来自最优控制终点条件,其开方运算在FPGA定点化时通常换成CORDIC或查表。调用step时,先算fhan再积分,顺序不能颠倒,否则x2会用更新后的值,离散模型变成隐式更新。

3.2 给TD一个阶跃输入,跑出跟踪和微分曲线

仿真脚本如下,输入在0.2s处从0跳到1:

fs = 1000 # 仿真频率 Hz dt = 1.0 / fs T = 1.0 n = int(T * fs) td = TrackingDifferentiator(r=20.0, h0=10.0 * dt, x1_0=0.0, x2_0=0.0) t_list, v_list, x1_list, x2_list = [], [], [], [] for k in range(n): t = k * dt v = 1.0 if t >= 0.2 else 0.0 x1, x2 = td.step(v, dt) t_list.append(t) v_list.append(v) x1_list.append(x1) x2_list.append(x2) print(f"x1_end={x1_list[-1]:.4f}") print(f"x2_max={max(x2_list):.4f}") print(f"x2_min={min(x2_list):.4f}")

运行结果里x1最终停在1.0附近,不超调,上升时间约0.4s量级;x2是一段正脉冲,峰值约20,这就是阶跃输入对应的等效导数。脉冲越窄,r越大,跟踪越快。这里h0取10倍dt:如果h0=dt,滤波几乎等于没有,x2会明显抖动;如果h0=20倍dt,曲线平滑但滞后又偏大。用这个脚本改不同的r和h0,能比空看公式更快建立手感。

3.3 从曲线形状判断实现是否正确

拿到波形先看三点。第一,x1单调上升,不超调,最终停留在输入值上;第二,x2是单脉冲,正峰值不超过r,脉冲宽度随r增大而变窄;第三,输入是方波时x2在每个跳变沿各出一个正负脉冲,不会像差分那样在跳变后留下持续抖动。若看到x1振荡或x2持续高频抖动,先从h0取太小、r取太大查起。h0过小,fhan进入饱和段过于频繁,离散步长下等效为一个高增益开关,噪声全被激发;r过大则v2峰值超过执行器能力,闭环后表现为控制量频繁撞限。把这些现象和参数对应起来,调参时就不会盲目。

4. 自抗扰仿真里TD的4个必调参数与3个坑

4.1 先记住这张参数表,调参顺序就出来了

参数含义增大后的影响建议范围
r速度因子,决定最大跟踪加速度跟踪快,x2幅值大,噪声容忍度下降按执行器限幅的5~10倍推算
h0滤波因子,决定最优切换的线性区范围滤波强,跟踪变慢,相位滞后增大5~20倍采样周期
dt采样周期或调用间隔影响离散误差,取大后fhan线性区含义改变不高于对象动态周期的1/20
x1(0)TD输出初值与输入初值不符时会产生大跳变对齐v(0)

调参顺序固定:先定dt,再按执行器允许的最大控制量估算r,然后按噪声水平选h0,最后检查x1是否超调。若闭环后系统振荡,多数情况不是TD的问题,而是后面的ESO带宽或控制器增益有问题。TD只负责提供干净的参考轨迹和参考导数,背不了整条回路的锅。

4.2 坑1:把h0当成仿真步长用

很多实现把fhan里的h0直接传成dt,然后抱怨噪声滤不掉。原因在于h0同时出现在d=r·h0²和线性区判断里,当h0=dt时,算法只是普通离散欧拉逼近,对噪声完全开放。我一般让h0取10倍左右采样周期起步:h0越大x2越平滑,但x1的相位滞后按约h0/2的时间量级增加。在采样周期有抖动的系统中,必须保证dt固定,否则h0与dt不一致会引入额外离散误差。仿真里先把求解器设成固定步长,再谈滤波效果,顺序不能反。

4.3 坑2:初值不同步带来的第一拍尖峰

TD是带初值的动态系统。输入v从0开始,TD的x1(0)默认也是0,没问题。但工程场景常是:给定从当前转速2000rpm开始,TD初值却是0,第一拍误差为2000,fhan直接输出满幅加速度r,x2瞬间冲到很大的值。这个尖峰进入ESO后,总扰动估计会饱和,控制器输出撞限,实际系统表现为起动瞬间明显前冲。

解决方法是每次启动或参考值切换时,把TD的x1重置为当前输入信号,x2重置为当前速度估计。若输入本身是阶跃,则保留x1重启前的值,再让TD自然过渡。代码里就是在初始化时传x1_0=v(0),而不是默认0。

4.4 坑3:Simulink代数环与求解器不匹配的异常自激

Simulink里若把TD用连续传递函数形式搭,再把输出反馈回去,可能连成代数环,仿真步长会自动切小,出现低频自激。更常见的是用变步长求解器跑离散TD,状态在不同步长下被重复计算,曲线像随机扰动。规范做法是:

Solver: Fixed-step Fixed-step size: 0.001

在Simulink中把TD写成MATLAB Function,状态用Unit Delay保存,按离散顺序逐拍更新。MATLAB Function核心代码如下:

function [x1_next, x2_next] = td_step(x1, x2, v, r, h0, dt) %#codegen u = fhan(x1 - v, x2, r, h0); x1_next = x1 + dt * x2; x2_next = x2 + dt * u; end function u = fhan(y, x2, r, h0) d = r * h0 * h0; y0 = y + h0 * x2; a0 = sqrt(d * (d + 8 * abs(y0))); a1 = h0 * x2 + (a0 - d) * 0.5 * sign(y0); sy = (sign(y0 + d) - sign(y0 - d)) / 2; a = (h0 * x2 + y0 - a1) * sy + a1; sa = (sign(a + d) - sign(a - d)) / 2; u = -r * (a / d - sign(a)) * sa - r * sign(a); end

MATLAB的sign(0)返回0,和Python里的处理一致。使用S-Function时,若把状态当作连续状态输出,又把输出即连回输入,也会触发代数环;正确做法是用DWork或Unit Delay保存离散状态,每个调用周期更新一次。做FPGA移植时同理,例如在安路TD系列芯片上定点化,h0用整数周期数表示,sqrt用CORDIC展开,避免浮点运算吃满DSP块。

另外,TD参数和“pid中pb ti td”里的微分时间没法换算,自抗扰里没有微分增益的概念,直接用误差做d控制,容易把TD的v2当成PID的D通道来加,结果系统抖得厉害。v2只作为参考速度前馈或状态反馈,不承担噪声抑制任务。

4.5 用三组输入验证TD是否调到可用状态

别只看阶跃曲线。有效的验证方式是同时跑三个输入:阶跃v=1验证无超调跟踪和x2峰值;斜坡v=t验证稳态误差,理想情况下x2稳态等于1;正弦v=sin(2πft)验证相移和幅值误差。上升时间可以由最速轨迹理论估算,理论值约为2/√r,实测值略大,参数扫描脚本如下:

for r in [10.0, 40.0, 160.0]: td = TrackingDifferentiator(r, h0=0.01) # 重复3.2的阶跃循环,记录上升时间、x2峰值、相位滞后 print(r, rise_time, peak, phase_deg)

不同r的趋势如下:

r理论最小上升时间2/√rx2峰值相位滞后趋势
10约0.63s不超过10较大
40约0.32s接近40中等
160约0.16s接近160较小

r增大,x2峰值同步增大,相位滞后减少。但这个趋势只在h0不变时成立,若同步加大h0,相位损失会重新变大。TD调好的标准不是“最快”,而是让整个自抗扰回路的控制量不撞限、噪声不放大、相位损失能被ESO补偿。

5. 自抗扰控制里把TD和ESO串起来的最小实现

5.1 ADRC三部件里TD的位置和连接关系

二阶自抗扰控制器回路中,TD放在给定端,ESO放在被控对象测量端,两者一起提供完整的系统状态。连接顺序:

参考输入r→TD(r)→v1,v2 对象输出y←ESO(z1,z2,z3) 控制律 u0 = kp·(v1-z1)+kd·(v2-z2) u = u0 - z3/b0

TD负责过渡过程和干净的参考速度v2;ESO估计对象内部状态z1、z2以及总扰动z3。控制律用误差和误差导数,对应PID的P和D,但积分功能由总扰动补偿完成,不直接存在于误差通道。这个结构决定了TD快慢必须与ESO带宽匹配。

5.2 一个最小完整仿真循环,用Python一条龙跑下来

ESO类先写上,用线性扩张状态观测器:

class ESO: def __init__(self, w0=50.0, b0=1.0, dt=0.001): self.w0 = w0 # 观测器带宽 self.b0 = b0 # 对象控制增益估计 self.dt = dt self.z1, self.z2, self.z3 = 0.0, 0.0, 0.0 def update(self, y, u): # 增益按带宽配置:3w0, 3w0^2, w0^3 e = self.z1 - y self.z1 += self.dt * (self.z2 - 3.0 * self.w0 * e) self.z2 += self.dt * (self.z3 - 3.0 * self.w0**2 * e + self.b0 * u) self.z3 += self.dt * (-self.w0**3 * e) return self.z1, self.z2, self.z3

ESO带宽w0越大,估计越快但噪声越高。b0是控制增益的粗略估计,误差会被z3吸收,不需要精确标定。TD与ESO带宽匹配原则:TD的跟踪带宽一般取ESO带宽的1/3到1/2。若TD太快,过渡过程形同虚设且微分噪声进来;若ESO比TD快太多,参考跟踪能力受限。

把TD、ESO、控制律、被控对象放进一个循环:

kp = wc * wc kd = 2.0 * wc wc = 20.0 # 控制器带宽 td = TrackingDifferentiator(r=50.0, h0=10.0*dt, x1_0=0.0, x2_0=0.0) eso = ESO(w0=80.0, b0=2.0, dt=dt) y = 0.0 yd = 0.0 for k in range(n): t = k * dt ref = 1.0 if t > 0.5 else 0.0 # TD生成参考状态 v1, v2 = td.step(ref, dt) # 控制器与扰动补偿 u0 = kp * (v1 - eso.z1) + kd * (v2 - eso.z2) u = u0 - eso.z3 / eso.b0 # 简单被控对象:二阶积分加未知摩擦力 ydd = 0.5 * yd + 2.0 * u yd += dt * ydd y += dt * yd # ESO根据测量y和实际输入u更新 z1, z2, z3 = eso.update(y, u)

这个循环把TD和ESO完整串起来。TD的输出v1、v2给参考通道,ESO的z1、z2给反馈通道,z3作为总扰动补偿实时抵消模型不确定性。观察y曲线,应无超调、稳态无静差。

5.3 串起来后的第1个坑:v2直接往后接的限幅问题

TD的v2在阶跃输入时会出现高度接近r的脉冲,这个脉冲若直接作为速度前馈叠加,启动瞬间控制量远大于稳态。仿真里表现为起动冲击,实际系统里可能触发过流保护。常见处理是在v2后面加一阶滤波或限幅,但更推荐限制r本身。r应小于执行器峰值加速度的80%,而不是越大越好。调参时先看x2曲线峰值,再看对象输出曲线是否过冲。自抗扰仿真有个好处:TD和ESO参数可以分开调试。先关掉ESO,令z1=y、z2取差分、z3=0,单独看TD响应;再关掉TD,检查ESO跟踪效果。分开调通再合并,定位问题会快得多。

6. 验证TD实现正确与否的干净技巧:斜坡测精度,正弦测相位

长期困扰自抗扰仿真调试的问题,是TD在跑但不知道是“真跟踪”还是“滤波后的巧合”,单看阶跃曲线不够。我用斜坡-正弦联合检验法,两次运行就能把动态响应和稳态误差分开。

第一次运行斜坡输入v=t。若TD实现正确,x1最终以斜率1跟踪输入,稳态滞后约为h0/2,x2收敛到1.0附近。检查x2最后500点的统计量:均值与1的误差大于0.01,说明r偏小或h0偏大;均值为1但方差大,说明h0偏小噪声未压住。统计代码片段如下:

import numpy as np # 假设最后一次斜坡运行已得到 x2 列表 tail tail = x2_list[-500:] err = np.mean(tail) - 1.0 print(f"err_mean={err:.4f}, x2_std={np.std(tail):.4f}")

第二次运行正弦v=sin(2πft),记录v2与理论导数信号的相位差和幅值。命令行写法:

python td_validation.py --input ramp python td_validation.py --input sine --freq 5 --r 50

输出示例:

ramp : err_mean=0.0062, x2_std=0.0210 sine : phase_deg=-8.5, amp_ratio=0.94

快速判定不等式:相位损失超过20度或幅值比例低于0.9时,直接提高r而不是盲目增加h0;相反,相位满足但x2高频抖动明显,再单独加大h0。另外注意fhan里的符号边界,sign(0)必须返回0,不能把它改写成sign(x)/|x|,那样会把原点附近的导数全打乱。接入完整ADRC系统后,再做一次初值对齐测试:把v(0)设为100,确认第一个控制周期内控制量不出现幅值脉冲。通过这两次验证和一次对齐测试,就能确认TD实现正确、参数在可用范围,后续问题不会甩锅给跟踪微分器。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:37:17

嵌入式开发强度本质:硬件约束下的工程直觉训练

1. “实话难听”不是态度问题&#xff0c;是嵌入式工程师的生存反射弧“实话难听”这四个字&#xff0c;放在2026年谈嵌入式入行&#xff0c;已经不是一句情绪化吐槽&#xff0c;而是一条被无数项目现场反复淬炼出的生理反应路径——它直接对应着你第一次在示波器上看到UART波形…

作者头像 李华
网站建设 2026/9/13 13:35:11

AI编码协议栈:Skills、MCP与Rules的协同架构解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:33:15

MATLAB地震射线追踪正演:从程函方程到Marmousi模型实践

简介&#xff1a;基于MATLAB实现的二维射线追踪程序&#xff0c;是一套面向地震声波正演模拟的源代码包&#xff0c;适用于地球物理、地震勘探、声波传播等方向的教学演示与科研复现。压缩包共30个文件&#xff0c;包含28个M脚本、1个MAT数据文件和1个Markdown说明文档&#xf…

作者头像 李华
网站建设 2026/9/13 13:32:46

I3C仿真调试实战:从协议原理到PGY I3C-EX-PD全流程详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:31:54

光猫桥接与超管配置:千兆宽带提速的关键一步

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华