赞
踩
PID分别是三个单词的缩写,P(Proportion)I(Integration)D(Differential),中文意思就是比例、积分、微分控制器。
对于离散的位置PID算法来说,其公式如下:
输出 = KP * (当前误差) + KI * (累计误差) + KD * (误差的变化)
对于三个变量来说,它们的计算方法如下:
1. 当前误差 = 目标值 - 当前值
2. 累计误差 = 每次当前误差的累加值
3. 误差的变化 = 当前误差 - 上一次当前误差
参考软件代码如下:
int PID_SpeedLoop(int speedfbk, int speedtarget)
{
static float speed_error,last_error;
static float Integral;
int result;
//误差计算,当前误差
speed_error = speedtarget - speedfbk;
//误差积分,累计误差
Integral += speed_error;
result = KP * speed_error +//比例项
KI * Integral +//积分项
KD * (speed_error - last_error);//微分项
last_error = speed_error;
return result;
}
Kp,Ki,Kd都是常数,根据不同的系统我们需要调整这三个不同的参数。
当误差信号较大时,P项会积极的对输出做出响应,贡献最大的值,所以此时P项占据输出的主要成分。当误差信号处于快速变化中的时候,D项微分运算会对输出做出较大贡献。而I项扮演着校准的角色,当系统的每次细小误差累积起来后,I项就会修正这个误差。
增量式PID算法实现:
/*pid*/
typedef struct
{
float target_val; //目标值
float actual_val; //实际值
float err; //定义当前偏差值
float err_next; //定义下一个偏差值
float err_last; //定义上一个偏差值
float Kp, Ki, Kd; //定义比例、积分、微分系数
}_pid;
float PID_realize(float actual_val)
{
/* 计算目标值与实际值的误差*/
pid.err=pid.target_val-actual_val;
/*PID 算法实现*/
pid.actual_val += pid.Kp*(pid.err - pid.err_next) + pid.Ki*pid.err + pid.Kd*(pid.err - 2 * pid.err_next + pid.err_last);
/* 传递误差*/
pid.err_last = pid.err_next;
pid.err_next = pid.err;
/* 返回当前实际值*/
return pid.actual_val;
}
这个函数主要实现了增量式PID 算法,用传入的目标值减去实际值得到误差值得到当前偏差值。
然后进行误差传递,将本次偏差和上次偏差保存下来,供下次计算时使用。
因为使用PID 控制,实际的控制过程中常常会遇到一些问题,例如积分饱和、死区处理。这就要求我们要在PID 运算中,对计算过程加以干预,防止控制效果达不到预期。进行常见的数据处理方式有许多。
闭环死区,是指执行机构的最小控制量,无法满足控制需求产生的。举个例子,假设有个水池,你期望控制水龙头让水从水池以1.5L 每秒的流速流出,但是你买的水龙头流量太大了,水龙头按最小刻度拧一下都会让流速增加1L 每秒。最终流速只能控制在1L 每秒或2L 每秒,始终无法达到预设值。这1.5L 小数点后的范围内,就是闭环死区,系统是无法控制的。如果不限定闭环,因为始终无法达到目标值,误差会一直存在,容易发生震荡现象。一般情况下要是系统要求的精确度不高,就可以设定闭环死区来解决。还是以上面为例,如果说水1L 或2L 每秒的流速流出也是能接受的,就可以认为只要实际值和目标值的误差在2 分之一升以内,就没有误差,将目标值与实际值之差赋值为0,这就限定了闭环死区。
积分饱和的处理。积分饱和,就是执行机构达到极限输出能力了,仍无法到达目标值,在很长一段时间内无法消除静差造成的。简单举例,就是电机满功率运行,仍达不到期望转速,在一段时间内没有到达目标值,这时候PID 的积分项累计了很大的数值,如果这时候到达了目标值或者重新设定了目标值,由于积分由于累计的误差很大,系统并不能马上稳定到目标值,并会造成严重的超调或失调的现象。解决办法有很多,代码中使用了积分分离的方法,在累计误差大于一定值后去掉积分项的作用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。