PID控制电机转速

PID控制电机转速转一个PID控制电机的小程序,被PID困扰好多天了,知道它的原理但是一直不明白如何将它运用到电机调速中间去,看了这个程序之后感觉茅塞顿开。原来也并不难^-^转载地址:呃,刚刚不小心把网页关掉了(大写的尴尬)。。。。#include#include#defineucharunsignedchar #defineuintunsignedint#define

大家好,又见面了,我是你们的朋友全栈君。

转一个PID控制电机的小程序, 被PID困扰好多天了, 知道它的原理但是一直不明白如何将它运用到电机调速中间去, 看了这个程序之后感觉茅塞顿开。原来也并不难^-^

转载地址:呃,刚刚不小心把网页关掉了(大写的尴尬)。。。。

#include<reg52.h>
#include<stdio.h>
#define uchar unsigned char 
#define uint unsigned int
#define THC0 0xf8
#define TLC0 0xcc   //2ms
unsigned char code Duan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};//共阴极数码管,0-9段码表
unsigned char Data_Buffer[8]={0,0,0,0,0,0,0,0};
unsigned char Data[4]={0,0,0,0};
unsigned char Arry[4]={0,0,0,0};
bit flag1=0;
bit flag0=0;
uchar i=0;
sbit AddSpeed=P1^1;
sbit SubSpeed=P1^2;
sbit PWM_FC=P1^0;
int e=0,e1=0,e2=0; //pid 偏差
float uk=0,uk1=0.0,duk=0.0; //pid输出值
float Kp=5,Ki=1.5,Kd=0.9; //pid控制系数 
//float Kp=10;
int out=0;
uint SpeedSet=1000;
uint cnt=0;
uint Inpluse=0,num=0; //脉冲计数
uint PWMTime=0; //脉冲宽度
unsigned char arry[];
void SendString(uint ch);
void PIDControl();
void SystemInit();
void delay(uchar x);
void PWMOUT();
void SetSpeed();
void SegRefre();
/**************主函数************/
void main()
{
 SystemInit();//系统初始化
 while(1)
 {
 SetSpeed();//设置速度
 SegRefre();//更新数码管显示
 PWMOUT();  //PWM输出
 if(flag0==1)
 {
 flag0=0;
 ES=0;//关串口中断,避免TI引起串口中断
 TI=1;//printf前TI置1
 printf("%f",(float)(num>>8));//脉冲计数
 printf("%f",(float)num);  //脉冲计数
 while(!TI);
 TI=0;    //TI软件清除
 ES=1;    //允许串口中断
 }
 }
}


//PID偏差控制计算
void PIDControl()        //pid偏差计算
{
 e=SpeedSet-num;//设置速度-实际速度,两者的差值 
 //对应于增量式PID的公式Δuk=uk-u(k-1)
// duk=(Kp*(e-e1))/100;//只调节P
 duk=(Kp*(e-e1)+Ki*e)/100;//只调节PI
// duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2))/100;//调节PID
 uk=uk1+duk;//uk=u(k-1)+Δuk
 out=(int)uk;//取整后输出
 if(out>250) //设置最大限制
 out=250;
 else if(out<0)//设置最小限制
 out=0;
 uk1=uk;   //为下一次增量做准备
 e2=e1;
 e1=e;
 PWMTime=out;  //out对应于PWM高电平的时间
}


//较短的延时。注意delay的值不要超过255
void delay(uchar x)
{
 uchar i,j;
 for(i=x;i>0;i--)
 for(j=50;j>0;j--);//接近500us
}


//PWM输出
void PWMOUT()
{
 if(cnt<PWMTime)//若小于PWM的设定时间,则输出高电平
 PWM_FC=1;
 else    //否则输出低电平
 PWM_FC=0;
 if(cnt>250)    //超过限制清零
 cnt=0;
}


