计算机系统应用教程网站

网站首页 > 技术文章 正文

用神经网络来估计PID控制器参数?(附核心代码)

btikc 2024-11-18 09:10:01 技术文章 28 ℃ 0 评论

前面的文章说了PID的本质,原来PID不止只能做控制器,还能做融合信号的功能,有兴趣的朋友可以去看看——你真的了解PID吗?PID的本质纯干货分析。或者点击我的头像在我发布的文章里也可以查看。

今天来说说PID控制器已经设计出来了,那怎么来调参数呢?调参是自动控制工程师头疼的事情,PID又是无模型的控制器,怎么确认初始参数都不好说。作为一个自动控制工程师,以懒……不对,以提高效率为原则,那何不让机器自己去调,工程师最后来检查参数效果就行了呗。

本文介绍的是整定PID控制器的参数,不会涉及到PID控制的理论,也不会讲解PID控制过程(头条上有很多优秀的文章有讲解)。本文会从一个项目来说明怎么让机器自己去调节PID,即纯粹的参数调节。机器自整定参数的框图如下:



我们主要是实现PID控制器前面的参数估计器,采用最近比较火的BP神经网络(关于BP神经网络朋友们可以自己去了解,目前是机器学习的热门方向)来整定参数。为什么选择神经网络,因为神经网络在理论上可以拟合任意非线性函数。

建立神经网络

我们建立一个最简单的神经网络,总共三层——输入层、隐藏层和输出层。输入层总共3个节点,隐藏层5个节点,输出层3个节点。输入层的输入信号分别为目标量x1,量测量x2和误差x3;输出层的输出为PID的三个参数(输出层的节点个数取决于PID参数个数,PI控制就只有2个节点);神经网络的隐藏层读者朋友们可以自己设定层数和节点数,看看多少层多少节点是比较合适的。下面是神经网络的结构图。



这个神经网络要实现的功能一目了然,当输入x1和x2相等,x3为0时,所有输出y会收敛到一组固定值。

神经网络输入层的输入为:x;

神经网络隐藏层的输入为:c=x*w0;

神经网络隐藏层的输出为:c'=f(c)=tanh(c);

神经网络输出层的输入为:c''=c'*w1;

神经网络输出层的输出为:y=f(c'')=tanh(c'');

隐藏层的激活函数采用tanh函数,tanh函数的定义域为R,值域为(-1,1),图像穿过原点,设f(x)=tanh(x),f(x)的导数为1-f(x)^2。tanh函数及其图像如下:


输出层的激活函数这里要考虑一下了,由于PID参数都为非负值,所以采用的激活函数的值域需要在(0,+∞),这里我们采用g(x)=0.5*(1+tanh(x)),非负的tanh函数。g(x)就是把tanh沿y轴向上平移1个单位的距离,然后再把值域限制到(0,1)。由于PID参数范围可能不在(0,1),这里读者朋友们可以自己调节输出的值域。

神经网络的传播过程

损失函数取为:e=0.5*(x1-x2)^2。x1为目标量,x2为量测量。

用梯度下降法来优化各层的加权系数,我们就需要计算损失函数对加权系数向量的偏导数,显然这是一个很复杂的函数,但是,对复合函数的求导我们拥有链式法则这一神奇的工具(感谢伟大的数学家牛某某,莱某某),问题就迎刃而解了。由导数链式法则我们可以得到:


其中w1为隐藏层到输出层的加权系数向量,u为PID输出量,所以第二项x2对u的偏导是未知的,和被控对象的传递函数有关,但是我们可以用符号函数代替,即PID输出量和量测量的变化趋势是一致,保证极性正确,计算误差可以用学习率来调整。其它的偏导数都可以直接得到,在此不赘述。同理w0还要多传几层。



神经网络测试

这里我模拟一个转速控制系统的过程。系统的开环传递函数为:



由于被控对象不同,我的学习率设置的是0.01,这里我先设置输出节点只有一个,即先只训练P值。我们给个阶跃信号做系统的输出测试。下面是训练结果:



可以发现P在0.86左右收敛,我们在系统上测试一下P=0.86是否合适。


看起来还可以啊,那让我们把3个参数都加入估计。


P值大约收敛到0.75,I值为0,D值为0.37。再来看看新参数下的阶跃响应:


嗯,这个参数也可以用。我们达到了想要的结果,说明机器通过神经网络可以整定PID参数。机器:哼,愚蠢的人类,要你们何用。

最后的话

实际上,我在调试这个网络的时候出现过很多次控制发散的结果,我发现初始权值参数对控制的发散起到了很关键的作用,所以调好一部分权值参数后就保存下来,下次接着调,才能保证控制不发散。学习率也是一个很重要的参数,刚开始建议使用较小的学习率,后面等稳定了再把学习率加大。

在工程应用中,更多的是靠经验法人为调试PID参数,实际上从很多角度来说都比用神经网络这种方式要快,你说你调参都有大量经验了,要机器自己去调节干嘛,还要花这么多时间来编写神经网络?你说你一点经验没有,那怎么建立起参数调试的神经网络?所以,在工程应用上,更多的还是靠经验法或者是公式法,下个篇章我将描述用公式法如何调试PID的参数。

附录:神经网络核心代码MATLAB格式

function [kp,ki,kd,w0_out,w1_out] = bp_get_pid_paras(target,current,integ,delE,pidout,k)
persistent last_current;
persistent last_pidout;
persistent w0;
persistent w1;
%神经网络输出为 pid参数
N=5; %隐藏层N个节点
Mi=3; %输出层M个节点
Mo=3; %输出层M个节点
ng = 0.00001;%学习率
if k == 2 % 初始化神经网络
%     load w0_save_pid; %直接用保存好的参数
%     load w1_save_pid;
%     w0 = w0_save;
%     w1 = w1_save;
    w0 = 2*rand(N,Mi) - 1;
    w1 = 2*rand(Mo,N) - 1;
    last_current = current;
    last_pidout = pidout;
end
error = target - current;
x = [target,current,error];% 神经网络输入
l0 = x;
x1 = l0*w0'; % 隐藏层输入
l1 = tanh(x1);% 隐藏层输出
x2 = w1*l1';% 输出层输入
for i=1:Mo
    l2(i) = exp(x2(i))/(exp(x2(i))+exp(-x2(i)));%输出层输出
end
% 输出层
temp = (exp(x2)+exp(-x2)).^2;
for i=1:Mo
    dl2_x2(i) = 2/temp(i);
end
dpid = [error; integ; delE];
sig = sign((current - last_current)/(pidout - last_pidout + 0.0001));
last_current = current;
last_pidout = pidout;
for i=1:Mo
    l2_del(i) = -error * sig * dpid(i) * dl2_x2(i);
end
% 隐藏层
temp = tanh(x1).^2;
dl1_x1 = (1 - temp);
l1_del = dl1_x1.*(l2_del * w1);
% 更新系数
w1 = w1 - ng*l2_del'*l1;
w0 = w0 - ng*l1_del'*l0;
% 更新PID参数
kp=l2(1);
ki=l2(2);
kd=l2(3);
% 供主程序查看
w0_out = w0;
w1_out = w1;
end

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表