用Excel教会你PID算法


Chapter1 用Excel教会你PID算法

原文链接:https://blog.csdn.net/Firefly_cjd/article/details/118977801

01、引入PID

电机控制
在这里插入图片描述
这样是没有反馈的,也就是说我们完全相信输入的数字,且是理想化的模型,比如输入占空比为50%的25Kz的PWM,车轮速度为1m/s,实际产品中会受到各种这样的影响,比如地面阻力,风阻等等,同样输入占空比为50%的25Kz的PWM,车轮的速度并不是1m/s。

这时候我们就引入测量单元,也就是反馈系统。
在这里插入图片描述

这个时候,最常见的反馈就是:直接使用反馈值。简单的例子,属于数值X和输出数值y的数学公式是:y=2x,这是这最见的关系。假设我们输入7,测量结果是5,那么我们就直接将输入修改为7+2*(7-5)=11。也就是我们一次直接调整到位。这样调节过于简单粗暴,因为我们直接将输入修改为11,有可能输出直接变成6,超过预期值了。这时候就自然而然的想到多次调节,每次只增加一点,然后测量速度,看一下是否达标。

这就是比例调节Kp。

02、比例调节

举例说明,当前小车速度为0.2,目标速度是1。输出y和输入x的关系是y=1*x。比例系数Kp=0.5。随着时间的增大,输出和输入关系如下。
在这里插入图片描述
直观折线图显示如下
在这里插入图片描述
我们发现这太完美了,那么比例环节就能够完美的解决问题了,可是等等,在下这个结论前,我们忽略一个特因素:噪声误差。

在很多系统中都是有噪声的,这我们举例小车中,噪声误差可能来自于电机的误差,外部因素风阻等误差,且是波动的。我们将问题简化,假设外部因素恒定,外部因素是的小车实际输出速度减去0.1。

还是上面的例子,当前小车速度为0.2,目标速度是1。输出y和输入x的关系是y=1*x。比例系数Kp=0.5,恒定的误差为-0.1,随着时间的增大,输出和输入关系如下。
在这里插入图片描述
直观折线图显示如下
在这里插入图片描述
最终输出稳定在0.8,因为我们初始值为0.2,到最后和目标差值是0.2,补偿是0.1,误差正好是-0.1,也就是说等于我们没有补偿。

如果我们需要速度达到1呢???办法就是增大比例系数Kp。

误差为0.8时
在这里插入图片描述
误差为1.9时
在这里插入图片描述
误差为2时,已经完全震荡
在这里插入图片描述
结论:比例控制引入了稳态误差,且无法消除。比例常数增大可以减小稳态误差,但如果太大则引起系统震荡,不稳定。

03、积分调节

为了消除稳态误差,第二次加入积分,使用PI(比例积分控制),积分控制就是将历史误差全部加起来乘以积分常数。公式如下:
在这里插入图片描述
u(t) -------------输出曲线,pid输出值随时间的变化曲线

Kp --------------比例系数

e(t)--------------偏差曲线,设定值与实际值的偏差随时间的变化曲线

t-----------------时间

关于定积分,如果你上过高数,且没有睡觉的话。
在这里插入图片描述
需要将数学公式离散化,才能用到计算机系统来。
在这里插入图片描述
①表示在时间点t,输出的值

②表示在时间点t,输出的误差

③表示从时间0到t,累计误差。

添加Ki参数之后的折线图如下
在这里插入图片描述
结论:

只要存在偏差,积分就不停的累计,直到误差为0,积分项不再累加,变成一个常数,可以抵消稳态误差.

04、微分调节

引入积分可以消除稳态误差,但会增加超调,且Ki增大,超调量也增大.

为了消除超调,我们引入微分作用
在这里插入图片描述
积分就是e(t)曲线的斜率。

将公式离散化为
在这里插入图片描述
ek是当前误差,ek-1是上一次误差,所以①就是误差曲线的斜率。

关于PID公式还有其他写法,本质是一样的
在这里插入图片描述
离散化后是
在这里插入图片描述
u(t) -------------输出曲线,pid输出值随时间的变化曲线

Kp --------------比例系数

e(t)------------- 偏差曲线,设定值与实际值的偏差随时间的变化曲线

Ti--------------- 积分时间

