PID算法详解(代码详解篇),位置式PID、增量式PID(通用)

8 篇文章 3 订阅
6 篇文章 0 订阅

1、关于PID算法的原理和详解,这里不作详细解释,读者请看之前的文章《PID算法详解(精华知识汇总)》,这是这篇文章的链接:PID算法详解(精华知识汇总)_pid运算_小小_扫地僧的博客-CSDN博客

2、关于写这篇文章的初衷,是源于我准备“全国大学生电子设计大赛”的暑期,深刻了解和学习了PID算法,同时阅读了大量的代码和参考了很多PID算法,觉得有必要把我觉得通用性较强、控制较精准的PID算法总结一篇,以供读者来参考学习。目的也在于助力准备准备各类电子大赛的同学和做控制类的电子爱好者。

3、有关PID参数调整的经验,大家可以参考以下文章: 

PID算法参数调节经验分享-CSDN博客

 这是PID.C文件内容

包含两个封装好的通用PID控制算法,读者只需要准确调用这两个函数即可。注意看代码里的详细注释。

#include "stm32f10x.h"
#include <math.h>
#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->out<PID_LIMIT_MIN)	PID->out=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->out<PID_LIMIT_MIN)	PID->out=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 <math.h>
#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的学习资料可以私信我,收到后会第一时间回复。

考虑到不能及时回复大家,因此我新建了一个学习交流群,我把相关的资料已经上传到群里,大家可以入群后在群文件里下载。群中文件资料我会时常更新,主要资料是单片机开发、编程、嵌入式学习、算法控制等。

由于群中文件还在不断更新上传,有些方面的资料还不太完善,希望大家理解。若群中涉及违规行为,欢迎举报!

 

 持续更新中……

  • 26
    点赞
  • 223
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论
PID控制是一种广泛应用于自动控制系统中的控制算法。它结合比例(P)、积分(I)和微分(D)三种控制模,通过比较实际值与设定值之间的差异来调整控制器的输出。PID控制分为位置和增量两种形位置PID控制是通过计算当前误差与历史误差之和,来调整控制器的输出。其公可简化为输出值= Kp × 误差 + Ki × 积分误差 + Kd × 微分误差,其中Kp、Ki和Kd为控制器的参数,分别表示比例增益、积分时间和微分时间。位置PID控制适用于稳态过程,能够快速响应并稳定系统,但容易产生超调和震荡。 增量式PID控制是根据当前误差的变化率来调整控制器的输出。其公为输出增量= Kp × (本次误差 - 上次误差) + Ki × 本次误差 + Kd × (本次误差 - 2×上次误差 + 上上次误差)。增量式PID控制通过控制信号的增量来实现控制过程,能够减少超调和震荡的产生,并且对于输出信号限幅的系统有较好的适应性。但是增量式PID控制相对复杂,并且对采样周期的选择要求较高。 总结而言,位置PID控制适用于稳态过程,具有快速响应和稳定性的特点,但容易产生超调和震荡;而增量式PID控制适用于具有输出信号限幅的系统,能够减少超调和震荡的产生,但相对复杂且对采样周期的选择要求较高。根据具体的控制需求和系统特性,可以选择适合的PID控制形

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小_扫地僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值