//系统初始化
void SystemInit()
{
 TMOD=0X21;    //T1用于串口的波特率发生器 T0用于定时
 TH0=THC0;   //2ms定时,晶振频率是11.0592MHz,事先算好装入THC0中
 TL0=TLC0;   //2ms定时,晶振频率是11.0592MHz,实现算好装入TLCO中
 ET0=1;   //允许定时器0中断
 TR0=1;   //启动定时器0
 EX0=1;   //允许外部中断
 IT0=1;   //中断方式设置为下降沿
 //用于串口调试时用
 PCON=0x00;  //SMOD不加倍
 SCON=0x50;  //串口工作方式1,允许接收
 TH1=0xfd;  //波特率9600
 TL1=0xfd;  //波特率9600
 TR1=1;  //启动定时器1
 ES=1;  //开串口中断
 EA=1;  //开总中断
 e =0;  //PID的差值初值均为0
 e1=0;  
 e2=0;
}


//设置转速
void SetSpeed()
{
 if(AddSpeed==0)//按键
 {
 delay(200);//消抖
 if(AddSpeed==0)//再次判断按键
 {
 SpeedSet+=100;//速度增加
 if(SpeedSet>9999)//超限
 SpeedSet=9999;//设置为最大9999
 }
 }
 if(SubSpeed==0)//按键
 {
 delay(200);//消抖
 if(SubSpeed==0)//按键
 {
 SpeedSet-=100;//速度减
 if(SpeedSet<0)//低于速度的最小值 
 SpeedSet=0;//速度置零
 }
 }
}


//数码管显示更新
void SegRefre()   //显示刷新
{
 Data_Buffer[0]=SpeedSet/1000;
 Data_Buffer[1]=SpeedSet%1000/100;
 Data_Buffer[2]=SpeedSet%100/10;
 Data_Buffer[3]=SpeedSet%10;
 Data_Buffer[4]=num/1000;
 Data_Buffer[5]=num%1000/100;
 Data_Buffer[6]=num%100/10;
 Data_Buffer[7]=num%10;
}


//外部中断,统计脉冲数目,实际上是统计电机的转速
//在实际中,电机没有一根线引出来可以表示他的转速,只能通过传感器如霍尔传感器的方式测量转速
//脉冲一次下降沿对应于一次自增
void int0() interrupt 0
{
 Inpluse++;
}


//定时器T0操作
void t0() interrupt 1
{
 static unsigned char Bit=0;//静态变量,退出程序值保留
 static unsigned int time=0;
 static unsigned int aa=0;
 TH0=THC0;//重新赋初值
 TL0=TLC0;
 aa++;
 if(aa==50)//每100ms
 {
 aa=0;
 flag0=1;
 }
 cnt++; //pid 周期
 Bit++;
 time++;  //转速测量周期
 if(Bit>8) Bit=0;//数码管总共只有8位,超过8位则在程序上进行清零
 //数码管显示部分
 P0=0xff;
 P2=Duan[Data_Buffer[Bit]];
 switch(Bit)
 {
 case 0:P0=0X7F;break;
 case 1:P0=0XBF;break;
 case 2:P0=0XDF;break;
 case 3:P0=0XEF;break;
 case 4:P0=0XF7;break;
 case 5:P0=0XFB;break;
 case 6:P0=0XFD;break;
 case 7:P0=0XFE;break;
 }
 //数码管显示部分
 if(time>500)//每1s处理一次脉冲
 {
 time=0;
 num=Inpluse*15;//实际转速,*15是由电机决定的,电机的一个脉冲对应着电机转过了15转
 Inpluse=0;    //清零,为下一次计数做准备
 PIDControl();  //调用PID控制程序
 }
}


