BP神经网络与PID控制算法结合(Pytorch实现)

简介

PID算法

作为控制领域现在最为流行的一种算法,在我们进行实现的过程中,一般有两种类型:位置式数字PID和增量式数字PID。在本文中,使用的PID算法就是位置式数字PID。位置式PID计算公式如下:

\triangle u_k = K_p\times e_k + K_i \times\sum_{i=0}^{k}e_i\times \triangle t +K_d\times \frac{e_k-e_{k-1}}{\triangle t}

根据上述公式进行程序编写。

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.

  • 39
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值