智能循迹小车.docx
《智能循迹小车.docx》由会员分享,可在线阅读,更多相关《智能循迹小车.docx(22页珍藏版)》请在冰豆网上搜索。
智能循迹小车
摘要
进入二十一世纪,随着计算机技术和科学技术的不断进步,机器人技术较以往已经有了突飞猛进的提高,智能循迹小车即带有视觉和触觉的小车就是其中的典型代表。
本文论述了基于单片机的智能循迹小车的控制过程。
智能循迹是基于自动引导机器人系统,用以实现小车自动识别路线,以及选择正确的路线。
智能循迹小车是一个运用传感器、单片机、电机驱动及自动控制等技术来实现按照预先设定的模式下,不受人为管理时能够自动实现循迹导航的高新科技。
该技术已经应用于无人驾驶机动车,无人工厂,仓库,服务机器人等多种领域。
本设计采用AT89C51单片机作为小车的控制核心;用红外探测传感器作为小车的循迹模块来识别白色路面中央的黑色引导线,采集信号并将信号转换为能被单片机识别的数字信号;采用驱动芯片L298构成双控制直流电机,其中软件系统采用C程序,本设计的电路结构简单,容易实现,可靠性高。
关键词:
单片机;智能;自动循迹。
1绪论1
6系统所需元器件3
7主要模块介绍3
7.1L298驱动模块简介3
7.2LM016L显示模块4
8小车运动逻辑6
9系统原理图6
10系统程序设计7
11系统仿真17
12结果分析17
13感想与收获19
1绪论
智能循迹小车又被称为AutomatedGuidedVehicle,简称AGV,是二十世纪五十年代研发出来的新型智能搬运机器人。
智能循迹小车是指装备如电磁,光学或其他自动导引装置,可以沿设定的引导路径行驶,安全的运输车。
工业应用中采用充电蓄电池为主要的动力来源,可通过电脑程序来控制其选择运动轨迹以及其它动作,也可把电磁轨道黏贴在地板上来确定其行进路线,无人搬运车通过电磁轨道所带来的讯息进行移动与动作,无需驾驶员操作,将货物或物料自动从起始点运送到目的地。
AGV的另一个特点是高度自动化和高智能化,可以根据仓储货位要求、生产工艺流程等改变而灵活改变行驶路径,而且改变运行路径的费用与传统的输送带和传送线相比非常低廉。
AGV小车一般配有装卸机构,可与其它物流设备自动接口,实现货物装卸与搬运的全自动化过程。
此外,AGV小车依靠蓄电池提供动力,还有清洁生产、运行过程中无噪音、无污染的特点,可用在工作环境清洁的地方。
随着社会的不断发展,科学技术水平的不断提高,人们希望创造出一种来代替人来做一些非常危险,或者要求精度很高等其他事情的工具,于是就诞生了机器人这门学科。
世界上诞生第一台机器人诞生于1959年,至今已有50多年的历史,机器人技术也取得了飞速的发展和进步,现已发展成一门包含:
机械、电子、计算机、自动控制、信号处理,传感器等多学科为一体的性尖端技术。
循迹小车共历了三代技术创新变革:
第一代循迹小车是可编程的示教再现型,不装载任何传感器,只是采用简单的开关控制,通过编程来设置循迹小车的路径与运动参数,在工作过程中,不能根据环境的变化而改变自身的运动轨迹。
支持离线编程的第二代循迹小车具有一定感知和适应环境的能力,这类循迹小车装有简单的传感器,可以感觉到自身的的运动位置,速度等其他物理量,电路是一个闭环反馈的控制系统,能适应一定的外部环境变化。
第三代循迹小车是智能的,目前在研究和发展阶段,以多种外部传感器构成感官系统,通过采集外部的环境信息,精确地描述外部环境的变化。
智能循迹小车,能独立完成任务,有其自身的知识基础,多信息处理系统,在结构化或半结构化的工作环境中,根据环境变化作出决策,有一定的适应能力,自我学习能力和自我组织的能力。
为了让循迹小车能独立工作,一方面应具有较高的智慧和更广泛的应用,研究各种新机传感器,另一方面,也掌握多个多类传感器信息融合的技术,这样循迹小车可以更准确,更全面的获得所处环境的信息
2设计要求
设计一个智能循迹小车,要求小车通过红外探测器自动探测路面轨迹标示,将数据传送给单片机进而控制电动机的转速,做到自动转向,自动行驶。
同时通过显示屏实时显示循迹小车的运动状态,直行、左转、右转、停止。
3系统设计分析
根据设计要求,将系统分为控制模块、检测模块、电机驱动模块。
其中信号检测部分通过红外探测器检测并将信号传回单片机进行处理。
控制部分的作用是接收并处理传感器检测到的信号,通过判断信号的类别控制小车的动作同时控制显示屏的显示状态。
本系统采用AT89C51单片机,其特点是小型、快速、低功耗、I/O口资源丰富等,能够满足本小车的设计要求。
采用两个直流电机驱动,电路简单,成本不高。
电机的驱动芯片采用L298,该芯片有四路输出,可以驱动一个四相步进电机或两个直流电机,四路总电流可达4A,输出电压最高可达46V,可以直接用单片机I/O口的输出信号来控制。
检测模块采用红外探测器。
红外探测模块主要由红外感光管、电压比较器、敏感调节器组成。
最左边是红外感光管,中间的黑色芯片是电压比较器,绿色的是敏感调节器。
4系统基本组成
5设计步骤及流程图
1)根据设计要求,确定控制方案。
2)画出程序流程图,使用C语言进行编程,运用KeilC进行模拟调试。
3)利用Proteus设计合理的硬件原理图。
4)进行仿真调试以实现控制功能。
6系统所需元器件
单片机AT89C51、瓷片电容CAP30pf、晶振CRYSTAL12MHZ、电解电容CAP-ELEC、按钮BUTTON、电阻RES、L298、COMPIM、MOTOR-ENCODER、RESPACK-8、SWITCH。
7主要模块介绍
7.1L298驱动模块简介
L298驱动模块:
采用L298作为电机驱动芯片。
L298具有高电压、大电流、响应频率高的全桥驱动芯片,一片L298可以分别控制两个直流电机,并且带有控制使能端。
该电机驱动芯片驱动能力强、操作方便,稳定性好,性能优良。
L298的使能端可以外接电平控制,也可以利用单片机进行软件控制,满足各种复杂电路的需要。
另外,L298的驱动功率较大,能够根据输入电压的大小输出不同的电压和功率,解决了负载能力不够的问题
表7.1L298引脚标号与功能
引脚编号
名称
功能
1
电流传感器A
在该引脚和地之间接小阻值电阻可用来检测电流
2
输出引脚1
内置驱动器A的输出端1,接至电机A
3
输出引脚2
内置驱动器A的输出端2,接至电机A
4
电机电源端
电机供电输入端,电压可达46V
5
输入引脚1
内置驱动器A的逻辑控制输入端1
6
使能端A
内置驱动器A的使能端
7
输入引脚2
内置驱动器A的逻辑控制输入端2
8
逻辑地
逻辑地
9
逻辑电源端
逻辑控制电路的电源输入端为5V
10
输入引脚3
内置驱动器B的逻辑控制输入端1
11
使能端B
内置驱动器B的使能端
12
输入引脚4
内置驱动器B的逻辑控制输入端2
13
输出引脚3
内置驱动器B的输出端1,接至电机B
14
输出引脚4
内置驱动器B的输出端2,接至电机B
15
电流传感器B
在该引脚和地之间接小阻值电阻可用来检测电流
7.2LM016L显示模块
1602字符型LCD通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线VCC(15脚)和地线GND(16脚),其控制原理与14脚的LCD完全一样。
引脚
符号
功能说明
1
VSS
一般接地
2
VDD
接电源(+5V)
3
V0
液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生"鬼影",使用时可以通过一个10K的电位器调整对比度)。
4
RS
RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
5
RW
RW为读写信号线,高电平
(1)时进行读操作,低电平(0)时进行写操作。
6
E
E(或EN)端为使能(enable)端,
写操作时,下降沿使能。
读操作时,E高电平有效
7
DB0
低4位三态、双向数据总线0位(最低位)
8
DB1
低4位三态、双向数据总线1位
9
DB2
低4位三态、双向数据总线2位
10
DB3
低4位三态、双向数据总线3位
11
DB4
高4位三态、双向数据总线4位
12
DB5
高4位三态、双向数据总线5位
13
DB6
高4位三态、双向数据总线6位
14
DB7
高4位三态、双向数据总线7位(最高位)(也是busyflag)
15
BLA
背光电源正极
16
BLK
背光电源负极
8小车运动逻辑
表8.1小车运动逻辑
HZ
HY
左电机
右电机
小车
1
1
低速
低速
直行
0
1
高速
低速
右转
1
0
低速
高速
左转
0
0
0
0
停止
9系统原理图
10系统程序设计
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
#defineLCD_IOP0//定义P0口与LCD1602的数据口相接
/***********************LCD1602接线引脚定义**************************/
#defineLCD_IOP0//定义P2口与LCD1602的数据口相接
sbitLCD_RS=P2^6;//数据、命令选择端1为数据
sbitLCD_RW=P2^5;//读、写选择端1为读
sbitLCD_EN=P2^7;//使能控制
sbitLCD_BUSY=LCD_IO^7;//忙碌标志位
/**********************另外相关的定*********************************/
#defineHIGH1
#defineLOW0
#defineTURE1
#defineFALSE0
#defineucharunsignedchar
#defineuintunsignedint
/**********************输入方式设置*****************************/
#defineLCD_AC_AUTO_INCREMENT0x06//数据读、写操作后,AC自动增一
#defineLCD_AC_AUTO_DECREASE0x04//数据读、写操作后,AC自动减一
#defineLCD_MOVE_ENABLE0x05//数据读、写操作,画面平移
#defineLCD_MOVE_DISENABLE0x04//数据读、写操作,画面不动
#defineLCD_GO_HOME0x02//AC=0,光标、画面回HOME位
#defineLCD_DISPLAY_ON0x0C//显示开
#defineLCD_DISPLAY_OFF0x08//显示关
#defineLCD_CURSOR_ON0x0A//光标显示
#defineLCD_CURSOR_OFF0x08//光标不显示
#defineLCD_CURSOR_BLINK_ON0x09//光标闪烁
#defineLCD_CURSOR_BLINK_OFF0x08//光标不闪烁
#defineLCD_LEFT_MOVE0x18//LCD显示左移一位
#defineLCD_RIGHT_MOVE0x1C//LCD显示右移一位
#defineLCD_CURSOR_LEFT_MOVE0x10//光标左移一位
#defineLCD_CURSOR_RIGHT_MOVE0x14//光标右移一位
#defineLCD_DISPLAY_DOUBLE_LINE0x38//两行显示,8位数据端口
#defineLCD_DISPLAY_SINGLE_LINE0x30//单行显示
#defineLCD_CLEAR_SCREEN0X01//清屏
/***********************LCD1602地址相关******************************/
#defineLINE1_HEAD0x80//第一行DDRAM起始地址
#defineLINE2_HEAD0xc0//第二行DDRAM起始地址
#defineLINE10//第一行
#defineLINE21//第二行
#defineLINE_LENGTH16//每行的最大字符长度
/*************************以下是函数的申明部*************************/
voidLCD_Init(void);//LCD1602初始化
voidLCD_Write_Command(uchar);//写命令
voidLCD_Write_Data(uchar);//写数据
voidLCD_Flash(uchar);//LCD画面闪烁
voidLCD_Char(uchar,uchar,uchar);//在第几行第几列显示一个字符
voidLCD_Str(uchar,uchar,char*);//从第几行第几列开始显示一个字符串
voidDelayMs(uchar);//延迟毫秒
voidDelayUs(uchar);//延迟微秒
voidLCD_Check_Busy(void);//LCD忙碌检测
ucharhigh_time1;//定义变量
uintzkb1,zkb2;
ucharcount1;
ucharhigh_time1;
ucharcount2;
ucharhigh_time2;
sbitOutput1=P1^0;
sbitOutput2=P1^1;
sbitHZ=P3^6;
sbitHY=P3^7;
sbitZ1=P1^2;
sbitZ2=P1^3;
sbitY1=P1^4;
sbitY2=P1^5;
voidTimer0Init(void);//函数的声明
voidTimer1Init(void);
voidPwm_set1(uintzkb);
voidPwm_set2(uintzkb);
voidxunji(unsignedcharvx);
voidLCD_Check_Busy(void);
/************延迟函数*******************************/
voidDelayUs(ucharus)//delayus
{
unsignedcharuscnt;
uscnt=us>>1;/*Crystalfrequencyin12MHz*/
while(--uscnt);
}
voidDelayMs(ucharms)//delayMs
{
while(--ms)
{
DelayUs(250);
DelayUs(250);
DelayUs(250);
DelayUs(250);
}
}
voidLCD_Init()
{
DelayMs(15);//延迟15ms,等待LCD电源稳定,使其进入工作状态
LCD_IO=0x00;
LCD_Write_Command(LCD_DISPLAY_DOUBLE_LINE);
DelayMs(5);
LCD_Write_Command(LCD_DISPLAY_DOUBLE_LINE);
DelayMs(5);
LCD_Write_Command(LCD_DISPLAY_DOUBLE_LINE);//显示模式设置为两行显示,8位数据接口,5*8点阵
DelayMs(5);
LCD_Write_Command(LCD_AC_AUTO_INCREMENT|LCD_MOVE_DISENABLE);//数据读、写操作后,AC自动增一,画面不动
DelayMs(5);
LCD_Write_Command(LCD_DISPLAY_ON|LCD_CURSOR_OFF);//显示开,光标不显示
DelayMs(5);
LCD_Write_Command(LCD_CLEAR_SCREEN);//清除LCD显示内容
}
/************LCD1602写指令*******************************/
voidLCD_Write_Command(ucharcom)
{
LCD_Check_Busy();
LCD_RS=LOW;
LCD_RW=LOW;
_nop_();//一个_nop_();是一个机器周期,是1us
LCD_EN=HIGH;
LCD_IO=com;
LCD_EN=LOW;
}
/*****************LCD1602写数据**************************/
voidLCD_Write_Data(uchardat)
{
LCD_Check_Busy();
LCD_RS=HIGH;
LCD_RW=LOW;
_nop_();
LCD_EN=HIGH;
LCD_IO=dat;
LCD_EN=LOW;
}
/**********************显示一个字节**************************/
voidLCD_Char(ucharx,ucharline,uchardat)//从第x开始写一个字节
{
unsignedcharaddress;
if(line==LINE1)//line=0,为第一行
address=LINE1_HEAD+x;
else//否则为第二行
address=LINE2_HEAD+x;
LCD_Write_Command(address);
LCD_Write_Data(dat);
}
/******************LCD1602显示字符串*********************/
voidLCD_Str(ucharx,ucharline,uchar*Str)//从第line行的第x位置开始显示字符串
{
uchari=x;
if(line==LINE1)
{
while(*Str!
='\0')
LCD_Char(i++,0,*Str++);
}
else
{
while(*Str!
='\0')
LCD_Char(i++,1,*Str++);
}
}
/***************************LCD忙碌状态*******************************/
voidLCD_Check_Busy(void)//检测LCD状态,看它是不是还在忙呢
{
do
{
LCD_EN=0;
LCD_RS=0;
LCD_RW=1;
LCD_IO=0xff;
LCD_EN=1;
}
while(LCD_BUSY==1);
LCD_EN=0;
}
/*****************屏幕闪烁********************/
voidLCD_Flash(uchartime)//控制停留时间
{
LCD_Write_Command(LCD_DISPLAY_OFF);//关闭显示
DelayMs(time);//延时
LCD_Write_Command(LCD_DISPLAY_ON);//开显示
DelayMs(time);
LCD_Write_Command(LCD_DISPLAY_OFF);//关闭显示
DelayMs(time);//延时
LCD_Write_Command(LCD_DISPLAY_ON);//开显示
DelayMs(time);
}
voidPwm_set1(uintzkb)
{
high_time1=zkb;
}
voidPwm_set2(uintzkb)
{
high_time2=zkb;
}
voidTimer0Init(void)//AUXR&=0x7F;//定时器时钟12T模式
{
TMOD&=0xF0;//设置定时器模式
TMOD|=0x01;//设置定时器模式
TL0=0x66;//设置定时初值
TH0=0xFC;//设置定时初值
TF0=0;//清除TF0标志
TR0=1;//定时器0开始计时
EA=1;//开总中断
ET0=1;//开定时器0中断
}
voidTimer1Init(void)//AUXR&=0x7F;//定时器时钟12T模式
{
TMOD&=0x0F;//设置定时器模式
TMOD|=0x10;//设置定时器模式
TL1=0x66;//设置定时初值
TH1=0xFC;//设置定时初值
TF1=0;//清除TF0标志
TR1=1;//定时器0开始计时
EA=1;//开总中断
ET1=1;//开定时器1中断
}
voidserve_T0()interrupt1using1
{
TL0=0x66;//设置定时初值
TH0=0xFC;//设置定时初值
if(++count1<=(high_time1))
{
Output1=1;
}
elseif(count1<=100)
{
Output1=0;
}
elsecount1=0;
}
voidserve_T1()interrupt3
{
TL1=0x66;//设置定时初值
TH1=0xFC;//设置定时初值
if(++count2<=(high_time2))
{
Output2=1;
}
elseif(count2<=100)
{
Output2=0;
}
elsecount2=0;
}
voidgo(ucharS)
{
Pwm_set1(S);
Pwm_set2(S);
Z1=1;
Z2=0;
Y1=1;
Y2=0;
LCD_Str(5,1,"GO!
");
}
voidright(void)
{
Pwm_set1(80);
Pwm_set2(30);
Z1=1;
Z2=0;
Y1=1;
Y2=0;
LCD_Str(5,1,"TURNR");
}
voidleft(void)
{
Pwm_set1(30);
Pwm_set2(80);
Z1=1;
Z2=