//串口中断,用于对串口的数据进行处理
void u_int(void) interrupt 4
{
 ES=0;//关串口中断,避免在数据处理的过程中造成影响
 if(RI)//若有RI==1,由RI产生中断
 {
 RI=0;//RI标志位必须通过软件进行清零
 arry[i]=SBUF;//将字符赋到arry中
 i++;  //下一个数
 if(i>3)  //接收完了指令,进行数据处理,一共有arry[0],arry[1],arry[2],arry[3]四个数据
 {
 flag1=1;//将标志置1
 i=0; //i清零,重新计数
 }
 if(flag1) //若flag1==1
 {
 flag1=0;//flag1清零
 SpeedSet=(arry[0]-'0')+(arry[1]-'0')*10+(arry[2]-'0')*100+(arry[3]-'0')*1000;//获得的速度值
 //由于串口发送的是字符,所以要减去'0'
 }
 }
 else//对应的由TI产生中断
 TI=0;//TI由软件进行清零 
 ES=1;//处理完后再开中断
}



#include<reg52.h>
#include<stdio.h>
#define uchar unsigned char 
#define uint unsigned int
#define THC0 0xf8
#define TLC0 0xcc   //2ms
unsigned char code Duan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};//共阴极数码管,0-9段码表
unsigned char Data_Buffer[8]={0,0,0,0,0,0,0,0};
unsigned char Data[4]={0,0,0,0};
unsigned char Arry[4]={0,0,0,0};
bit flag1=0;
bit flag0=0;
uchar i=0;
sbit AddSpeed=P1^1;
sbit SubSpeed=P1^2;
sbit PWM_FC=P1^0;
int e=0,e1=0,e2=0; //pid 偏差
float uk=0,uk1=0.0,duk=0.0; //pid输出值
float Kp=5,Ki=1.5,Kd=0.9; //pid控制系数
//float Kp=10;
int out=0;
uint SpeedSet=1000;
uint cnt=0;
uint Inpluse=0,num=0; //脉冲计数
uint PWMTime=0; //脉冲宽度
unsigned char arry[];
void SendString(uint ch);
void PIDControl();
void SystemInit();
void delay(uchar x);
void PWMOUT();
void SetSpeed();
void SegRefre();
/**************主函数************/
void main()
{

SystemInit();//系统初始化
while(1)
{

SetSpeed();//设置速度
SegRefre();//更新数码管显示
PWMOUT();  //PWM输出
if(flag0==1)
{

flag0=0;
ES=0;//关串口中断,避免TI引起串口中断
TI=1;//printf前TI置1
printf(“%f”,(float)(num>>8));//脉冲计数
printf(“%f”,(float)num); //脉冲计数
while(!TI);
TI=0;    //TI软件清除
ES=1;    //允许串口中断
}
}
}

//PID偏差控制计算
void PIDControl()        //pid偏差计算
{

e=SpeedSet-num;//设置速度-实际速度,两者的差值 
//对应于增量式PID的公式Δuk=uk-u(k-1)
// duk=(Kp*(e-e1))/100;//只调节P
duk=(Kp*(e-e1)+Ki*e)/100;//只调节PI
// duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2))/100;//调节PID
uk=uk1+duk;//uk=u(k-1)+Δuk
out=(int)uk;//取整后输出
if(out>250) //设置最大限制
out=250;
else if(out<0)//设置最小限制
out=0;
uk1=uk;   //为下一次增量做准备
e2=e1;
e1=e;
PWMTime=out;  //out对应于PWM高电平的时间
}

//较短的延时。注意delay的值不要超过255
void delay(uchar x)
{

uchar i,j;
for(i=x;i>0;i–)
for(j=50;j>0;j–);//接近500us
}

//PWM输出
void PWMOUT()
{

if(cnt<PWMTime)//若小于PWM的设定时间,则输出高电平
PWM_FC=1;
else    //否则输出低电平
PWM_FC=0;
if(cnt>250)    //超过限制清零
cnt=0;
}

//系统初始化
void SystemInit()
{

TMOD=0X21;    //T1用于串口的波特率发生器 T0用于定时
TH0=THC0;   //2ms定时,晶振频率是11.0592MHz,事先算好装入THC0中
TL0=TLC0;   //2ms定时,晶振频率是11.0592MHz,实现算好装入TLCO中
ET0=1;   //允许定时器0中断
TR0=1;   //启动定时器0
EX0=1;   //允许外部中断
IT0=1;   //中断方式设置为下降沿
//用于串口调试时用
PCON=0x00; //SMOD不加倍
SCON=0x50; //串口工作方式1,允许接收
TH1=0xfd; //波特率9600
TL1=0xfd; //波特率9600
TR1=1; //启动定时器1
ES=1; //开串口中断
EA=1; //开总中断
e =0; //PID的差值初值均为0
e1=0;  
e2=0;
}