Td--------------微分时间

T----------------调节周期

那么PID的参数如下
在这里插入图片描述
将前面的示例,加入微分项,Ki=0.3。

折线图如下
在这里插入图片描述
结论:微分能够减弱超调趋势。

05、总结

PID调节示意图如下
在这里插入图片描述
可以发现:

比例项是纠正偏差的主力,越远离偏差绝对值就越大,快速把偏差纠正回来。

积分项和以往的状态有关,面积的绝对值越大它的绝对值就越大,它的作用是消除累计偏差。

微分项跟斜率有关,比较难解释,总的来说它的作用是:当目标靠近设定值时加速它靠近,当目标远离设定值时阻止它远离。因此微分可以增加系统稳定性,因为到达目的之后,离开会受到阻碍。
在这里插入图片描述

06、增量PID

上面我们讲解的是位置PID,还有一种增量PID,输出的不是目标值,而是与上次值的差值。直观上将就是u(t)-u(t-1)。
在这里插入图片描述
那么u(t)-u(t-1)的公式是
在这里插入图片描述
位置型PID控制器的基本特点:

位置型PID控制的输出与整个过去的状态有关,用到了偏差的累加值,容易产生累积偏差。

位置型PID适用于执行机构不带积分部件的对象。

位置型的输出直接对应对象的输出,对系统的影响比较大。

增量型PID控制器的基本特点:

增量型PID算法不需要做累加,控制量增量的确定仅与最近几次偏差值有关,计算偏差的影响较小。

增量型PID算法得出的是控制量的增量,对系统的影响相对较小。

采用增量型PID算法易于实现手动到自动的无扰动切换。

07、代码编程

位置PID

/*******************************************************************
位置式pid    
********************************************************************/
double PID(double Actual,double SET)
{     
  static double E_sum,Error_last;          //上一次误差  
 
  double  kp=20.767,ki=1.867,kd=115.55;
 
  double pid_out;
  double Error_now;          //当前误差  
   Error_now = SET-Actual;               //当前误差    
//  if(Error_now>-0.9&&Error_now<0.9)    //防静态误差  
//  {
//       Error_now=0;
//         Error_last=0;
//  }  
    E_sum +=  Error_now;                       //误差累计     
//  if(E_sum>484)E_sum=484;          //积分限幅度,防止积分饱和
//  if(E_sum<-484)E_sum=-484;  
 
  pid_out= kp * Error_now + ki * E_sum + kd * (Error_now-Error_last);            //pid计算公式      
  Error_last=Error_now;
 
//  if(pid>900)  pid=900;             //输出限幅
//  if(pid<-900)pid=-900;    
     return -pid_out;          
}

增量PID

error = target_speed - current_speed;
P_error = error;
I_error = error - left_motor.L_error;
D_error = error - 2*left_motor.L_error + left_motor.LL_error;
 
add = (s16)(KP * P_error + KI * I_error + KD * D_error);
left_motor.ESC_output_PWM += add;
 
left_motor.LL_error = left_motor.L_error;
left_motor.L_error = error;

excel文件下载:

下载链接:百度网盘 请输入提取码

提取码:5ohs

Chapter2 通过Excel学习PID算法(一步步理解它的KP,KI,KD)

原文链接:https://blog.csdn.net/zengqz123/article/details/102499647

PID原理
在这里插入图片描述

PID控制算是应用非常广泛的经典控制算法。但是怎么理解PID这三个参数呢?在参考了别人的文章之后,我还是有点一知半解,这时候发现不自己动手算一算是很难理解PID了,但是我又不想做这种重复的计算,于是想到了通过Excel来举个例子。

接下来,我们通过一个很简单的例子来表示一个PID控制,并用Excel来计算PID带来的好处等等。这个例子就是,我们要往桶里倒水,并且我们的目标是把水位维持在1米高,初始水位为0.5米,只能往桶里一次一次的倒水。那么我们开始我们的学习吧。

比例控制算法

