SaintStefan
12/22/2019 - 9:11 PM

抗积分饱和的积分分离的增量型PID控制算法C语言实现

/*******************************************************************************
 * 抗积分饱和的积分分离的增量型PID控制算法C语言实现
 *******************************************************************************
 * 在普通PID控制中,引入积分环节的目的,主要是为了消除静差,提高控制精度。
 * 但是在启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成PID运算的积分积累,
 * 导致控制量超过执行机构可能允许的最大动作范围对应极限控制量,
 * 从而引起较大的超调,甚至是震荡,这是绝对不允许的。
 * 为了克服这一问题,引入了积分分离的概念,其基本思路是 
 * 当被控量与设定值偏差较大时,取消积分作用; 
 * 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。
 *******************************************************************************
 * 所谓的积分饱和现象是指
 * 如果系统存在一个方向的偏差,PID控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,
 * 若控制器输出 u(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。
 * 一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。
 * 在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,
 * 这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。
 * 防止积分饱和的方法之一就是抗积分饱和法,
 * 该方法的思路是在计算 u(k)时,首先判断上一时刻的控制量 u(k-1)是否已经超出了极限范围: 
 * 如果 u(k-1) > umax,则只累加负偏差(设定值小于输出值); 
 * 如果 u(k-1) < umin,则只累加正偏差(设定值大于输出值)。从而避免控制量长时间停留在饱和区。
 *******************************************************************************
 * PID增量表达式:∆u(k) = Kp*(error(k) - error(k-1)) + Ki*(error(k)) + Kd*(error(k) - 2*error(k-1) + error(k-2))
 * 最终的结果 = u(k) + ∆u(k)
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

struct _pid
{
    float SetSpeed;                 // 定义设定值
    float ActualSpeed;              // 定义实际值
    float error;                    // 定义偏差值
    float error_next1;              // 定义偏差值(k-1)
    float error_next2;              // 定义偏差值(k-2)
    float err;
    float Kp,Ki,Kd;                 // 定义比例、积分、微分系数
    float umax;
    float umin;
}pid;                               // 定义了一个结构名为_pid的结构变量pid

void PID_init()
{
    printf("PID_init begin\n");
    pid.SetSpeed = 0.0;
    pid.ActualSpeed = 0.0;
    pid.error = 0.0;
    pid.error_next2 = 0.0;
    pid.error_next1 = 0.0;
    pid.err = 0.0;
    pid.Kp = 0.2;                   // 需要自行整定
    pid.Ki = 0.1;                   // 注意,和普通增量,以及积分分离类型相比,这里加大了积分环节的值
    pid.Kd = 0.2;                   //
    pid.umax = 400;
    pid.umin = -200;
    printf("PID_init end\n");
}

float PID_realize(float speed)
{
    float incrementSpeed;
    int index;

    pid.SetSpeed = speed;
    pid.error = pid.SetSpeed - pid.ActualSpeed;

    if(pid.ActualSpeed > pid.umax)       // 抗积分饱和过程
    {
        // abs()是用来求int型的绝对值,fabsf()是用来求float型的绝对值,fabs()是用来求double型的绝对值,要用库math.h
        if(fabsf(pid.error) > 200)       // 积分分离过程 
        {
            index = 0;
        }
        else
        {
            index = 1;
            if(pid.error < 0)
            {
                pid.err = pid.error;
            }
            else
            {
                pid.err = 0.0;
            }
        }
    }
    else if(pid.ActualSpeed < pid.umin)  // 抗积分饱和过程
    {
        if(fabsf(pid.error) > 200)       // 积分分离过程        
        {
            index = 0;
        }
        else
        {
            index = 1;
            if(pid.error > 0)
            {
                pid.err = pid.error;
            }
            else
            {
                pid.err = 0.0;
            }
        }
    }
    else
    {
        if(fabsf(pid.error) > 200)
        {
            index = 0;
        }
        else
        {
            index = 1;
            pid.err = pid.error;
        }  
    }
    
    incrementSpeed = pid.Kp*(pid.error - pid.error_next1) + index*pid.Ki*pid.err+ pid.Kd*(pid.error - 2*pid.error_next1 + pid.error_next2);
    
    pid.ActualSpeed += incrementSpeed;
    pid.error_next2 = pid.error_next1;
    pid.error_next1 = pid.error;
    return pid.ActualSpeed;
}

int main()
{
    PID_init();
    int count = 0;
    while(count < 1000)             // 运行1000次,得到1000个数据
    {
        float speed = PID_realize(200.0);         // speed = pid.ActualSpeed
        printf("%f\n",speed);       
        count++;
    }
    return 0;
}