增量式PID计算公式4个疑问与理解

PID控制是一个二阶线性控制器

    定义:通过调整比例、积分和微分三项参数,使得大多数的工业控制系统获得良好的闭环控制性能。

    优点

            a. 技术成熟

       b. 易被人们熟悉和掌握

       c. 不需要建立数学模型

       d. 控制效果好

       e. 鲁棒性

      通常依据控制器输出与执行机构的对应关系,将基本数字PID算法分为位置式PID和增量式PID两种。

1  位置式PID控制算法

基本PID控制器的理想算式为

扫描二维码关注公众号,回复: 4735935 查看本文章

PID控制 - lql990832 - lql990832的博客               (1)

 

式中

u(t)——控制器(也称调节器)的输出;

e(t)——控制器的输入(常常是设定值与被控量之差,即e(t)=r(t)-c(t));

Kp——控制器的比例放大系数;

Ti ——控制器的积分时间;

Td——控制器的微分时间。

u(k)为第k次采样时刻控制器的输出值,可得离散的PID算式

                              PID控制 - lql990832 - lql990832的博客       (2)

式中 PID控制 - lql990832 - lql990832的博客,   PID控制 - lql990832 - lql990832的博客  。

    由于计算机的输出u(k)直接控制执行机构(如阀门),u(k)的值与执行机构的位置(如阀门开

度)一一对应,所以通常称式(2)为位置式PID控制算法。

    位置式PID控制算法的缺点:当前采样时刻的输出与过去的各个状态有关,计算时要对e(k)进

行累加,运算量大;而且控制器的输出u(k)对应的是执行机构的实际位置,如果计算机出现故

障,u(k)的大幅度变化会引起执行机构位置的大幅度变化。

  2  增量式PID控制算法

增量式PID是指数字控制器的输出只是控制量的增量Δu(k)。采用增量式算法时,计算机输出的控制量Δu(k)对应的是本次执行机构位置的增量,而不是对应执行机构的实际位置,因此要求执行机构必须具有对控制量增量的累积功能,才能完成对被控对象的控制操作。执行机构的累积功能可以采用硬件的方法实现;也可以采用软件来实现,如利用算式 u(k)=u(k-1)+Δu(k)程序化来完成。

由式(2)可得增量式PID控制算式

PID控制 - lql990832 - lql990832的博客  (3)

式中 Δe(k)=e(k)-e(k-1)

进一步可以改写成

PID控制 - lql990832 - lql990832的博客                                                (4)

式中 PID控制 - lql990832 - lql990832的博客PID控制 - lql990832 - lql990832的博客 、PID控制 - lql990832 - lql990832的博客

一般计算机控制系统的采样周期T在选定后就不再改变,所以,一旦确定了Kp、Ti、Td,只要使用前后3次测量的偏差值即可由式(3)或式(4)求出控制增量。

增量式算法优点:①算式中不需要累加。控制增量Δu(k)的确定仅与最近3次的采样值有关,容易通过加权处理获得比较好的控制效果;②计算机每次只输出控制增量,即对应执行机构位置的变化量,故机器发生故障时影响范围小、不会严重影响生产过程;③手动—自动切换时冲击小。当控制从手动向自动切换时,可以作到无扰动切换。

对于增量式PID计算公式4个疑问与理解

  

一开始见到PID计算公式时总是疑问为什么是那样子?为了理解那几道公式,当时将其未简化前的公式“活生生”地算了一遍,现在想来,这样的演算过程固然有助于理解,但假如一开始就带着对疑问的答案已有一定看法后再进行演算则会理解的更快!

  首先推荐白志刚的《由入门到精通—吃透PID 2.0版》看完一、二章之后,建议你先通过实践练习然后再回来看接下来的所有章节,这样你对这本书的掌握会更加牢固、节省时间。

  PID就是对输入偏差进行比例积分微分运算,运算的叠加结果去控制执行机构。实践练习中,如何把这一原理转化为程序?为什么是用那几个error进行计算?

  以下是我摘录的一段PID程序,我曾用其对智能车的速度进行闭环控制:

  P:Proportional  比例

  I:Integrating 积分

  D:Differentiation 微分

  Pwm_value:输出Pwm暂空比的值

  Current_error:当前偏差  last_error:上次偏差   prev_error:上上次偏差

  增量式PID计算公式: 

  P=Kp*(current_error﹣last_error);

  D=Kd*(current_error﹣2*last_error﹢prev_error);

  I=Ki*current_error;

  PID_add=Pwm_value+P﹢I﹢D;

