各种PID算法——用C语言去实现

(一) 前言

  PID算法在工业应用中随处可见。大学期间,想做各类科创也少不了PID算法的身影。PID除了需要理解原理,用合理的代码实线PID算法也让许多同学们苦恼,我总结了常用的多种PID算法的C语言实现,供大家参考学习。
  我只是简单总结了常用的一些PID算法。对于模糊PID和专家PID,因为我也没用到过,就没有具体给出代码,这其中最难的还是参数的整定。当然仿真软件也十分有帮助,大家可以看我另外的Simulink的学习。
  整理不易,请留个赞呗、

(二) PID原理简单介绍

 PID的流程本质上是通过误差信号控制被控量,而控制器本身就是比例、积分、微分三个环节的加和。
 先大致介绍一下PID的控制流程。
在这里插入图片描述
 首先,我们给出定义的连续的PID公式:
在这里插入图片描述
 但在工程中,我们想要用处理器去计算,必须将其化为离散化模型。这样处理器才可以进行计算。

 偏差            err(K)=rin(K)-rout(K);
 积分环节用加和的形式表示  err(K)+err(K+1)+……;
 微分环节用斜率的形式表示  [err(K)-err(K-1)]/T;

 通过以上公式,可以得到PID离散表示形式:

[1] 位置式PID

下面是位置式PID:
位置型PID

[2] 增量式PID

下面是PID的增量式表示方式:
增量式PID
  增量式的结果和近三次的偏差相关,这就很大地提高了系统的稳定性。
 注意的是最终的输出结果应该为:
      u(K)+调节值;

(三) 位置式PID——C语言

① 定义PID变量结构体:

struct _pid{
    float SetSpeed;           //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_last;           //定义上一个偏差值
    float Kp,Ki,Kd;           //定义比例、积分、微分系数
    float voltage;            //定义电压值(控制执行器的变量)
    float integral;           //定义积分值
}pid;

②初始化变量:

void PID_init(){
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.2;
    pid.Ki=0.015;
    pid.Kd=0.2;
}

Kp,Ki,Kd三个参数,调试过程当中,对于要求的控制效果,可以通过调节这三个量直接进行调节。
 当然也可以写函数来直接修改比例系数的值,这很简单我就不写了。
③控制算法:

float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    pid.integral+=pid.err;
    pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}

 注:这是最基本的算法实现形式,稳定到设定值的速度慢,且没有考虑死区问题,还没有设定阈值,。这只是一种原理上算法的直接实现,并不能直接用于工程中

(四) 增量型PID——C语言

①定义并初始化PID

struct _pid{
    float SetSpeed;            //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_next;            //定义上一个偏差值
    float err_last;            //定义最上前的偏差值
    float Kp,Ki,Kd;            //定义比例、积分、微分系数
}pid;

void PID_init(){
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.err_next=0.0;
    pid.Kp=0.15;
    pid.Ki=0.20;
    pid.Kd=0.25;
}

②控制算法:

float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    float incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
    pid.ActualSpeed+=incrementSpeed;
    pid.err_last=pid.err_next;
    pid.err_next=pid.err;
    return pid.ActualSpeed;
}

③测试算法

int main(){
    PID_init();
    int count=0;
    while(count<1000)
    {
        float speed=PID_realize(150.0);
        printf("%f\n",speed);
        count++;
    }
    return 0;
}

(五) 积分分离的PID控制算法——C语言

  当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度与稳定速度。
  部分控制代码

if(abs(pid.err)>100)
    {
    index=0;
    }else{
    index=1;
    pid.integral+=pid.err;
    }
    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);  

 这种控制算法的速度很快

(六) 抗积分饱和的PID控制算法——C语言

  积分饱和现象是指当系统是一个方向出现的偏差,使PID控制器的输出与积分作用不断积累更多,导致执行机构超出极限位置
  这时如果控制器输出U (k)继续增加,致动器的开放不可能增加,电脑输出控制量超出了正常操作范围,进入饱和区。当系统出现反向偏差时,u(k)逐渐退出饱和区。你进入饱和区越深,你离开的时间就越长。系统就会失控,导致控制性能的恶化
  防止积分饱和的方法之一——抗积分饱和法,该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围: 若u(k-1)>umax,则只累加负偏差; 若u(k-1)<umin,则只累加正偏差。避免了控制量长时间在饱和区。

struct _pid{
    float SetSpeed;            //定义设定值
    float ActualSpeed;        //定义实际值
    float err;                //定义偏差值
    float err_last;            //定义上一个偏差值
    float Kp,Ki,Kd;            //定义比例、积分、微分系数
    float voltage;             //定义控制执行器的变量
    float integral;            //定义积分值
    float umax;
    float umin;
}pid;