对于比例控制算法,我们可以简单的理解为,每次倒入水前计算当前的误差(error),然后给这个误差乘以一个比例系数kp。那么我们水位的改变量u与error之间的关系为:u=kperror。然后就不停的计算误差,不停的往桶里倒水。在这里,通过excel表格计算可很直观的看出来,误差越来越小的,实际越来越接近1。
在这里插入图片描述
我们的比例算法很好的完成了它的任务,随着倒的次数越来越多,那么我们实际也更加接近于我们的目标值。那么我们为什么还需要使用到KI和KD呢?那么我们接下来提出一个假设,假设桶在漏水,每次往桶里倒水的同时,桶都会流失0.1米水高的水。因此每次的变化量为u=kp
error-0.1,继续用excel进行一次计算吧。
在这里插入图片描述
可以看到,我们每次往桶里加水之后,由于我们的桶漏水了,所以导致在多次倒水之后,我们的实际水高维持到0.8m附近,所以导致了一个稳态误差,这意味着我们通过比例控制算法是永远都不能实现到1米水高的愿望。 (在实际情况中,这种类似水缸漏水的情况往往更加常见,比如控制汽车运动,摩擦阻力就相当于是“漏水”,控制机械臂、无人机的飞行,各类阻力和消耗都可以理解为本例中的“漏水”)所以,单独的比例控制,在很多时候并不能满足要求。

积分控制算法

由于比例控制算法在实际应用过程中的局限性很大,因此我们就需要想个办法去消除这个稳态误差,而如何去消除这个稳态误差呢?我们可以想一个办法,我们把每次计算出来的误差累加起来,当如果遇到了稳态误差,那我们这个数据就会越来越大,因此每次更新数据时,就可以把它添加上去,就能把稳态误差解决啦。每次添加的水量的计算公式为u=kperror-0.1+ ki∗积分
在这里插入图片描述
这里我们取了一段很长的数据来分析一下,可以看到我们的实际值在1m附近不断的波动。我们的想法是,无限的趋进于1。但是看图表中的第九次(标红)和第十次(标红),我们可以发现我们的误差不是在缩小反而还更大了。那么为什么会有这种问题呢?这个肯定不是kp
error的问题,因为error非常小了。我们可以看到,由于积分维持在0.8左右,因此每次改变的量对于趋紧稳定后的数值来说,其变化量是很大的,因此其稍微改变一点,就会导致其误差变大。

同时可以看到在初期,对于使用积分控制算法来说,其超过了我们的目标值(超调量),最大达到了1.2m,如果我们的桶只有1.1m高,那么可能就会导致我们的水往外溢出来,这是我们不想看到的情况。如果在实际过程中,可能就因为温度过高或者电压过高,发生不可逆的后果。

通过百度百科我们可以知道:

1.比例环节 即时成比例的反映控制系统的偏差信号,偏差一旦产生,控制器立即产生控制作用,以减少偏差。通常随着值的加大,闭环系统的超调量加大,系统响应速度加快,但是当增加到一定程度,系统会变得不稳定。

2.积分环节 主要用于消除静差,提高系统的无差度(型别)。 [1] 积分作用的强弱取决于积分常数,积分常数越大,积分作用越弱,反之越强。闭环系统的超调量越小,系统的响应速度变慢。

微分控制算法

引进了积分项之后,我们的控制模型虽然解决了稳态误差,但是还是有问题。这个问题就是实际值在不断的波动。同时它还有一个超调量的东西。这是我们不希望看到的,那么我们该如何去解决这个问题呢?这里就需要引进微分的概念了。其理解可以想象成在往桶里加水时,我们可能能够预感到其会超过我们想要的数值,然后在加水之前,把一部分水给倒掉,从而达到降低其超调量的作用。微分在这里可以用相邻的error的差值进行表示,因此微分可以表示为:kd*(error(t)-error(t-1)),在这里我们的u=kperror-0.1+ ki∗积分+kd(error(t)-error(t-1))。值得注意的是,由于误差是越来越小的,因此其微分的值一般为负值。

在这里插入图片描述
其最大值为1.15,超调量得到了减小。至此,PID的每个过程的功能能够简单的理解到了。

Chapter3 PID的增量式与位置式

原文链接:https://blog.csdn.net/dbqwcl/article/details/125736272

前言

在刚接触到PID控制器的时候,我对增量式,位置式这些其实也是很懵的,然后又有什么速度环啊,位置环啊,电流环啊…巴拉巴拉一堆的,但是现在理解起来其实也就利用一些简简单单的离散数据运算出期望罢了。

