PID算法个人理解及C代码实现

一、PID算法简介

        PID 是 Proportional(比例)、Integral(积分)、Differential(微分)的首字母缩写,它是一种结合比例、积分和微分三个环节于一体的闭环控制算法。

    本质:根据输入的偏差值,按照比例、积分、微分的函数关系进行运算,运算结果用以控制输出。

二、PID离散公式

        1、位置式

                

        Kp: 比例系数。

        Ki: 积分系数。

        Kd: 微分系数。

        ek : 实际与预期的误差值。

        ∑_j=0^k▒e_j : 每一次误差值的和。

        ek-ek-1 :本次误差值与上一次误差值的插值。        

        有人喜欢把P认为是过去(及过去的误差)、I认为是现在(及现在所要达到的值)、D认为是将来(及对将来所要达到值的控制)。

代码实现:

#include <stdio.h>
#include <string.h>

#define Kp 0.1f
#define Ki 0.01f
#define Kd 0.0f
typedef struct{
   float target_value;           //设定的目标值及需要达到的最终值
   float current_value;          //当前值(可认为外部的反馈值)
   float CAL_value;              //计算需要输出的值
   float sum_error;              //累计的偏差值
   float error;                  //误差值
   float last_error;             //上一次误差值
   float pre_error;              //上上一次误差值(增量式pid中使用)
} PID;

   PID pid;
   float control_value = 0;
float Velocity_FeedbackControl(float Targetvalue, float Currentvalue){

   pid.error =  Targetvalue - Currentvalue;
   pid.sum_error += pid.error;

//   if(pid.sum_error>500){
//      pid.sum_error = 500;
//   }
//   if(pid.sum_error < 0){
//      pid.sum_error = 0;
//   }
   pid.CAL_value += (Kp * pid.error  + Ki * pid.sum_error + Kd * (pid.error - pid.last_error));
   pid.last_error = pid.error;
   pid.current_value = pid.CAL_value;          //由于没有外部反馈  就默认当值已经到达计算值 
   return pid.current_value;
}

int main(void){
   memset(&pid, 0,sizeof(PID));
   int i = 0;   
   while(i<100){
      i++;
      pid.current_value = Velocity_FeedbackControl(10, pid.current_value);
      printf("%f\n", pid.current_value);
   }

   return 0;
}

运行结果如下:

 从结果看出结果还是理想的,由于Kd设置为0没有消除静态误差,所以出现了超过目标值10.

while循环中仅仅设置了100次而已,在正真的闭环中,次数式不限的。

注意到代码中注释的段落,对pid.sum_error设限制是因为在一个闭环系统中pid会无限制的进行计算,这个pid.sum_error会不断地累计。就电机控制中的速度环而言,若停在一个速度保持很近,突然给定一个减速指令,整个系统的响应会变得很慢所以需要设定限制。

2、增量式

        增量式的pid是有位置式推到出来的如下:

 第一步:把 k = k −1 代入公式①中,得:

第二步:把公式① − 公式②,得:

 

 可以看出增量式PID计算的是相对上一次输出的增量,即u_k= u_k−1+ ∆u_k 。增量只与近3次的偏差有关,计算出现异常对系统工作影响较小。计算量较小。

代码实现如下:

#include <stdio.h>
#include <string.h>

#define Kp 0.1f
#define Ki 0.01f
#define Kd 0.1f
typedef struct{
   float target_value;           //设定的目标值及需要达到的最终值
   float current_value;          //当前值(可认为外部的反馈值)
   float CAL_value;              //计算需要输出的值
   float sum_error;              //累计的偏差值
   float error;                  //误差值
   float last_error;             //上一次误差值
   float pre_error;              //上上一次误差值(增量式pid中使用)
} PID;

   PID pid;

float Velocity_FeedbackControl(float Targetvalue, float Currentvalue){
      pid.error =  Targetvalue - Currentvalue;
      pid.CAL_value +=(Kp * (pid.error - pid.last_error) + Ki * pid.error + Kd * (pid.error - 2* pid.last_error + pid.pre_error));
      pid.pre_error = pid.last_error;
      pid.last_error = pid.error;
      pid.current_value = pid.CAL_value;                    //由于没有外部反馈  就默认当值已经到达计算值 
      return pid.current_value;
}

int main(void){
   memset(&pid, 0,sizeof(PID));
   int i = 0;   
   while(i<500){
      i++;
      pid.current_value = Velocity_FeedbackControl(10, pid.current_value);
      printf("%f\n", pid.current_value);
   }

   return 0;
}

运行结果如下:

         从上结果可以看出,在同样的系数下位置式的响应要高于增量式。当然这跟pid的系数有直接关系。在实际运用过程中这三个系数是要慢慢调整的。

总结:

        以上只是对PID有一个初步的认识。至于更加复杂的算法,有待自己去思考,但是万变不离其宗。文中的公式推导来自原子的电机控制教程ppt。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值