经典PID算法

一.什么是PID

PID,即比例Proportion积分Integral微分Derivative三个单词的缩写。

PID

引用一段网上经典的话:

在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法,如果能够熟练掌握PID算法的设计与实现过程,对于一般的研发人员来讲,应该是足够应对一般研发问题了,而难能可贵的是,在我所接触的控制算法当中,PID控制算法又是最简单,最能体现反馈思想的控制算法,可谓经典中的经典。经典的未必是复杂的,经典的东西常常是简单的,而且是最简单的,想想牛顿的力学三大定律吧,想想爱因斯坦的质能方程吧,何等的简单!简单的不是原始的,简单的也不是落后的,简单到了美的程度。

二.PID原理

常规的模拟 PID 控制系统原理框图如下:

PID框图

该系统由模拟 PID 控制器和被控对象组成。

PID控制器分为三个部分:

  1. P - 比例部分

    ​ 比例环节的作用是对偏差瞬间作出反应。偏差一旦产生控制器立即产生控制作用, 使控制量向减少偏差的方向变化。 控制作用的强弱取决于比例系数Kp, 比例系数Kp越大,控制作用越强, 则过渡过程越快, 控制过程的静态偏差也就越小; 但是Kp越大,也越容易产生振荡, 破坏系统的稳定性。 故而, 比例系数Kp选择必须恰当, 才能过渡时间少, 静差小而又稳定的效果。

  2. I - 积分部分

    ​ 从积分部分的数学表达式可以知道, 只要存在偏差, 则它的控制作用就不断的增加; 只有在偏差e(t)=0时, 它的积分才能是一个常数,控制作用才是一个不会增加的常数。 可见,积分部分可以消除系统的偏差。

    ​ 积分环节的调节作用虽然会消除静态误差,但也会降低系统的响应速度,增加系统的超调量。积分常数Ti越大,积分的积累作用越弱,这时系统在过渡时不会产生振荡; 但是增大积分常数Ti会减慢静态误差的消除过程,消除偏差所需的时间也较长, 但可以减少超调量,提高系统的稳定性。

    当 Ti 较小时, 则积分的作用较强,这时系统过渡时间中有可能产生振荡,不过消除偏差所需的时间较短。所以必须根据实际控制的具体要求来确定Ti 。

  3. D - 微分部分

    ​ 实际的控制系统除了希望消除静态误差外,还要求加快调节过程。在偏差出现的瞬间,或在偏差变化的瞬间, 不但要对偏差量做出立即响应(比例环节的作用), 而且要根据偏差的变化趋势预先给出适当的纠正。为了实现这一作用,可在 PI 控制器的基础上加入微分环节,形成 PID 控制器。

    ​ 微分环节的作用使阻止偏差的变化。它是根据偏差的变化趋势(变化速度)进行控制。偏差变化的越快,微分控制器的输出就越大,并能在偏差值变大之前进行修正。微分作用的引入, 将有助于减小超调量, 克服振荡, 使系统趋于稳定, 特别对髙阶系统非常有利, 它加快了系统的跟踪速度。但微分的作用对输入信号的噪声很敏感,对那些噪声较大的系统一般不用微分, 或在微分起作用之前先对输入信号进行滤波。

三个部分叠加组成,形成的模拟 PID 控制器的控制规律为:

模拟 PID 控制器的控制规律

其中有几个非常重要的参数:

r(t):系统实际上需要的输出值,这是一个标准值,在我们设定了之后让这个系统去逼近的一个值(随时间变化的原因是,我们对系统的需求不同才会改变!)

y(t):系统当前的输出值,这个值应该需要趋近于我们设定的值,当我们没有增加PID控制模块之前,它是由被控对象通过r(t)输入直接产生的。

e(t):系统由于某些扰动,导致的系统产生的偏差,实际输出的值和想要设定的初始值r(t)的差值。

u(t):系统通过PID控制器输出的新的输入值,实际上他是在r(t)的基础上,针对当前的实际情况做出的改变。

Kp比例系数:系统PID比例因子,Kp能够对于产生的偏差e(t)能够迅速的作出反应,减少偏差。

Ti积分系数:系统PID积分因子,Ki能够用于消除静差,由于前面的误差有正有负,所以当前偏差的加入能够抵消部分,保持系统的稳定性,让系统有记忆功能。

Td微分系数:系统微分因子,Kd能够体现出当前误差的变化趋势,引入有效早期修正信号,从而加快系统的动作速度,减少调节时间

三.PID算法代码

PID 控制算法可以分为位置式 PID增量式 PID 控制算法。

两者的区别:

  1. 位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大;
  2. 增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。
  3. 由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。

1.位置式PID算法

位置式PID算法

参考代码:

typedef struct
{
  float Kp;                       //比例系数Proportional
  float Ki;                       //积分系数Integral
  float Kd;                       //微分系数Derivative

  float Ek;                       //当前误差
  float Ek1;                      //前一次误差 e(k-1)
  float Ek2;                      //再前一次误差 e(k-2)
  float LocSum;                   //累计积分位置
}PID_LocTypeDef;

/************************************************
函数名称 : PID_Loc
功    能 : PID位置(Location)计算
参    数 : SetValue ------ 设置值(期望值)
            ActualValue --- 实际值(反馈值)
            PID ----------- PID数据结构
返 回 值 : PIDLoc -------- PID位置
作    者 : RootCode
*************************************************/
float PID_Loc(float SetValue, float ActualValue, PID_LocTypeDef *PID)
{
  float PIDLoc;                                  //位置

  PID->Ek = SetValue - ActualValue;
  PID->LocSum += PID->Ek;                         //累计误差

  PIDLoc = PID->Kp * PID->Ek + (PID->Ki * PID->LocSum) + PID->Kd * (PID->Ek1 - PID->Ek);

  PID->Ek1 = PID->Ek;

  return PIDLoc;
}

