专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

智能循迹避障小车设计

作者:佚名   来源:本站原创   点击数:  更新时间:2014年03月11日   【字体:

本文介绍了一种基于51单片机的小车寻迹系统。该系统采用四组高灵敏度的光电对管,对路面黑色轨迹进行检测,并利用单片机产生PWM波,控制小车速度。测试结果表明,该系统能够平稳跟踪给定的路径。整个系统基于普通玩具小车的机械结构,并利用了小车的底盘、前后轮电机及其自动复原装置,能够平稳跟踪路面黑色轨迹运行。

一、设计任务

1、 要求

(1)车辆从起跑线出发,开始直线行走一段时间。当遇到黑线时,小车自动检测黑线并沿着黑线自动行进。黑线走完之后,直线行走。然后遇到障碍物,通过检测障碍物离自己的距离来判断自己是前进、后退还是左转、右转,最终达到绕过障碍物的目的。

(2)小车能够自动记录,自动显示行驶时间,从而判断是否符合要求。(1602必须安装在小车上)。

二、方案论证与比教

1、小车控制单元的选择

主控部分是整个小车的大脑,是整个小车运行的核心部件,起着控制小车所有运行状态的作用。通常选用单片机作为小车的核心控制单元,本文以STC公司的STC89c52单片机为例予以介绍。STC89c52是一款拥有8K RAM、32 个I/O 口的单片机,它还拥有丰富的处理功能,为小车的功能扩展提供了相当大的空间,只要按照该单片机的要求对其编制程序就可以实现很多不同的功能。此外,STC是国产单片机,价格便宜,性价比更高。             

2、小车驱动单元的选择

小车驱动电机一般不使用现成的玩具小车上的配套直流电机,而使用减速电机,因为其驱动力更强,优于普通直流电机。考虑到小车必须能够前进、倒退、停止,并能灵活转向,在左右两轮各装一个电机分别进行驱动。当左轮电机转速高于右轮电机转速时小车向右转,反之则向左转。为了能控制车轮的转速,可以采取PWM调速法,即由单片机的IOB8、IOB9输出一系列频率固定的方波,再通过功率放大来驱动电机,在单片机中编程改变输出方波的占空比就可以改变加到电机上的平均电压,从而可以改变电机的转速。左右轮两个电机转速的配合就可以实现小车的前进、倒退、转弯等功能。用单片机控制电机的时候,需要加驱动电路,为电机提供足够大的驱动电流。使用不同的电机,其驱动电路也不同,我们应该根据实际需求选择合适的驱动电路,通常有以下几种方案:

方案一:

用三极管电流放大驱动电路。三极管是一种常见的电子元器件,价格便宜,也很实用。但是,它仅能驱动单个单片机并且要求所驱动电流不大,而且用三极管搭建驱动电路也很麻烦。

方案二:

用电机的专用芯片来驱动电机。专用驱动芯片虽说价格稍微贵了一点,但是,专用芯片可同时操作多可电机,且电路连接简单,稳定性好。是一种比较好的驱动电路。

考虑到电机的稳定性及便捷性,我们选择了L298芯片作为驱动电机的驱动电路。

3、 轨迹检测模块选择:

方案一、使用简易光电传感器结合外围电路探测。

 光电传感器实际效果并不理想,对行驶过程中的稳定性要求很高,且误测几率较大、易受光线环境和路面介质影响。在使用过程极易出现问题,而且容易因为 该部件造成整个系统的不稳定。

方案二、利用两只光电开关。

分别置于轨道的两侧,根据其接受到白线的先后来控制小车转向来调整车向,但测试表明,如果两只光电开关之间的距离很小,则约束了速度,如果着重于小车速度的提升,则随着车速的提升,则势必要求两只光电开关之间的距离加大,从而使得小车的行驶路线脱离轨道幅度较大,小车将无法快速完成准确的导向从而有可能导致寻迹失败。

方案三、用四只光电开关。