void PID_init(){
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.2;
    pid.Ki=0.1;         //这里加大了积分环节的值
    pid.Kd=0.2;
    pid.umax=100;
    pid.umin=-100;
}
float PID_realize(float speed){
    int index;
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;

   if(pid.ActualSpeed>pid.umax)  
    {
       if(abs(pid.err)>100)      
        {
            index=0;
        }else{
            index=1;
            if(pid.err<0)
            {
              pid.integral+=pid.err;
            }
        }
    }else if(pid.ActualSpeed<pid.umin){
        if(abs(pid.err)>100)      //积分分离过程
        {
            index=0;
        }else{
            index=1;
            if(pid.err>0)
            {
            pid.integral+=pid.err;
            }
        }
    }else{
        if(abs(pid.err)>200)                    //积分分离过程
        {
            index=0;
        }else{
            index=1;
            pid.integral+=pid.err;
        }
    }

    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}

(七) 变积分的PID控制算法——C语言

  根据系统的偏差大小改变积分速度。
  变积分PID的基本思想是设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢; 偏差越小,积分越快。
 具体实现思路为:
  给积分系数前加上一个比例值index:
   当abs(err)<150时,  index=1;
  当100<abs(err)<150时,index=(150-abs(err)/20;
  当abs(err)>150时,  index=0;

 最终的比例环节的比例系数值为ki*index;
控制函数为:

 float PID_realize(float speed){
    float index;
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    
    //变积分过程
    if(abs(pid.err)>150)
   {
    index=0.0;
    }else if(abs(pid.err)<100){
    index=1.0;
    pid.integral+=pid.err;
    }else{
    index=(150-abs(pid.err))/20;
    pid.integral+=pid.err;
    }
    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);

    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}

 这种控制方式会使系统的稳定速度非常快

  这就是一些简单的PID实现的大体思路。在实际工程中需要结合实际再加调整,选择合适项目的PID算法。在有些时候,也许只用到PI或PD即可。

(八) 附言

附送一个参数整定口诀

                参数整定找最佳, 从小到大顺序查。
                先是比例后积分, 最后再把微分加。
                曲线振荡很频繁, 比例度盘要放大。
                曲线漂浮绕大弯, 比例度盘往小扳。
                曲线偏离回复慢, 积分时间往下降。
                曲线波动周期长, 积分时间再加长。
                曲线振荡频率快, 先把微分降下来。
                动差大来波动慢, 微分时间应加长。
                理想曲线两个波, 前高后低四比一。
                一看二调多分析, 调节质量不会低。

  这只是PID纯算法部分,如何将PID算出的数值合理地反馈到输入上,如何定时去捕获执行PID,可以在我的其他博文中查看。
  ——《PID采样时间精准控制——基于stm32主控》
https://blog.csdn.net/Nirvana_Tai/article/details/105404601

  • 81
    点赞
  • 639
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
pid(比例-积分-微分)控制器是一种常用于工业控制系统中的反馈控制算法。它是根据系统当前的误差、误差的积分以及误差的微分来计算控制信号,以实现系统的稳定和精确控制。 在C语言中,可以实现不同种类的pid算法来满足不同控制需求。以下是几种常见的pid算法C语言实现示例: 1. 标准PID算法: ```c float computePID(float error) { float kp = 0.5; // 比例系数 float ki = 0.2; // 积分系数 float kd = 0.1; // 微分系数 static float integral = 0; // 积分项 static float previous_error = 0; //上一次误差 float proportional = error * kp; integral += error; float derivative = (error - previous_error) * kd; float output = proportional + (ki * integral) + derivative; previous_error = error; return output; } ``` 2. 增量式PID算法: ```c float computePID(float error) { float kp = 0.5; float ki = 0.2; float kd = 0.1; static float previous_error = 0; static float previous_output = 0; float incremental_change = kp * (error - previous_error) + ki * error + kd * (error - 2 * previous_error + previous_output); float output = previous_output + incremental_change; previous_error = error; previous_output = output; return output; } ``` 3. 位置式PID算法: ```c float computePID(float error) { float kp = 0.5; float ki = 0.2; float kd = 0.1; static float integral = 0; static float previous_error = 0; float proportional = kp * error; integral += error; float derivative = kd * (error - previous_error); float output = proportional + ki * integral + derivative; previous_error = error; return output; } ``` 上述代码中,通过根据给定的比例、积分和微分系数计算出相应的控制输出。不同的PID算法会有不同的计算方式,实现上略有差异。需要根据具体控制系统的需求选择合适的算法和参数进行调整。以上代码仅作示例,实际应用中可能需要根据具体情况进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TaiBai-let

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值