ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MATLAB神经网络PID控制实战:从单神经元到BP整定

MATLAB神经网络PID控制实战:从单神经元到BP整定 简介面向MATLAB控制领域的开发与学习者资源包聚焦神经网络与PID控制的结合内容涵盖基于二次型性能指标的单神经元自适应PID、自适应神经网络补偿PID以及改进型单神经元自适应PID三类典型方法可用于解决传统PID在非线性、时变或不确定系统中的参数整定难题。压缩包共6个文件以5个MATLAB脚本和1个Simulink模型为主整体仅12KB代码结构覆盖控制器定义、被控对象建模、性能绘图与仿真运行等模块便于边看边练。已有673人学习下载适合正在研究智能控制算法、需要参考示例程序或进行课程设计的高校学生与工程师。通过研读这些程序可清晰理解神经网络如何在线调整PID参数并提升鲁棒性同时获得可扩展的仿真框架为后续算法改进与实际项目部署提供基础。1. 神经网络PID控制当固定参数守不住时让权重自己找增益在实际工程里PID三环再成熟也解决不了同一个问题对象老化或工况切换之后整定好的Kp、Ki、Kd会失灵。加热棒电阻随温度漂移、伺服电机负载突变后速度环振荡、化工管道流量特性随工作点变化——这些场景的共同点是没有精确模型。在MATLAB里做神经网络PID控制核心思路并不复杂不抛弃PID而是用神经网络在线修正这三个增益。被控对象被当作黑箱误差信号反复冲刷网络权重增益跟着工况走这就是“MATLAB神经网络PID控制”这条技术路线的实用价值。它特别适合模型不准、参数时变、又不想上无模型自适应算法的场合。下面按“架构选型→MATLAB最小实现→BP整定的工程细节→收敛性验证技巧”展开适合正在搭仿真或准备把算法落到控制器里的工程师。2. 三种神经网络PID架构怎么选单神经元、BP整定与模型辨识要说清MATLAB里的实现得先分清“神经网络PID”有三种完全不同的结合方式。很多新手直接把论文里的4-5-3网络抄下来仿真跑不动就以为是算法问题实际上是架构选错了。2.1 单神经元自适应PID开销最小、最容易在控制器上跑单神经元自适应PID在电机驱动、温控仪表里出现得最多本质上是把增量式PID换了一种写法。增量式PID的控制量是u(k) u(k-1) Kp*Δe Ki*e Kd*(e - 2*e1 e2)其中Δe是当前误差与上一拍误差之差e是当前误差括号里那一项可以近似看成误差的二阶差分。单神经元PID把这三项分别作为神经元的三个输入分量x1 e(k) x2 e(k) e(k-1) ... 误差累计 x3 e(k) - e(k-1) 误差变化神经元输出就是控制增量Δu K * Σ(w_i * x_i)权值w1、w2、w3其实就对应着Ki、Kp、Kd的角色只是它们由学习规则在线修改而不是人工固定。常见的学习规则是有监督的Hebb学习当误差大时往误差减小的方向修正权值误差小时权值变化也小防止来回抖。这种结构的优势是计算量极低一个循环里只有十来次乘加在裸机PID控制器的代码里插入一段即可不需要额外的浮点协处理器。缺点是它的“学习能力”本质上只能调三个增益不能补偿严重非线性所以它适合对象工作点缓慢漂移、但整体特性没有突变的情况。2.2 BP神经网络整定PID论文中最常见参数在线映射BP神经网络整定PID是目前中文文献里出现频率最高的结构。它用一个前馈神经网络把系统状态实时映射成Kp、Ki、Kd三个参数。最典型的网络是4-5-3结构输入层为参考输入r(k)、对象输出y(k)、误差e(k)和常数1隐层5个节点输出层3个节点分别对应Kp、Ki、Kd。隐层激活函数用tansig输出层用purelin。它的思路是神经网络本身不是一个控制器而是一个“参数映射器”。每一拍网络读入系统状态输出一组PID增益PID控制器再按常规增量式公式计算控制量控制量作用到对象后产生新的误差误差再反传回去调整网络权重让网络输出的参数逐步接近当前工况下的最优增益。这种做法的好处是结构通用几乎可以套在任何对象上所以很多毕业设计和专利都采用它。工程上要注意一点如果对象特性变化太快在线反向传播的收敛速度跟不上输出增益会出现明显振荡所以它更适合工况缓变的高精度跟踪场合而不是负载突变频繁的场合。2.3 基于神经网络辨识的PID对象完全未知时的做法第三种做法的思路完全不同先用神经网络离线辨识被控对象再用辨识出的模型来整定PID或做前馈补偿。MATLAB里可以用NARX网络非线性自回归外生输入网络对对象的输入输出数据建模narxnet命令创建训练后得到一个“虚拟对象”然后用这个虚拟对象做反复的PID整定试验或者把它嵌入Smith预估器补偿大滞后。这种方案是最重的一种但它在温度场、反应釜这类强非线性、大滞后对象上确实有效因为对象本身没法用二阶惯性加纯滞后近似。它适合的是前期建模时间充裕、对实时性要求不高的场合。2.4 三种结构的工程选型表结构实现复杂度在线计算量适用对象参数整定难度单神经元自适应PID低低电机转速、温控、实时性高的嵌入式场景学习率和K需手调初值不敏感BP神经网络整定PID中中工况缓变、需要高精度跟踪的过程控制需要离线预训练网络规模影响大神经网络辨识PID高高大滞后、强非线性对象建模质量决定最终控制效果我一般建议刚上手做仿真优先选单神经元因为代码短、排错容易要在论文里体现“智能控制”成果或者做复杂对象仿真选BP整定结构真正碰到建不出机理模型的工业对象再考虑NARX辨识这条路。2.5 单神经元PID与级联PID的配合边界在电机控制、无人机姿态控制里级联PID位置环、速度环、电流环或者外环加内环依然是主流框架。神经网络PID通常放在最内层一个环里替代固定增益而不是同时把所有环的PID参数都交给神经网络去学习。原因很简单多个学习器同时在线更新环间耦合会让误差信号互相污染权值收敛过程会变得非常不稳定仿真里表现为参数发散、系统振荡。常见的做法是外环保留固定PID内环用单神经元自适应这样一个变量一个变量地改出问题也容易定位。3. MATLAB里跑通单神经元自适应PID的最小方案前面说到单神经元架构实现代价最低在MATLAB里也最容易复现。这一章给出一个不需要Simulink、不依赖任何工具箱的完整脚本把“MATLAB实现神经网络PID控制”的最小闭环先跑起来。3.1 为什么先用单神经元练手单神经元PID的整个计算过程只有一个神经元不需要搭建复杂的网络结构也不需要准备训练集。对刚接触这个领域的人来说它能让人把注意力集中在“权值如何在线变化”这件事上。另外它的所有状态量——误差、误差累计、误差变化、三个权值——都可以直接在工作区里查看画成曲线就能直观看到权值收敛过程。有了单神经元的基础再去理解4-5-3的BP整定结构就只是把“3个输入节点1个输出节点”扩展成“4个输入节点5个隐层节点3个输出节点”而已反传过程虽然多逻辑是完全一致的。3.2 完整可复现的MATLAB脚本以下脚本在MATLAB里直接保存为oneNeuronPID.m运行即可。被控对象取一个带输出延迟的二阶惯性环节离散化后用差分方程递推这样可以完全避开Control System Toolbox任何版本的MATLAB都能跑。%% 单神经元自适应PID —— 离散仿真主程序 clear; clc; %% 被控对象带纯滞后的二阶惯性环节 Ts 0.01; % 采样周期 [s] sys tf(2.5, [0.8 1.2 1], OutputDelay, 0.15); dsys c2d(sys, Ts, zoh); % 零阶保持器离散化 num dsys.Numerator{1}; den dsys.Denominator{1}; %% 控制器参数 K 0.8; % 神经元总增益 eta [0.35 0.25 0.25]; % 比例/积分/微分通道学习率 w [0.1 0.1 0.1]; % 权值初值 %% 仿真状态初始化 N 4000; r ones(1, N); % 单位阶跃参考输入 y zeros(1, N); % 对象输出 u zeros(1, N); % 控制量 e zeros(1, N); % 误差 u_prev 0; e1 0; e2 0; % e(k-1), e(k-2) %% 递推主循环 for k 3:N e(k) r(k) - y(k); % 神经元三输入当前误差、误差累计、误差变化 x [e(k); e1 e(k); e(k) - e2]; xi x / (abs(x(1)) abs(x(2)) abs(x(3)) eps); % 归一化 % 增量式控制量 du K * w * xi; u(k) u_prev du; u_prev u(k); % 对象差分方程递推 y(k) -den(2)*y(k-1) - den(3)*y(k-2) ... num(1)*u(k-1) num(2)*u(k-2); % 有监督Hebb学习权值在线修正 w w eta .* e(k) .* xi .* sign(u(k)); % 状态滚动 e2 e1; e1 e(k); end %% 绘图 figure; subplot(2,1,1); plot(Ts*(0:N-1), r, --, Ts*(0:N-1), y, LineWidth, 1.2); xlabel(t/s); ylabel(y); title(阶跃响应); subplot(2,1,2); stairs(Ts*(0:N-1), u); xlabel(t/s); ylabel(u); title(控制量);3.3 代码逻辑说明与参数含义先看被控对象的离散化。c2d把连续传递函数转化为离散传递函数差分方程里num(1)、num(2)是分子系数den(2)、den(3)是分母系数递推时注意den(1)通常为1代码里没有使用。这一步的含义是MATLAB里做控制仿真绝大多数情况都要把连续对象离散化因为数字PID本身就是逐拍工作的离散化后仿真的每一拍与控制器一拍完全对应。神经元输入的归一化是这段代码里最容易被忽略的地方。误差累计项会随仿真时间不断增大如果不归一化x2可能比x1、x3大几个数量级权值更新会被积分项主导微分项和比例项失去作用。归一化的分母取三个分量的绝对值之和再加eps保证分母不为零。这里的eps是MATLAB内置的机器精度常数约2.22e-16只是用来防止除零。控制量的计算采用增量式du K * w * xi的结果是控制量的增量累加到u_prev上得到当前控制量。这样写的好处是控制量天然带有积分记忆即使权值抖动也不会让控制量突变。权值更新使用了有监督Hebb学习的简化形式w w eta * e * xi * sign(u)。其中sign(u)表示权值修正方向受控制量符号约束防止误调。不同文献里这个公式略有差异有的去掉sign项有的改成e * xi * u仿真时如果控制量很小建议保留sign以维持修正方向的一致。下表是这段代码里的可调参数也是单神经元PID里唯一需要你来定的东西参数经验取值范围作用调大后的效果K0.12.0控制量总增益响应加快超调增大过大时发散eta(1)0.10.5比例通道学习率误差收敛速度加快eta(2)0.10.4积分通道学习率消除残差过大引起低频振荡eta(3)0.10.3微分通道学习率抑制超调过大放大测量噪声w初值0.050.5权值起点影响初始控制力度但对收敛终点不敏感调试顺序我一般是先固定eta为0.1把K从0.3往上加观察阶跃响应的超调量出现振荡后回调K再把eta慢慢加大直到误差收敛速度不再明显变化。K和学习率不要同时加大否则问题很难归因。3.4 在Simulink里把这段逻辑封装成S函数如果要把它接到更复杂Simulink模型里常见做法是写成Level-2 M文件S函数。下面这个骨架可以直接放到msfun_neuronPID.m里然后在Simulink的S-Function模块中填写函数名即可。S函数封装的核心价值是它保留了上一章脚本里的递推逻辑同时把采样周期、输入输出接口和Simulink仿真引擎对接起来。function msfun_neuronPID(block) % Level-2 M文件S函数单神经元PID原型 setup(block); function setup(block) block.NumInputPorts 2; block.NumOutputPorts 1; block.SetPreCompInpPortInfoToDynamic; block.SetPreCompOutPortInfoToDynamic; block.InputPort(1).DirectFeedthrough true; block.InputPort(2).DirectFeedthrough true; block.NumContStates 0; block.NumDworks 0; block.SampleTimes [0.01 0]; block.RegBlockMethod(Outputs, Output); function Output(block) r block.InputPort(1).Data; y block.InputPort(2).Data; K block.DialogPrm(1).Data; % 神经元增益 eta block.DialogPrm(2).Data; % 1x3 学习率向量 persistent e1 e2 ulast w if isempty(e1) e1 0; e2 0; ulast 0; w [0.1 0.1 0.1]; end e r - y; % 神经元三输入误差、误差累计、误差变化 x [e; e1 e; e - e2]; xi x / (abs(x(1)) abs(x(2)) abs(x(3)) eps); u ulast K * w * xi; w w eta .* e .* xi .* sign(u); % 状态滚动 e2 e1; e1 e; ulast u; block.OutputPort(1).Data u;这段代码用的是persistent变量保存状态原型验证足够用。如果要生成C代码或者跑快速加速模式建议把四个状态量改到Dwork里Simulink才能正确处理仿真时序。参数方面block.DialogPrm(1)和block.DialogPrm(2)对应S-Function模块参数对话框里填写的两个参数前者是标量K后者是行向量eta两个参数要按照对话框里的显示顺序填入。采样周期block.SampleTimes [0.01 0]表示固定步长0.01秒要和模型求解器的步长保持一致否则e1、e2记录的差分节奏与仿真步长对不上微分项的含义就变了。4. BP神经网络整定PID的Simulink实现离线预训练加在线微调单神经元的结构只能调三个增益的线性组合。如果对象在小范围工作区内切换明显单神经元的三个权值往往顾此失彼这时需要引入BP神经网络整定PID让Kp、Ki、Kd成为工作点的非线性函数。4.1 网络结构选定与权值初值工程上最常用的是4-5-3结构输入为r(k)、y(k)、e(k)、常数1隐层5个节点输出Kp、Ki、Kd。输出层用纯线性函数purelin这样K值可以为负PID增益在在线调整过程中不会被限幅在正数范围内如果某通道的输出受非负约束可以在后续处理中单独做下限截断。权值初值取均匀分布在[-0.5, 0.5]的随机数。这个范围不是拍脑袋定的tansig激活函数在输入绝对值大于3时输出接近±1梯度接近0如果初值给得太大网络一开始就进入饱和区反向传播的梯度迅速消失训练等于没做。rng(0); % 固定随机种子保证可复现 W1 (rand(5, 4) - 0.5); % 隐层权值 b1 (rand(5, 1) - 0.5); % 隐层偏置 W2 (rand(3, 5) - 0.5); % 输出层权值 b2 (rand(3, 1) - 0.5); % 输出层偏置4.2 离线训练数据怎么来离线训练的输入是系统状态输出是期望的PID参数。在实际实施中有两种主流做法。第一种是“开环阶跃辨识法”对对象施加一组不同幅值的阶跃信号记录r、y、e的时间序列然后人工或通过Ziegler-Nichols方法在每个工作点整定出一组“局部最优PID”把这些参数作为训练标签。这种做法的缺点是数据覆盖不均匀阶跃响应前半段误差大、后半段误差小训练出的网络对稳态附近的映射更准。第二种是“PRBS激励法”也是我更推荐的做法在PID闭环中给参考输入叠加伪随机二进制序列让系统在各个频段都被激励到。采集到的数据覆盖面宽训练出的网络在线泛化能力更好。伪随机序列在MATLAB里可以用idinput函数生成% 生成幅值±0.5的PRBS信号采样周期1s共500个点 u_prbs idinput(500, prbs, [0 0.2], [-0.5 0.5]);无论用哪种方法输入矩阵X的每一列是一个4维状态向量[r; y; e; 1]输出矩阵T的每一列是对应的[Kp; Ki; Kd]。训练前需要对输入X做归一化处理把数据映射到[-1, 1]区间这是神经网络训练的通用要求直接关系到tansig是否进入饱和区。4.3 用feedforwardnet完成离线训练MATLAB里训练单隐层前馈网络三行命令就够X x_train; % 4×N 归一化后的输入 T y_train; % 3×N 期望PID参数 net feedforwardnet(5); net.layers{1}.transferFcn tansig; net.layers{2}.transferFcn purelin; net.trainFcn traincgp; % 共轭梯度法离线收敛优于traingd net.trainParam.epochs 2000; net.trainParam.goal 1e-5; [net, tr] train(net, X, T);feedforwardnet(5)创建单隐层5个节点的网络trainFcn选择训练算法。traincgp是共轭梯度法对学习率的敏感度低适合离线训练traingd是最基础的梯度下降法收敛慢容易陷在局部极小。训练结束后用nntraintool打开训练窗口看误差曲线的下降趋势。训练好的网络可以用genFunction导出成独立的MATLAB函数文件方便在Simulink里调用genFunction(net, nnPIDfun.m);这个命令会生成一段包含网络结构和权值的函数代码输入输出和sim一致省去在Simulink里搭网络模块的麻烦。4.4 Simulink里的在线微调结构离线训练完成的网络放到Simulink里有两条路。一条是用genFunction生成的函数配合MATLAB Function模块调用网络权值固定只做参数映射另一条是把反传算法直接写到MATLAB Function里每拍做一次在线更新。工程实践中基本采用“离线预训练在线微调”的折中离线训练让网络先有个合理的初值在线微调的学习率取离线训练学习率的十分之一左右防止权值大幅波动。在线微调部分的核心代码如下放在MATLAB Function模块里function [Kp, Ki, Kd] nnTune(r, y, e, xmin, xmax, W1, b1, W2, b2) %#codegen % 输入归一化xmin/xmax 必须与离线训练时完全一致 x [r; y; e; 1]; x 2 * (x - xmin) ./ (xmax - xmin) - 1; % 隐层 tansig A1 2 ./ (1 exp(-2 * (W1 * x b1))) - 1; % 输出层 purelin out W2 * A1 b2; Kp out(1); Ki out(2); Kd out(3); % 输出限幅防止负增益进入PID Kp max(Kpmin, min(Kpmax, Kp)); Ki max(Kimin, min(Kimax, Ki)); Kd max(Kdmin, min(Kdmax, Kd));这段代码里最容易出错的是归一化参数xmin、xmax的传递。离线训练时用mapminmax函数做了归一化在线推导时必须用同一组xmin、xmax而不是在Simulink每个模块里重新计算一遍否则网络的输入分布偏移输出参数直接失真。4.5 BP整定PID的三个典型故障与排查方法先看梯度消失。tansig在输入大于3时输出接近1导数接近0反向传播的误差没法传到前面层。现象是训练几千步后误差曲线不再下降权值基本不变。排查方法是画出隐层节点的输出分布如果大量节点输出集中在±1附近就要减小输入归一化的范围或减少隐层节点数。再看学习率失配。离线用traincgp训练收敛很快在线微调时如果把学习率按离线训练的数值直接填进去权值会在最优值附近来回震荡表现为Kp、Ki、Kd曲线高频抖动控制量毛刺严重。解决的办法是把在线学习率限制在0.01到0.05之间同时加一个权值变化的死区误差小于某个阈值时不更新权值。最后是采样周期不一致。Simulink模型求解器的固定步长、MATLAB Function模块的采样时间、PID模块的采样时间三者必须一致。如果其中一个是0.01秒另一个是0.001秒误差序列e(k)的差分项e(k)-2e(k-1)e(k-2)会被放大或缩小两个数量级输出层映射出的Kd会完全失真。5. 收敛性验证与两个实用小技巧5.1 被控对象参数突变实验神经网络PID和固定PID的差距只有在对象参数变化时才能体现出来。在仿真中把对象增益在中间时刻改大50%比如从2.5改成3.75固定PID会出现明显超调单神经元PID的权值会在线修正超调明显更小。这个实验不需要额外代码把第3章脚本里的循环体加一个判断即可if k 2000 sys_k 3.75; % 对象增益突变 end注意突变后权值可能需要几十拍到上百拍才能收敛观察窗口要留足。5.2 用ITAE指标量化比较主观看曲线不如算指标。ITAE时间乘绝对误差积分是控制领域最常用的性能指标对前期误差大、后期误差小的权重分配比较合理。仿真结束后在MATLAB命令行运行ITAE Ts * sum((0:N-1) .* abs(e));分别对固定PID和神经网络PID各跑一次ITAE数值越小说明整体跟踪性能越好。建议记录三组不同突变幅度下的结果而不是只测一组因为神经网络权值的随机性会导致单次结果有波动。5.3 学习率退火让收敛更快、后期更稳单神经元PID和BP整定PID的在线学习率初期可以大一些让权值快速逼近后期应该小一些避免过调。简单有效的做法是让学习率随时间衰减eta eta0 ./ (1 beta * k);其中eta0取0.3左右beta取0.0001到0.001之间。这个技巧只需要在权值更新前插入一行代码。需要注意的是学习率退火会让权值在线修正能力逐渐减弱如果对象突变恰好发生在仿真后期权值就来不及跟踪了。因此它更适合工况相对平稳、偶发小幅扰动的系统工况频繁切换时还是应使用固定的学习率。本文还有配套的精品资源点击获取
返回列表