一只置于轨道中间,两只置于轨道外侧,当小车脱离轨道时,即当置于中间的一只光电开关脱离轨道时,等待外面任一只检测到黑线后,做出相应的转向调整,直到中间的光电开关重新检测到黑线(即回到轨道)再恢复正向行驶。现场实测表明,虽然小车在寻迹过程中有一定的左右摇摆(因为所购小车的内部结构决定了光电开光之间的距离到达不了精确计算值1厘米),但只要控制好行驶速度就可保证车身基本上接近于沿靠轨道行驶。综合循迹的准确性和行车的速度,我们选择了方案三。并采用了ST168光电对管。因为ST168采用高发射功率红外光、电二极管和高灵敏光电晶体管组成,采用非接触式检测方式。ST168的检测距离很小,一般为8~15毫米,因为8毫米以下是它的检测盲区,而大于15毫米则很容易受干扰。经过多次测试、比较,发现把传感器安装在距离检测物表面10毫米时,检测效果最好。

传感器的安装

  正确选择检测方法和传感器件是决定循迹效果的重要因素,而且正确的器件安装方法也是循迹电路好坏的一个重要因素。从简单、方便、可靠等角度出发,同时在底盘装设4个红外探测头,进行两级方向纠正控制,将大大提高其循迹的可靠性,具体位置分布如图1所示。图中循迹传感器全部在一条直线上。其中X1与Y1为第一级方向控制传感器,X2与Y2为第二级方向控制传感器,并且黑线同一边的两个传感器之间的宽度不得大于黑线的宽度。小车前进时,始终保持(如图3中所示的行走轨迹黑线)在X1和Y1这两个第一级传感器之间,当小车偏离黑线时,第一级传感器就能检测到黑线,把检测的信号送给小车的处理、控制系统,控制系统发出信号对小车轨迹予以纠正。第二级方向探测器实际是第一级的后备保护,它的存在是考虑到小车由于惯性过大会依旧偏离轨道,再次对小车的运动进行纠正,从而提高了小车循迹的可靠性。

图1红外探头的分布图

4、 显示模块选择

方案一:

 采用LED显示,LED常见且价格便宜,但是仅能显示数字和简单的字母,且外部电路比较复杂,需要专门的驱动电路实现。

方案二

     采用LCD1602显示,1602是市面上价格适中的显示器件,不仅仅能显示数字还能显示字母和特殊符号,且支持背光解决了一半LCD显示不够清晰的问题,且有3.3低电压产品,更大的解决了功耗问题。

方案三

采用LCD12864显示,12864显示效果清晰,且可以显示更多信息,支持中文字库,有很好的人及交换功能,能显示更多更清楚的信息。但是12864显示器市场上价格比较昂贵,且功耗很大,在开背光的情况下耗电非常之大,不易于使用在移动设备上。

方案选择

使用LCD1602不仅价格适中,且功耗很低,用拥有较好的人机交互价值。且在此方案中仅显示小车需要采集的数据即可,所以本系统采用了低功耗的1602显示器,即方案二。

三、硬件设计

1、 最小系统的设计,如图2。

单片机最小系统能够运行的必要条件是:(1)电源,(2)晶振电路,(3)复位电路。

图2

2、 LCD显示电路

图3

本次选用的1602液晶显示屏最主要的是显示小车运行时间。我们将1602的液晶的D0~D7数据口接到单片机的P0口;然后将读写选择端(R/W)直接接地;液晶显示对比度调节端(VO)接一个10K的电位器,最后与电源正极相连;数据/命令选择端(RS)P3^5口,是能信号(E)接P3^4口。如图3。

3、 循迹模块,如图4

图4

4、 避障模块

采用超声波模块壁障。

四、软件设计

软件流程图:

其程序控制框图如图5。小车进入循迹模式后,即开始不停地扫描与探测器连接的单片机I/O口,一旦检测到某个I/O口有信号,即进入判断处理程序(switch),先确定4个探测器中的哪一个探测到了黑线,如果InfraredML(左面第一级传感器)或者InfraredSL(左面第二级传感器)探测到黑线,即小车左半部分压到黑线,车身向右偏出,此时应使小车向左转;如果是InfraredMR(右面第一级传感 器)或InfraredSR(右面第二级传感器)探测到了黑线,即车身右半部压住黑线,小车向左偏出了轨迹,则应使小车向右转。在经过了方向调整后,小车再继续向前行走,并继续探测黑线重复上述动作。由于第二级方向控制为第一级的后备,则两个等级间的转向力度必须相互配合。第二级通常是在超出第一级的控制范围的情况下发生作用,它也是最后一层保护,所以它必须要保证小车回到正确轨迹上来,则通常使第二级转向力度大于第一级,即level2>level1(level1、level2为小车转向力度,其大小通过改变单片机输出的占空比的大小来改变),具体数值在实地实验中得到。
   

