PID算法是一种很常用的算法,对于PID算法最关键的是调参,关于调参的文章会在后续去发表,这次想简要的阐述下PID算法的应用
P:Proportion(比例),就是输入偏差乘以一个常数。
I :Integral(积分),就是对输入偏差进行积分运算。
D:Derivative(微分),对输入偏差进行微分运算。
(输入偏差=读出的被控制对象的值-设定值。比如说我要把温度控制在26度,但是现在我从温度传感器上读出温度为28度。则这个26度就是”设定值“,28度就是“读出的被控制对象的值”。)
1稳定性(P和I降低系统稳定性,D提高系统稳定性):在平衡状态下,系统受到某个干扰后,经过一段时间其被控量可以达到某一稳定状态;
2 准确性(P和I提高稳态精度,D无作用):系统处于稳态时,其稳态误差;
3快速性(P和D提高响应速度,I降低响应速度):系统对动态响应的要求。一般由过渡时间的长短来衡量。
对于P项,表述的是当前偏差值,就是e(t)=期望值-当前值,期望值就是你最后想达到的值,例如步进电机,你想让它转90度,那么这个就是期望值,当前值就不多说了。如果使用pid算法只用P项的话,如上图公式Kp*e(t)在系统稳点的时候会达到零,这会发生什么情况呢,比如说你在做一个四轴飞行器,你用pid算法控制电机转速,当达到某一高度的时候你想让四轴停止,如果你只用P项的话,当四轴飞行器到达目标位置时,u(t)=0就代表着电机不转了,你好容易搞的四轴直接GG,显然只有P项肯定是不行的。
所以我们就引入了I项,I项是累加误差,也很简单,就是从开始计时起到当前时刻误差的累加和,代码实现上也很简单,就是a+=e(t)(a代表误差累加和),有了积分项,四轴飞行器就不会出现到达目标位置u(t)=0这种问题了,但是又有新的问题出现了,虽然这两项帮助我们去调节电机的目标位置,但是这两项加起来的d(u(t))实在是太大了,四轴飞行器可能在电机惯性的作用下直接飞过了目标位置,虽然可以慢慢等去让算法自己去调节,但是问题在于调节的时间很长,根本满足不了要求,就比如说电赛2011年赛题,出题者让你在15s内调节到目标位置,但你这pid要2分钟,这肯定得完蛋;。
所以我们引入了D项,D即求微分,在模拟信号中是求误差的微分,数字信号中是e(t)-e(t-1)大家学过高数都明白微分的含义,用微分项可以去对e(t)进行负反馈调节(高中生物),当我的积分项和比例项过大时,即u(t)增长过快时,利用微分项可以抑制这种快速的增长。
这就是pid的基本概念下面时pid的常用几种代码和思路
位置式PID
位置式PID是最简单的PID形式,利用位置式PID可以做出不少形式的应用了,上面图的公式便是位置式PID,位置式PID相较于增量式PID由于积分项的存在,对于时间积累的误差较大。
增量式PID
增量式PID的公式表现形式为
P=Kp*(current_error﹣last_error);
D=Kd*(current_error﹣2*last_error﹢prev_error);
I=Ki*current_error;
这是数字信号的增量式PID,模拟信号换成微分就好。如上述公式所示,增量式PID对于信号实时性比较高,用的数据都是这一段时间前后的数据,没有积分项的误差。
双环PID控制
双环PID控制
双环PID控制相较于单环PID有明显的优势,它的存在能让系统达到目标状态更快更稳定,双环PID的代码实现也非常简单,以直流电机为例
//外环控制(用的是增量式pid)
float PIDwaihuan_output ()
{
float PIDOUT;
pid.Ek=pid.Sv-pid.Pv;//微分项(离散形式)
PIDOUT=pid.Kp*pid.Ek+pid.Ki*(pid.Ek+pid.Ek_1+pid.Ek_2)+pid.Kd*(pid.Ek-pid.Ek_1)+pid.OUT0;//PID公式
pid.Ek_2=pid.Ek_1;
pid.Ek_1=pid.Ek;
pid.PIDOUT=PIDOUT;
pid.PIDOUT=(pid.PIDOUT<pid.lowlimit) ? pid.lowlimit:(pid.PIDOUT>pid.uplimit) ? pid.uplimit : pid.PIDOUT;//输出限幅
return pid.PIDOUT;返回位置信息
}
//内环控制(同样是增量式PID,若想改成位置式,稍加修改代码就可以)
float PID1_speedoutput ()
{
float PIDOUT;
pid1.Ek=pid1.Sv-pid1.Pv;
PIDOUT=pid1.Kp*(pid1.Ek-pid1.Ek_1)+pid1.Ki*pid1.Ek+pid1.Kd*(pid1.Ek_2*pid1.Ek_1+pid1.Ek_2)+pid1.OUT0;
pid1.Ek_2=pid1.Ek_1;
pid1.Ek_1=pid1.Ek;
pid1.PIDOUT+=PIDOUT;
pid1.PIDOUT=(pid1.PIDOUT<pid1.lowlimit) ? pid1.lowlimit:(pid1.PIDOUT>pid1.uplimit) ? pid1.uplimit : pid1.PIDOUT;//输出限幅
return pid1.PIDOUT;
}
上述代码两个环毫无联系,接下来要做的就是将内外环联系在一起做到内外环控制
我们可以对这两个函数定义参数
对于第一个函数作为外环我们可以让入口参数为
float PIDwaihuan_output (Position,Target_position)//当前位置信息,目标位置信息
让该函数返回值为目标速度;
第二个函数作为内环控制我们可以使入口参数为
float PID1_speedoutput (Speed,target_speed)
返回值为电机pwm,
这就是简单的双环PID控制。