基于stm32的PID算法粗略讲解
一、PID算法的分类
增量式与位移式的区别
1.输出不同:位置PID控制的输出与整个过去状态有关,并且使用了误差的累加值;而增量PID的输出仅与当前拍和前两拍的误差有关,因此位置PID控制的累积误差相对较大。
2.是否有积分部分:增量PID控制输出为控制量增量,没有积分功能,因此该方法适用于带有积分部分的对象,例如步进电机等。 ,但位置PID适用于执行没有积分部件的对象,例如电动液压伺服阀。
3.是否具有记忆功能:由于增量PID输出是控制量增量,因此,如果计算机出现故障,则故障影响较小,执行器本身具有记忆功能,该功能仍可保留且不会严重影响系统的工作,而位置输出直接对应于对象的输出,因此对系统影响较大。
但通常情况下使用增量式
二、PID的三个参数
1. kP比例
成比例地反映控制系统的偏差信号e(t),偏差一旦产生,控制器立即产生控制作用,以减小偏差。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。
P参数越小比例作用越强,动态响应越快,消除误差的能力越强。通常将P参数由大向小调,以能达到最快响应又无超调(或无大的超调)为最佳参数。
2. kI积分
为消除静差,提高系统的无差度。积分作用的强弱取决于积分时间常数T,T越大,积分作用越弱,反之则越强。
3. KD微分
反映偏差信号的变化趋势,并能在偏差信号变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减少调节时间。在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。
D越大,微分作用越强,D越小,微分作用越弱。系统调试时通常把D从小往大调,具体参数由试验决定。
三.增量式程序
/************************** .h文件******************************/
#ifndef _pid_H
#define _pid_H
#define MODEL_P 1
#define MODEL_PI 2
#define MODEL_PID 3
typedef struct
{
u8 choose_model; //使用哪个模式调节,以方便分布调试
float curr; //当前值
float set; //设定值
float En; //当前时刻误差值
float En_1; //前一时刻误差值
float En_2; //前二时刻误差值
float Kp; //比例系数
float T; //每隔T控制器输出一次PID运算结果
u16 Tdata; //判断PID周期到没到
float Ti; //积分时间常数
float Td; //微分时间常数
float Dout; //增量PID计算本次应该输出的增量值-本次计算的结果
float OUT0; //一个维持的输出,防止失控
short currpwm; //当前的pwm宽度
u16 pwmcycle; //pwm周期
}PID;
extern u8 STATUS;
extern PID pid;
void PID_Init(void); //增量式PID初始化
void pid_calc(void); //pid计算 并输出
/************************** .c文件******************************/
#endif
#include "pid.h"
#include "PWM_Config.h"
#include "USART_Config.h" //USART设置
PID pid;
void PID_Init() //
{
pid.choose_model = MODEL_PID;
pid.T=330; //采样周期,定时器使用1ms,则最小执行PID的周期为330ms
pid.set =280; //用户设定值
pid.Kp=0.5; //比例系数
pid.Ti=40; //微分系数常数
pid.Td=10; //积分时间常数
pid.OUT0=0; //一个维持的输出
pid.pwmcycle = 330; //PWM的周期
}
void pid_calc()
{
float dk1;float dk2;
float t1,t2,t3;
if(pid.Tdata < (pid.T)) //最小计算周期未到
{
return ;
}
pid.Tdata = 0;
pid.En=pid.set-pid.curr; //本次误差
dk1=pid.En-pid.En_1; //本次偏差与上次偏差之差
dk2=pid.En-2*pid.En_1+pid.En_2;
t1=pid.Kp*dk1; //比例
t2=(pid.Kp*pid.T)/pid.Ti; //积分
t2=t2*pid.En;
t3=(pid.Kp*pid.Td)/pid.T; //微分
t3=t3*dk2;
switch(pid.choose_model)
{
case MODEL_P: //仅用P
pid.Dout= t1;
printf("使用P运算\r\n") ;
break;
case MODEL_PI: //仅用PI
pid.Dout= t1+t2;
printf("使用PI运算\r\n") ;
break;
case MODEL_PID: //用PID
pid.Dout= t1+t2+t3;
printf("使用PID运算\r\n") ;
break;
}
pid.currpwm+=pid.Dout; //本次应该输出的PWM
printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ;
if(pid.currpwm>pid.pwmcycle) //确保值在0-pid.pwmcycle之间
{
pid.currpwm=pid.pwmcycle;
}
if(pid.currpwm<0)
{
pid.currpwm=0;
}
printf("实际输出使用的OUT:\t%d\r\n",(int)pid.currpwm) ;
pid.En_2=pid.En_1;
pid.En_1=pid.En;
}
总结
1首先整定比例部分。将比例系数Kp由小变大
2置积分时间Ti为一较大值,并将经第一步整定很到的比例系数略为缩小(如缩小为原值的4/5),观察系统响应的情况
3 比例系数由小到大,然后找出超调小的Kp
4 积分时间常数Ti由大变小,适当调整Kp
5 微分时间常数Td由小变大,适当调整Ti和Kp
6 KP设定,最初使用1,假如控制之后实际值比设定值小不够,那就增大。反之就减少。
7 TI设定,数据先很大。看效果
8 如果TI加进去之后数据很久才变化到目标值就逐渐减小。如果TI减少到执行几次都是比设定值大的时候那就逐渐增大
9如果刚加进去变化很快,并且超调很高,就增大来调节
10 积分就看情况调节