手撕PID(带死区、积分分离、不完全微分)
一、增量式PID
ΔU(k)=kp*(err(k)-err(k-1))+kie(k)+kd(e(k)-2*e(k-1)+e(k-2))
U(k)=U(k-1)+ΔU(k)
二、积分分离
在过程的启动、结束或大幅度增减设定值时,短时间内系统输出有很大偏差,会造成PID运算的积分累积,引起超调或者振荡。为了解决这一干扰,人们引入了积分分离的思想。其思路是偏差值较大时,取消积分作用,以免于超调量增大;而偏差值较小时,引入积分作用,以便消除静差,提高控制精度。
具体的实现步骤是:根据实际情况,设定一个阈值;当偏差大于阈值时,消除积分仅用PD控制;当偏差小于等于阈值时,引入积分采用PID控制。则控制算法可表示为:
三、不完全微分PID
微分项有引入高频干扰的风险,但若在控制算法中加入低通滤波器,则可使系统性能得到改善。方法之一就是在PID算法中加入一个一阶低通滤波器。这就是所谓的不完全微分,其结构图如下:
微分部分表达式为:
其中 其中α的取值在0和1之间,有滤波常数和采样周期确定
四、带死区的PID
带死区的PID控制算法就是检测偏差值,若是偏差值达到一定程度,就进行调节。若是偏差值较小,就认为没有偏差。用公式表示如下:
其中的死区值得选择需要根据具体对象认真考虑,因为该值太小就起不到作用,该值选取过大则可能造成大滞后。
带死区的PID算法,对无论位置型还是增量型的表达式没有影响,不过它是一个非线性系统。
除以上描述之外还有一个问题,在零点附近时,若偏差很小,进入死去后,偏差置0会造成积分消失,如是系统存在静差将不能消除,所以需要人为处理这一点。
c++程序实现
#头文件
#ifndef PID_H
#define PID_H
typedef struct
{
float kp;
float ki;
float kd;
float ek;//当前误差
float ekk;//上一次误差
float ekkk; //上两次误差
float alpha;//不完全微分系数
float lastDev;//上一次微分环节的增量
float separationThreshold;//积分分离阈值,误差大于阈值时PD,误差小于阈值时PID
float ekDeadZone;//PID死区
float increment; //PID增量
float lastOutputValue;//上一次PID输出值
float upLimit;//输出上限
float lowLimit;//输出下限
}PidParameter;
class PID{
public:
//增量式PID
int IncrementalPID(float exceptionValue,float feedbackValue,float outputValue);
//积分分离式PID,防止超调
int integrationSeparationPID(float exceptionValue,float feedbackValue,float outputValue);
//不完全微分PID,微分环节加低通滤波,抑振高频噪声
int incompleteDifferentialPID(float exceptionValue,float feedbackValue,float outputValue);
//带死区的PID,偏差限定最小值,小于最小值则为0,防止系统稳定点频繁调节
int deadZonePID(float exceptionValue,float feedbackValue,float outputValue);
//带积分分离、死区的不完全微分的增量式PID
int fullPID(float exceptionValue,float feedbackValue,float outputValue);
private:
PidParameter pidParam;
};
#endif // PID_H
#源文件
#include "PID.h"
#include "math.h"
int PID::IncrementalPID(float exceptionValue,float feedbackValue,float outputValue)
{
pidParam.kp=10;
pidParam.ki=10;
pidParam.kd=10;
pidParam.ek=exceptionValue-feedbackValue;
pidParam.increment = pidParam.kp*(pidParam.ek-pidParam.ekk)+pidParam.ki*pidParam.ek+pidParam.kd*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk);
pidParam.ekkk=pidParam.ekk;
pidParam.ekk=pidParam.ek;
outputValue = outputValue+pidParam.increment;
return 0;
}
int PID::integrationSeparationPID(float exceptionValue,float feedbackValue,float outputValue)
{
pidParam.kp=10;
pidParam.ki=10;
pidParam.kd=10;
pidParam.separationThreshold=5;
pidParam.ek=exceptionValue-feedbackValue;
if(pidParam.ek<pidParam.separationThreshold)//小于阈值则PID
{
pidParam.increment = pidParam.kp*(pidParam.ek-pidParam.ekk)+pidParam.ki*pidParam.ek+pidParam.kd*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk);
}
else {
pidParam.increment = pidParam.kp*(pidParam.ek-pidParam.ekk)+pidParam.kd*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk);
}
pidParam.ekkk=pidParam.ekk;
pidParam.ekk=pidParam.ek;
outputValue = outputValue+pidParam.increment;
return 0;
}
int PID::incompleteDifferentialPID(float exceptionValue,float feedbackValue,float outputValue)
{
float proportion=pidParam.ek-pidParam.ekk;
float integration=pidParam.ek;
float differ=pidParam.kd*(1-pidParam.alpha)*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk)+pidParam.alpha*pidParam.lastDev;
pidParam.increment=pidParam.kp*proportion+pidParam.ki*integration+pidParam.kd*differ;
pidParam.lastDev=differ;
pidParam.ekkk=pidParam.ekk;
pidParam.ekk=pidParam.ek;
outputValue = outputValue+pidParam.increment;
return 0;
}
int PID::deadZonePID(float exceptionValue,float feedbackValue,float outputValue)
{
pidParam.kp=10;
pidParam.ki=10;
pidParam.kd=10;
pidParam.ek=exceptionValue-feedbackValue;
if((pidParam.ek<pidParam.ekDeadZone)||(pidParam.ek>-pidParam.ekDeadZone))
{
pidParam.increment=0;
pidParam.ek=0;
}else {
pidParam.increment = pidParam.kp*(pidParam.ek-pidParam.ekk)+pidParam.ki*pidParam.ek+pidParam.kd*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk);
}
pidParam.ekkk=pidParam.ekk;
pidParam.ekk=pidParam.ek;
outputValue = outputValue+pidParam.increment;
if(outputValue>pidParam.upLimit)
{
outputValue=pidParam.upLimit;
}
if(outputValue<pidParam.lowLimit)
{
outputValue=pidParam.lowLimit;
}
return 0;
}
int PID::fullPID(float exceptionValue,float feedbackValue,float outputValue)
{
float up=0;//比例环节的输出
float ui=0;//积分环节的输出
float uk=0;//微分环节的输出
outputValue=0;
pidParam.ek=exceptionValue-feedbackValue;
if(abs(pidParam.ek)<pidParam.ekDeadZone)//PID死区,小于死区则不进行调节,认为误差为零 PID不进行调节,防止稳定点频繁振荡
{
pidParam.increment=0;
pidParam.ek=0;
}else{
if(abs(pidParam.ek)>pidParam.separationThreshold)//积分分离,误差大于阈值采用PID控制,误差小于阈值采用PID控制 防止超调
{
pidParam.ek=0;
}
up=pidParam.kp*(pidParam.ek-pidParam.ekk);
ui=pidParam.ki*pidParam.ek;
uk=pidParam.kd*(1-pidParam.alpha)*(pidParam.ek-2*pidParam.ekk+pidParam.ekkk)+pidParam.lastDev;//不完全微分环节,alpha为0~1的滤波系数 消除微分环节引入的高频噪声
pidParam.increment=up+ui+uk;
}
pidParam.lastDev=uk;
pidParam.ekkk=pidParam.ekk;
pidParam.ekk=pidParam.ek;
outputValue=outputValue+pidParam.increment;
if(outputValue>pidParam.upLimit)
{
outputValue=pidParam.upLimit;
}
if(outputValue<pidParam.lowLimit)
{
outputValue=pidParam.lowLimit;
}
return 0;
}
引用:
https://blog.csdn.net/kuizhao8951/article/details/89508113?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162952512616780366515697%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=162952512616780366515697&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-89508113.pc_search_download_positive&utm_term=PID%E9%9D%A2%E8%AF%95&spm=1018.2226.3001.4187