zoukankan      html  css  js  c++  java
  • 【转】位置式、增量式PID算法C语言实现

    位置式、增量式PID算法C语言实现

    芯片:STM32F107VC

    编译器:KEIL4

    作者:SY

    日期:2017-9-21 15:29:19

    概述

    PID 算法是一种工控领域常见的控制算法,用于闭环反馈控制。有以下两种分类:

    • 增量式

      每次周期性计算出的 PID 为增量值,是在上一次控制量的基础上进行的调整。

    • 位置式

      每次周期性计算出的 PID 为绝对的数值,是执行机构实际的位置。

    我们使用高级语言的思想去实现两种 PID ,做到对于用户来说,调用相同的接口,内部实现不同的 PID 算法。

    代码

    pid.h

     1 enum PID_MODE {
     2     PID_INC = 0,    //增量式
     3     PID_POS,        //位置式
     4 };
     5 
     6 struct PID {
     7     enum PID_MODE mode;
     8 
     9     float kp;                   //比例系数
    10     float ki;                   //积分系数
    11     float kd;                   //微分系数
    12 
    13     double targetPoint;         //目标点
    14     double lastError;           //Error[-1]
    15     double prevError;           //Error[-2]
    16 
    17     void (*init)(struct PID *this, double targetPoint);         //PID初始化
    18     double (*outputLimit)(struct PID *this, double output);     //PID输出限制
    19     void (*setParameter)(struct PID *this, 
    20         float kp, float ki, float kd);                          //设置PID参数
    21     double (*calculate)(struct PID *this, double samplePoint);  //计算PID
    22 };
    23 
    24 /* 增量式PID */
    25 struct PID_INC {
    26     struct PID pid;
    27 };
    28 
    29 /* 位置式PID */
    30 struct PID_POS {
    31     struct PID pid;
    32     double iSum;                //积分和
    33 };

    其中 struct PID 就是我们提供给用户的接口,可以理解为 抽象类 ,增量式和位置式 PID 都去继承该抽象类,然后实现其中的抽象方法。

    对于位置式 PID 拥有自己的成员 iSum 。

    pid.c

      1 /*
      2 *********************************************************************************************************
      3 *                                           PID
      4 *********************************************************************************************************
      5 */
      6 /*
      7 *********************************************************************************************************
      8 * Function Name : PID_Init
      9 * Description   : PID初始化
     10 * Input         : None
     11 * Output        : None
     12 * Return        : None
     13 *********************************************************************************************************
     14 */
     15 static void PID_Init(struct PID *this, double targetPoint)
     16 {
     17     this->targetPoint = targetPoint;
     18     this->lastError = 0;
     19     this->prevError = 0;    
     20 }
     21 
     22 /*
     23 *********************************************************************************************************
     24 * Function Name : PID_OutputLimit
     25 * Description   : PID输出限制
     26 * Input         : None
     27 * Output        : None
     28 * Return        : None
     29 *********************************************************************************************************
     30 */
     31 static double PID_OutputLimit(struct PID *this, double output)
     32 {
     33     if (output < 0) {
     34         output = 0;
     35     } else if (output > DIGITAL_THROTTLE_VALVE_MAX_DEGREE) {
     36         output = DIGITAL_THROTTLE_VALVE_MAX_DEGREE;
     37     }
     38     return output;
     39 }
     40 
     41 /*
     42 *********************************************************************************************************
     43 * Function Name : PID_SetParameter
     44 * Description   : PID设置参数
     45 * Input         : None
     46 * Output        : None
     47 * Return        : None
     48 *********************************************************************************************************
     49 */
     50 static void PID_SetParameter(struct PID *this, float kp, float ki, float kd)
     51 {
     52     this->kp = kp;
     53     this->ki = ki;
     54     this->kd = kd;
     55 }
     56 
     57 /*
     58 *********************************************************************************************************
     59 * Function Name : PID_SetTargetValue
     60 * Description   : PID设置目标值
     61 * Input         : None
     62 * Output        : None
     63 * Return        : None
     64 *********************************************************************************************************
     65 */
     66 void PID_SetTargetValue(struct PID *this, double targetPoint)
     67 {
     68     this->targetPoint = targetPoint;
     69 }
     70 
     71 /*
     72 *********************************************************************************************************
     73 * Function Name : PID_GetTargetValue
     74 * Description   : PID获取目标值
     75 * Input         : None
     76 * Output        : None
     77 * Return        : None
     78 *********************************************************************************************************
     79 */
     80 double PID_GetTargetValue(struct PID *this)
     81 {
     82     return this->targetPoint;
     83 }
     84 
     85 /*
     86 *********************************************************************************************************
     87 *                                           增量式PID
     88 *********************************************************************************************************
     89 */
     90 static double PID_IncCalculate(struct PID *this, double samplePoint);
     91 
     92 struct PID_INC g_PID_Inc = {
     93     .pid = {
     94         .mode           = PID_INC,
     95         .init           = PID_Init,
     96         .outputLimit    = PID_OutputLimit,
     97         .setParameter   = PID_SetParameter,
     98         .calculate      = PID_IncCalculate,
     99     },
    100 };
    101 
    102 /*
    103 *********************************************************************************************************
    104 * Function Name : PID_IncCalculate
    105 * Description   : 增量式PID计算
    106 * Input         : None
    107 * Output        : None
    108 * Return        : None
    109 *********************************************************************************************************
    110 */
    111 static double PID_IncCalculate(struct PID *this, double samplePoint)
    112 {   
    113     double nowError = this->targetPoint - samplePoint;
    114     double out = this->kp * nowError +
    115                  this->ki * this->lastError +
    116                  this->kd * this->prevError;
    117     this->prevError = this->lastError;
    118     this->lastError = nowError;
    119 
    120     if (this->outputLimit) {
    121         out = this->outputLimit(this, out);
    122     }
    123 
    124     return out;
    125 }
    126 
    127 /*
    128 *********************************************************************************************************
    129 *                                           位置式PID
    130 *********************************************************************************************************
    131 */
    132 static double PID_PosCalculate(struct PID *this, double samplePoint);
    133 static void PID_PosInit(struct PID *this, double targetPoint);
    134 
    135 struct PID_POS g_PID_Pos = {
    136     .pid = {
    137         .mode           = PID_POS,
    138         .init           = PID_PosInit,
    139         .outputLimit    = PID_OutputLimit,
    140         .setParameter   = PID_SetParameter,
    141         .calculate      = PID_PosCalculate,
    142     },
    143 };
    144 
    145 /*
    146 *********************************************************************************************************
    147 * Function Name : PID_PosInit
    148 * Description   : 位置式PID初始化
    149 * Input         : None
    150 * Output        : None
    151 * Return        : None
    152 *********************************************************************************************************
    153 */
    154 static void PID_PosInit(struct PID *this, double targetPoint)
    155 {
    156     PID_Init(this, targetPoint);
    157     struct PID_POS *pid_Handle = (struct PID_POS *)this;
    158     pid_Handle->iSum = 0;
    159 }
    160 
    161 /*
    162 *********************************************************************************************************
    163 * Function Name : PID_PosCalculate
    164 * Description   : 位置式PID计算
    165 * Input         : None
    166 * Output        : None
    167 * Return        : None
    168 *********************************************************************************************************
    169 */
    170 static double PID_PosCalculate(struct PID *this, double samplePoint)
    171 {
    172     struct PID_POS *pid_Handle = (struct PID_POS *)this;
    173 
    174     double nowError = this->targetPoint - samplePoint;
    175     this->lastError = nowError;
    176     //积分累计误差
    177     pid_Handle->iSum += nowError;
    178     double out = this->kp * nowError +
    179                  this->ki * pid_Handle->iSum +
    180                  this->kd * (nowError - this->prevError);   
    181     this->prevError = nowError;
    182 
    183     if (this->outputLimit) {
    184         out = this->outputLimit(this, out);
    185     }
    186 
    187     return out;
    188 }

    对于上述内容:最关键的是两个变量 struct PID_INC g_PID_Inc 和 struct PID_POS g_PID_Pos ,他们针对抽象类实现了各自的算法。

    其中有一个很重要的小技巧,举例:

    1 static double PID_PosCalculate(struct PID *this, double samplePoint)
    2 {
    3     struct PID_POS *pid_Handle = (struct PID_POS *)this;
    4 }

    该函数的接口是 struct PID *this ,但是我们内部将他强制转换为 struct PID_POS *pid_Handle ,这是两种不同的数据类型,为什么可以进行转换呢?而且转换后的数据是正确的?

    因为对于 C 语言来说,结构体内部的第一个成员的地址和该结构体变量的地址是一样的,所以可以相互转换,实现向下转型,这就是 C 语言的强大之处。

    测试

    app.c

     1 struct KernelCtrl {
     2     struct DTV dtv;
     3     struct PID *dtvPid;
     4 };
     5 
     6 static struct KernelCtrl g_kernelCtrl;
     7 
     8 /*
     9 *********************************************************************************************************
    10 * Function Name : Kernel_Ctrl_Init
    11 * Description   : 内核控制初始化
    12 * Input         : None
    13 * Output        : None
    14 * Return        : None
    15 *********************************************************************************************************
    16 */
    17 void Kernel_Ctrl_Init(void)
    18 {
    19 #if 1
    20     /* 增量式 */
    21     g_kernelCtrl.dtvPid = &g_PID_Inc.pid;
    22 #else
    23     /* 位置式 */
    24     g_kernelCtrl.dtvPid = &g_PID_Pos.pid;
    25 #endif  
    26 }

    只要在初始化时,指定使用哪一种 PID 模式,在调用时两种方式可以使用同一个接口,这样对于用户来说就屏蔽了内部细节。

    参考

    Pid控制算法-变积分的pid算法的C++实现

    来源

  • 相关阅读:
    Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等
    Excel里的单元格提行
    Ubuntu各版本的历史发行界面
    CentOS常用命令、快照、克隆大揭秘
    虚拟网络编辑器的知识和出现的一些问题(没有VMnet0或VMnet8)
    CentOS图形界面下如何安装Eclipse和使用maven
    Hadoop MapReduce编程 API入门系列之mr编程快捷键活用技巧详解(四)
    Hadoop MapReduce概念学习系列之mr程序详谈(二十三)
    Hadoop MapReduce概念学习系列之mr的Shuffle(二十二)
    Hadoop MapReduce概念学习系列之JobTracker、ResourceManager、Task Tracker、NodeManager(二十一)
  • 原文地址:https://www.cnblogs.com/skullboyer/p/9700762.html
Copyright © 2011-2022 走看看