python版:

import matplotlib.pyplot as plt 
import numpy as np 
import random
import sys
import os

time_length = 600
time_sample = 100
time_interval = float(time_length/time_sample)
error_coeff = 3
t = np.linspace(0,time_length,time_sample)
Slope = 1
Intercept = 0
standard_in = 20

# The system model
system_model = lambda i : Slope*i + Intercept
standard_out = system_model(standard_in)
print("The Standard Output:%d" % standard_out)

Kp = 0.08 # average
Ki = -0.7 # intergre
Kd = 0.01 # diff

error_bef = []
real_out_ajust = []
real_out_ajust.append(70)
real_out_ajust.append(75)
error_bef.append(real_out_ajust[0]-standard_out)
Out_plt = np.linspace(standard_out,standard_out,time_sample)

# 标准直接计算公式1:Pout=Kp*e(t) + Ki*Sum[e(t)] + Kd*[e(t) - e(t-1)]
def PID_Controller_Direct_Mem(standard_out,t):
        global time_sample,Kp,Ki,Kd,error_bef,real_out_ajust
        if t > time_sample:
                print("Time Out! Quit!")
                return -1
        error_now = real_out_ajust[t] - standard_out
        error_bef.append(error_now) # 记录了所有的误差
        integrate_res = np.sum(error_bef)
        Diffirent_res = error_now - error_bef[t-1]
        return Kp*error_now + Ki*integrate_res + Kd*Diffirent_res

for t_slice in range(1,time_sample-1):
        Pout = PID_Controller_Direct_Mem(standard_out,t_slice)
        real_out_ajust.append(system_model(Pout))

plt.figure('PID_Controller_Direct_Mem')
plt.xlim(0,time_length)
plt.ylim(0,2*standard_out)
plt.plot(t,real_out_ajust)
plt.plot(t,Out_plt)

2.增量式PID算法

增量式PID算法

参考代码:

typedef struct
{
  float Kp;                       //比例系数Proportional
  float Ki;                       //积分系数Integral
  float Kd;                       //微分系数Derivative

  float Ek;                       //当前误差
  float Ek1;                      //前一次误差 e(k-1)
  float Ek2;                      //再前一次误差 e(k-2)
}PID_IncTypeDef;

/************************************************
函数名称 : PID_Inc
功    能 : PID增量(Increment)计算
参    数 : SetValue ------ 设置值(期望值)
            ActualValue --- 实际值(反馈值)
            PID ----------- PID数据结构
返 回 值 : PIDInc -------- 本次PID增量(+/-)
作    者 : RootCode
*************************************************/
float PID_Inc(float SetValue, float ActualValue, PID_IncTypeDef *PID)
{
  float PIDInc;                                  //增量

  PID->Ek = SetValue - ActualValue;
  PIDInc = (PID->Kp * PID->Ek) - (PID->Ki * PID->Ek1) + (PID->Kd * PID->Ek2);

  PID->Ek2 = PID->Ek1;
  PID->Ek1 = PID->Ek;

  return PIDInc;
}

python版:

import matplotlib.pyplot as plt 
import numpy as np 
import random
import sys
import os

class PID_Prama:
        def __init__(self):
                self.Kp = 0
                self.Ki = 0
                self.Kd = 0
                self.set_val = 0
                self.error_last = 0
                self.error_prev = 0
                self.error_sum = 0

# 增量计算公式:
# Pout=Kp*[e(t) - e(t-1)] + Ki*e(t) + Kd*[e(t) - 2*e(t-1) +e(t-2)]
def PID_Controller_Increa(pid,out_now):
        error = pid.set_val - out_now
        Res = pid.Kp*(error-pid.error_last) + pid.Ki*error + \
              pid.Kd*(error-2*pid.error_last+pid.error_prev)
        pid.error_prev = pid.error_last
        pid.error_last = error
        return Res

standard_out = 100
PID_val = PID_Prama()

# PID参数
PID_val.Kp = 0.01
PID_val.Ki = 0.1
PID_val.Kd = 0.05
PID_val.set_val = standard_out # 标准输出值
# 增量型PID控制器输出值
PID_Controller_Increa_Out = []
Sys_In = []
# 0时刻系统输入值
Sys_In.append(5)
# 系统响应函数
SystemFunc = lambda x : 5*x + np.random.normal(0,0.5,1)[0]

Sys_Out = []
# 0时刻系统输出值
Sys_Out.append(SystemFunc(Sys_In[0]))

for t_slice in range(Time):
        Diff = PID_Controller_Increa(PID_val,Sys_Out[t_slice]) #系统误差
        PID_Controller_Increa_Out.append(Diff) # 记录所有的系统误差
        Sys_In.append(Sys_In[0]+np.sum(PID_Controller_Increa_Out)) # 计算增量之后的新的系统输入
        Sys_Out.append(SystemFunc(Sys_In[t_slice+1])) # 计算下一时刻系统新的输出值

standard = np.linspace(PID_val.set_val,PID_val.set_val,Time)

plt.figure('PID_Controller_Increa')
plt.xlim(0,Time)
plt.ylim(0,2*standard_out)
plt.plot(Sys_Out)
plt.plot(standard)

plt.show()

四.参考文章

小淼博客

RootCode from CSDN

最后修改:2020 年 03 月 01 日 01 : 06 PM
如果觉得我的文章对你有用,请随意赞赏