资讯动态

RBF神经网络自适应PID的方波跟踪控制设计与仿真

发布时间:2026/10/22 14:56:55 来源:尧图企业网站定制
先说个真实感受PID 这玩意儿教科书上三五行就能写完公式可真要拿它去跟踪方波信号、负载一变就露怯的场景估计不少做过控制仿真的朋友都体会过那种调了半天 Kp、Ki、Kd换一组工况又得重来的无力感。传统固定增益 PID 在对象模型清晰、工作点固定的时候确实很能打可一旦对象带非线性、参数时变或者输入信号像方波这样存在高频突变固定增益就顾头不顾尾了。我前阵子把 RBF 神经网络和 PID 结合做了一个自适应控制器专门用来做方波信号跟踪效果比想象中稳整个过程也踩了不少坑。这篇文章就把完整的设计思路、推导过程、仿真代码和调参心得都整理出来想自己复现的同学可以直接照着做。自适应 PID 的核心逻辑说白了很简单PID 的三个增益不再是一成不变的常数而是根据系统实时运行状态在线调整。而让自动调增益这件事能落地的关键就是需要一个足够快的、能在线感知系统状态的模型。RBF 神经网络正好干这个活——它结构简单、局部逼近能力强、收敛速度快特别适合嵌进闭环里当在线辨识器。整个程序跑起来之后方波信号从一个电平跳变到另一个电平的瞬间控制器的输出响应、超调量、稳态误差表现都比固定 PID 好得多这就是兴奋点所在。1. 为什么方波跟踪会让固定 PID 露怯从对象特性聊起方波信号看起来简单无非是高电平、低电平来回切换但恰恰是这个突变特性把固定增益 PID 的短板暴露得干干净净。一个典型的二阶惯性加非线性环节在方波上升沿到来时误差突然变大PID 输出需要快速拉起可如果 Kp 给大了上升沿确实跟得快但到达目标电平之后又容易超调、振荡Kp 给小了超调倒是没了上升沿又变得拖拖拉拉。这就形成了典型的快与稳的矛盾。工程上更麻烦的是很多被控对象的增益和工作点是会漂移的。比如电机驱动系统负载转动惯量一变对象的等效时间常数就变了又比如温度控制对象随着工况温度上升散热系数也在变化。固定增益 PID 是在某个工作点上调出来的最优解一旦工况偏离这个最优就变成了次优甚至不优。我在之前的仿真里试过一组工况变化对象增益从 1.0 变化到 1.5固定 PID 的超调量从 8% 直接飙升到 22%稳态时还出现了肉眼可见的等幅振荡。方波跟踪最折磨 PID 的地方在于它强迫控制器在两个不同的目标值之间反复横跳而且切换时刻的误差变化率非常大。传统的微分类控制在这种时刻很容易产生微分突变——误差 er 在跳变瞬间从 0 变为一个较大值微分项瞬间输出一个尖峰把执行机构顶到饱和。很多同学在做方波跟踪仿真时遇到控制量剧烈抖动其实很大程度不是算法错了而是微分项处理不当。自适应 PID 的一个额外好处就是通过在线调整增益可以让微分项在跳变时刻自动收敛锋芒而不是靠人为去设置一个固定的微分滤波系数。从这个角度出发控制方案至少得具备两个能力第一能够在线感知系统当前的状态或者说灵敏度信息第二能够根据这个感知结果实时修正增益。神经网络嵌入控制系统的价值在这里就非常明显了它能通过输入输出的实时数据近似出系统当前时刻的 Jacobian 信息这个信息正是增益在线修正所需要的核心输入。2. RBF 神经网络的核心起效链路局部逼近与雅可比在线辨识RBF 神经网络的全称是径向基函数神经网络它属于前馈网络的一种结构非常规整输入层、隐含层、输出层隐含层神经元激活函数用的是径向基函数最常见的是高斯函数。它最吸引人的特性是局部逼近能力——每个隐含层节点只对输入空间的一个局部区域产生响应离节点中心越近响应越强越远则越弱。这个性质和 BP 网络那种全局逼近的思路完全不同带来的直接好处就是学习速度快、不容易陷入局部极小而且非常适合在线实时更新。我用它做自适应 PID 的在线辨识器网络结构设计成 3 输入、单隐含层、单输出。输入取的是系统实时的控制量、系统输出以及上一时刻的输出输出则用来估算被控对象的 Jacobian 信息也就是系统输出对控制输入的偏导数。这个 Jacobian 是自适应律里最关键的桥梁它告诉 PID 参数修正方向控制量变化多少会引起输出变化多少、方向是什么。如果连方向都搞错自适应不仅不能改善性能反而会把系统调发散。隐含层节点数我最终取了 5 个这个数量是根据仿真试出来的。节点太少了网络逼近能力不够辨识出来的 Jacobian 噪声很大节点太多了计算量上去了而且容易出现过拟合在实际闭环控制中表现为参数抖动加剧。每个隐含层节点都有两个核心参数中心 c 和宽度 b有时候也叫基宽。高斯基函数的形式是$$ \varphi_j \exp\left(-\frac{|x - c_j|^2}{2b_j^2}\right) $$也就是说输入向量 x 离第 j 个节点中心 c_j 越近这个节点的输出就越接近 1离得越远输出指数级衰减到接近 0。整个网络的前向输出就是隐含层输出的加权和。在实际程序里网络权值、中心、宽度都可以用梯度下降法修正但对于控制器设计来说最常用的做法是只在线修正输出层权值中心和宽度在初始化时按输入范围均匀设置然后固定不动。这么做的原因有两个一是减少在线计算负担二是避免中心和宽度也参与在线更新时系统响应过激、稳定性变差。做方波跟踪仿真时RBF 网络的输入输出采样周期和控制周期保持一致每个采样点都做一次前向计算和权值更新更新律用最简洁的一步梯度下降。说实话在仿真的时间尺度下这个计算量完全可以忽略但在嵌入式实时环境里单步更新带来的延迟也就是微秒级到几十微秒级完全够用。这也是 RBF 相比 BP 在实际嵌入式控制器里更受青睐的原因——收敛快、结构小、算得快。那么Jacobian 到底怎么从 RBF 网络里挖出来做法是这样的网络输出 ym 是隐含层节点输出 h_j 的线性加权和权值为 w_j那么 ym 对输入向量中某一维比如控制量 u求偏导可以通过链式法则计算得到的结果就是该时刻系统输出对控制输入的近似灵敏度。具体公式在后面的自适应律推导里会完整给出来这里先建立直觉这个 Jacobian 本质上就是一个实时在线测出来的系统增益它告诉自适应控制器当前应该用多大的力度去调整。3. 自适应律推导PID 三参数在线修正的三个核心公式自适应 PID 的控制律在形式上跟传统 PID 没有区别还是标准的增量式结构$$ u(k) u(k-1) K_p \left[e(k) - e(k-1)\right] K_i , e(k) K_d \left[e(k) - 2e(k-1) e(k-2)\right] $$误差定义为 $e(k) r(k) - y(k)$其中 $r(k)$ 是方波参考输入$y(k)$ 是系统输出。控制量直接作用到被控对象上同时作为 RBF 网络的一个输入。整个闭环的核心不在这条控制律而在后面三条参数修正律也就是 PID 三个增益的在线更新公式。定义性能指标为误差平方的一半$$ J \frac{1}{2} e^2(k) $$为了让 J 最小化三个增益沿负梯度方向修正修正步长由各自的学习率决定。以 Kp 为例梯度计算需要先做链式展开$$ \frac{\partial J}{\partial K_p} \frac{\partial J}{\partial y} \cdot \frac{\partial y}{\partial u} \cdot \frac{\partial u}{\partial K_p} $$其中 ∂J/∂y -e(k)∂y/∂u 正是前面说的 Jacobian用 Jacobian 符号表示而 ∂u/∂Kp 则是控制律对 Kp 的偏导。把这几项组合起来就得到 Kp 的修正律。Ki、Kd 的推导完全同理区别只在于控制律对各自增益的偏导项形式不同。最终三条在线修正公式写成$$ \Delta K_p \eta_p , e(k) , \text{Jac} , \left[e(k) - e(k-1)\right] $$$$ \Delta K_i \eta_i , e(k) , \text{Jac} , e(k) $$$$ \Delta K_d \eta_d , e(k) , \text{Jac} , \left[e(k) - 2e(k-1) e(k-2)\right] $$三条公式里的共同核心就是 Jacobian 项。RBF 网络在这里的任务就很明确了每个采样周期都更新网络权值然后用训练好的网络计算出一个当前的 Jacobian 值把它代入上面三条式子三个增益各自动态调整一步。整套逻辑下来相当于给 PID 装了一个实时增益调度器但这个调度器不用人工查表而是完全靠数据驱动。这里要解释一个初学者经常问的问题既然已经有了 Jacobian为什么还用 PID而不是直接用神经网络做逆模型控制我的体会是PID 的工程鲁棒性和可解释性仍然是神经网络无法替代的。神经网络负责感知灵敏度PID 负责执行控制策略两者各干各的强项。当系统模型不精确、扰动不可测时PID 的积分项仍然能提供有力的稳态调节能力而神经网络只需要保证 Jacobian 的大致准确即可——过于精确反而没有必要。在实际仿真中三个学习率 $\eta_p$、$\eta_i$、$\eta_d$ 的取值并不需要完全相同。通常 Kp 和 Kd 的学习率可以取稍大一些Ki 的学习率取得小一点避免积分增益在动态过程中出现过快调整导致稳态振荡。我最终使用的组合是 $\eta_p 0.4$、$\eta_i 0.1$、$\eta_d 0.3$在此基础上加入动量项动量系数取 0.05用来平滑增益的修正轨迹防止单个采样点的噪声把增益带偏。4. 仿真环境搭建与程序整体框架Simulink 和纯 M 脚本两条路都试过自适应 PID 的实现路径有两条一条是 Simulink 搭模型另一条是纯 MATLAB 脚本做离散仿真。两条路我都走过这里把各自的优劣说清楚方便大家按自己的习惯选。Simulink 方案的好处是图形化、直观被控对象、控制器、神经网络辨识器每个模块都能单独看波形特别适合先验证算法逻辑再做修改。但 RBF 神经网络用 S-Function 嵌入 Simulink 会比较繁琐MATLAB Function 模块配合全局变量保存网络状态也不够优雅调试信息一多就乱了。所以我更推荐先写纯脚本把算法链路打通之后再做 Simulink 移植。纯脚本方案的框架非常简单整体可以分成三大块被控对象模拟自己定义一个差分方程模拟一个非线性、含惯性环节的离散系统RBF 网络辨识器维护网络结构参数完成前向计算、权值更新和 Jacobian 输出自适应 PID 控制器基于误差序列计算控制量并根据 Jacobian 更新三个增益整个仿真做成一个固定步长的循环步长取 1ms仿真时长为 5s方波信号周期 1s幅值在 1 和 -1 之间切换。被控对象我采用的是带输出限幅的非线性二阶系统离散差分方程形式写出来大概是这样$$ y(k) a_1 y(k-1) a_2 y(k-2) b_1 u(k-1) b_2 u(k-2) f_{\text{nl}}(y(k-1)) $$其中 $f_{\text{nl}}$ 是一个非线性项用来模拟实际系统中常见的死区或饱和特性。这样的对象比纯线性二阶系统更能体现自适应 PID 的价值因为对象本身就带非线性变增益的属性固定增益 PID 在这种对象上表现确实一般。RBF 网络的参数初始化是另一个容易被忽视的环节。隐含层中心 c 的取值应覆盖输入变量的动态范围我这里输入向量是三维各维度物理意义和控制量 u 有关范围在 [-2,2] 附近所以中心在 [-2,2] 内均匀分布宽度 b 取 1.0。输出层权值 w 初始化为 0向量的初始学习率设定为 0.3。初始化不合理的话最直观的现象就是前几百个采样点内系统输出剧烈波动因为网络还没认识系统的输入输出关系给出的 Jacobian 误差很大。代码整体拓扑用伪代码表示是这个样子初始化 PID 参数、RBF 网络参数、被控对象状态、输出历史序列主循环 for k 1:N计算参考输入 r(k)——方波发生器计算误差 e(k) r(k) - y(k)RBF 前向计算得到辨识输出 ym(k)更新 RBF 输出层权值计算 Jacobian即系统输出对控制输入的偏导估计用 Jacobian 代入三条自适应律更新 Kp、Ki、Kd用更新后的增益和控制律公式计算控制量 u(k)将被控对象的状态向前推进一步得到新的 y(k1)记录所有需要绘图的中间量进入下一轮循环5. 关键代码逐段拆解从梯度采集到参数更新纯脚本的核心代码不算长一百多行就能跑通整套逻辑。我把自己调试通过的版本按功能块拆开来讲每一段我都标注了它解决什么问题、有哪些容易写错的地方。第一段是初始化。PID 初值我先给了一组保守参数Kp0.6、Ki0.03、Kd0.1这三个值在固定 PID 下也能让系统不发散但跟踪效果一般。这样设置的好处是自适应启动的初始状态不是一个危险状态网络有足够的缓冲时间去调整增益而不是一上来就要应付发散局面。% 初始化 ts 0.001; T 5; N T / ts; r_amp 1.0; freq 1.0; % PID 状态 Kp 0.6; Ki 0.03; Kd 0.1; e_prev 0; e_prev2 0; u_prev 0; % 被控对象状态 y_prev 0; y_prev2 0; u_prev2 0; u_prev1 0; % RBF 网络参数 node_num 5; w zeros(node_num, 1); c linspace(-2, 2, node_num); b ones(node_num, 1) * 1.0; lr_w 0.3; lr_p 0.4; lr_i 0.1; lr_d 0.3; momentum 0.05; dw_prev zeros(node_num, 1);接下来进入主循环。每个采样周期里的第一步是计算方波参考输入for k 1:N t k * ts; if mod(t, 1.0) 0.5 r r_amp; else r -r_amp; end e r - y_prev; ... end方波用上面的 mod 判断方式实现非常干净——每个 1s 周期内前半段输出 1后半段输出 -1。这里有个小细节如果希望方波上升沿更陡峭可以认为从算法层面不需要额外处理因为离散系统的采样间隔本身已经把这个跳变体现出来了。第二步是 RBF 网络的前向计算和权值更新。输入向量由控制量、前一时刻输出和前两个时刻输出组成。这里需要特别强调输入向量的组成不是拍脑袋定的必须包含控制量 u因为 Jacobian 是输出对 u 求偏导输入里没有 u 就根本算不出这个偏导。输出 y 的历史值是为了让网络能感知系统动态特性相当于给网络提供了系统记忆。x [u_prev; y_prev; y_prev2]; h exp(-sum((x - c).^2, 1) ./ (2 * b.^2)); ym w * h; % 权值更新 error_rbf y_prev - ym; % 真实输出减去网络输出 dw lr_w * error_rbf * h momentum * dw_prev; w w dw; dw_prev dw;RBF 权值更新公式存在不少资料里但有一个细节很少有人提训练误差到底用哪个输出一种是用被控对象输出的实测值 y 作为训练目标另一种是用参考输入 r 作为训练目标。实际调试下来用实测值 y 更合理因为 RBF 要做的是辨识对象而不是拟合目标轨迹。第三步是 Jacobian 计算。给出符号公式可能有点抽象我直接放代码并且解释每一步在干什么% 计算 Jacobiandy/du Jacobian 0; for j 1:node_num % h_j 对 x(1)即 u求偏导 dh_du h(j) * (c(j,1) - x(1)) / (b(j)^2); Jacobian Jacobian w(j) * dh_du; end这段循环展开了雅可比计算的完整过程。高斯函数对输入的偏导形式是这样的h 本身的值乘以中心与输入之差再除以基宽的平方。这个表达式在高斯函数里是标准结果不需要重新推导但要注意符号——中心减输入还是输入减中心取决于你写的距离形式方向和符号必须统一否则 Jacobian 正负号搞反整个自适应方向就反了。第四步是自适应律更新和 PID 控制量计算% 更新 PID 增益 dKp lr_p * e * Jacobian * (e - e_prev); dKi lr_i * e * Jacobian * e; dKd lr_d * e * Jacobian * (e - 2*e_prev e_prev2); Kp Kp dKp; Ki Ki dKi; Kd Kd dKd; % 计算控制量增量式 PID du Kp * (e - e_prev) Ki * e Kd * (e - 2*e_prev e_prev2); u u_prev du; % 限幅防止控制量过大 u max(-5, min(5, u));观察这几行代码能发现增益更新和控制量计算用的是同一组误差序列。这个顺序很重要——先更新增益再用新增益计算控制量相当于当前时刻的增益服务于当前时刻的控制。如果顺序反了就会出现一个周期滞后虽然不至于发散但收敛速度和跟踪精度都会变差。第五步是被控对象的更新。我这里模拟的是一个带有非线性的二阶离散系统y 1.2 * y_prev - 0.4 * y_prev2 0.8 * u_prev 0.2 * u_prev2 0.3 * tanh(y_prev); % 记录数据 e_prev2 e_prev; e_prev e; u_prev2 u_prev1; u_prev1 u_prev; u_prev u; y_prev2 y_prev; y_prev y;非线性项我用 tanh 函数模拟它的输出在 [-1,1] 之间平滑饱和会让系统的等效增益随输出幅值变化——这正是增益漂移的数字化代表。固定 PID 在方波幅值切换到不同水平时跟踪表现会明显变差而自适应 PID 因为能在线感知增益变化跟踪差不多能保持一致。整套代码跑完把 Kp、Ki、Kd 和输出波形画出来就能看到非常有意思的过程Kp 和 Kd 在方波跳变瞬间会有一个快速的调整动作然后迅速恢复到一个稳态附近Ki 则呈现一种缓慢爬升的趋势。增益的波动幅度大体上被限制在一个合理范围内没有出现剧烈发散。6. 调参实操记录收敛速度、参数抖动与发散排查仿真代码跑通只是第一步真正花时间的在对参数的理解上。我前前后后折腾了大概两天时间积攒了几个比较典型的调参经验写出来供大家参考。第一个是学习率的血量关系。一开始我三个学习率都设成 0.4方波跟踪的前 0.5 秒内 Kp 剧烈振荡看起来像增益在抽筋。我把 Kp、Ki、Kd 的曲线拉出来看了一下发现问题出在 Ki 的更新量太大当系统进入稳态且误差很小的时候积分增益还在快速更新会给控制量带来额外扰动。后来把 Ki 的学习率降到 0.05 以下整个系统的增益曲线立刻稳定下来。这个经验也符合直觉——积分项对稳态精度贡献大但它的调节动作必须慢快了就容易让系统在目标值附近来回折腾。第二个经验是关于动量项的取值。动量项的作用是让增益修正轨迹更加平滑可以理解成给增益变化加了一个低通滤波器。但动量系数不是越大越好取 0.1 以上时就出现了明显的滞后效应方波下降沿到来时增益还停留在上一个高电平状态下的数值需要额外几个采样周期才能调整过来表现为输出波形有一个小台阶。最后我把动量系数定在 0.030.05既保证了平滑又不至于滞后。第三个坑是关于 RBF 网络中心初始值范围。如果中心的范围设置过窄比如 c 只在 [-1,1] 内而输入 u 的实际范围到过 ±3网络对超出范围的部分几乎没有响应能力Jacobian 输出接近 0自适应律基本失效——效果等同于一个固定 PID。反过来如果初始输入范围设得比实际大很多网络的灵敏度会变差。我建议先取一个比较靠近实际输入范围的值跑一轮仿真观察 u 的边界然后再微调初始化范围。第四个是稳定性占比的问题。自适应 PID 不是调试一次就一劳永逸的它在极端条件下有可能出现参数发散。最常见的一个征兆是前一两秒运行良好某个方波跳变瞬间 Kd 突然冲到一个很大的值之后整个控制量跟着一起振荡。我排查后发现这是微分增益学习率取值偏大的结果。方波跳变瞬间误差序列的二次差分 $e(k) - 2e(k-1) e(k-2)$ 会非常大如果此时 Jacobian 也为正Kd 会被一个很大的梯度推出去。解决方式有两条一是降低 $\eta_d$二是在误差突变时对微分增益的更新做限幅——比如把每次更新的幅度限制在 0.02 以内就能有效避免这种跳变瞬间炸掉的情况。还有一点是关于采样周期的选择。方波跟踪中采样周期越短控制器能获取的误差变化信息越密集理论上响应越平滑。但采样周期也不能无脑缩短因为被控对象的动态特性需要足够的采样间隔来体现否则对象模型在离散化时会变得更难辨识。我在不同采样周期下对比过1ms 和 0.5ms 的结果几乎一致但 1ms 情况下 RBF 权值在同样仿真时长内更新的次数少一半计算负担也小很多。如果你的对象动态比较慢甚至 10ms 采样都没有问题不一定非要追求小步长。7. 从方波到任意信号这套方案的扩展空间在哪里方波跟踪验证通过之后整套方案的实际能力其实远不止于此。RBF 自适应 PID 的价值在于它在线辨识了系统 Jacobian而这个 Jacobian 本质上就是系统的灵敏度模型和参考信号的形态没有强绑定关系。换句话说把方波换成正弦波、三角波甚至随机规划轨迹理论上程序只需要改参考信号的生成部分控制器和辨识器不需要动任何一个参数。当然不同信号对自适应律的激励强度不同方波突变丰富参数收敛快正弦波变化平缓增益调整幅度小随机信号则更考验系统的鲁棒性。如果要把它往工程方向推有几个明确的扩展路径值得提一下。第一是引入扰动观测环节——当前结构里 RBF 网络只辨识了对象本身没有显式区分外部扰动如果系统存在持续的阶跃型扰动积分增益会承担较大的补偿压力可以在自适应律里增加一个扰动项让 RBF 网络把扰动也一起辨识出来。第二是向多输入多输出扩展——RBF 网络支持多维输出把每个输出的 Jacobian 分别算出来再分别驱动对应回路的 PID 增益整个框架可以直接搬到多变量系统上。第三是嵌入式部署优化——把 MATLAB 代码转成 C 后网络部分大约只需要三次矩阵乘法和若干元素级运算即使在主频不高的 MCU 上也能跑得动。做这个仿真项目的过程中我最大的体会是自适应控制并不神秘核心就是把系统增益在线测出来并反馈给控制器这件事做得足够快、足够准。RBF 神经网络恰好提供了一种轻量级的测速手段。方波跟踪只是一个验证场景把这个场景跑通之后后面接任何工程对象都有了一种比较可信的快速试错框架。如果你也在做控制相关的仿真或者实际项目建议先不要急着去堆复杂的算法先把 PID 和 RBF 的这套闭环链路在一段模拟数据上跑通再考虑换对象、加扰动、改结构——这个顺序基本不会走弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