本文转载自微信公众号“电子搬砖师”,原文链接
这篇文章是PID控制的进阶说明,如果没有看过PID控制(上)的读者,请先看看PID控制(上),以便更容易看懂这篇文章。
一、PID算法优化
先讲PID算法优化。奉上最经典的角度PID算法:
float inteAngle, lastErrorAnglel; //角度积分,上一次的角度误差
//函数功能:角度PID算法
//输入参数:Angle,传感器测量得到的角度
//返回参数:PID算法输出值
float PID_cal(float angle)
{
float errorAngle; //角度误差
float diffAngle; //角度微分
errorAngle = expectAngle - angle; //比例:角度误差 = 期望角度 - 传感器测的角度
inteAngle = inteAngle + errorAngle; //积分:角度误差积分 = 角度误差积分 + 本次角度误差
diffAngle = errorAngle - lastErrorAnglel; // 微分:角度微分 = 本次角度误差 - 上次角度误差
lastErrorAnglel = errorAngle; //记录本次角度误差
return (P * errorAngle + I * inteAngle + D * diffAngle); //返回PID计算结果
}
以上PID算法是不是很简单?只要5行代码,只要调节P、I、D这3个系数,这个算法就可以让平衡车立起来!
我们开始对这简单算法做优化。
1
如果输入角度Angle是一个噪声比较大的值,可以对Angle做一个简单滤波,比如做个平滑滤波:
errorAngle = K * (expectAngle - angle) + (1 - K) * lastErrorAngle;
当然角度数据最好能在输入PID算法前就先做滤波了。
2
某些控制模型的误差绝对值大于一定值时可能需要停止运行,比如说平衡车倒地了,这时平衡车可能就不要再转轮了,不然可能产生意外。这时我们就可以判断平衡车角度误差绝对值是否大于限定值,如果大于的话我们就直接返回0,停止车轮滚动。如下:
if ((abs)errorAngle > limitAngle1) { return 0; }
3
如果误差绝对值大于一定值,积分项就会累积到很大的积分值,太大的积分值会使得系统更难控制,所以我们可以判断误差绝对值是否超标,过大的话就不积分,合规的话就可以积分,如下:
if ((abs)errorAngle < limitAngle2) { inteAngle += errorAngle; }
4
以上还有一种处理积分的方式叫积分分离法,它指的是如果误差绝对值大于一定值,积分项干脆直接清零。这样做的好处是可以使控制模型更快回复到平衡状态,或者避免加剧控制模型的失控,如下:
if ((abs)errorAngle > limitAngle2) {inteAngle = 0; }
5
由于过大的积分值会使系统难以控制或者回归稳定太慢,我们需要对积分做限制幅度处理,如下:
if (inteAngle > limitInteAngle) { inteAngle = limitInteAngle; }
if (inteAngle < -limitInteAngle) { inteAngle = -limitInteAngle; }
6
微分项往往是噪声比较大的项,如果微分噪声大我们也最好加个平滑滤波。本例中的角度微分项其实就是角速度,它可以用本次角度误差减去上次角度误差获得,也可以直接用陀螺仪测量得到的角速度。假设我们用的是前者,那对前者微分滤波如下:
diffAngle = J * (errorAngle - lastErrorAngle) + (1 - J) * lastDiffAngle;
7
执行机构的死区指的是PID算法给出一个控制数值到执行机构,执行机构可能会因为输入值较小而没反应。比如你给电机1V的电压,可能电机根本不会动,直到你给3V电压时,它才会动。这个3V电压就是该电机的死区。
死区通常可以用PID的积分项消除掉,但是有的地方不想用积分累积那么久才越过死区值,就可以在PID输出值上直接加上死区,如下:
PID_Out = P * errorAngle + I * inteAngle + D * diffAngle;
if (PID_Out > 0) { PID_Out += deadValue; }
else if (PID_Out < 0) { PID_Out += -deadValue; }
8
有的执行机构没法接收过大的PID输出值,比如PWM占空比最大只能100%,而如果PID输出的值大于这个PWM上限,那就要将PID输出值限幅,如下:
if (PID_Out > LimitPID) { PID_Out = LimitPID; }
if (PID_Out < LimitPID) { PID_Out = -LimitPID; }
9
高深点的PID的P、I、D三个系数还可以不是固定的,而是随着误差值的变化而变化的,这样就可以实现将非线性控制模型给线性控制住。这种方式我也没有用过,不过它的简单实现方式可以列出,还是应用本例,有:
P *f(ErrorAngle) * ErrorAngle + I *g(ErrorAngle) * InteAngle + D *h(ErrorAngle) * DiffAngle;
大概的PID算法优化以上都已经给出了,读者不用把每一项优化都添加到自己的算法中。毕竟每个控制模型都不一样,都有自己各自需要优化的地方,不要拘泥于此。
二、串级PID分析
奉上串级PID的框图:
其中:
- 圆圈带+-号的是误差比较机构
- E(S)是误差
- G(S)是被控对象
- H(S)是反馈机构
- 左边的PID属于外环
- 右边的PID属于内环
回归到上一篇PID控制(上)中,那篇文章里面提到推箱子,箱子距离目的地越远就应该用越大的力气推,这个说法我当时是为了简单不绕提出的,但实际上是不对的。推力大会使得加速度大,如果摩擦力比较小,到达终点时推力为0只是说加速度为0,速度可能是非常大的,这个是不对的。真正正确的是距离和速度成比例,距离越远,速度越大,距离为0,速度为0,即推箱子的人应该控制的是速度而不是推力。
问:那速度要怎么控制呢?
答:可以靠串级PID算法控制。
串级PID在无人机上用得很多,它具有比单级PID控制更稳定,更顺滑的优点。它和单级PID控制的区别是:无人机单级PID是直接计算输入角度和反馈角度的误差,然后利用PID算法直接算出PWM值控制电机转动;而串级PID是先计算输入角度和反馈角度的误差,然后利用外环PID算法计算出在此角度误差下无人机的角速度最应该是多少,接着再计算外环PID输出的角速度和无人机当前角速度的误差,将此误差再送入控制角速度的内环PID算法中计算,最终内环PID输出一个PWM值控制电机转动。
串级PID中的内环控制频率可能会比外环控制频率高,因为外环给内环一个角速度值,内环不可能瞬间达到这个角速度,它必须经过几个周期的调整才可能达到这个角速度值。但是很多无人机内外环控制频率是一致的,这个也没什么问题,因为虽然内环没法及时达到外环的要求,但是却是往外环要求的趋势走的,最终无人机姿态还是会稳定的。
前面提到积分是消除静差用的,那这里无人机外环传递给内环的只是一个角速度,而内环利用自己的PID算法会负责将这个角速度实现出来,那这样外环输出的只是一个数字而已,哪怕是0.01度/秒,内环也会帮它实现,这就不该存在静差了,为什么外环要加个积分呢?
这里要说明积分的另一个作用,就是减小调节时间。假设比例项是5,角度误差是2度,纯比例控制输出的值会是10度/秒,但是如果有积分项存在,输出就会是:52+I积分,这个值会使角速度得到更快的变化。如果将外环的P和I系数调到一个合适值,虽然被控对象达到设定值时会过冲,然后反冲,如此震荡几次再稳定,但这个过程还是可以比仅有P系数的外环调节更快达到设定值。
拓展阅读:浅谈四轴PID调试心得