简介
PID算法
作为控制领域现在最为流行的一种算法,在我们进行实现的过程中,一般有两种类型:位置式数字PID和增量式数字PID。在本文中,使用的PID算法就是位置式数字PID。位置式PID计算公式如下:
根据上述公式进行程序编写。
BP神经网络
神经网络作为近些年来较为流行的一种方法,被广泛应用于各个领域。我们这里将BP神经网络应用于PID算法参数的动态调整,使得整个控制系统到达更加理想的性能。有关神经网络的相关知识就不多做赘述,本文主要介绍的是使用Pytorch库进行BP神经网络与PID控制算法结合的实现。
代码实现
PID控制算法
首先利用PID位置式的公式,编写一个Python的控制程序。这里控制对象为温度,PID直接控制。
class PID_INC():
def __init__(self, kp, ki, kd, min, max, dt) -> None:
self.kp = kp
self.ki = ki
self.kd = kd
self.min = min
self.max = max
self.dt = dt
self.ei = 0
self.ed = 0
self.pre_err = 0
def cal(self, target, measure):
error = target - measure
u = self.kp*error + self.ki*self.ei + self.kd*self.ed
self.ei += error*self.dt
self.ed = (error - self.pre_err)/self.dt
self.pre_err = error
return u, error
上述定义了一个PID算法的类,下面对这个类进行调用,实现一个控制过程仿真:
if __name__ == '__main__':
target = 20 # 设定值
min = -100 #下限
max = 100 #上限
dt = 0.1 #PID采样时间
T = 10 #模拟时长
y_0 = 7 #初始温度
kp, ki, kd = [1.0, 0.1, 0.01] #PID参数
pid = PID_INC(kp, ki, kd, min, max, dt)
res = [5]
time_current = [0]
for i in range(int(T/dt)):
delta_u, _error = pid.cal(target, y_0)
y_0 += delta_u
res.append(y_0)
time_current.append(dt*(i+1))
plt.plot(time_current, res, label='temp')
plt.plot(time_current, [target for x in range(int(T/dt)+1)], label='SetPoint')
plt.legend()
plt.show()
运行后的结果如下图所示:
图1. PID控制仿真
BP神经网络与PID结合
如何将BP神经网络与PID进行结合呢?我们假设一个神经网络,MLP,三个输入单元和三个输出单元,将设定值、误差、当前的测量值当作这三个输入单元,那么三个输出单元就分别对应着PID的三个参数。
现在开始,神经网络的定义及结合很明确了。但是一般的网络都是需要大量的数据去进行训练,用这个训练好的模型再进行预测。我们这里也是嘛?我们这里采用一种不同的策略,将设定值与测量值的误差作为神经网络的损失函数,也就是说,神经网络每次训练都是为了更快更好的使得这个误差降为0。这不就正好是我们所需要的效果。但是在使用Pytorch实现的过程中,直接使用误差作为损失函数不可行,因此我们这里采用PID的输出作为损失函数。当PID不再输出时,也就是相当于系统稳定,等同于误差为0。
下面就是我们对上述想法进行实现的代码:
首先定义一个神经网络,它由一个输入层、两个隐藏层、一个输出层组成。
class BP_PID(nn.Module):
def __init__(self, inputchannels, hiddenchannel, outputchannels) -> None:
super(BP_PID, self).__init__()
self.inputlayer = nn.Sequential(nn.Linear(inputchannels, hiddenchannel),
nn.ReLU())
self.hiddenlayers = nn.Sequential(nn.Linear(hiddenchannel, hiddenchannel*2),
nn.ReLU(),
nn.Linear(hiddenchannel*2, hiddenchannel),
nn.ReLU())
self.outputlayer = nn.Sequential(nn.Linear(hiddenchannel, outputchannels),
nn.ReLU())
def forward(self, x):
x = self.inputlayer(x)
x = self.hiddenlayers(x)
x = self.outputlayer(x)
return x
接下来就是定义我们所需的损失函数,也就是根据上面的PID算法进行改写即可。
class pid_loss(nn.Module):
def __init__(self, min, max, dt) -> None:
super(pid_loss, self).__init__()
self.min = min
self.max = max
self.dt = dt
self.ei = 0
self.ed = 0
self.pre_err = 0
def forward(self, input, measure, target):
kp, ki, kd = input
error = target - measure
u = kp*error + ki*self.ei + kd*self.ed
self.ei = self.ei + error*self.dt
self.ed = (error - self.pre_err)/self.dt
self.pre_err = error
return u
将他们定义好之后,就只需要将初始条件设定好,网络模型初始化,再进行训练。
model = BP_PID(3, 10, 3)
opt = torch.optim.SGD(model.parameters(), lr=1e-4, weight_decay=0.00)
loss_func = pid_loss(-100, 100, 0.1)
y = 5
target = 20
data_in = torch.tensor([target, 5, target-y], dtype=torch.float32) #输入为目标值,实际值,误差
y_list = [5]
T_list = [0]
k_list = []
for epoch in range(100):
opt.zero_grad()
out = model(data_in)
loss = loss_func(out, torch.tensor(y, dtype=torch.float32), torch.tensor(target, dtype=torch.float32))
loss.requires_grad_(True)
loss.backward()
opt.step()
data_in = torch.tensor([target, 5+loss.item(), target-(5+loss.item())], dtype=torch.float32)
y = y+loss.item()
print(loss.item())
T_list.append((epoch+1)*0.1)
y_list.append(y)
k_list.append(out.detach().numpy())
上述利用神经网络对PID参数进行动态调整,使系统性能最佳。将上述数据保存,后面再与PID进行对比,保存数据代码如下:
import pandas as pd
y_bp_df = pd.DataFrame({'T': T_list, 'BP_PID_OUT': y_list})
k_bp_df = pd.DataFrame({'T': T_list[1:], 'KP': a[:,0], 'KI': a[:,1], 'KD': a[:,2]})
y_bp_df.to_csv('result//BP_PID_OUT.csv', index=None)
k_bp_df.to_csv('result/K_BP.csv', index=None)
画对比图的代码如下:
import pandas as pd
import matplotlib.pyplot as plt
bp_y = pd.read_csv('result/BP_PID_OUT.csv')
pid_y = pd.read_csv('result/PID_OUT.csv')
bp_y = bp_y.values
pid_y = pid_y.values
plt.plot(bp_y[:,0], bp_y[:,1], label='BP-PID')
plt.plot(pid_y[:,0], pid_y[:,1], label='PID')
plt.plot(bp_y[:,0], [20 for i in range(101)], label='TARGET')
plt.legend()
plt.savefig('result/compare.eps', dpi=600)
plt.show()
***注意,上面这个模型,可能在进行反向传播的过程中,数值求解不稳定,可能会出现一些爆炸的情况,对于这个问题我还在调试。但是多运行几遍,可以得到不错的效果。***
下面是我运行的一份结果图:
下面这个是神经网络动态调整的PID参数的变化图:
以上就是我对于BP神经网络与PID控制算法结合的实现过程,不尽完善,仅供参考!完整代码可以参考: https://github.com/Roy-fyq/Adjusting-parameters-of-PID-with-Neural-Network.