图5

五、作品总结

    这个避障循迹小车使我们自己设计硬件、自己编辑软件制作的。在制作的过程中,我们遇到了许多困难,在困难中也学到了许多知识,亦得到了许多乐趣。在这个过程中,我懂得了,遇到困难不要退缩,只有迎难而上,方能成就大事。同时,在学习的过程中,要注意细节。所谓细节决定成败,便是如此。就像我们编辑软件的时候,出现过这样一个问题,在现实时间这个模块中,我们用定时器来定时,50ms一次中断,同时定义一个变量count,每次中断count都自加一次,然后用if(count==20)来判断时间,但结果却是几十秒才加一次,我们花了近一天的时间没有想出结果,最后在学长的帮助下将判断时间的语句改为if(count>=20),结果与自己想的一模一样了。所以我们在以后的学习过程中一定要注重理论知识,不要把事情想得太简单了,这样才可以提高我们的能力。

六、附录

参考文献:《51单片C语言教程》  郭天祥

              《C程序设计》  谭浩强

《全国大学生电子设计竞赛培训系列教程》  高吉

#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define speed  170
uchar code aa1[]="distance:";
uchar code aa2[]="t=";
uchar code aa3[]="s=";

sbit E=P3^4;
sbit RS=P3^5;
sbit echo=P2^0;
sbit trig=P2^1;
sbit bell=P1^0;
sbit dj1=P1^4;
sbit dj2=P1^5;
sbit dj3=P1^6;
sbit dj4=P1^7;
sbit A1=P2^2;
sbit B1=P2^3;
sbit C1=P2^4;
/*
sbit left_red = P3^4;
sbit mid_red1 = P3^5;
sbit mid_red2 = P3^6;
sbit right_red = P3^7;
*/

uchar m,count,miao;
uint juli;

/*void xunji();*/
void delay(uint z);
void delay50us() ;
void write_com(uchar com);
void write_data(uchar date);
void init(void);
uint getdistance(void);
void write_data2(uchar add,uchar date);
void write_data3(uchar add,uchar date);
void write_date1(unsigned long x);
void forward(void);
void back(void);
void turnleft(void);
void turnright(void);
void xianshi(void);
void bizhang(uint x);
void zhishideng(uint p);

/*延时函数*/
void delay(uint z)
{
 uint x,y;
 for(x=z;x>0;x--)
  for(y=120;y>0;y--);
}

/*10us的延时函数*/
void delay50us()  
{
    uchar i;
    for(i=50;i>0;i--);
}

/*写指令函数*/
void write_com(uchar com)
{
 E=0;
 RS=0;
 delay(5);
 E=1;
 P0=com;
 E=0;
}

/*写数据函数*/
void write_data(uchar date)
{
 E=0;
 RS=1;
 delay(5);
 E=1;
 P0 = date;
 E=0;
 RS=0;
}

 

/*初始化函数*/
void init()
{
/*
 left_red =  0;
 mid_red1 =  1;
 mid_red2 =  1;
 right_red = 0;
*/
 TMOD = 0x11;
 trig=0;   //超声波模块控制端                        
    echo=0;   //超声波模块接收端
    TH0=0;
    TL0=0;
    TR0=0;
 A1=1;
 B1=1;
 C1=1;
 bell = 0;
 miao = 0;
 juli = 0;
 delay(15);
 write_com(0x38);
 delay(5);
 write_com(0x38);
 delay(5);
 write_com(0x08);
 write_com(0x01);
 write_com(0x06);
 write_com(0x0c);
 write_com(0x80+0x40);
 for(m=0;m<2;m++)
 {
  write_data(aa2

­­);
  delay(5);
 }
 write_com(0x80+0x40+6);
 for(m=0;m<2;m++)
 {
  write_data(aa3 ­);
  delay(5);
 }
 TH1 = (65535-50000)/256;
 TL1 = (65535-50000)%256;
 EA = 1;
 ET1 = 1;
 TR1 = 1;
}