首先假设我们已经知道了什么是Kp、Ki、Kd了(不知道的话那就看看我上一篇讲PID的那个文章)。

关于PID的算法可以分为两大类,一类是位置式,还有一类是增量式,下面就开始说说我对这两种算法的理解。

位置式PID

首先是位置式,也是我用得最多的一种算法。位置式PID其实就是当前系统实际位置,与你期望想要达到的位置的偏差而进行的PID控制。那么一个用位置式的系统中是什么样的呢?

看公式来理解:

在这里插入图片描述

由上式可以看出,PID的每次运算的输出都与过去的状态有关,并且积分项的误差会进行累加。如果偏差一直都是正的或者是负的,位置式PID在积分项就会一直累积,当偏差开始反向变化的时候,位置式PID需要一段时间才能从最大值减下来,会造成位置PID控制输出的滞后。所以我们还需要对积分项进行限幅(做一个最大值max和最小值min),同时也要对输出进行限幅。同时,我们输出的Uk对应的是执行机构的实际位置,一旦控制输出出错了,也就是说我们控制的对象当前的状态值出现了问题,Uk 的大幅度变化就会影响到系统的大幅度变化。

所以我们如果单单使用位置式PID的时候,一般都是直接使用PD控制的,也正因为这样,位置式PID是用于执行机构不带积分部件的对象,像平衡车的直立控制呀、温控系统呀…等等。

下面是代码实现:

#define HAVE_PID_INTEGRAL

#define LIMIT(TargetValue, LimitValue) \
if (TargetValue > LimitValue)\
{\
  TargetValue = LimitValue;\
}\
else if (TargetValue < -LimitValue)\
{\
  TargetValue = -LimitValue;\
}\

typedef struct{
  float Kp;
  float Ki;
  float Kd;
#ifdef HAVE_PID_INTEGRAL
  int index;            // 积分分离系数
  float Integral;       // 积分项
  float I_outputMax;    // 积分限幅
#endif
  float Last_Err;       // 上次误差
  float Output;         // PID输出
  float OutputMax;      // 位置式PID输出限幅
}Position_PID;

void PositionPID_Calculate(Position_PID *pid,const float Target,const float Measure)
{

  if(pid == NULL)
    return;
    
  float Err;
  
  Err = Target - Measure;
  
  pid->Output = pid->Kp * Err + pid->Kd * (Err - pid->Last_Err)
  
#ifdef HAVE_PID_INTEGRAL
    /* 积分分离 */
  if(abs(pid->Err) > Integraldead_zone)
  {
    pid->index=0;
  }else
  {
    pid->index = 1;
  }
  pid->Integral += pid->Ki * Err * pid->index;
  
  LIMIT(pid->Integral,I_outputMax);
  
  pid->Output += pid->Integral;
#endif
  
  LIMIT(Output,OutputMax);
  
  pid->Last_Err = Err;
  
}

优点:

位置式是一种非递推式算法,可以直接控制对象,U(k)的值与对象的实际当量是一一对应的,所以在不带积分部件的控制对象中可以很好应用。

缺点:

每次输出都与之前的状态有关,并且还要对误差值err进行累加,计算量大。

增量式PID

那么什么是增量式PID呢?增量式PID的输出只是控制量的增量ΔUk 。当执行机构需要的控制量是增量,那么我们就可以采用增量式PID控制算法进行控制。(增量式PID的计算输出结果是增量,并不是直接作用到执行机构)

(增量式PID可以由位置式推导出,感兴趣的可以百度,这里就不浪费篇幅了。)

看公式来理解:
在这里插入图片描述
对于增量式PID来说,给定一个输入量,系统反馈回来的量与设定的量的偏差为Err,系统中保存上一次的偏差Last_Err和上上次的偏差Previous_Err,这三个输入量经过增量式PID可以计算得到上述说的控制量增量ΔUk 。而得出的控制量Δ U ( k ) \Delta U(k)ΔU(k)对应的是近几次位置误差的增量,而不是对应与实际位置的偏差,也就是说没有误差累加。即在上一次的控制量的基础上需要增加控制量。

以下是代码实现:

#define LIMIT(TargetValue, LimitValue) \
if (TargetValue > LimitValue)\
{\
  TargetValue = LimitValue;\
}\
else if (TargetValue < -LimitValue)\
{\
  TargetValue = -LimitValue;\
}\

