1、关于PID算法的原理和详解,这里不作详细解释,读者请看之前的文章《PID算法详解(精华知识汇总)》,这是这篇文章的链接:PID算法详解(精华知识汇总)_pid运算_小小_扫地僧的博客-CSDN博客
2、关于写这篇文章的初衷,是源于我准备“全国大学生电子设计大赛”的暑期,深刻了解和学习了PID算法,同时阅读了大量的代码和参考了很多PID算法,觉得有必要把我觉得通用性较强、控制较精准的PID算法总结一篇,以供读者来参考学习。目的也在于助力准备准备各类电子大赛的同学和做控制类的电子爱好者。
这是PID.C文件内容
包含两个封装好的通用PID控制算法,读者只需要准确调用这两个函数即可。注意看代码里的详细注释。
#include "stm32f10x.h"
#include
#include "sys.h"
#include "pid.h"
#define PID_LIMIT_MIN -10000 //PID输出最低值
#define PID_LIMIT_MAX 10000 //PID输出最大值
//注意:PID结构体必须定义为全局变量或静态变量,然后在函数中给KP,KI,KD赋值
/************************采样周期未知且不变************************************/
//位置式PID
//pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
//setvalue : 设置值(期望值)
//actualvalue: 实际值
//由于全量输出,每次输出均与过去状态有关,计算时要对ek累加,计算量大
float PID_location(float setvalue, float actualvalue, PID_LocTypeDef *PID)
{
PID->ek =setvalue-actualvalue;
PID->location_sum += PID->ek; //计算累计误差值
if((PID->ki!=0)&&(PID->location_sum>(PID_LIMIT_MAX/PID->ki))) PID->location_sum=PID_LIMIT_MAX/PID->ki;
if((PID->ki!=0)&&(PID->location_sum<(PID_LIMIT_MIN/PID->ki))) PID->location_sum=PID_LIMIT_MIN/PID->ki;//积分限幅
PID->out=PID->kp*PID->ek+(PID->ki*PID->location_sum)+PID->kd*(PID->ek-PID->ek1);
PID->ek1 = PID->ek;
if(PID->outout=PID_LIMIT_MIN;
if(PID->out>PID_LIMIT_MAX) PID->out=PID_LIMIT_MAX;//PID->out限幅
return PID->out;
}
//增量式PID
//pidout+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
//setvalue : 设置值(期望值)
//actualvalue: 实际值
float PID_increment(float setvalue, float actualvalue, PID_LocTypeDef *PID)
{
PID->ek =setvalue-actualvalue;
PID->out+=PID->kp*(PID->ek-PID->ek1)+PID->ki*PID->ek+PID->kd*(PID->ek-2*PID->ek1+PID->ek2);
// PID->out+=PID->kp*PID->ek-PID->ki*PID->ek1+PID->kd*PID->ek2;
PID->ek2 = PID->ek1;
PID->ek1 = PID->ek;
if(PID->outout=PID_LIMIT_MIN;
if(PID->out>PID_LIMIT_MAX) PID->out=PID_LIMIT_MAX;//限幅
return PID->out;
}
PID.h文件
#ifndef __PID_H
#define __PID_H
#include "stm32f10x.h"
#include
#include "sys.h"
typedef struct
{
float kp; //比例系数Proportional
float ki; //积分系数Integral
float kd; //微分系数Derivative
// float ti; //积分时间常数
// float td; //微分时间常数
// float period; //采样周期
float ek; //当前误差
float ek1; //前一次误差e(k-1)
float ek2; //再前一次误差e(k-2)
float location_sum; //累计积分位置
float out; //PID输出值
}PID_LocTypeDef;
float PID_location(float setvalue, float actualvalue, PID_LocTypeDef *PID);
float PID_increment(float setvalue, float actualvalue, PID_LocTypeDef *PID);
#endif
以上两个PID文件是通用性较强的,下面再分享一些关于电机控制的PID算法。
其中参数“int encoder”是编码器采集到的电机的速度,“int setspeed”是用户自己设定的目标值
第一类:
int speed_control_left(int encoder,int setspeed)//左电机速度环控制
{
static int pwmout=0,last_error=0,error=0;
error=setspeed-encoder;
pwmout=pwmout+pid_speed.kp*(error-last_error)+pid_speed.ki*error;
last_error=error;
if(pwmout>6500)//限幅
pwmout=6500;
if(pwmout<-6500)
pwmout=-6500;
return pwmout;//计算出符合目标值的pwm值
}
int speed_control_right(int encoder,int setspeed)//右电机速度环控制
{
static int pwmout=0,last_error=0,error=0;
error=setspeed-encoder;
pwmout=pwmout+pid_speed.kp*(error-last_error)+pid_speed.ki*error;
last_error=error;
if(pwmout>6500)
pwmout=6500;
if(pwmout<-6500)
pwmout=-6500;
return pwmout;//计算出符合右电机目标值的pwm输出值
}
int Position_control_left(int Encoder,int Target)//左电机位置环控制
{
static int Bias,Pwm,Integral_bias,Last_Bias;
Bias=Encoder-Target; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
if(Integral_bias>100000)Integral_bias=10000;
if(Integral_bias<-100000)Integral_bias=-10000;
Pwm=pid_Position.kp*Bias+pid_Position.ki*Integral_bias+pid_Position.kd*(Bias-Last_Bias); //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return (int)Pwm; //增量输出
}
int Position_control_right(int Encoder,int Target)//右电机位置环控制
{
static int Bias,Pwm,Integral_bias,Last_Bias;
Bias=Encoder-Target; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
if(Integral_bias>100000)Integral_bias=10000;
if(Integral_bias<-100000)Integral_bias=-10000;
Pwm=pid_Position.kp*Bias+pid_Position.ki*Integral_bias+pid_Position.kd*(Bias-Last_Bias); //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return (int)Pwm; //增量输出
}
int Turn_ways(int yaw)//转向环电机控制,一般用于智能车的转弯。参数yaw是MPU6050测出的俯仰角
{
static int turn_pwm,bias,last_bias,Turn_Target=1;
int Kd;
bias=yaw-0;
turn_pwm=Turn_Target*bias*pid_Yaw.kp+pid_Yaw.kd*(bias-last_bias);//结合Z轴陀螺仪进行PD控制
last_bias=bias;
return (int)turn_pwm;
}
第二类:
#include "PID.h"
/*
位置式 PID
*/
int Position_Zero=10000; //编码器的脉冲计数
float Balance_KP=340,Balance_KD=2600,Position_KP=6.5,Position_KD=320; //PID系数 350 2500 6.5 210
int ZHONGZHI;
//==== 用于串口调试 USMART
void Set_PID_Angle(int Kp,int Kd)
{
Balance_KP = Kp;
Balance_KD = Kd;
}
void Set_PID_Location(int Kp,int Kd)
{
Position_KP = Kp/10.0;
Position_KD = Kd/10.0;
}
/**************************************************************************
函数功能:倾角PD控制
入口参数:角度
返回 值:倾角控制PWM
作 者:小小扫地僧
**************************************************************************/
int balance(float Angle)
{
float Bias; //倾角偏差
static float Last_Bias,D_Bias; //PID相关变量
int balance; //PWM返回值
Bias=Angle-ZHONGZHI; //求出平衡的角度中值 和机械相关
D_Bias=Bias-Last_Bias; //求出偏差的微分 进行微分控制
balance=-Balance_KP*Bias-D_Bias*Balance_KD; //===计算倾角控制的电机PWM PD控制
Last_Bias=Bias; //保持上一次的偏差
return balance;
}
/**************************************************************************
函数功能:位置PD控制
入口参数:编码器
返回 值:位置控制PWM
作 者:小小扫地僧
**************************************************************************/
int Position(int Encoder)
{
static float Position_PWM,Last_Position,Position_Bias,Position_Differential;
static float Position_Least;
Position_Least =Encoder-Position_Zero; //===
Position_Bias *=0.8;
Position_Bias += Position_Least*0.2; //===一阶低通滤波器
Position_Differential=Position_Bias-Last_Position;
Last_Position=Position_Bias;
Position_PWM=Position_Bias*Position_KP+Position_Differential*Position_KD; //===速度控制
return Position_PWM;
}
第三类:电机速度闭环控制
/**************************************************************************
函数功能:增量PI控制器
入口参数:编码器测量值,目标速度
返回 值:电机PWM
根据增量式离散PID公式
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差 以此类推
pwm代表增量输出
在我们的速度控制闭环系统里面,只使用PI控制
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
**************************************************************************/
int Incremental_PI (int Encoder,int Target)
{
float Kp=20,Ki=30;
static int Bias,Pwm,Last_bias;
Bias=Encoder-Target; //计算偏差
Pwm+=Kp*(Bias-Last_bias)+Ki*Bias; //增量式PI控制器
Last_bias=Bias; //保存上一次偏差
return Pwm;
}
第四类:电机位置闭环控制
/**************************************************************************
函数功能:位置式PID控制器
入口参数:编码器测量位置信息,目标位置
返回 值:电机PWM
根据位置式离散PID公式
pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差
∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
pwm代表输出
**************************************************************************/
int Position_PID (int Encoder,int Target)
{
float Position_KP=80,Position_KI=0.1,Position_KD=500;
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias=Encoder-Target; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias); //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return Pwm; //增量输出
}
以上代码都是封装好的函数,读者只需根据自己的需要移植调用即可。需要PID的学习资料可以私信我,收到后会第一时间回复。
持续更新中……