/*所的距离函数*/
uint getdistance()
{
 uint time;
 uint i = 65530;
 TR0 = 0;
 TH0 = 0;
 TL0 = 0;
 echo = 1;
 trig = 1;
 delay50us();
 trig = 0;
 while((echo==0)&&(i>10))
 {
  i--;
 }
 if(echo==1)TR0=1;
 else TR0=0;
 i=0;
 while((echo==1)&&(i<4672))
 {
  i++;
 }
    TR0=0;
 time=(TH0*0x0100)+TL0;
 return time;
}

/*测得运行时间的函数*/
void write_data2(uchar add,uchar date)
{
 uchar bai,shi,ge;
 bai = date/100;
 shi = date%100/10;
 ge = date%10;
 write_com(0x80+0x40+add);
 write_data(0x30+bai);
 write_data(0x30+shi);
 write_data(0x30+ge);
}

 
/*显示离障碍物的距离函数*/
void write_date1(unsigned long x)
{
 uchar qian,bai,shi,ge;
 qian = x/1000;
 bai = (x%1000)/100;
 shi = (x%100)/10;
 ge = x%10;
 write_com(0x80);
 for(m=0;m<9;m++)
 {
  write_data(aa1

­­­);
  delay(1);
 }
 write_com(0x80+10);
 write_data(0x30+qian);
 delay(1);
 write_data(0x30+bai);
 delay(1);
 write_data(0x30+shi);
 delay(1);
 write_data(0x30+ge);
 delay(1);
}

/*小车前进函数*/
void forward()
{
 dj1 = 1;
 dj2 = 0;
 dj3 = 1;
 dj4 = 0;
}

/*小车后退函数*/
void back()
{
 dj1 = 0;
 dj2 = 1;
 dj3 = 0;
 dj4 = 1;
}

/*小车左转函数*/
void turnleft()
{
 dj1 = 0;
 dj2 = 1;
 dj3 = 1;
 dj4 = 0;
}

/*小车右转函数*/
void turnright()
{
 dj1 = 1;
 dj2 = 0;
 dj3 = 0;
 dj4 = 1;
}

/*显示时间和路程函数*/
void  xianshi()
{
  if(count>=20)
  {
   count = 0;
   miao++;
   write_data2(2,miao);
   if(miao%3==0)
   {
    bell = 1;
    delay(20);
    bell = 0;
   }
  }
}

/*避障函数*/
void bizhang(uint x)
{
  switch(x/100)
  {
   case 0:
   case 1:
   case 2: back();break;
   case 3:
   case 4:
   case 5: forward();break;
   default: forward();break;
  }
}

/*指示灯函数 */
void zhishideng(uint p)
{
 if(p>300)
 {
  C1 = 0;
  B1 = 1;
 }
 else
 {
  B1 = 0;
  C1 = 1;
  bell = 1;
 }
 
}

/*循迹函数*/
/*
void xunji()
{

 uchar flag;
 if((left_red==1)||(mid_red1==1))
  flag = 1;
 else
  if((right_red==1)||(mid_red2==1))
   flag = 2;
  else
   if((mid_red2==1)&&(mid_red1==1))
    flag = 3;
   else
    flag = 0;

 switch(flag)
 {
  case 0:forward();break;
  case 1:turn_left();break;
  case 2:turn_right();break;
  case 3:forward();break;
  default:break;
 }
}
*/

void main() 
{
    uint dis;
  init();
 while(1)
   {
    A1 = 0;
  xianshi();
  /*xunji();*/
  dis= getdistance();
  dis = (uint)(dis*0.17);
  zhishideng(dis);
     write_date1(dis);
  bizhang(dis);
 }
}

void timer0() interrupt 3
{
    TH1 = (65535-50000)/256;
 TL1 = (65535-50000)%256;
 count++;
}

关闭窗口

相关文章