这一段话给了我启发:

首先对其进行分析,

一、为什么是PID_add=Pwm_value+(P﹢I﹢D)而不是PID_add=P+I+D?

如左图,有一个人前往目的地A,他用眼睛视觉传感器目测到距离目的地还有100m,即当前与目的地的偏差为100,他向双脚输出Δ=100J的能量,跑呀跑,10s之后,他又目测了一次,此时距离为40m,即current_error=40,他与10s前的偏差last_error=10对比,即current_error—last_error=—60,这是个负数,他意识到自己已经比较接近目的地,可以不用跑那么快,于是输出Δ=100+(—60)=40J的能量,40J的能量他刚好以4m/s的速度跑呀跑,10s之后,他发现已经到达目的点,此时current_error=0,大脑经过思考得出current_error—last_error=0—40=—40,两脚获得的能量Δ=40+(—40)=0,即他已经达到目的地,无需再跑。在刚才的叙述中,可知增量式P+I+D输出的是一个增量,将该增量与调节量相加后的到的才是最终输出量,P+I+D反应的是之前的输出量是在当前的状态中是该增加还是该减少。

二、纯比例控制P=Kp*(current_error﹣last_error),怎样理解﹙current_error﹣last_error ﹚?

  PID中纯比例控制就是把被控制量的偏差乘以一个系数作为调节器的输出,在增量式PID中,反映在程序上的,我们被控制量就是error,而实际上,例如在速度控制中error=目标速度﹣当前速度,所以明确目的:我们通过控制error趋近于0,最终使得当前速度趋近于目标速度。

如右图,假如考试时有这么一种题:函数经过时间Δt,由y1变化为y2时,问y增长的比例为多少?你很容易地得出答案:K=﹙y2-y1﹚/Δt;

以速度控制为例,若y为error,得右图,在时间t1到t2的过程中,我们可以得到输出控制量error变化的趋势为(current_error—last_error)/Δt。得到偏差的变化趋势后,乘以Kp使输出量与error相对变化。这个道理犹如模拟电子电路中,声音信号经过功放管放大输出的信号与输入信号相对应变化。

三、微分控制:

然而,通常情况下,我们的被控制量并非纯比例式地变化,如下图:

