摘要
本设计主要有单片机模块、传感器模块、电机驱动模块以及电源模块组成,小车具有自主寻迹的功能。本次设计采用STC公司的89C52单片机作为控制芯片,传感器模块采用红外光电对管和比较器实现,能够轻松识别黑白两色路面,同时具有抗环境干扰能力,电机模块由L298N芯片和两个直流电机构成,组成了智能车的动力系统,电源采用7.2V的直流电池,经过系统组装,从而实现了小车的自动循迹的功能。
关键词 智能小车 STC89C52单片机 L298N 红外光对管
1 绪论
随着科学技术的发展,机器人的设计越来越精细,功能越来越复杂,智能小车作为其的一个分支,也在不断发展。在近几年的电子设计大赛中,关于小车的智能化功能的实现也多种多样,因此本次我们也打算设计一智能小车,使其能自动识别预制道路,按照设计的道路自行寻迹。
2 设计任务与要求
采用MCS-51单片机为控制芯片(也可采用其他的芯片),红外对管为识别器件、步进电机为行进部件,设计出一个能够识别以白底为道路色,宽度10mm左右的黑色胶带制作的不规则的封闭曲线为引导轨迹并能沿该轨迹行进的智能寻迹机器小车。
3 方案设计与方案选择
3.1 硬件部分
可分为四个模块:单片机模块、传感器模块、电机驱动模块以及电源模块。
3.1.1 单片机模块
为小车运行的核心部件,起控制小车的所有运行状态的作用。由于以前自己开发板使用的是ATMEL公司的STC89C52,所以让然选择这个芯片作为控制核心部件。STC89C52是一种低损耗、高性能、CMOS八位微处理器,片内有4k字节的在线可重复编程、快速擦除快速写入程序的存储器,能重复写入/擦除1000次,数据保存时间为十年。其程序和数据存储是分开的。
3.1.2 传感器模块
方案一:使用光敏电阻组成光敏探测器采集路面信息。阻值经过比较器输出高低电平进行分析,但是光照影响很大,不能稳定工作。
方案二:使用光电传感器来采集路面信息。使用红外光电对管,其结构简明,实现方便,成本低廉,没有复杂的图像处理工作,因此反应灵敏,响应时间少。但也存在不足,它能获取的信息是不完全的,容易受很多扰动(如背景光源,高度等)的影响,抗干扰能力较差。
方案三:使用CCD传感器来采集路面信息。使用CCD可以获取大量的图像信息,掌握全面的路径信息,抗干扰能力强,为以后功能的扩展提供方便。但使用CCD需要大量的图像处理工作,进行大量数据的存储和计算,因此电路复杂,实现起来工作量大。
方案四:使用光电对管采集路面信息。RPR220结构紧凑,体积小,调整电路简单工作性能稳定。
可见方案四最适宜,但仅从此项目考虑,方案二成本低,也能完成设计,故选用方案二。
3.1.3 电机控制模块
3.1.3.1电机的选择
方案一:采用步进电机,其转过的角度可以精确定位,可实现小车行进过程的精确定位。但步进电机的输出力矩低,随转速的升高而降低,且转速越快下降得越快。
方案二:采用直流电机,其转动力矩大,体积小,重量轻,装配简单,操作方便。速度的调节可以改变电压也可以调节PWM。
基于以上,我们选择了方案二,使用直流电机作为驱动电机。
3.1.3.2电机的驱动
采用专用芯片L298N作为电机驱动芯片,其操作方便,稳定性好,性能优良。一片L298N就可以分别控制两个直流电机。
3.1.4 电源模块
给整个系统稳定供电以保持其正常工作,包括7.2V的电源以及转5V部分,其中7.2V的是给电机和其驱动供电,5V的用来驱动单片机及其他芯片。
以上单元连接如下图所示:
3.2 软件部分
3.2.1程序流程图
此系统采用89C52单片机,再根据硬件连接,通过相应的软件来完成对信号的采集和数据的分析,再控制小车的运行状态,以下为主程序流程图:
3.2.2程序设计思路
3.2.2.1寻迹模块程序
通过传感器获得路面信息然后反馈给单片机,再通过单片机来实现相应的功能。
3.2.2.2电机驱动模块程序
控制两个直流电机,实现前进、后退、前左转、前右转、停车等功能。
4 各部分电路的作用及电路工作原理分析
4.1 信号采集模块
4.1.1 TCRT500结构与工作原理
TCRT5000(L)具有紧凑的结构发光灯和检测器安排在同一方向上,利用红外光谱反射对象存在另一个对象上,操作的波长大约是950毫米。探测器由光电晶体三极管组成的,它由高发射功率红外光电二极管和高度灵敏光电晶体管组成。通过测试,其检测距离在2mm-10mm。TCRT5000的发射管和接收管是一起封装在矩形塑料壳中,为了使检测更加准确,我们用了5只TCRT5000检测黑线,实物见图4-1。
4.1.2 信号采集电路图及原理
小车在白色地面行驶时,红外发射管发出的红外信号被反射,接收管收到信号后,输出端为低电平,经过比较器比较后输出为低电平。而当红外信号遇到黑色导轨时,红外信号被吸收,接收管不能接收信号,输出端为高电平,经过比较器比较后输出高电平。单片机通过采集每个比较器的输出端电压,便可以检测出黑线的相对位置的位置,从而控制小车的行驶方向。
4.2 信息处理模块
4.2.1 原理
检测到白色路面的红外接收头处理后送出的是低电平,而检测到黑色路线的检测头送出的是高电平,由此可根据这5个红外接收头的高低电平判断路线情况而调整小车前进方向。具体情况有如下几种:
a 检测到 1 1 1 1 1 或
0 0 0 0 0小车应该停止。
b 检测到 1 0 0 0 0 或
0 1 0 0 0 或
1 1 0 0 0 说明路线向左偏,小车向左转。
c 检测到 0 0 0 0 1 或
0 0 0 1 0 或
0 0 0 1 1说明路线向右偏,小车向左转。
d 检测到 x x 1 x x(x不全为1) 说明线路是直的,小车直走。
4.3 电机驱动模块
4.3.1直流电机
给两个电刷A和B加上直流电源,如上图(a)所示,则有直流电流从电刷 A 流入,经过线圈abcd,从电刷 B 流出,根据电磁力定律,载流导体ab和cd收到电磁力的作用,其方向可由左手定则判定,两段导体受到的力形成了一个转矩,使得转子逆时针转动。如果转子转到如上图(b)所示的位置,电刷 A 和换向片2接触,电刷 B 和换向片1接触,直流电流从电刷 A 流入,在线圈中的流动方向是dcba,从电刷 B 流出。
此时载流导体ab和cd受到电磁力的作用方向同样可由左手定则判定,它们产生的转矩仍然使得转子逆时针转动。这就是直流电动机的工作原理。外加的电源是直流的,但由于电刷和换向片的作用,在线圈中流过的电流是交流的,其产生的转矩的方向却是不变的。实用中的直流电动机转子上的绕组也不是由一个线圈构成,同样是由多个线圈连接而成,以减少电动机电磁转矩的波动,绕组形式同发电机。
4.3.2电路图
我们采用成品L298N电机驱动模块,采用光电耦合器件隔离单片机与L298N的控制电路,工艺精度高,性能可靠。L298N模块内部通过H桥电路实现直流电机的正转,反转,其原理如下:
如图4-3所示,全桥式驱动电路的4只开关管都工作在斩波状态,S1、S2为一组,S3、S4 为另一组,两组的状态互补,一组导通则 另一组必须关断。当S1、S2导通时,S3、 S4关断,电机两端加正向电压,可以实 现电机的正转或反转制动;当S3、S4导 通时,S1、S2关断,电机两端为反向电 压,电机反转或正转制动。
桥驱动电路
4.3.3原理
L298N是ST公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间峰值电流可达3A,持续工作电流为2A;额定功率25W。内含两个H桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机和步进电动机、继电器线圈等感性负载;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有一个逻辑电源输入端,使内部逻辑电路部分在低电压下工作;可以外接检测电阻,将变化量反馈给控制电路。使用L298N芯片驱动电机,该芯片可以驱动两台直流电机。
5 系统调试
5.1硬件部分
焊接完成后,首先进行的调试是用数字万用表测量各个电路是否焊接正常,是否有虚焊漏焊等现象的出现,以及各个电容是否是正常的未被击穿状态、电阻的阻值是否与设计的原理图上的一致。接通电源,用数字万用表测量当有+5V的各引脚是否有+5V的电压,测量电路中是否出现了不该有的短路现象。接入光电传感器模块,使各个光电检测器的光电对管靠近白纸,观察对应的发光二极管是否发光,不发光表示正常。 然后再使各个光电对管靠近黑线,观察对应的发光二级管是否发光,发光表示正常。
5.2软件部分
我们先测试了小车的前进,停止,左转和右转。组装信号采集模块后,实现小车的自动循迹功能。
具体实现程序见附录一
6 总结
实验结果如符合实验要求,小车按照黑胶布轨迹前进,并能够及时正确显示小车的行进状态以及行进距离。具体现象如下:
左边传感器检测到黑线,小车左转;
右边传感器检测到黑线,小车右转;
中间传感器检测到黑线,小车直行。
从而就可以完成对黑胶布的循迹功能。
7 参考文献
[1]电子信息专业实验教程 赵刚 李佐儒 四川大学出版社
[2]单片机C语言教程 郭天祥 电子工业出版社
[3]模拟电子技术 童诗白 清华大学出版社
附录一
程序:
#include<reg52.h>
sbit DJ_left_s = P1^0; //直流电机控制
sbit DJ_left_n = P1^1;
sbit DJ_right_s = P1^2;
sbit DJ_right_n = P1^3;
//左转函数
void Turn_right()
{
DJ_left_s = 0;
DJ_left_n = 1;
DJ_right_s = 1;
DJ_right_n = 0;
}
//右转函数
void Turn_left()
{
DJ_left_s = 1;
DJ_left_n = 0;
DJ_right_s = 0;
DJ_right_n = 1;
}
//前进函数
void Go_ahead()
{
DJ_left_s = 1;
DJ_left_n = 0;
DJ_right_s = 1;
DJ_right_n = 0;
}
//停止函数
void Stop()
{
DJ_left_s = 0;
DJ_left_n = 0;
DJ_right_s = 0;
DJ_right_n = 0;
}
//循迹函数
void xunji(unsigned int m)
{
if(m==0x7c)
{
Turn_right();
return;
}
if(m&0x10)
{
Go_ahead();
return;
}
if(m&0x0c)
{
Turn_right();
return;
}
if(m&0x60)
{
Turn_left();
return;
}
}
//主函数
void main()
{
while(1)
{
xunji(P2&0x7c);
}
}
附录二
实物图:
第二篇:智能小车实验报告
北京邮电大学实习报告
附1 实习总结
为期两周的电子工艺实习,我过得十分忙碌和充实。从茫然地走进实验室,到学习最基本的焊接,到组装小车,再到无数次地调试程序,最后获得全院比赛的二等奖,有很多的辛苦,但是有更多的收获。
焊接是电子工艺实习最基本的部分,也是我们小学期的第一课。最开始是焊接基本的元件,包括电阻、电容、二极管、三极管等,虽然看起来是很简单的工作,但总是掌握不好电烙铁和焊锡,于是焊点有大有小,还有一些虚焊和漏焊的点。直到按照老师的要求一点一点把整块板子焊满,才逐渐掌握了标准、规范的焊接方法,最后烙铁往上一提很重要。到后来焊连着的四十个点时,焊点已经比较整齐划一了。对于焊接这种基本功来说,反复练习真的十分重要,这也考验了我们的耐心和细心。
焊接部分的小测试,是焊一个发光二级管交替亮的功能电路,老师要求正面用硬线布线,背面用软线连接。由于一开始设计布线的时候,元件之间距离比较近,导致在背面焊接连线时必须把线剪得特别短,我们两个人一个扶着线,一个焊,位置十分不好把握,一不小心就会碰到旁边的焊点,又需要吸掉重焊,浪费了很多时间。所以我们的工作进行得十分慢,到中午很晚才焊完。虽然焊完后通电顺利地亮了,但以后再布线的时候一定要考虑到背面连线的问题,把原件之间的距离排得大一些。
基本焊接技术后就正式进入小车的组装了。小车的零件有很多都不认识,电路板也很复杂,刚拿到手里有些摸不着头脑,还好说明书上对焊接步骤有详细的说明。在焊芯片和散热片的时候,我们把顺序搞反了,应该先焊散热片,再根据螺丝孔的位置焊芯片,才能把两个元件固定在一起。但我们先焊了芯片,把散热片插在板子上后,发现两个孔怎么也对不上,可是芯片已经焊死了,即使用吸锡器也拆不下来。最后我们只好在散热片上又钻了一个孔,才勉强把螺丝拧上去。所以焊接的顺序是极其重要的,不光要考虑元件的高低,还要考虑元件之间的关系,才能少做无用功。还好其他步骤我们没有再出问题,小车焊出来后把测试程序烧进去,也能够正常的跑。
进入程序编写阶段,我们两个人先一起在测试程序的基础上编写了一个逻辑,预想了小车在行进过程中可能遇到的各种状况,主要使用了if……else if……else的多层嵌套。这个逻辑我们梳理了好长时间,在纸上画了逻辑图,想办法把所有的情况都包含进去。除了正常行进的程序,我们还添加了轮子卡住时的情况。对于延时函数中的参数,我们先写了一个待定值,这要在以后的调试中不断调整。当这个基本的程序写好后,我们把它烧进单片机试验了一下,结果是不出意料的磕磕碰碰。于是,漫长的调试阶段开始了。
我相信对于每一组同学来说,调试都是最劳心劳力的工作。因为前期的工作比较标准化,大家的工作内容有着高度一致性,可以互相帮助、讨论,但是调试阶段就是靠每个组自己的思考、观察和调整了。因为每个车的焊接情况都有差别,大家各自编的程序也不一样,所以小车跑的时候出的问题也不一样。我们的第一个任务是是小车顺利冲出班级内测的小跑道,然后还有全院比赛的大跑道等着我们。
在小跑的调试中,我们组的分工是:我负责观察每次测试时问题出在哪里,我的搭档负责根据我找出的问题调试。我们调试的主要问题有三个:传感器的灵敏度,小车转弯的幅度,和转弯的优先级。传感器的灵敏度直接决定了小车转弯的位置,如果传感器感应距离太近,小车就容易直接撞到障碍上;如果传感器感应距离太远,小车则会转弯太早,无法进入预定轨道。小车转弯的幅度是靠延时函数的参数来控制的,参数较大,小车容易转过头撞在障碍上;参数较小,小车转的幅度不够也会撞在障碍上。经过反复的测试,我们终于把传感器的灵敏度和小车转弯的幅度调得比较合适,小车走迷宫的前几个弯道也很顺利,但是总卡在出口处。经过不断地讨论和思考,我们发现是程序中的优先级问题,当前方和右方都无障碍时,我们编写的是“前进”,但根据迷宫的实际状况,应该“右转”才能顺利出去。当我们修改了这个问题后,小车就能走出迷宫了,不过成功的几率并不是很高,中间还有许多的磕磕碰碰。
在小车与障碍的不断碰撞中,传感器与电路板的连接线会比较容易脱落,导致走迷宫的过程中小车会出现一些反常的行为,于是我决定把原本插在电路板上的连接线直接焊死在板子上。但是三个10pin排针挨在一起,焊点都离得很近,焊的时候还是很困难的,还好在搭档的帮助下顺利的完成了。此后传感器的稳定性就比较好了,不会再出现连接不好的问题。
当小车路线基本正确后,我们又不断调整传感器的灵敏度和小车转弯的幅度,让小车在行走在道路的中央,转弯也更准确。最后验收时我们走得十分顺利,一次都没有撞障碍,用了11秒多走出了迷宫。
小迷宫走成功后,我们开始向大迷宫进发。这一个阶段我主要负责硬件的改装,我的搭档主要负责程序的调试。刚开始我们观看了几组跑大迷宫的小车,速度都比我们快好多,于是我们决定再加电池。之前我们的小车是四节电池供电,现在要加到六节(我们怕八节太多不好控制)。这需要在小车上再加一个电池盒,还要把其中两节电池的位置用导线短路。由于小车已经装得比较慢了,我最终把电池盒加在了小车底下的前方。在用导线短路的时候,由于电池盒是塑料材质的,烙铁一碰到烧化,所以焊接时需要非常小心。这样加上两节电池后,小车的前部变重了,跑的时候经常头点地,于是我又在尾部绑了两节废电池增加重量,这样小车跑起来平衡性就比较好了。
由于马力加大,小车冲得比原来快了,再加上大迷宫比小迷宫跑道宽,所以转弯的地方幅度全都有变化。而且根据大迷宫的实际情况,我们还需要改动一些之前预设情况,比如当前方和左方都无障碍时,优先左转;当左方和右方都无障碍时,优先右转。这样又经过了多次测试和程序的改进,我们的小车才跑的比较顺畅了。可是我们又发现,一旦小车侧面卡在障碍上,就很难再前进,于是我们决定在小车前方加一个导轮,这样就可以让小车在遇到这种情况时滑过去。
添加导轮需要把电路板裁开,用来连接导轮和小车。电路板材质较硬,裁制、打孔都费了很大的劲,我左右两边分别裁了一块三角形的板子用来连接导轮和小车。导轮是在瓶盖中间钻孔制成的。第一次做出来的导轮没有超出车轮的宽度,位置也容易移动,所以在实际跑的过程中并没有起到很大的作用。于是我每边又加了一块长方形的板子,这样把两个导轮之间的距离拉得更长一些。每个节点我从原来的一根螺丝换成了两根螺丝固定,这样即使遇到碰撞也不会改变位置。经过这样的改进后,导轮就比较完善了,能够在调整小车的方向上起到应有的作用。
相对于其他小组的大动力车来讲,我们的小车主要求稳。在比赛的前一天晚上,我们已经能大部分测试都走出迷宫了,并且成绩稳定在12到13秒。这主要是因为我们的小车从头到尾碰障碍比较少,很少卡住。
再跑迷宫能力已经比较好的的情况下,我给小车设计了外观。由于我们的导轮做得很像触角,我们索性给小车起名为“金龟号”,取形似金龟子之意。还给小车加上了绿色的外衣,更美观一些。
最后的全院比赛,我们发挥出了应有的水平,以12秒多的成绩获得了二等奖。近两周的辛苦没有白费,终于得到了回报,我十分开心。这次的小学期,和大二小学期时的理论课不同,需要我们自己动手,一点一点把一堆零件变成智能的迷宫小车。纵使反复调试的过程中烦躁过,也迷茫过,纵使加班加点付出了很多的辛苦,但当反复看着小车最后成功跑出迷宫的视频,我觉得这一切都是值得的。我所为之高兴的,不是最后的奖项,也不是奖金,是我和我的搭档合作的劳动成果,是在这个小学期里动手能力的提高,是很多次失望时候终于成功的满足。感谢电路中心给了我们这个平台,让我们靠自己的努力做了一件很有意义的事情。小车就像我们自己的孩子,一点一点把它培养出来,看着它不断地成长,从跌跌撞撞到顺利跑出迷宫。我想,我们都应该为自己的作品感到骄傲。
附2 程序代码
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define t 100
sbit IN1 =P1^0;
sbit IN2=P1^1;
sbit IN3=P1^4;
sbit IN4=P1^5;
sbit LED1=P2^6;
sbit LED2=P2^7;
sbit S1=P1^6;
sbit recieve_left = P3^3;
sbit recieve_right = P3^6;
sbit recieve_front = P3^0;
uint count = 0;
/********以下是延时函数********/
void Delay_ms(uint xms) //延时程序,xms是形式参数
{
uint i, j;
for(i=xms;i>0;i--) // i=xms,即延时xms, xms由实际参数传入一个值
for(j=115;j>0;j--); //此处分号不可少
}
stop() //高电平有效
{
IN1=0;
IN2=0;
IN3=0;
IN4=0;
Delay_ms(50);
}
houtui()
{
IN1=1;
IN2=0;
IN3=0;
IN4=1;
}
qianjin()
{
IN1=0;
IN2=1;
IN3=1;
IN4=0;
}
turnleft()
{
LED2 = 0;
IN1 = 0;
IN2 = 0;
IN3 = 0;
IN4 = 0;
Delay_ms(30);
IN1 = 0;
IN2 = 1;
Delay_ms(35);
LED2 = 1;
while(recieve_front == 1)
{
qianjin();
}
if(recieve_left == 0&&recieve_right == 0)
{
Delay_ms(40);
if(recieve_left == 0&&recieve_right == 0)
{
while(recieve_left == 0&&recieve_right == 0)
qianjin();
}
}
}
Delay_ms(80); //不碰到障碍物时,继续前行
Delay_ms(180);
}
turnright()
{
LED1 = 0;
IN1 = 0;
IN2 = 0;
IN3 = 0;//左轮
IN4 = 0;
Delay_ms(30);
IN3 = 1;//左轮
IN4 = 0;
Delay_ms(15);
LED1 = 1;
while(recieve_front == 1)
{
qianjin();
if(recieve_left == 0&&recieve_right == 0)
{
Delay_ms(40);
if(recieve_left == 0&&recieve_right == 0)
{
while(recieve_left == 0&&recieve_right == 0)
qianjin();
}
}
}
Delay_ms(85); //不碰到障碍物时,继续前行
Delay_ms(180);
}
right_back() //左轮卡住时
{
LED2 = 0;
IN1 = 1;
IN2 = 0;
IN3 = 0;
IN4 = 0;
Delay_ms(50);
}
left_back() //右轮卡住时
{
LED2 = 0;
houtui();
Delay_ms(5);
LED2 = 0;
IN1 = 0;
IN2 = 0;
IN3 = 0;
IN4 = 1;
Delay_ms(50);
}
//****************主程序******************//
void main()
{ P0=0xff;
P2=0xff;
P1=0xff;
while(1)
{
if(recieve_left == 1 && recieve_right == 1)//两边都无障碍物
{
if(recieve_front == 1) //前无障碍物 ,直行 pass
{
qianjin();
continue;
}
else//前有障碍物,左转
{ stop();
IN1 = 0;
IN2 = 1;
IN3 = 0;
IN4 = 0;
Delay_ms(170);//待定
if(recieve_front == 0)
right_back();
continue;
}
}
else if(recieve_left == 1) //左边无障碍物 ,左转
{
if(recieve_front == 1){
while(recieve_front == 1)
{
qianjin();
}
turnleft();
continue;
}
else
{
turnleft();
Delay_ms(170);
if(recieve_front == 0&&recieve_left == 1&&recieve_right == 0)
left_back();
continue;
}
}
else if(recieve_right == 1)//右边无障碍物,右转
{
if(recieve_front == 1){
while(recieve_front == 1)
{
qianjin()
}
if(recieve_left == 1)
turnleft();
}
else
{
turnright();
Delay_ms(230);
if(recieve_front == 0&&recieve_left == 0&&recieve_right == 1)
right_back();
continue;
}
}
else if(recieve_front == 1)
{
qianjin();
continue;
}
else
{
stop();
while(recieve_right == 0 && recieve_left == 0)houtui();
houtui();
Delay_ms(150); //待定
if(recieve_right == 1)
{
IN1 = 0;
IN2 = 0;
IN3 = 0;//左轮
IN4 = 0;
Delay_ms(5);
IN3 = 1;//左轮
IN4 = 0;
Delay_ms(370);
}
if(recieve_left == 1)
{
IN1 = 0;
IN2 = 0;
IN3 = 0;//左轮
IN4 = 0;
Delay_ms(5);
IN1 = 0;//右轮
IN2 = 1;
Delay_ms(320);
}
}
}
}