PID算法分为两种:

  1. 位置式PID

  2. 增量式PID

位置式PID算的是当前控制系统应该输出的值,增量式PID算的是当前控制系统该输出值的变化值。

以下仅介绍增量式PID。

 

PID公式解析

PID公式:

u(t) = K_p[e(t)+ {1\over T_i} \int_0^t e(t)dt + T_d {de(t) \over dt}]
  • u(t)输出曲线,pid输出值随时间的变化曲线

  • K_p比例系数

  • e(t)偏差曲线,设定值与实际值的偏差随时间的变化曲线

  • T_i积分时间

  • T_d微分时间

Kp比例系数放到括号外面:

u(t) = K_p*e(t)+ K_p*{1\over T_i} \int_0^t e(t)dt + K_p*T_d {de(t) \over dt}

比例: P = K_p

积分: I = K_p*{1\over T_i}

微分: P = K_p*T_d

int_0^t e(t)dt是求时间0到时间t,偏差曲线e(t)与X轴围起来的面积值的代数和

de(t) \over dt求偏差曲线e(t)的斜率

即:

u(t) = P*偏差 + I*面积 + D*斜率

偏差=设定值-实际值,因此偏差曲线e(t)=SET-实际值曲线,也就是实际值曲线的x轴向上平移SET便是误差曲线e(t)。

根据高等数学的知识我们可以知道,在曲线SET下围成的面积为正的,在上面围成的面积为负的,即:面积A和面积C符号为正面积B符号为负,假设P、I、D都不为零,且都大于零。

 

比例项

积分项

微分项

输出

被调量结果

0

偏差>0

正大

斜率绝对值变大,且>0

慢慢变大

快速靠近设定值

T1

偏差<0

减小

斜率绝对值变小,且>0

 

 

T2

偏差<0

减小

斜率绝对值变大,且<0

 

 

可以发现:

比例项是纠正偏差的主力,越远离偏差绝对值就越大,快速把偏差纠正回来。

积分项和以往的状态有关,面积的绝对值越大它的绝对值就越大,它的作用是消除累计偏差。

微分项跟斜率有关,比较难解释,总的来说它的作用是:当目标靠近设定值时加速它靠近,当目标远离设定值时阻止它远离。因此微分可以增加系统稳定性,因为到达目的之后,离开会受到阻碍。

 

PID公式离散化

因为实际应用中我们用到的都是离散的数据,因此对PID进行编程之前我们得先把模拟公式转换成离散公式。

其实就是把积分求面积、微分求斜率的方法换成离散的求面积和斜率的方法,离散转换后:

u_k = K_p[e_k + {T\over T_i}\sum_{j=0}^{k}{e_j} + T_d{e_k-e_{k-1}\over T}]

其中:

  • u_k输出曲线,pid输出值随时间的变化曲线

  • K_p比例系数

  • e_k偏差曲线,设定值与实际值的偏差随时间的变化曲线

  • T_i积分时间

  • T_d微分时间

  • T​调节周期

离散化后,多了参数T。

如果T为定值,则:

K_i = K_p * {T \over T_i}
K_d = K_p * {T_d \over T}

换算后公式:

u_k = K_p*e_k+K_i\sum_{j=0}^k e_j+K_d(e_k-e_{k-1})

如果T不为定值,则换算后公式:

u_k = K_p*e_k + T*K_i\sum_{j=0}^k e_j + {K_d(e_k-e_{k-1})\over T}

C语言实现

#define I_MAX 100.0
#define I_MIN -100.0
float kP = 0;
float kI = 0;
float kD = 0;
float PID(float current, float target)
{
    static float iValue = 0;
    static float errorLast = 0;
    float pValue, dValue;
  
    float error = target - current; //误差
    pValue = kP * error; //比例值
    iValue = iValue + kI * error; //积分值
    if (iValue >= I_MAX)          //积分限幅,可选
        iValue == I_MAX;
    if (iValue <= I_MIN)
        iValue = I_MIN;
    dValue = (error - errorLast) * kD; //微分值
    errorLast = error; //记录上一次误差
}
void MainPIDHandler_10ms()
{
    float target = 0;                 //设定目标值
    float current = 0;                //获取当前值
    float out = PID(current, target); //计算执行量
}

致敬:PID公式通俗理解_灵魂Maker的博客-CSDN博客_pid公式

附: