标题:
51单片机PID算法控制直流电机转速
[打印本页]
作者:
教教我把
时间:
2024-12-18 16:12
标题:
51单片机PID算法控制直流电机转速
这是一个论坛老哥(
ID:171036
)20年写的有一部分我不会调了,我想让电机的实际转速与目标转速一致
新建文件夹.zip
(41.75 KB, 下载次数: 0)
2024-12-18 16:10 上传
点击文件名下载附件
程序与原理图
111.png
(105.64 KB, 下载次数: 0)
下载附件
2024-12-18 16:09 上传
/*PID的参数设置可以参照以下来进行:
参数整定找最佳,从小到大顺序查;
先是比例后积分,最后再把微分加;
曲线振荡很频繁,比例度盘要放大;
曲线漂浮绕大湾,比例度盘往小扳;
曲线偏离回复慢,积分时间往下降;
曲线波动周期长,积分时间再加长;
曲线振荡频率快,先把微分降下来;
动差大来波动慢。微分时间应加长;
理想曲线两个波,前高后低4比1 ;
一看二调多分析,调节质量不会低;
*/
#include <reg51.h>
#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
sbit qidong=P1^3;//启动键
sbit tingzhi=P1^4;//停止键
sbit fangxiang=P1^5;//转向键
sbit AddSpeed=P1^6;//加速键
sbit SubSpeed=P1^7;//减速键
sbit RS = P2^5;//LCD1602数据命令选择端口
sbit RW = P2^6;//LCD1602读写选择端口
sbit EN = P2^7;//LCD1602使能端口
sbit IN2=P3^3;//L298输入端2
sbit IN1=P3^4;//L298输入端1
sbit PWM_FC=P3^5;//L298使能端口
uchar aa[]={'T','a','r','g','e','t',' ',' ',' ',' ',' ','r','/','s','e','c'};//目标转速:Target r/sec
uchar cc[]={'A','c','t','u','a','l',' ',' ',' ',' ',' ','r','/','s','e','c'};//实测转速: Actual r/sec
uchar displayflag;//显示标志位变量
uint SetSpeed=3000;//声明设定速度变量
uint ActualSpeed=0;//声明实际速度变量
int e ,e1 ,e2 ;//声明当前偏差值变量、之后偏差值变量、再后偏差值变量
int out=0;//PID调节后输出偏差值变量
uint cnt=0;//定时器1中断次数变量
uint Inpluse=0;//声明脉冲计数变量、
uint PWMTime=100;//声明脉冲宽度时间变量
float uk ,uk1 ,duk ;//声明目前总偏差值变量、之后偏差值总变量、偏差值总变量
float Kp=0.36,Ki=0.05,Kd=0.016;//pid控制系数p=0.1,i=0.05,d=0.016。
void delay(uchar x)
{
uint i,j;
for(i=x;i>0;i--)
for(j=50;j>0;j--);
}
void DelayUs2x(unsigned char t)
{
while(--t);
}
void DelayMs(unsigned char t)
{
while(t--)
{
DelayUs2x(245);
DelayUs2x(245);
}
}
void write_com(uchar com)//写命令
{
RS=0;
RW=0;
P0=com;
DelayMs(5);
EN=1;
DelayMs(5);
EN=0;
}
void write_data(uchar date)//写一个字符
{
RS=1;
RW=0;
P0=date;
DelayMs(5);
EN=1;
DelayMs(5);
EN=0;
}
void init()//初始化
{
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
}
void LCD_Write_String(uchar x,uchar y,uchar *s)//写字符串
{
if (y == 0)
{
write_com(0x80 + x);
}
else
{
write_com(0xC0 + x);
}
while (*s)
{
write_data( *s);
s++;
}
}
void PIDControl()//pid偏差计算
{
e=SetSpeed-ActualSpeed;//计算当前偏差值变量
duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2));//PID连续系统离散化增量型PID算法,算出总偏差值变量。
uk=duk+uk1;//计算偏差值总变量加上之后偏差值总变量之和赋给目前总偏差值变量
out=(int)uk;//强制类型转化为整数型的目前总偏差值变量赋给PID调节后输出偏差值变量
if(out>1000)//判断PID调节后输出偏差值变量是否大于100
{
out=1000;//PID调节后输出偏差值变量为100
}
else if(out<0)//判断PID调节后输出偏差值变量是否小于0
{
out=0;//PID调节后输出偏差值变量为0
}
uk1=uk;//目前总偏差值变量赋给之后偏差值总变量
e2=e1;//之前偏差值变量赋给之后偏差值变量
e1=e;//当前偏差值变量赋给之前偏差值变量
PWMTime=out;//PID调节后输出偏差值变量赋给脉冲宽度时间变量
}
void PWMOUT()
{
if(cnt<PWMTime)//判断定时器1中断次数变量是否小于脉冲宽度时间变量
{
PWM_FC=1;//脉冲宽度输入端口输出高电平
}
else
{
PWM_FC=0;//脉冲宽度输入端口输出低电平
}
if(cnt>1000)//判断定时器1中断次数变量是否大于100
cnt=0;//定时器1中断次数变量归0
}
void SystemInit()//定时器0定时器1外部中断0初始化函数
{
TMOD=0X21;//定时器0方式1,定时器1方式2。
TH0=0xf8;//初装定时器0高八位寄存器定时数值
TL0=0x50 ;//初装定时器0低八位寄存器定时数值,即2毫秒。
TH1=0xC0;//初装定时器1高八位寄存器定时数值
TL1=0XC0;//初装定时器1低八位寄存器定时数值,即16毫秒。
EA=1;//开总中断
EX0=1;//开外部中断0
IT0=1;//外部中断0下降沿触发
ET0=1;//开定时器0中断允许
ET1=1;//开定时器1中断允许
TR0=1;//开定时器0中断
TR1=1;//开定时器1中断
e =0;//偏差值变量为0
e1=0;//之后偏差值变量为0
e2=0;//再后偏差值变量为0
IN1=1;
IN2=1;
}
void SpeedSet()//设定速度函数
{
if(qidong==0)
{
delay(5);
if(qidong==0)
{
IN1=0;
IN2=1;
while(qidong==1);
}
}
if(tingzhi==0)
{
delay(5);
if(tingzhi==0)
{
IN1=1;
IN2=1;
EN=1;
while(tingzhi==1);
}
}
if(fangxiang==0)
{
delay(5);
if(fangxiang==0)
{
IN1=~IN1;
IN2=~IN2;
while(fangxiang==1);
}
}
if(AddSpeed==0)//判断加速键是否按下
{
delay(5);//延时
if(AddSpeed==0)//再次判断加速键是否按下
{
SetSpeed+=100;//设定速度变量每次加100
if(SetSpeed>3500)//判断设定速度变量是否大于3500
{
SetSpeed=3500;//设定速度变量归为3500
}
}
}
if(SubSpeed==0)//判断减速键是否按下
{
delay(5);//延时
if(SubSpeed==0)//再次判断减速键是否按下
{
SetSpeed-=100;//设定速度变量每次减100
if(SetSpeed<0)//判断设定速度变量是否小于0
SetSpeed=0;//设定速度变量归0
}
}
aa[7]=SetSpeed/1000+'0';
aa[8]=SetSpeed/100%10+'0';
aa[9]=SetSpeed/10%10+'0';
aa[10]=SetSpeed%10+'0';
LCD_Write_String(0,0,aa);
}
/**************主函数************/
void main()
{
SystemInit();
init();
LCD_Write_String(0,0,aa);
displayflag=1;
while(1)
{
SpeedSet();
if(displayflag==1)
{
displayflag=0;
cc[7]=ActualSpeed/1000+'0';
cc[8]=ActualSpeed/100%10+'0';
cc[9]=ActualSpeed/10%10+'0';
cc[10]=ActualSpeed%10+'0';
LCD_Write_String(0,1,cc);
}
}
}
void int0() interrupt 0//外部中断0函数
{
Inpluse++;//脉冲计数变量加加
}
void Timer0() interrupt 1//定时器0中断服务函数
{
static uint time=0;//转速测量周期变量
TH0=0xf8;//重装定时器0高八位寄存器计数值
TL0=0x50 ;//重装定时器0低八位寄存器计数值,即2毫秒。
time++;//转速测量周期变量加加
if(time>500)//判断转速测量周期变量是否大于500,等于500就是500x2毫秒=1000毫秒,也就是1s。
{
time=0;//转速测量周期变量归0
displayflag=1;//显示标志位变量置1
ActualSpeed=Inpluse;//脉冲计数变量表示实际速度变量
Inpluse=0;//脉冲计数变量归0
PIDControl();//PID控制函数
}
PWMOUT();
}
void Timer1() interrupt 3//定时器1中断服务函数
{
cnt++;//定时器1中断次数变量
}
作者:
人工置顶员
时间:
2024-12-22 17:02
顶一下
作者:
xiaobendan001
时间:
2024-12-22 18:38
我测试了一些,是单管控制,过冲比较严重,搞很久,最后就是把e限制了值,我编码器60脉冲,只有一个信号,0.1秒一个测量周期,输出锁定在+—10转,除了因为限制e的值后使提速变的比较缓慢以外,倒是可以实现空载时锁定速度,200-3000之间都可以,目前没有测试加载情况。因为电流还要控制,目前没有好的限流方案用啊。
作者:
jovew
时间:
2024-12-22 19:14
怎么可以调节 步进电机的步数?
作者:
TTQ001
时间:
2024-12-23 01:16
PID 速度控制的目的不是使实际输出速度等于目标速度吗? 如果程序正确,则可能是由于比例,积分和微分系数缺乏调整。
作者:
Y_G_G
时间:
2024-12-23 09:17
1,你这个电机控制并不需要D项,PI控制就足够了
2,I项需要限幅
3,过流控制不过是程序流程上的需要而已,在这个电路中意义不大
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1