日期 | 作者 | 版本 | 说明 |
---|---|---|---|
2020.10.29 | Tao | V1.0 | Release as V1.0 |
PID算法介绍
PID即:Proportional(比例)、Integral(积分)、**Differential(微分)**的缩写。顾名思义,PID控制算法是结合比例、积分和微分三种环节于一体的控制算法,它是连续系统中技术最为成熟、应用最为广泛的一种控制算法,该控制算法出现于20世纪30至40年代,适用于对被控对象模型了解不清楚的场合。实际运行的经验和理论的分析都表明,运用这种控制规律对许多工业过程进行控制时,都能得到比较满意的效果。在理论上可以证明,对于过程控制的典型对象——“一阶滞后+纯滞后”与“二阶滞后+纯滞后”的控制对象,PID控制器是一种最优控制。PID控制的实质就是根据输入的偏差值,按照比例、积分、微分的函数关系进行运算,运算结果用以控制输出。
- PID的控制过程:
- PID的计算公式:
源码
头文件
#ifndef __PID_H__
#define __PID_H__
#include "stm32f10x_conf.h"
#include "stm32f10x.h"
typedef struct
{
float sv;
float pv;
float kp;
float ki;
float kd;
float error;
float error_last;
float integral;
float differential;
float output;
float upThres; //PID启动上阈值
float downThres; //PID启动下阈值
float maxInteg; //最大积分限幅值(正值)
float minInteg; //最小积分限幅值(负值)
float maxDiff; //最大微分限幅值
float minDiff; //最小微分限幅值
}PID_TypeDef;
#define PID_DEFAULT_P 0.1
#define PID_DEFAULT_I 0.001
#define PID_DEFAULT_D 0.0
#define PID_DEFAULT_UPTHRES 3.0
#define PID_DEFAULT_DOWNTHRES 3.0
#define PID_DEFAULT_MAXINTEG 10.0
#define PID_DEFAULT_MININTEG -10.0
#define PID_DEFAULT_MAXDIFF 10.0
#define PID_DEFAULT_MINDIFF -10.0
void PID_SetPIDPara(PID_TypeDef *PID_InitStruct, float KP, float KI, float KD);
void PID_SetThresPara(PID_TypeDef *PID_InitStruct, float upThres, float downThres);
void PID_SetIntegLimiting(PID_TypeDef *PID_InitStruct, float maxInteg, float minInteg);
void PID_SetDiffLimiting(PID_TypeDef *PID_InitStruct, float maxDiff, float minDiff);
void PID_Init(PID_TypeDef *PID_InitStruct);
float PID_Update(PID_TypeDef *PID_InitStruct, float SV, float PV);
void PID_Reset(PID_TypeDef *PID_InitStruct);
float PID_GetOutput(PID_TypeDef *PID_InitStruct);
float PID_GetError(PID_TypeDef *PID_InitStruct);
float PID_GetIntegral(PID_TypeDef *PID_InitStruct);
float PID_GetDifferential(PID_TypeDef *PID_InitStruct);
#endif // __PID__H
源文件
#include "pid.h"
void PID_SetPIDPara(PID_TypeDef *PID_InitStruct, float KP, float KI, float KD)
{
PID_InitStruct->kp = KP;
PID_InitStruct->ki = KI;
PID_InitStruct->kd = KD;
}
void PID_SetThresPara(PID_TypeDef *PID_InitStruct, float upThres, float downThres)
{
PID_InitStruct->upThres = upThres;
PID_InitStruct->downThres = downThres;
}
void PID_SetIntegLimiting(PID_TypeDef *PID_InitStruct, float maxInteg, float minInteg)
{
PID_InitStruct->maxInteg = maxInteg;
PID_InitStruct->minInteg = minInteg;
}
void PID_SetDiffLimiting(PID_TypeDef *PID_InitStruct, float maxDiff, float minDiff)
{
PID_InitStruct->maxInteg = maxDiff;
PID_InitStruct->minInteg = minDiff;
}
void PID_Init(PID_TypeDef *PID_InitStruct)
{
PID_InitStruct->error = 0.0;
PID_InitStruct->error_last = 0.0;
PID_InitStruct->integral = 0.0;
PID_InitStruct->differential = 0.0;
PID_InitStruct->output = 0.0;
//将PID参数初始化为宏定义确定的默认通用参数
PID_SetPIDPara(PID_InitStruct, PID_DEFAULT_P, PID_DEFAULT_I, PID_DEFAULT_D);
PID_SetThresPara(PID_InitStruct, PID_DEFAULT_UPTHRES, PID_DEFAULT_DOWNTHRES);
PID_SetIntegLimiting(PID_InitStruct, PID_DEFAULT_MAXINTEG, PID_DEFAULT_MININTEG);
PID_SetDiffLimiting(PID_InitStruct, PID_DEFAULT_MAXDIFF, PID_DEFAULT_MINDIFF);
}
/**
* @brief 静态函数,用来计算一次P值,I值,D值
* @param PID_InitStruct: 传入的PID类型定义的结构体指针
* @return 计算的结果,经过限幅处理,范围为0~1
*/
static float PID_Calculate(PID_TypeDef *PID_InitStruct)
{
float pid_output = 0;
///当输出限幅的时候,积分累加部分也应同时进行限幅,以防输出不变而积分项继续累加,也即所谓的积分饱和过深。
PID_InitStruct->integral += PID_InitStruct->error;
//积分量限幅
if(PID_InitStruct->integral >PID_InitStruct->maxInteg)
{
PID_InitStruct->integral = PID_InitStruct->maxInteg ;
}
else if(PID_InitStruct->integral < PID_InitStruct->minInteg)
{
PID_InitStruct->integral = PID_InitStruct->minInteg ;
}
else
{
}
PID_InitStruct->differential = PID_InitStruct->error - PID_InitStruct->error_last;
//微分量限幅限制
if(PID_InitStruct->differential > PID_InitStruct->maxDiff)
{
PID_InitStruct->differential = PID_InitStruct->maxDiff;
}
else if(PID_InitStruct->differential < PID_InitStruct->minDiff)
{
PID_InitStruct->differential = PID_InitStruct->minDiff;
}
else
{
}
pid_output = PID_InitStruct->kp * PID_InitStruct->error + PID_InitStruct->ki * PID_InitStruct->integral
+ PID_InitStruct->kd * PID_InitStruct->differential;
if (pid_output > 1)
{
pid_output = 1;
}
else if (pid_output < 0)
{
pid_output = 0;
}
else
{
}
return pid_output;
}
float PID_Update(PID_TypeDef *PID_InitStruct, float SV, float PV)
{
PID_InitStruct->sv = SV;
PID_InitStruct->pv = PV;
PID_InitStruct->error_last = PID_InitStruct->error;
PID_InitStruct->error = PID_InitStruct->sv - PID_InitStruct->pv;
///PV < SV - PIDDownThres. PID use full power.
if(PID_InitStruct->error > PID_InitStruct->downThres)
{
PID_InitStruct->output = 1;
}
///PV < SV + PIDUpThres. PID use calculated data.
else if(PID_InitStruct->error > 0 - PID_InitStruct->upThres)
{
PID_InitStruct->output = PID_Calculate(PID_InitStruct);
}
///PV >= SV + PIDUpThres. PID use zero power.
else
{
PID_InitStruct->output = 0;
}
return PID_InitStruct->output;
}
void PID_Reset(PID_TypeDef *PID_InitStruct)
{
PID_InitStruct->sv = 0;
PID_InitStruct->pv = 0;
PID_InitStruct->kp = 0;
PID_InitStruct->ki = 0;
PID_InitStruct->kd = 0;
PID_InitStruct->error = 0;
PID_InitStruct->error_last = 0;
PID_InitStruct->integral = 0;
PID_InitStruct->differential = 0;
PID_InitStruct->output = 0;
}
float PID_GetOutput(PID_TypeDef *PID_InitStruct)
{
return PID_InitStruct->output;
}
float PID_GetError(PID_TypeDef *PID_InitStruct)
{
return PID_InitStruct->error;
}
float PID_GetIntegral(PID_TypeDef *PID_InitStruct)
{
return PID_InitStruct->integral;
}
float PID_GetDifferential(PID_TypeDef *PID_InitStruct)
{
return PID_InitStruct->differential;
}
使用指南
直接使用
声明并定义PID类型数据(或数组)
声明并定义PID类型数据(或数组)。可以在声明的时候直接初始化变量,也可以利用void PID_Init(PID_TypeDef *PID_InitStruct)
将PID成员参数初始化为宏定义设定的默认参数:
#define PID_DEFAULT_P 0.1
#define PID_DEFAULT_I 0.001
#define PID_DEFAULT_D 0.0
#define PID_DEFAULT_UPTHRES 3.0
#define PID_DEFAULT_DOWNTHRES 3.0
#define PID_DEFAULT_MAXINTEG 10.0
#define PID_DEFAULT_MININTEG -10.0
#define PID_DEFAULT_MAXDIFF 10.0
#define PID_DEFAULT_MINDIFF -10.0
根据需要修改PID参数
使用库函数提供的参数修改接口,在需要的地方对PID进行修改(程序初始化时、轮询PID控制动作时):
void PID_SetPIDPara(PID_TypeDef *PID_InitStruct, float KP, float KI, float KD)
:设置P(Proportional), I(Integral), D(Differential)参数。void PID_SetThresPara(PID_TypeDef *PID_InitStruct, float upThres, float downThres)
:设置PID作用的上下阈值(PV进入[SV-downThres, SV+upThres]区间,PID才起作用)。void PID_SetIntegLimiting(PID_TypeDef *PID_InitStruct, float maxInteg, float minInteg)
:设置PID算法的最大、最小积分量(防止PID进入积分过饱和)。void PID_SetDiffLimiting(PID_TypeDef *PID_InitStruct, float maxDiff, float minDiff)
:设置PID算法的最大、最小微分量(防止PID进入微分过饱和)。
对于不需要支持动态修改的参数(修改后重启生效),可以在程序初始化的时候(根据相应保持寄存器的值)设置参数。对于需要实时修改的参数(修改后立即生效),可以在轮询PID控制动作的时候(调用PID_Update
进行输出计算之前)进行修改。
//Set PID
PID_SetPara(&this->PidData, HoldingReg_GetData(11), HoldingReg_GetData(12), HoldingReg_GetData(13));
//Set threshold
PID_SetThresPara(&this->PidData, HoldingReg_GetData(14), HoldingReg_GetData(15));
计算PID输出值
在定时器更新中断服务函数或者RTOS的一个轮询任务中,周期性的调用float PID_Update(PID_TypeDef *PID_InitStruct, float SV, float PV)
函数计算PID的输出值(返回值)。执行器根据输出值调整一次动作(例如加热器根据输出值调整PWM的占空比以实现功率调节)。
间接使用
PID数据已经被封装为一个结构体类型,可以直接声明并定义PID类型的变量作为其他结构体封装的成员。通过PID算法库提供的一套PID操作函数,可以在需要的地方调用并传入需要操作的PID变量。具体的例子可以参考C语言模块化设计控温器的实现。