您好,欢迎来到意榕旅游网。
搜索
您的当前位置:首页基于PID算法的恒温控制系统设计

基于PID算法的恒温控制系统设计

来源:意榕旅游网


课程设计说明书

题目:基于PID算法的

恒温控制系统设计

学号: 姓名: 指导教师:

日期:

目录

一、设计题目 ................................................................................................................... 1 二、设计要求 ................................................................................................................... 1 三、设计思路 ................................................................................................................... 1 四、实验设备 ................................................................................................................... 1 五、硬件介绍 ................................................................................................................... 1 六、硬件接线图 ............................................................................................................... 3 七、软件流程图、 ........................................................................................................... 4 八、PID参数确定 ........................................................................................................... 5 九、实验总结 ................................................................................................................... 6 附件:实验程序 ............................................................................................................... 7

一、设计题目

基于PID算法的恒温控制系统设计

二、设计要求

1. 利用DS18B20采集温度并显示;

2. 利用单片机I/O管角输出PWM控制功率电阻发热; 3. 基于PID算法实现恒温控制。

三、设计思路

本设计要求实时采集温度并实现恒温控制,根据设计要求,本次设计拟采用ATC52单片机作为控制芯片,采集部分使用DS18B20温度传感器,显示部分采用数码管显示实时温度,功率电阻作为控制对象。在PID算法的基础上完成恒温控制系统的设计。

四、 实验设备

单片机开发试验仪1台 ATC52单片机芯片1个 DS18B20温度传感器1个 C9013三极管1个 1W功率二极管1个

五、 硬件介绍 DS18B20:

DS18B20是常用的温度传感器,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。

DS18B20的主要特征:

全数字温度转换及输出。 先进的单总线数据通信。

最高12位分辨率,精度可达土0.5摄氏度。

1

12位分辨率时的最大工作周期为750毫秒。 可选择寄生工作方式。

检测温度范围为–55°C ~+125°C (–67°F ~+257°F)

内置EEPROM,限温报警功能。

位光刻ROM,内置产品序列号,方便多机挂接。

多样封装形式,适应不同硬件系统。

DS18B20数据采集过程

⑴ GND 地信号

⑵ DQ 数据输入/输出引脚。开漏单总线接口引脚。当被用着在寄生电源下,也可以向器件提供电源。

⑶ VDD 可选择的VDD引脚。当工作于寄生电源时,此引脚必须接地。

由于DS18B20采用的是1-Wire总线协议方式,即在一根数据线实现数据的双向传输,而对ATS51单片机来说,硬件上并不支持单总线协议,因此,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问。

由于DS18B20是在一根I/O线上读写数据,因此,对读写的数据位有着严格的时序要求。DS18B20有严格的通信协议来保证各位数据传输的正确性和完整性。该协议定义了几种信号的时序:初始化时序、读时序、写时序。所有时序都是将主机作为主设备,单总线器件作为从设备。而每一次命令和数据的传输都是从主机主动启动写时序开始,如果要求单总线器件回送数据,在进行写命令后,主机需启动读时序完成数据接收。数据和命令的传输都是低位在先。

2

六、 硬件接线图

八位七段码显示 P0

P1

1W C9013 P2.0

P3.7 DS18B20

恒温控制系统硬件连接图

硬件部分分别也P0口和P1口作为八位七段码的段选和位选的控制端口,DS18B20连接至P3.1口作为控制线,P2.0口作为PWM波输出口,经过C9013信号放大后接功率电阻作为被控对象。通过温度传感器采集电阻上的温度反馈给系统,构成闭环调节系统,构成恒温控制系统。

ATC52

3

七、 软件流程图、

开始 计算采样初值 设初值e(k-1)=e(k-2)=0 被 控 对 象 本次采样输入c(k) A/D 计算偏差量e(k)=r(k)-c(k) 计算控制增量Δu(k) Δu(k)=q0e(k)-q1e(k-1)+q2e(k-2) D/A 输出Δu(k) 为下一时刻做准备 e(k)=e(k-1),e(k-1)=e(k-2) 采样时刻到否 PID算法流程图 4

