系列文章目录
PID简介
PID控制(Proportional Integral Derivative Control)是一种常见的控制算法,通常用于控制电机等设备的位置、速度和加速度等参数。PID控制的目标是使电机的输出值尽量接近设定值,同时尽可能快地响应变化。
PID控制算法将目标值与实际输出值进行比较,计算出误差,并利用比例、积分和微分三个参数进行调整,从而使误差尽量小。其中比例控制用于根据误差大小来调整输出值;积分控制用于消除稳态误差;微分控制则用于预测误差的变化趋势,使输出值更加稳定。
PID控制电机可以通过不断地检测电机的输出值和目标值之间的误差来实现自动校正。
一、硬件方案
硬件构成:电源5V供电、51单片机最小系统、AB相霍尔传感器模块、电源12V供电、JGB520电机、L298n电机驱动模块。
二、设计功能
1.本设计基于STC89C51/52(与AT89S51/52、AT89C51/52通用)单片机。
2.采用AB相霍尔传感器非接触式测电机转速。
3.本文采用增量式PID控制电机转速。
三、程序源码
代码如下(示例):
#include <intrins.h>
#include <reg52.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
// 定义引脚
sbit ENA = P2^6;
sbit ENB = P2^1;
sbit A_MOTOR_A = P3^3; // 左电机A相编码器
sbit A_MOTOR_B = P0^7; // 左电机B相编码器
sbit B_MOTOR_A = P3^2; // 右电机A相编码器
sbit B_MOTOR_B = P0^6; // 右电机B相编码器
int speedA = 0;
int speedB = 0;
int currentCountA=0;
int currentCountB=0;
uint tuint = 65535;
uint tpwm = 1; //pwm周期为10000us tpwm变量表示pwm高电平时间,也相当于占空比 (仿真时,频率高时,电机反应慢。在实物上要加大频率)
uint A_B_flag=0;
uint flagSpeed=0;
uint GetSpeed_flag=0;
//************************ PID *************************************
float err_now = 0,last_err = 0,last_last_err = 0; //本次采样值,上次采样值,上上次采样值
float targetSpeed=11;
float currentSpeed=0;
float I = 0;
float kp=300;
//float kp=250;
float ki=0.5;
float kd=0;
//数据处理函数
float getfabs(float a)
{
if(a>=0) return a;
else return -a;
}
// 初始化外部中断0用于左电机霍尔编码器
void initExtInt0()
{
IT0 = 1; // 电平下降沿触发
EX0 = 1; // 开启INT0中断
EA = 1; // 开总中断
}
外部中断0中断服务程序
void extInt0_ISR(void) interrupt 0
{
if (A_MOTOR_A==0 && A_MOTOR_B==1) //A相为高电平/B相为低电平 //正转
currentCountA++;
if (A_MOTOR_A==1 && A_MOTOR_B==0) //A相为低电平/B相为高电平 //反转
currentCountA--;
}
// 初始化外部中断1用于左电机霍尔编码器
void initExtInt1()
{
IT1 = 1; // 电平下降沿触发
EX1 = 1; // 开启INT0中断
EA = 1; // 开总中断
}
外部中断1中断服务程序
void extInt1_ISR(void) interrupt 2
{
if (B_MOTOR_A==0 && B_MOTOR_B==1) //A相为高电平/B相为低电平 //正转
currentCountB++;
if (B_MOTOR_A==1 && B_MOTOR_B==0) //A相为低电平/B相为高电平 //反转
currentCountB--;
}
//定时器初始化函数
void Time0_Init()
{
//定时器0,1工作方式1
TMOD=0x11; //定时或者计数模式控制寄存器
ET0 = 1; //打开定时器中断允许
EA = 1; //打开总中断
TR0 = 1; //打开定时器
}
void GetSpeed()
{
//求速度
if(flagSpeed==0)
{
flagSpeed=1;
currentSpeed = 12000*getfabs(currentCountA/0.1);//10us更新一次速度//只认大小不看方向
currentCountA =0;//重新计数
}
else
{
flagSpeed=0;
currentSpeed = 12000*getfabs(currentCountB/0.1);//20us更新一次速度 330/60/1000/10x13=100us的脉冲数
currentCountB =0;//重新计数
}
}
//************************************************************************
/* PID 口诀
参数整定找最佳,从小到大顺序查
先是比例后积分,最后再把微分加
曲线振荡很频繁,比例度盘要放大
曲线漂浮绕大湾,比例度盘往小扳
曲线偏离回复慢,积分时间往下降
曲线波动周期长,积分时间再加长
曲线振荡频率快,先把微分降下来
动差大来波动慢。微分时间应加长
理想曲线两个波,前高后低4比1
一看二调多分析,调节质量不会低
*/
// 电机闭环调速
int PID() //增量式PID
{
int change=0;
err_now = targetSpeed - currentSpeed;//当前误差
if(targetSpeed >= currentSpeed)
{
if(targetSpeed - currentSpeed > 3)
change = kp*(err_now - last_err) + I + kd*(err_now - 2*last_err + last_last_err);
else
change = 0.2*kp*(err_now - last_err) + 0.5*I + kd*(err_now - 2*last_err + last_last_err);
}
else if(currentSpeed > targetSpeed)
{
if(currentSpeed - targetSpeed > 3)
change = kp*(err_now - last_err) +I + kd*(err_now - 2*last_err + last_last_err);
else
change = 0.2*kp*(err_now - last_err) + 0.5*I + kd*(err_now - 2*last_err + last_last_err);
}
// 存储上一次偏差
last_last_err = last_err;//上上时刻误差
last_err = err_now; //上时刻误差
//积分限幅
I = I + ki*err_now*10;
if(I >=100 )
I = 100;
if(I <=-100 )
I = -100;
return (change);
}
//产生PWM
void timer0() interrupt 1
{
if(flagSpeed==0)//flagSpeed==0 //A更新完速度
{
ENA = 1;
ENB = 0;
GetSpeed();
TH0 = (tuint - tpwm + 1)/256;
TL0 = (tuint - tpwm + 1)%256;
}
else //flagSpeed==0 //B更新完速度
{
ENA = 0;
ENB = 1;
GetSpeed();
TH0 = (tuint - tpwm + 1)/256;
TL0 = (tuint - tpwm + 1)%256;
}
tpwm = tpwm + PID(); //增量式PID
}
void Init_Ext_Time()
{
initExtInt0(); // 初始化外部中断0
initExtInt1(); // 初始化外部中断1
Time0_Init();
TH0 = TL0 = 255;
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了PID增量式的使用,所有程序代码均在中断中完成更新,若需要在main中更新,请自行更改。
需要完整的资料可扫描以下二维码回复[51增量PID]