//设置转速
void SetSpeed()
{

if(AddSpeed==0)//按键
{

delay(200);//消抖
if(AddSpeed==0)//再次判断按键
{

SpeedSet+=100;//速度增加
if(SpeedSet>9999)//超限
SpeedSet=9999;//设置为最大9999
}
}
if(SubSpeed==0)//按键
{

delay(200);//消抖
if(SubSpeed==0)//按键
{

SpeedSet-=100;//速度减
if(SpeedSet<0)//低于速度的最小值 
SpeedSet=0;//速度置零
}
}
}

//数码管显示更新
void SegRefre()   //显示刷新
{

Data_Buffer[0]=SpeedSet/1000;
Data_Buffer[1]=SpeedSet%1000/100;
Data_Buffer[2]=SpeedSet%100/10;
Data_Buffer[3]=SpeedSet%10;
Data_Buffer[4]=num/1000;
Data_Buffer[5]=num%1000/100;
Data_Buffer[6]=num%100/10;
Data_Buffer[7]=num%10;
}

//外部中断,统计脉冲数目,实际上是统计电机的转速
//在实际中,电机没有一根线引出来可以表示他的转速,只能通过传感器如霍尔传感器的方式测量转速
//脉冲一次下降沿对应于一次自增
void int0() interrupt 0
{

Inpluse++;
}

//定时器T0操作
void t0() interrupt 1
{

static unsigned char Bit=0;//静态变量,退出程序值保留
static unsigned int time=0;
static unsigned int aa=0;
TH0=THC0;//重新赋初值
TL0=TLC0;
aa++;
if(aa==50)//每100ms
{

aa=0;
flag0=1;
}
cnt++; //pid 周期
Bit++;
time++;  //转速测量周期
if(Bit>8) Bit=0;//数码管总共只有8位,超过8位则在程序上进行清零
//数码管显示部分
P0=0xff;
P2=Duan[Data_Buffer[Bit]];
switch(Bit)
{

case 0:P0=0X7F;break;
case 1:P0=0XBF;break;
case 2:P0=0XDF;break;
case 3:P0=0XEF;break;
case 4:P0=0XF7;break;
case 5:P0=0XFB;break;
case 6:P0=0XFD;break;
case 7:P0=0XFE;break;
}
//数码管显示部分
if(time>500)//每1s处理一次脉冲
{

time=0;
num=Inpluse*15;//实际转速,*15是由电机决定的,电机的一个脉冲对应着电机转过了15转
Inpluse=0;    //清零,为下一次计数做准备
PIDControl();  //调用PID控制程序
}
}

//串口中断,用于对串口的数据进行处理
void u_int(void) interrupt 4
{

ES=0;//关串口中断,避免在数据处理的过程中造成影响
if(RI)//若有RI==1,由RI产生中断
{

RI=0;//RI标志位必须通过软件进行清零
arry[i]=SBUF;//将字符赋到arry中
i++; //下一个数
if(i>3) //接收完了指令,进行数据处理,一共有arry[0],arry[1],arry[2],arry[3]四个数据
{

flag1=1;//将标志置1
i=0; //i清零,重新计数
}
if(flag1) //若flag1==1
{

flag1=0;//flag1清零
SpeedSet=(arry[0]-‘0’)+(arry[1]-‘0’)*10+(arry[2]-‘0’)*100+(arry[3]-‘0’)*1000;//获得的速度值
//由于串口发送的是字符,所以要减去’0′
}
}
else//对应的由TI产生中断
TI=0;//TI由软件进行清零
ES=1;//处理完后再开中断
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137777.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号