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

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

优点

a. 技术成熟

b. 易被人们熟悉和掌握

c. 不需要建立数学模型

d. 控制效果好

e. 鲁棒性

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

1  位置式PID控制算法

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

               (1)

式中

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

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

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

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

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

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

       (2)

式中 ,     。

由于计算机的输出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控制算式

  (3)

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

进一步可以改写成

                                                (4)

式中  、

一般计算机控制系统的采样周期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;
      }
  }

增量式PID计算公式4个疑问与理解相关推荐

  1. 增量式PID的P和I怎么理解(一)

    黎明曙光篇之新生 增量式PID的P.I理解之推翻篇 一.据我所知的资料里关于PID三个参数的理解是这样的:(我这里只说P和I) P的作用:比例P可以加快系统调节速度,P过小调节时间变长,响应性不够,P ...

  2. 入门智能车 | 带你认识PID闭环控制 - 增量式PID实现电机速度闭环

    带你认识PID闭环控制 - 增量式PID实现电机速度闭环 闭环控制是指控制论的一个基本概念.指作为被控的输出量以一定方式返回到作为控制的输入端,并对输入端施加控制影响的一种控制关系.带有反馈信息的系统 ...

  3. 位置式PID和增量式PID的区分

    目录 位置式PID和增量式PID的区分 位置式PID: 增量式PID: 附上本人参考别人写的代码----链接: 附上本人收集的资料----链接: 位置式PID和增量式PID的区分 仅是个人理解,如有错 ...

  4. 【转】增量式PID控制算法

    (转载 出处blog.ednchina.com/tengjingshu )blog.ednchina.com/tengjingshu/211739/message.aspx# 当执行机构需要的不是控制 ...

  5. 零基础制作平衡小车【连载】8---位置式PID和增量式PID

    上一节说的PID公式属于位置式PID,位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值,而增量式PID的输出只与当前拍和前两拍的误差有关.就温控系统来说,位置式PID输出的结果就是PWM ...

  6. STM32增量式pid直流电机调速(内附源码)

    目录 一. 1.硬件组成 2.模块分析 1.TB6612电机驱动模块 2.直流减速电机 3.电源稳压模块 二.接线 三.代码思路讲解(详见源码) 四.STM32cubmx配置 1.系统基础配置:(重要 ...

  7. 位置式PID与增量式PID的介绍和代码实现

    PID分为位置式PID与增量式PID. 一.位置式PID 1.表达式为: 2.缺点: 1).由于全量输出,所以每次输出均与过去状态有关,计算时要对ek进行累加,工作量大: 2).因为计算机输出的uk对 ...

  8. C语言实现PID算法:位置式PID和增量式PID

    原创者微信公众号 PID算法可以说是在自动控制原理中比较经典的一套算法,在现实生活中应用的比较广泛. 大学参加过电子竞赛的朋友都应该玩过电机(或者说循迹小车),我们要控制电机按照设定的速度运转,PID ...

  9. 位置式PID与增量式PID区别浅析

    1PID控制算法 什么是PID PID 控制器以各种形式使用超过了 1 世纪,广泛应用在机械设备.气动设备 和电子设备.在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法 PI ...

  10. 增量式速度pid调节策略_增量式PID是什么?不知道你就落伍了

    目录 1 什么是增量式PID? 2 举个例子 2.1 位置式PID 2.2 增量式PID 3 伪算法 4 C语言实现 5 总结 在之前一篇博客中( 简易PID算法的快速扫盲 )简单介绍了PID算法的基 ...

最新文章

  1. Tengine 一个注重性能和兼容性的AI框架
  2. 为什么古人如此的注重天文学
  3. php sqlite3 sql,在PHP中准备SQLite SQL语句
  4. C++多态案例二-制作饮品
  5. 每天一道LeetCode-----找到一个字符串在另一个字符串出现的位置,字符串内部顺序无要求
  6. python字符编码
  7. java面试题十四 基本类型的默认值
  8. mysql 解压缩安装_[mysql] MySQL解压缩安装步骤
  9. 怎么彻底关闭计算机应用程序,电脑自动关机前如何强行关闭应用程序
  10. 集腋成裘-13-git使用-02进阶篇
  11. 681_python安装win32com模块
  12. C语言将字符串转换为数字
  13. wps打开pdf乱码_wps打开pdf乱码
  14. 京东无法登录显示服务器异常,京东账号异常怎么解决?方法介绍
  15. WebStorm如何设置不自动保存,修改出现星星图案
  16. 3d模型多怎么优化_高德地图又出逆天黑科技!全国各大城市模型直接获取
  17. 关于九针串口的公头和母头
  18. linux 查询文件大小大于1g_常用Linux命令
  19. PolynomialFeatures进行逻辑回归拟合
  20. Flash(Flex)对文件下载进度的监控原理分析

热门文章

  1. android小米深色模式,小米深色模式开关
  2. xp系统打开计算机硬盘分区,如何在xp系统对硬盘进行分区
  3. JRE和JDK的区别
  4. python实现游戏同步翻译字幕
  5. 计算机人工智能领域英文文献,人工智能英文文献译文.doc
  6. 通俗易懂的TextCNN
  7. 初识阿里云环境搭建:无法远程连接,入过的坑:服务器ping不通,FTP搭建,服务器搭建数据库,远程连接服务器数据库
  8. 清明上河图对计算机技术的启发,吴良镛院士:《清明上河图》启示的城市方向...
  9. 调用企业微信接口发送微信消息
  10. java正则表达式yyyymmdd_java验证日期yyyyMMdd正则表达式,