软件部分采用PID算法,其中数据采集与显示部分不属于本设计的主要部分在此不再赘述。PID算法的参数有实验确定,遵循先比例,后积分,再微分的原则,从最小看是逐步调试,最终取得最佳的PID参数。

八、PID参数确定

PID算法运用比例、积分、微分算法,来对回路中的偏差进行修正,通过执行器调节参数,使测量值稳定在设定值附近,,达到控制某一参数的目的。

必须先明白P,I,D各自的含义及控制规律

比例P:比例项部分其实就是对预设值和反馈值差值的发大倍数。

积分I:顾名思义,积分项部分其实就是对预设值和反馈值之间的差值在时间上进行累加。当差值不是很大时,为了不引起振荡。可以先让电机按原转速继续运行。当时要将这个差值用积分项累加。当这个和累加到一定值时,再一次性进行处理。从而避免了振荡现象的发生。可见,积分项的调节存在明显的滞后。而且I值越大,滞后效果越明显。

微分D:微分项部分其实就是求电机转速的变化率。也就是前后两次差值的差而已。也就是说,微分项是根据差值变化的速率,提前给出一个相应的调节动作。可见微分项的调节是超前的。并且D值越大,超前作用越明显。可以在一定程度上缓冲振荡。比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调

其次进行参数调整 a.确定比例增益P

确定比例增益P 时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0,PID为纯比例调节。输入设定为系统允许的最大值的60%~70%,由0逐渐加大比例增益P,直至系统出现振荡;再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的60%~70%。比例增益P调试完成。

b.确定积分时间常数Ti

比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至

5

系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。

c.确定积分时间常数Td

积分时间常数Td一般不用设定,为0即可。若要设定,与确定 P和Ti的方法相同,取不振荡时的30%。

九、 实验总结

本次课程设计的题目为基于PID算法的恒温控制系统。在接到这个题目的同时,我们就开始思考如何去设计这个系统。首先是硬件电路的设计与器件的选择。其中温度采集与显示部分的设计,我们在单片机课程上已经有所涉及,在这里我们就可以直接拿来用了。硬件部分需要我们设计的就是被控对象。为了试验方便我们选择了1W的功率电阻作为被控对象,然后通过DS18B20温度传感器去测量电阻附近的温度变化。从而实现系统的控制与反馈。考虑到单片机I/O口的驱动能力不足,不足以驱动功率二极管,我们在I/O口与功率二极管之间串入一个C9013最为驱动与开关器件,用以对二极管的驱动和控制。

软件部分采用了PID控制算法,其中最重要的环节为PID控制参数的确定,一开始我们准备利用计算法来确定,可是在实施的过程终发现,计算法所需要的原始数据难以确定,而且在实际测量中也有很大的不确定性。最终,经过我们的分析研究,得出实验法确定参数是一个比较切实可行的办法。然后就通过实验一步一步测试出所需要的参数,经过我们几天的而艰苦奋战,终于测试好了参数。最后在进行系统的整体调试,最终完成这次课程设计,并顺利通过老师的验收。

这次的课程设计,让我有了很多的收获和感受。首先,让我认识到了,在学习的过程中仅仅有理论知识是不足的,必须要理论与实践相结合起来。有的东西看起来非常简单,可是到了实现环节,就会变得异常困难。在实现的过程中可能会出现各种各样的状况,需要在现场分析解决。在最任何事情的时候都必须认真细致,有时一个小小的环节出现问题,都会导致结果出现巨大的差异。在最任何是的过程中遇到困难都是正常的,我们需要做的就是去面对它,通过自己的分析理解,最终找到解决方法,克服困难。

6

附件:实验程序

/*******************************************************************************

*

* 微机控制课程设计

-------------------------------------------------------------------------------- * 实 验 名 : 基于PID算法的恒温控制系统设计

* 实验说明 : 通过DS18B20采集温度,反馈给系统,构成闭环调节系统 * 连接方式 : 见连接图 * 注 意 :

*******************************************************************************/

#include #include\"temp.h\"

//--定义使用的IO--// #define GPIO_DIG P0

#define GPIO_PLACE P1

sbit PWM=P2^0;

//--定义全局变量