#define Integraldead_zone 100 // 积分死区 根据自己的需求定义
typedef struct{
  float Kp;
  float Ki;
  float Kd;
  float p_out;
  float i_out;
  float d_out;
  float Err;
  float Last_Err;       // 上次误差
  float Previous_Err;   // 上上次误差
  float Output;
  float OutputMax;      // 增量式式PID输出限幅
}Incremental_PID;

void IncrementalPID_Calculate(Incremental_PID *pid,const float Target,const float Measure)
{ 
  if(pid == NULL)
    return;
    
  pid->Err = Target - Measure;
  
  pid->p_out = pid->Kp * (Err - Last_Err);
  pid->i_out = pid->Ki * Err;
  pid->d_out = pid->Kd * (Err - 2.0f*Last_Err + Previous_Err);
  
  pid->Output += p_out + i_out + d_out; 
  
  LIMIT(pid->Output, pid->OutputMax); // 限幅
  
  pid->Previous_Err = pid->Last_Err;
  pid->Last_Err = Err;
  
}

优点:

系统在误动作时影响小,并且可以利用逻辑判断来对错误数据进行去除。
冲激小,便于实现无扰动切换。
不需要累加误差值,并且控制增量的确定只与最近几次采样值有关。

缺点:

积分的阶段效应大,有稳态误差。
溢出的影响大,有的被控对象用增量式不好。

总结(增量式与位置式的区别)

增量式算法不需要做累加,增量式PID求出来的是系统需要的增量,并且增量的确定仅与最近几次偏差采样值有关,计算的误差对控制量计算的影响比较小。与位置式相比的话,位置式就需要用到偏差的累加值(Ki*err),容易产生累计误差。

增量式PID控制输出的是控制量增量,没有积分作用,所以该方法适用于带积分部件的对象。而位置式PID适用于执行机构不带积分部件的对象。

上面的代码也很清楚的显示了,位置式PID需要积分限幅和输出限幅,而增量式PID只需要输出限幅。 这句话的正确性有待验证。

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当谈到动态规划中的背包问题时,有两种常见的变体:0/1背包和完全背包。下面分别介绍这两种变体的动态规划解决方案。 1. 0/1背包问题: 0/1背包问题是指在给定一组物品和一个背包容量的情况下,选择一些物品放入背包,使得放入的物品总价值最大,且不能超过背包的容量。 动态规划解决方案步骤如下: - 定义一个二维数组dp,dp[i][j]表示在前i个物品中,背包容量为j时可以获得的最大价值。 - 初始化数组dp的第一行和第一列为0。 - 对于每个物品i,遍历背包容量j从1到背包总容量。对于每个容量j,有两种情况: - 如果物品i的重量大于背包容量j,则无法放入物品i,此时dp[i][j]等于dp[i-1][j]。 - 如果物品i的重量小于等于背包容量j,则可以选择放入或不放入物品i。比较选择放入物品i和不放入物品i两种情况下的最大价值,即dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]),其中w[i]为物品i的重量,v[i]为物品i的价值。 - 最终,dp[n][C]即为问题的解,其中n为物品个数,C为背包总容量。 2. 完全背包问题: 完全背包问题是指在给定一组物品和一个背包容量的情况下,选择一些物品放入背包,使得放入的物品总价值最大,且可以重复放入同一种物品。 动态规划解决方案步骤如下: - 定义一个一维数组dp,dp[j]表示背包容量为j时可以获得的最大价值。 - 初始化数组dp为0。 - 对于每个物品i,遍历背包容量j从1到背包总容量。对于每个容量j,有两种情况: - 如果物品i的重量大于背包容量j,则无法放入物品i,此时dp[j]保持不变。 - 如果物品i的重量小于等于背包容量j,则可以选择放入或不放入物品i。比较选择放入物品i和不放入物品i两种情况下的最大价值,即dp[j] = max(dp[j], dp[j-w[i]] + v[i]),其中w[i]为物品i的重量,v[i]为物品i的价值。 - 最终,dp[C]即为问题的解,其中C为背包总容量。 这是动态规划解决背包问题的一般思路,你可以根据具体的问题细化和实现这些步骤。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值