最近在学习与无人机有关的一些控制算法,在这里做一些笔记,今天学的是有关于PID的算法。
什么是PID
首先关于PID的定义,因为我本身不是自动控制专业出身所以对于概念这个东西比较模糊,可以去社区里面搜一些有趣的小故事来看,前几天自学的时候有看到一个讲小明给水桶装水的故事挺有趣,也比较直观,可以去搜一下看看。
话说回来,什么是PID。其实就是一种控制算,P表示比例,I表示积分,D表示微分:
PID的目的是为了消除误差给系统带来的不稳定状态,从而使系统能够维持在一个平稳运行的状态。
比例的作用(P)
成比例地反映控制系统的偏差信号,偏差一旦产生,立即产生控制作用以减小偏差。比例控制器的输出u(t)与输入偏差e(t)成正比,能迅速反映偏差,从而减小偏差,但不能消除静差。
积分的作用(I)
积分环节的作用,主要用于消除静差提高系统的无差度。积分作用的强弱,取决于积分时间常数Ti,Ti越大积分作用越弱,反之则越强。积分控制作用的存在与偏差e(t)的存在时间有关,只要系统存在着偏差,积分环节就会不断起作用,对输入偏差进行积分,使控制器的输出及执行器的开度不断变化,产生控制作用以减小偏差。在积分时间足够的情况下,可以完全消除静差,这时积分控制作用将维持不变。Ti越小,积分速度越快,积分作用越强。积分作用太强会使系统超调加大,甚至使系统出现振荡。
微分的作用(D)
微分环节的作用能反映偏差信号的变化趋势(变化速率),并能在偏差信号的值变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减小调节时间。积分控制作用的引入虽然可以消除静差,但是降低了系统的响应速度,特别是对于具有较大惯性的被控对象,用PI控制器很难得到很好的动态调节品质,系统会产生较大的超调和振荡,这时可以引入微分作用。在偏差刚出现或变化的瞬间,不仅根据偏差量作出及时反应(即比例控制作用),还可以根据偏差量的变化趋势(速度)提前给出较大的控制作用(即微分控制作用),将偏差消灭在萌芽状态,这样可以大大减小系统的动态偏差和调节时间,使系统的动态调节品质得以改善。
以上摘自百度百科
PID方式
PID分两种,位置式PID算法和增量式PID算法,因为我要做无人机的控制算法,所以用的是位置式PID,公式如下:
比例系数为Kp,积分系数为Kp/Ti,微分系数为Kp/Td。
可以看到其实积分系数和微分系数实际上都与比例系数Kp有关,但是为了我们调节方便,使用控制变量的方法,假设Ki=Kp/Ti,Kd=Kp/Td,这样就可以一个一个的对系数进行调节了。
那么公式也就变成了:
比例用于消除当前误差,积分用于消除过往误差,微分用于超前调节。
代码实现PID算法
程序代码如下:
float kp,ki,kd;
float pid_caculation(float expectation_value,float acctual_value,float frequency)
{
float p,i,d,error_previous,i_previous;
float error = expectation_value-acctual_value; //计算误差=期望值-实际值
float output;
//计算P
p=error*kp;
//计算I
i=(i_previous + error * frequency)*ki; //i=上次i+误差*采样周期
i_previous = i;
//计算D
d=((error-error_previous)/frequency)*kd; //[期望值-实际值-(上次期望值-上次实际值)]/采样周期 若两次期望值相同或期望值为0,则d= -(实际值-上次实际值)/采样周期 是一个导数,斜率,负变化率
error_previous=error; // 更新误差
//计算PID后的输出
output = p+i+d;
return output;
}
输出值直接作为控制的输入值使用
——————————分割线————————————————————————
上面是PID的位置式算法, 最近用到了增量式的算法,这里再来补充一下。其实增量式的算法就是用这一次的位置式减去上一次的位置式,得到的结果就是增量式。因此可以得到增量式的公式是这样的:
增量式得到是一个差值,因此需要加上之前的值,才能够作为输出值使用。
struct PID_data{
float kp;
float ki;
float kd;
float P;
float I;
float D;
float output;
float OutputMax;
float OutputMin;
float preoutput;
}PID;
void PID_Init()
{
PID.kp=0.005;
PID.ki=0.001;
PID.kd=0;
PID.P=0;
PID.I=0;
PID.D=0;
PID.output=0;
PID.preoutput=0;
}
float PID_Caculate(uint16_t setspeed,uint16_t currentspeed)
{
int error = 0;
int error_pre = 0;
int error_pre_pre = 0;
error = setspeed - currentspeed;
// printf("error=%d\t",error);
PID.P = PID.kp*(error-error_pre);
printf("PID.P=%f\t", PID.P);
PID.I = PID.ki*error;
printf("PID.I=%f\t",PID.I);
PID.D = PID.kd*(error-2*error_pre+error_pre_pre);
printf("PID.D=%f\t",PID.D);
PID.output = PID.P+PID.I+PID.D+PID.preoutput;
// printf("PID.output=%f\t", PID.output);
PID.preoutput = PID.output;
error_pre_pre = error_pre;
error_pre = error;
return PID.output;
}
PID的算法其实就是为了消除误差而存在,至于什么时候该用什么样的调节方法,需要自行判断。