unsigned char code DIG_PLACE[8] = {

0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位选控制 查表的方法控制 unsigned char code DIG_CODE[17] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码 unsigned char DisplayData[8]; unsigned char timer1; unsigned int count=0;

//用来存放要显示的8位数的值

//定义PID参数

float uk,uk1,uk2; float ek,ek1,ek2; float x; float t,Auk;

7

float Kp,Ki,Kd;

//--声明全局函数--// float LcdDisplay(int); void DigDisplay(); void Time1Config(); float AD_init(void); void PID_init(void); void DA_init(void); //void delay(); unsigned int j,m; unsigned int d;

/*******************************************************************************

* 函 数 名 : main * 函数功能 : 主函数 * 输 入 : 无 * 输 出 : 无

*******************************************************************************/

main() { int m; m=Auk/10;

Time1Config(); Ds18b20Init();

uk=0,uk1=0,uk2=0; ek=0,ek1=0;ek2=0; x=40; Kp=2.83,Ki=0.59,Kd=5.41; while(1) { AD_init(); PID_init(); DA_init();

LcdDisplay(Ds18b20ReadTemp()); //delay(); } }

8

float AD_init() { float temp; temp=LcdDisplay(Ds18b20ReadTemp()); return(temp); }

void PID_init() { t=AD_init(); ek=x-t; Auk=Kp*(ek-ek1); if(x>=35&x<=45) { Auk=Auk+Ki*ek; } Auk=Auk+Kd*(ek-2*ek1+ek2); uk=Auk+uk; ek2=ek1; ek1=ek; }

void DA_init() { if(Auk>=0) count++; if(Auk<0) PWM=0; if(count==0|count==99) count=count ; if(Auk>=5) count=count+5; if(Auk<5) switch(m%10) { case 0:count=count; break; case 1:count++;break; case 2:count=count+2;break; case 3:count=count+3;break; case 4:count=count+4;break; default:break; }

9

count--; if(timer1>100) //PWM周期为100*0.5ms { timer1=0; } if(timer1 < count) //改变30这个值可以改变直流电机的速度 { PWM=1; } else { PWM=0; } }

/*******************************************************************************

* 函 数 名 : LcdDisplay() * 函数功能 : LCD显示读取到的温度 * 输 入 : v * 输 出 : 无

*******************************************************************************/

/*void delay() { { for(j=0;j<1000;j++) { for(m=0;m<100;m++) { ; } } } } */

float LcdDisplay(int temp) //lcd显示 {

float tp; if(temp< 0) //当温度值为负数 { DisplayData[0] = 0x40; //因为读取的温度是实际温度的补码,所以减1,再取反求出原码

10

temp=temp-1; temp=~temp; tp=temp; temp=tp*0.0625*100+0.5; //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点 //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就 //算由?.5,还是在小数点后面。 } else { DisplayData[0] = 0x00; tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量 //如果温度是正的那么,那么正数的原码就是补码它本身 temp=tp*0.0625*100+0.5; //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点 //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就 //算加上0.5,还是在小数点后面。 } DisplayData[1] = DIG_CODE[temp / 10000]; DisplayData[2] = DIG_CODE[temp % 10000 / 1000]; DisplayData[3] = DIG_CODE[temp % 1000 / 100] | 0x80; DisplayData[4] = DIG_CODE[temp % 100 / 10]; DisplayData[5] = DIG_CODE[temp % 10]; DigDisplay(); return(temp); //扫描显示 }

/*******************************************************************************

* 函 数 名 : DigDisplay * 函数功能 : 使用数码管显示 * 输 入 : 无 * 输 出 : 无

*******************************************************************************/

void DigDisplay() { unsigned char i; unsigned int j;

11

for(i=0; i<8; i++) { GPIO_PLACE = DIG_PLACE[i]; //发送位选 GPIO_DIG = DisplayData[i]; //发送段码 j = 10; //扫描间隔时间设定 while(j--); GPIO_DIG = 0x00;//消隐 } }

void Time1Config() { TMOD|= 0x10; //设置定时计数器工作方式1为定时器 //--定时器赋初始值,12MHZ下定时0.5ms--// TH1 = 0xFE; TL1 = 0x0C; ET1 = 1; //开启定时器1中断 EA = 1; TR1 = 1; //开启定时器 }

/*******************************************************************************

* 函 数 名 : Time1 * 函数功能 : 定时器1的中断函数 * 输 入 : 无 * 输 出 : 无

*******************************************************************************/

void Time1(void) interrupt 3 //3 为定时器1的中断号 1 定时器0的中断号 0 外部中断1 2 外部中断2 4 串口中断 { TH1 = 0xFE; //重新赋初值 TL1 = 0x0C; timer1++; }

12

temp.c

#include\"temp.h\"

/*******************************************************************************

* 函 数 名 : Delay1ms * 函数功能 : 延时函数 * 输 入 : 无 * 输 出 : 无

*******************************************************************************/

void Delay1ms(uint y) { uint x; for( ; y>0; y--) { for(x=110; x>0; x--); } }

/*******************************************************************************

* 函 数 名 : Ds18b20Init * 函数功能 : 初始化 * 输 入 : 无

* 输 出 : 初始化成功返回1,失败返回0

*******************************************************************************/

uchar Ds18b20Init() { uchar i; DSPORT = 0; //将总线拉低480us~960us i = 70; while(i--);//延时2us DSPORT = 1; //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低 i = 0; while(DSPORT) //等待DS18B20拉低总线 { Delay1ms(1); i++; if(i>5)//等待>5MS { return 0;//初始化失败

13

} } return 1;//初始化成功 }

/*******************************************************************************

* 函 数 名 : Ds18b20WriteByte * 函数功能 : 向18B20写入一个字节 * 输 入 : com * 输 出 : 无

*******************************************************************************/

void Ds18b20WriteByte(uchar dat) { uint i, j; for(j=0; j<8; j++) { DSPORT = 0; //每写入一位数据之前先把总线拉低1us i++; DSPORT = dat & 0x01; //然后写入一个数据,从最低位开始 i=6; while(i--); //延时68us,持续时间最少60us DSPORT = 1; //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值 dat >>= 1; } }

/*******************************************************************************

* 函 数 名 : Ds18b20ReadByte * 函数功能 : 读取一个字节 * 输 入 : com * 输 出 : 无

*******************************************************************************/

uchar Ds18b20ReadByte() { uchar byte, bi; uint i, j;

14

for(j=8; j>0; j--) { DSPORT = 0;//先将总线拉低1us i++; DSPORT = 1;//然后释放总线 i++; i++;//延时6us等待数据稳定 bi = DSPORT; //读取数据,从最低位开始读取 /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/ byte = (byte >> 1) | (bi << 7); i = 4; //读取完之后等待48us再接着读取下一个数 while(i--); } return byte; }

/*******************************************************************************

* 函 数 名 : Ds18b20ChangTemp * 函数功能 : 让18b20开始转换温度 * 输 入 : com * 输 出 : 无

*******************************************************************************/

void Ds18b20ChangTemp() { Ds18b20Init(); Delay1ms(1); Ds18b20WriteByte(0xcc); //跳过ROM操作命令 Ds18b20WriteByte(0x44); //温度转换命令

// Delay1ms(100); //等待转换成功,而如果你是一直刷着的话,就不用这个延时了 }

/*******************************************************************************

* 函 数 名 : Ds18b20ReadTempCom * 函数功能 : 发送读取温度命令 * 输 入 : com * 输 出 : 无

*******************************************************************************/

void Ds18b20ReadTempCom() {

15

Ds18b20Init(); Delay1ms(1); Ds18b20WriteByte(0xcc); //跳过ROM操作命令 Ds18b20WriteByte(0xbe); //发送读取温度命令 }

/*******************************************************************************

* 函 数 名 : Ds18b20ReadTemp * 函数功能 : 读取温度 * 输 入 : com * 输 出 : 无

*******************************************************************************/

int Ds18b20ReadTemp() { int temp = 0; uchar tmh, tml; Ds18b20ChangTemp(); //先写入转换命令 Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令 tml = Ds18b20ReadByte(); //读取温度值共16位,先读低字节 tmh = Ds18b20ReadByte(); //再读高字节 temp = tmh; temp <<= 8; temp |= tml; return temp; }

16

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- yrrf.cn 版权所有 赣ICP备2024042794号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务