比例表示变化趋势,微分则表示变化趋势的变化率,映射到一个图像曲线中即为导数的变化!图3中若求曲线中x2至x1某点的斜率,当Δt足够小时,则可近似为(y2—y1)/Δt ,可知x3到x1导数的变化为﹛﹙y3—y2﹚—(y2—y1﹚﹜/Δt =﹙y3—2*y2﹢y1﹚/Δt 。将不同时间的y1、y2、y3映射为prev_error、last_error、current_error;则error变化趋势的变化为﹛﹙current_error—last_error﹚﹣﹙last_error—prev_error﹚﹜/Δt=﹛﹙current_error—2*last_error﹢prev_error﹚﹜/Δt,可得微分D=Kd*(current_error﹣2*last_error﹢prev_error)。 在系统中加入微分放映系统偏差信号的变化率,能预知偏差变化的趋势,具有超前控制作用,提前处理偏差。

四、积分控制:

积分控制可以消除偏差,体现在公式中较容易理解,当前的偏差差经过系数Ki的放大后映射为输出控制量,即I=Ki*current_error。P只要前后偏差之差为0,即current_error—last_current=0,则不进行调节,D只要前后偏差变化率为0,即(current_error﹣2*last_error﹢prev_error)=0,则不进行调节。而对于积分只要偏差存在,调节就始终进行,因此积分可以消除误差度,但在在某些情况下,一定范围内的误差是允许的,而如果此时积分调节始终存在,可能会导致系统稳定性下降,如右图,此时可通过弱化积分系数Ki使系统稳定。

以下为arduino 程序

我的pid 调节思路:

#include <PID_v1.h>
#include "constants.h"
#include "output_px4.h"
#include "deal_src04.h"

#define yellowboxleft 282

int thisleft;
int thisfront;

int current_height;
int current_disFront;
int current_disLeft;
int current_disRight;
int current_disLeftlast;


int current_disback;
long long ic_left = 0;
long long ic_front = 0;
int dc_left[2] = {0};
int dc_front[2] = {0};

long step_1start;
long step_2start;
long step_4start;
long step_6start;
long step_3start;


int sign = 0;
int signb = 0;
char signA;//0 for the blue box, 1 for the yellow box.


void getDistances() {
  int height = getHeight();
  if (height != 0) {
    current_height = height;
  }
  //print_channel_input();
  //delay(50);
  current_disFront = getDisFront();
  // delay(50);
  current_disRight = getDisRight();
  current_disLeft = getDisLeft();
  Serial3.print("height:   ");
  Serial3.print(current_height);
  //  Serial3.print("   front:   ");
  //  Serial3.print(current_disFront);
  //  Serial3.print("   left:   ");
  //  Serial3.print(current_disLeft);
  Serial3.print("   mission_step:   ");
  Serial3.print(mission_step);
  Serial3.println();

  //Serial3.println(current_height);
  //Serial.println(current_height);
}

void initSerial()
{
  Serial3.begin(9600);
  Serial2.begin(9600);
  Serial.begin(9600);
}

void setup() {
  // put your setup code here, to run once:
  initSerial();
  init_servo();
  init_input();
  init_timer();

  Throw_Servo.writeMicroseconds(1200);//舵机初始化  上球
  delay(100);
  Throw_Servo.writeMicroseconds(1600);
}

void loop() {
  // put your main code here, to run repeatedly:
  //print_channel_input();
  getDistances();
  print_channel_input();
  //Throw_Servo.writeMicroseconds(1680);
  //delay(2000);
  //Throw_Servo.writeMicroseconds(1550);
  //delay(4000);
  //Throw_Servo.writeMicroseconds(1000);
  //delay(2000);
  //Throw_Servo.writeMicroseconds(2000);
  //delay(2000);
  //Throw_Servo.writeMicroseconds(-1500);
  //delay(2000);
  //Throw_Servo.writeMicroseconds(1750);
  switch (mission_step)
  {
      int left_speed;
      int front_speed;
    case 0://任务未开始
      setOutput(Roll_Middle, Pitch_Middle, Thro_Middle, Yaw_Middle);
      thisleft = current_disLeft;
      //thisfront=current_disFront;

      break;
    case 1://第一步,起飞悬停

      Throw_Servo.writeMicroseconds(1550);

      dc_left[1] = current_disLeft - thisleft - dc_left[0];
      //dc_front[1] = current_disFront - thisfront - dc_front[0];

      if (sign >= 1)
      {
        left_speed = (int)((current_disLeft - thisleft) * 3  + dc_left[1] * 0.1);
        front_speed = (int)((current_disFront - thisfront) * 3 + dc_front[1] * 0.1);


        if (left_speed > 50) {
          left_speed = 50;
        }
        if (left_speed < -50) {
          left_speed = -50;
        }
        if (front_speed > 50) {
          front_speed = 50;
        }
        if (front_speed < -50) {
          front_speed = -50;
        }

        setOutput(Roll_Middle , Pitch_Middle, Thro_Middle - 150, Yaw_Middle);
        setOutput(Roll_Middle, Pitch_Middle, Thro_Middle + (Default_Height - current_height) * 0.4, Yaw_Middle);
        if ((current_height >= Default_Height - 15) && (current_height <= Default_Height + 100))
        {
          setOutput(Roll_Middle , Pitch_Middle, Thro_Middle, Yaw_Middle);
          mission_step = 2;
          step_2start = millis();
          sign = 0;
          signb = 0;
        }
      }
      else
      {
        if ((current_height >= 1200))
        {
          if (signb == 0)
          {
            step_1start =  millis();
            signb = signb + 10;
          }
          setOutput(Roll_Middle, Pitch_Middle, Thro_Middle, Yaw_Middle);
          if (millis() >= (step_1start + 12400))
          {
            sign = sign + 10;
          }
        }
        else
        {
          setOutput(Roll_Middle , Pitch_Middle, Thro_Middle + (1500 - current_height) * 0.15, Yaw_Middle);
        }
      }
      break;
    case 2://第二步,悬停1s
      setOutput(Roll_Middle, Pitch_Middle, Thro_Middle + (Default_Height - current_height) * 0.4, Yaw_Middle);
      if (millis() >= (step_2start + 6200))
      {
        mission_step = 3;
        step_3start = millis();
      }
      break;
    case 3://第三步,到达目的地
      ic_left = ic_left + current_disLeft;
      ic_front = ic_front + current_disFront;
      dc_left[1] = current_disLeft - Default_Left - dc_left[0];
      dc_front[1] = current_disFront - Default_Front - dc_front[0];

      if (current_disLeft - Default_Left >= 30 || current_disLeft - Default_Left <= 30)
      {
        left_speed = (int)((current_disLeft - Default_Left) * 2 + (0.001 * (ic_left / (millis() - step_3start))) + dc_left[1] * 0.02);
        front_speed = (int)((current_disFront - Default_Front) * 2 + (0.001 * (ic_front / (millis() - step_3start))) + dc_front[1] * 0.02);
      }
      else
      {
        left_speed = (int)((current_disLeft - Default_Left) * 1  + dc_left[1] * 0.02);
        front_speed = (int)((current_disFront - Default_Front) * 1 + dc_front[1] * 0.02);
      }


      if (left_speed > 130) {
        left_speed = 130;
      }
      if (left_speed < -130) {
        left_speed = -130;
      }
      if (front_speed > 130) {
        front_speed = 130;
      }
      if (front_speed < -130) {
        front_speed = -130;
      }

      setOutput(Roll_Middle - left_speed , Pitch_Middle - front_speed, Thro_Middle, Yaw_Middle);
      if (current_disLeft <= (Default_Left + 25))
      {
        if (current_disFront <= (Default_Front + 25))
        {
          sign = sign + 1;
          if (sign >= 4)
          {
            mission_step = 6;
            step_6start = millis();
            sign = 0;
            dc_left[0] = 0;
            dc_front[0] = 0;
            ic_left = 0;
            ic_front = 0;
          }

        }
      }

      if (current_disLeft - Default_Left>=30||current_disLeft - Default_Left<=-30)
      { 
      dc_left[1] = current_disLeft - yellowboxleft - dc_left[0];
        dc_front[1] = current_disFront - Default_Front - dc_front[0];
        left_speed = (int)((current_disLeft - yellowboxleft) * 2  + dc_left[1] * 0.02);
        front_speed = (int)((current_disFront - Default_Front) * 2 + dc_front[1] * 0.02);

        dc_left[1] = current_disLeft - yellowboxleft - dc_left[0];
        dc_front[1] = current_disFront - Default_Front - dc_front[0];
        if ( current_disLeftlast - current_disLeft  >= 40)
        {
          current_disLeft = current_disLeftlast;
        }

      }

        ic_left = ic_left + current_disLeft;
        ic_front = ic_front + current_disFront;

        if(current_disLeft - Default_Left>=30||current_disLeft - Default_Left<=-30)
        {
                  left_speed = (int)((current_disLeft - yellowboxleft) * 3  +(0.001*(ic_left/(millis()- step_3start)))+ dc_left[1]*0.02);
                  front_speed = (int)((current_disFront - Default_Front) * 4 + (0.001*(ic_front/(millis()- step_3start)))+ dc_front[1]*0.02);
         }
          else
          {
           left_speed = (int)((current_disLeft - yellowboxleft) * 1  +(0.001*(ic_left/(millis()- step_3start)))+ dc_left[1]*0.02);
           front_speed = (int)((current_disFront - Default_Front) * 1 + (0.001*(ic_front/(millis()- step_3start)))+ dc_front[1]*0.02);
           }


        if (left_speed > 130) {
          left_speed = 130;
        }
        if (left_speed < -130) {
          left_speed = -130;
        }
        if (front_speed > 130) {
          front_speed = 130;
        }
        if (front_speed < -130) {
          front_speed = -130;
        }

        setOutput(Roll_Middle - left_speed , Pitch_Middle - front_speed, Thro_Middle + (Default_Height - current_height) * 0.4, Yaw_Middle);
        current_disLeftlast = current_disLeft;
        if ((current_disLeft <= (yellowboxleft + 20)) && (current_disLeft >= (yellowboxleft - 20)))
        {
          if ((current_disFront <= (Default_Front + 15)) && (current_disFront <= (Default_Front - 15)))
          {
            //sign = sign + 1;

            mission_step = 6;
            step_6start = millis();
            sign = 0;
            dc_left[0] = 0;
            dc_front[0] = 0;
            ic_left = 0;
            ic_front = 0;


          }
        }
        
        dc_left[0] = dc_left[1];
        dc_front[0] = dc_front[1];
        break;


      case 6://第六步,悬停1s,扔球
        setOutput(Roll_Middle, Pitch_Middle, Thro_Middle, Yaw_Middle);
        Throw_Servo.writeMicroseconds(2000);
        if (millis() >= (step_6start + 5200))
        {
          mission_step = 7;
          dc_left[0] = 0;
          dc_front[0] = 0;
          dc_left[1] = 0;
          dc_front[1] = 0;

        }
        break;
      case 7://第七步,返回

        dc_left[1] = current_disLeft - Start_Left - dc_left[0];
        dc_front[1] = current_disFront - Start_Front - dc_front[0];

        left_speed = (int)((current_disLeft - Start_Left) * 3  + dc_left[1] * 0.1);
        front_speed = (int)((current_disFront - Start_Front) * 3 + dc_front[1] * 0.1);

        if (left_speed > 100) {
          left_speed = 100;
        }
        if (left_speed < -100) {
          left_speed = -100;
        }
        if (front_speed > 100) {
          front_speed = 100;
        }
        if (front_speed < -100) {
          front_speed = -100;
        }
        setOutput(Roll_Middle - left_speed, Pitch_Middle - front_speed, Thro_Middle, Yaw_Middle);
        if (current_disFront >= (Start_Front - 20))
        {
          if (current_disLeft >= (Start_Left - 20)||(current_disback >= (Start_back - 20)))
          {
            sign = sign + 1;
            if (sign >= 2)
            {
              mission_step = 8;
              sign = 0;
            }
          }
        }
        break;
      case 8://第八步,降落


        dc_left[1] = current_disLeft - Start_Left - dc_left[0];
        dc_front[1] = current_disFront - Start_Front - dc_front[0];


        left_speed = (int)((current_disLeft - Start_Left) * 3  + dc_left[1] * 0.1);
        front_speed = (int)((current_disFront - Start_Front) * 3 + dc_front[1] * 0.1);


        if (left_speed > 50) {
          left_speed = 50;
        }
        if (left_speed < -50) {
          left_speed = -50;
        }
        if (front_speed > 50) {
          front_speed = 50;
        }
        if (front_speed < -50) {
          front_speed = -50;
        }


        setOutput(Roll_Middle - left_speed, Pitch_Middle, Thro_Middle - 150, Yaw_Middle);

        Throw_Servo.writeMicroseconds(1680);
        break;
      default:
        setOutput(Roll_Middle, Pitch_Middle, Thro_Middle, Yaw_Middle);
        break;
      }
  }
 

猜你喜欢

转载自blog.csdn.net/qq_36958104/article/details/83660828