首页 > 编程语言 >多种PID算法用C语言来实现

多种PID算法用C语言来实现

时间:2022-10-06 21:44:37浏览次数:45  
标签:err PID float pid C语言 算法 积分 ActualSpeed

原文链接:https://blog.csdn.net/Nirvana_Tai/article/details/105409311,随后整理验证,再补充 (一) 前言   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:     [2] 增量式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算法在工业应用中随处可见。大学期间,想做各类科创也少不了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:

[2] 增量式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;123456789②初始化变量:
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;}1234567891011 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;}123456789 注:这是最基本的算法实现形式,稳定到设定值的速度慢,且没有考虑死区问题,还没有设定阈值,。这只是一种原理上算法的直接实现,并不能直接用于工程中。
(四) 增量型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;}
12345678910111213141516171819②控制算法:
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;}123456789③测试算法
int main(){    PID_init();    int count=0;    while(count<1000)    {        float speed=PID_realize(150.0);        printf("%f\n",speed);        count++;    }    return 0;}1234567891011(五) 积分分离的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);  12345678 这种控制算法的速度很快
(六) 抗积分饱和的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;}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768(七) 变积分的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;}
12345678910111213141516171819202122 这种控制方式会使系统的稳定速度非常快
  这就是一些简单的PID实现的大体思路。在实际工程中需要结合实际再加调整,选择合适项目的PID算法。在有些时候,也许只用到PI或PD即可。
(八) 附言附送一个参数整定口诀
                参数整定找最佳, 从小到大顺序查。                先是比例后积分, 最后再把微分加。                曲线振荡很频繁, 比例度盘要放大。                曲线漂浮绕大弯, 比例度盘往小扳。                曲线偏离回复慢, 积分时间往下降。                曲线波动周期长, 积分时间再加长。                曲线振荡频率快, 先把微分降下来。                动差大来波动慢, 微分时间应加长。                理想曲线两个波, 前高后低四比一。                一看二调多分析, 调节质量不会低。12345678910  这只是PID纯算法部分,如何将PID算出的数值合理地反馈到输入上,如何定时去捕获执行PID,可以在我的其他博文中查看。————————————————版权声明:本文为CSDN博主「TaiBai-let」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/Nirvana_Tai/article/details/105409311

标签:err,PID,float,pid,C语言,算法,积分,ActualSpeed
From: https://www.cnblogs.com/guochaoxxl/p/16758582.html

相关文章

  • C语言操作符
    1.在进行除法运算的时候,如果两个操作数都是整数,那么结果就是整数。要想得到的结果是浮点数,至少有一个操作数是浮点数,最后的结果也是定义为浮点类型。2.在进行取模运算的时候......
  • 【C语言】初始函数
    ......
  • C语言下for循环的一点技巧总结
    for循环是普遍应用与各种计算机语言的一种循环方式。一般情况下,for循环规则:for(条件一;条件二;条件三)条件一为满足条件,也就是条件一为1时,进入这个for循环。条件二为循环......
  • C语言基础笔试题解析
    题目在这里:​​c语言笔试面试大全,C语言基础笔试题_Thomas杨大炮的博客-CSDN博客t​​2.C语言程序的三种基本结构都有哪些呢?3. ​​递归调用​​和间接递归调用​​定义​......
  • 混音算法记录
    因项目需求实现混音效果,故学习记录了一下。算法调研常见混音方式:加和后再除以混音通道数,防止溢出加和并箝位,如有溢出就设最大值饱和处理,接近最大值时进行扭曲(“软件......
  • 算法学习笔记(数学):数论分块 + 容斥原理 + 莫比乌斯函数
    算法学习笔记(数学):数论分块+容斥原理+莫比乌斯函数这篇文章主要是要讲一道题目(链接在这里)以及梳理一下数论分块,莫比乌斯函数,容斥原理这些知识。先介绍下知识点吧qwq......
  • C语言:ASCII码为0的字符成为循环条件
    #include<stdio.h>main(){chars[]="012xy\08s34f4w2";//ascii码0对应的字符为空字符//本来\08可以理解为1个字符,但8不是8进制数,斜线只能转义0//......
  • 人脸对齐之GBDT(ERT)算法解读
    1.概述文章名称:OneMillisecondFaceAlignmentwithanEnsembleofRegressionTrees 文章作者:VahidKazemi,JosephineSullivan 简要介绍:OneMillisecondFaceAlignme......
  • C语言:字符串复制与连接的特殊应用
    #include<stdio.h>#include<string.h>main(){chara[10]="abc",b[10]="012",c[10]="xyz";strcpy(a+1,b+2);//b+2对应的字符2\0,结果bc改为2\0,所以a结果......
  • 最短路径问题---Dijkstra算法详解
    0.最短路径问题介绍问题解释:从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径1.Dijkstra算法介绍算法特点:迪科斯彻算法使用......