前 言
随着生产自动化的发展需要,机器人已经越来越广泛地应用到生产自动化上,随着科学技术的发展,机器人的传感器种类也越来越多,其中红外传感器已经成为自动行走和驾驶的重要部件。
红外的典型应用领域为自主式智能导航系统,机器人要实现自动避障功能就必须要感知障碍物,感知障碍物相当给机器人一个视觉功能。智能避障是基于红外传感系统,采用红外传感器实现前方障碍物检测,并判断障碍物远近。而利用红外对不同颜色物体反射强弱差别又可以实现循迹功能。
由于时间和水平有限,我们暂选最基本的避障,循迹功能作为此次设计的目标。
本设计通过小车这个载体再结合由STC89C52为核心的控制板可以达到其基本功能,再辅加由漫反射式光电开关组成的避障电路,基于PWM技术的转速控制、电源电路、电机驱动电路就可以完善整个设计。
1.【摘 要】:本文提出一种智能避障,循迹小车的设计方法,利用红外技术检测障碍物,轨迹信息,采用STC89C52单片机进行实时控制,实现智能避障,智能小车采用前轮驱动,两轮各用一个直流电机控制,避障,循迹用的传感器采用红外漫反射式传感器。
【关键词】: 避障 循迹 光电开关 差分控制
PWM LCD 测速
2. 功能概述
智能小车采用前轮驱动,前轮左右两边各用一个电机驱动,分别控制两个轮子的转动从而达到转向的目的,后轮是万向轮,起支撑的作用。避障部分,将3个红外线光电传感器分别装在车体的左中右,当车的左边的传感器检测到障碍物时,主控芯片控制右轮电机停止左轮转动,车向右方转向,当车的右边传感器检测到障碍物时,主控芯片控制左轮电机停止转动,车向左方转向,当前面有障碍物时规定车右转。而当小车同时有两个传感器接收到信号时,采用倒退方式转弯以避免碰到障碍物,于此同时测定速度并显示,在避障小车前进的同时从LCD点阵液晶显示器上显示小车当时速度和行驶的路程。
循迹部分,采用七个红外传感器置于车身前下方,中间五个主要用于循迹普通道路,外边两个略比中间的靠前,主要用来检测直角弯道。车向左偏时右拐,右偏时左拐,左右拐又分为校正和转弯两档。遇到直角时极易冲出跑道,故给车施加一个反向脉冲。行驶过程中显示屏显示车速及路程。
3.硬件设计
如下图所示,是本次设计智能小车的电路框图。以STC89C52为电路的中央处理器,来处理传感器采集来的数据,处理完毕之后以便去控制电机驱动电路来驱动电机。电源部分是为整个电路模块提供电源,以便能正常工作。
4. 避障电路
(1) 障碍物探测方案的选择
方案一:脉冲调制的反射式红外线发射接受器。由于采用该有交流分量的调制信号,则可大幅度减少外界干扰;另外红外线接受官的最大工作电流取决于平均电流。如果采用占空比小的调制信号,再品均电流不变的情况下,顺势电流很大(50—100mA),则大大提高了信噪比。并且其反应灵敏,外围电路也很简单。它的优点是消除了外界光线的干扰提高了灵敏度。
方案二:采用超声波传感器,如果传感器接收到反射的超声波,则通知单片机前方有障碍物,如则通知单片机可以向前行驶。市场上很多红外光电探头也都是基于这个原理。这样不但能准确完成测量,而且能避免电路的复杂性
由以上两种方案比较可知。方案一要比方案二优势大,市场上很多红外观点探头也都基于这个原理。其电路简单,工作可靠,性能比较稳定。从而避免了电路的复杂性,因此我先用方案二作为小车的监测系统。
避障电路采用漫反射式光电开关进行避障。光电开关是集发射头和接收头于一体的检测开关,其工作原理是根据发射头发出的光束,被障碍物反射,接收头据此做出判断是否有障碍物。当有光线反射回来时,输出低电平;当没有光线反射回来时,输出高电平。单片机根据接收头电平的高低做出相应控制,避免小车碰到障碍物,由于接收管输出TTL电平,有利于单片机对信号的处理。
光电开关工作原理:
光电开关是通过把光强度的变化转换成电信号的变化来实现控制的。
光电开关在一般情况下,有三部分构成,它们分为:发送器、接收器和检测电路。
避障电路功能表: | |||||
传感器 | 避障电路输出(上升沿动作) | 待执行命令 | |||
左 | 中 | 右 | 左转信号(P2.1) | 右转信号(P2.0) | |
0 | 0 | 0 | √ | 后右转 | |
0 | 0 | 1 | √ | 右转 | |
0 | 1 | 0 | √ | 右转 | |
0 | 1 | 1 | √ | 右转 | |
1 | 0 | 0 | √ | 左转 | |
1 | 0 | 1 | √ | 右转 | |
1 | 1 | 0 | √ | 左转 | |
1 | 1 | 1 | 前进 |
注解(“0”表示有障碍物; “1”表示无障碍物)
(2)信号检测模块
小车循迹原理是小车在画有黑线的白纸
红外探测法,即利用红外线在不同颜色的物理表面具有不同的反射性质的特点。在小车行驶过程中不断地向地面发射红外光,当红外光遇到白色地面时发生漫发射,反射光被装在小车上的接收管接收;如果遇到黑线则红外光被吸收,则小车上的接收管接收不到信号,再通过LM324作比较器来采集高低电平,从而实现信号的检测。避障亦是此原理。电路图如图3.4。
市面上有很多红外传感器,在这里我选用TCRT5000型光电对管。
图3.4循迹原理图
4. 单片机电路
本设计的主控芯片选择STC89C52,负责检测传感器的状态并向电机驱动电路发出动作命令。复位电路采用手动复位。
单片机电路如下:
5. 电机转速控制电路
转速控制采用基于PWM技术的脉冲调制技术,通过单片机输出两列PWM信号,经过l298N对电机进行速度调控。
6. 电源电路
本系统所有芯片都需要+5V的工作电压,而干电池只能提供的电压为1.5V的倍数的电压,并且随着使用时间的延长,其电压会逐渐下降,故采用了一个12v蓄电池,再用LM7805稳压芯片。L7805能提供最大1A的电流,足以满足芯片供电的要求。虽然微处理器和微控制器不需要支持电路,功耗也很低,但必须要加以考虑。
电源电路拟定为:
7.电机驱动电路
市场上用很多种类的小电压直流电动机,很方便的选择到。主要有普通电动机、和步进电动机。
方案一:采用步进电机,步进电动机的一个显著的特点就是具有快速启动和停止能力,能够达到我们所要求的标准。如果负荷不超过步进电机所能提供的动态转矩值,就能够立即是步进电机启动或反转。其转换灵敏度比较高。正转、反转控制灵活。但是步进电机的价格比较昂贵,对于我们的现状相差太远。
方案二:采用普通的直流电机。直流电机具有优良的调速特性,调速平滑、方便。调整范围广;过载能力强,能承受频繁的冲击负载,可实现频繁的无极快速启动、制动和反转。能满足各种不容的特殊运行要求。
由于普通直流电机价格适宜,更易于购买,并且电路相对简单,因此采用直流电机作为动力源
本设计采用L298N驱动使电机正反转从而做到前进,左转右转。
驱动电路(参考文献[4])
电机驱动一般采用H桥式驱动电路,L298N内部集成了H桥式驱动电路,从而可以采用L298N电路来驱动电机。通过单片机给予L298N电路PWM信号来控制小车的速度,起停。其引脚图如3.2,驱动原理图如图3.3。
图3.2 L298N引脚图
图3.3 电机驱动电路
源程序:
#include<reg52.h>
#include <intrins.h>
#include <string.h>
#define Busy 0x80 //用于检测LCM状态字中的Busy标识
#define LCM_Data P0
#define uchar unsigned char
sbit LCM_RS=P2^2; //1602寄存器选择
sbit LCM_RW=P2^1; //读/写控制
sbit LCM_E=P2^0;
sbit LLT=P1^7;//避障口
sbit RLT=P2^7;
sbit MLT=P3^4;
sbit S1=P1^0; 传感器信号输入端口
sbit S2=P1^1;
sbit S3=P1^3;//INT
sbit S4=P1^5;//LLT
sbit S5=P1^6;//RLT
sbit S6=P1^2;
sbit S7=P1^4;//OUT
sbit LH=P2^3; 电机1 左轮
sbit LL=P2^4;
sbit RH=P2^5; 电机2 右轮
sbit RL=P2^6;
sbit ENA=P3^6;//电机左轮使能
sbit ENB=P3^7;//电机右轮使能
sbit key1=P3^0;
sbit key2=P3^1;
sbit key3=P3^2;
sbit key4=P3^3;
/******************************************************
* 名 称:引脚定义
* 功 能:引脚定义
* 入口参数:无
* 出口参数:无
*******************************************************/
unsigned char
character1[][8]={{0x2,0x02,0x2,0x02,0x1F,0x02,0x06,0x7},{0x00,0x0F,0x8,0x08,0xF,0x08,0x08,0x0F},
{0x00,0x1C,0x4,0x4,0x1C,0x4,0x4,0x1C},{0xA,0xA,0x2,0x02,0x02,0x02,0x02,0x2},{0x19,0x09,0x8,0x08,0x8,0x0A,0x0C,0x08},
{0x2,0x4,0x18,0x10,0x8,0x4,0x2,0x03}};
int C1=0,C2=0,C3;//计数单位
static int SU0=0,SU1=0,SU2=0,SU=0;
static int SU3=0,V=0,V0=0,V1=0;
int tmp1=0,tmp2=0;//左右占空比调整tmp/T
int SPEED1,SPEED2;
uchar count=0; //初始化计数变量
uchar T=90; //pwm信号周期T*100us
/***********************************************************************
测距模块儿
***********************************************************************/
void delay10us() //延时,>10us
{
_nop_( );
_nop_( );
_nop_( );
_nop_( );
_nop_( );
_nop_( );
_nop_( );
_nop_( );
}
int mainceju()
{
char T;
int START,END,longth;
S3=0;S4=0;//初始化接口值
S4=1; //开始发射脉冲
delay10us(); //延时
while(!S3); //等待返回信号
START=(TH2*256+TL2);//开始计时
while(S3); //等待返回信号结束
END=(TH2*256+TL2);//关闭计时器
T=(TH0*256+TL0); //计算返回脉冲时间
lnotallow=340*T/20000;//计算测距结果厘米
return(longth);
}
/*****************************************************************************************************
循迹模块儿
****************************************************************************************************/
/******************************************************
*名 称elay(long int Delay_time)
*功 能:延时
*入口参数:Delay_time
*出口参数:无、
*说 明: 延时
******************************************************/
void delay(long int Delay_time)//延时函数
{
int i;
while(Delay_time)
{
for(i=0; i<150;i++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
Delay_time--;
}
}
//-----------------------------------------------
void correct_left()//向左校正,赋值
{
tmp1=T-30;
tmp2=T-15;
LH=1;
LL=0;
RH=1;
RL=0;
}
//------------------------------------------------
向右校正,赋值
{
tmp1=T-15;
tmp2=T-30;
LH=1;
LL=0;
RH=1;
RL=0;
}
//--------------------------------------------------
void turn_left()//左转,赋值
{
tmp1=0;
tmp2=T-25;
LH=0; 转弯时一个正转,一个反转,
LL=0;
RH=1;
RL=0;
}
//---------------------------------------------------
void turn_right()//右转,赋值
{
tmp1=T-25;
tmp2=0;
LH=1; 转弯时一个正转,一个反转,
LL=0;
RH=0;
RL=0;
}
//-----------------------------------------------------
void straightL() //慢直走,赋值
{
tmp1=T-40; //左右电机占空比初始化,调节直线运动速度
tmp2=T-40;
LH=1;
LL=0;
RH=1;
RL=0;
}
//-----------------------------------------------------
void straight() //直走,赋值
{
tmp1=T-10; //左右电机占空比初始化,调节直线运动速度
tmp2=T-10;
LH=1;
LL=0;
RH=1;
RL=0;
}
//-----------------------------------------------------
void back() //后退
{
tmp1=T-20; //左右电机占空比初始化,调节直线运动速度
tmp2=T-20; //鉴于左右轮电机内部阻力不同,故占空比取不同值,这组值需要单独写程序取值
LH=0;
LL=1;
RH=0;
RL=1;
}
//-----------------------------------------------------
void stop() //停车
{
tmp1=0;
tmp2=0;
LH=0;
LL=0;
RH=0;
RL=0;
ENA=0;
ENB=0;
}
/******************************************************
* 名 称:void turn()
* 功 能:路径控制
* 入口参数:int Position
* 出口参数:无
******************************************************/
void turn ()
{
if(!(S1|S2|S3|S4|S5|S6|S7))
{
_nop_();
_nop_();
back();
delay(20);
if((!S1)&(!S2)&S4&(!S6)&(!S7))
{
if(S4&S5&S6);
else
back();
}
}//后退寻线
//全白后退寻线
if(S1&S2&S3&S4&S5&S6&S7)
{
delay(1);
stop();
back();
delay(1);
}//全黑,停止
if( (S1&S3&(!S7))||(S1&S4&(!S7))||(S2&S4&(!S7)) )//遇到左直角
{
back();
delay(5);
turn_left();
delay(5);
while( (S1&S3&(!S7))||(S1&S4&(!S7))||(S2&S4&(!S7)) )
turn_left();
//delay(50);
}
if( (S5&S7&(!S1))||(S4&S7&(!S1))||(S4&S6&(!S1)) )//遇到右直角
{
back();
delay(5);
turn_right();
delay(5);
while( (S5&S7&(!S1))||(S4&S7&(!S1))||(S4&S6&(!S1)) )
turn_right();
//delay(50);
}
if((!S1)&(!S2)&S4&(!S6)&(!S7))
{
_nop_();
_nop_();
_nop_();
_nop_();
if(S3&S4&S5)//避免死循环
{
back();
delay(20);
if(S3&S4)
{
turn_left();
delay(5);
}
if(S4&S5)
{
turn_right();
delay(5);
}
}
else
straight();
}//全速速前行
if(!(S4|S5|S6|S7))
{
_nop_();
_nop_();
_nop_();
if((!S1)&S3)
correct_left(); //校正
if( ((!S3)&S2)||(S1&(!S3)) )
turn_left(); //左拐
if(S1&S2&S3)//遇到左直角
{
back();
delay(5);
while(S1&S2&S3)
turn_left();
delay(5);
}
}
if( !(S1|S2|S3|S4))
{
_nop_();
_nop_();
_nop_();
_nop_();
if(S5&(!S7))
correct_right();//校正
if( ( (!S5)&S6 )||((!S5)&S7) )
turn_right(); //右拐
if(S5&S6&S7)//遇到右直角
{
back();
delay(5);
while(S5&S6&S7)
turn_right();
delay(5);
}
}
}
/****************************************************************************************************
避障模块儿
*****************************************************************************************************/
void Rback()
{
RH=0;
RL=1;
LH=0;
LL=0;
}
void Lback()
{
RH=0;
RL=0;
LH=0;
LL=1;
}
void mainbizhang()
{
if((!LLT)&MLT&(!RLT))//010
{
Rback();
delay(20);
}
if((LLT)&(!MLT)&(!RLT))//100
turn_left();
if((!LLT)&(!MLT)&(RLT))//001
turn_right();
if((!LLT)&(!MLT)&(!RLT))//000
{
Rback();
delay(15);
}
if((!LLT)&(MLT)&(RLT))//011
turn_right();
if((LLT)&(!MLT)&(RLT))//101
turn_left();
if((LLT)&(MLT)&(!RLT))//110
turn_left();
if((LLT)&(MLT)&(RLT))//111
straight();
}
/*****************************************************************************************************
测速显示子程序
*****************************************************************************************************/
//=====================================================================================
//延时程序
//=====================================================================================
void Delay5Ms()
{
unsigned long int TempCyc = 5000;
while(TempCyc--);
}
uchar ReadStatusLCM()
{
char i;
uchar busy;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 1;
for(i=0;i<100;i++);
busy=(P0&0x80);
LCM_E = 0;
return(busy);
}
//=====================================================================================
//读写子程序
//=====================================================================================
//读数据
unsigned char ReadDataLCM()
{
while (ReadStatusLCM());
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 1;
return(LCM_Data);
}
//读状态
//写数据
void WriteDataLCM(unsigned char WDLCM)
{char i;
while (ReadStatusLCM());
LCM_Data = WDLCM;
LCM_RS = 1;
LCM_RW = 0;
LCM_E=0;
LCM_E = 1;
若晶振速度太高可以在这后加小的延时
for(i=0;i<100;i++);//延时
LCM_E =0;
}
//写指令
void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测
{
char i;
while(ReadStatusLCM());
LCM_Data = WCLCM;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 0;
LCM_E = 1;
for(i=0;i<100;i++);
LCM_E = 0;
}
//=====================================================================================
//初始化子程序
//=====================================================================================
void LCMInit(void) //LCM初始化
{
LCM_Data = 0;
WriteCommandLCM(0x38,0); // 三次显示模式设置,不检测忙信号
Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,1); // 显示模式设置,开始要求每次检测忙信号
Delay5Ms();
WriteCommandLCM(0x08,1); // 关闭显示
Delay5Ms();
WriteCommandLCM(0x01,1); // 清屏
Delay5Ms();
WriteCommandLCM(0x06,1); // 显示光标移动设置
Delay5Ms();
WriteCommandLCM(0x0c,1); // 显示开及光标设置
Delay5Ms();
}
//=====================================================================================
//按指定位置显示一个字符
//=====================================================================================
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
X |= 0x80;
算出指令码
WriteCommandLCM(X, Y); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
//=====================================================================================
//按指定位置显示一串字符
//void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
//说明: x(0-15):x参数 y(0-1):y参数 DData(字符串):要显示的内容(英文、数字、符号)
//=====================================================================================
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
unsigned char ListLength,j;
ListLength = strlen(DData);
Y &= 0x1;
限制X不能大于15,Y不能大于1
坐标应小于0xF
{
for(j=0;j<ListLength;j++)
{
显示单个字符
X++;
}
}
}
//=====================================================================================
//显示自定义字符
//void mychar(char xx,char yy,unsigned char *character,unsigned char saveto)
//说明:xx(0-15):为x参数.yy(0-1):y参数.character:要显示的字符的列表地址,在程序前面有定义
//saveto(1-7)为字符保存的RAM,每屏最多显示7个自定义字符
//(0x00-0x0h是自定义字符)
//=====================================================================================
//=====================================================================================
//主函数
//=====================================================================================
void mychar(char xx,char yy,unsigned char *character,unsigned char saveto)
{
unsigned char add = (saveto<<3) | 0x40;
临时变量,每一行的值
for(o=0;o<8;o++)
{
t=*(character+o);
WriteCommandLCM(add+o, 0);
WriteDataLCM(t);
}
显示字符
}//====
void main1602()
{
char m,n;
char k=0,l=1;
LCMInit();//1602初始化
delay(400);
for(m=0;m<2;m++)
for(n=0;n<3;n++,k++,l++)
{
mychar(n+13,m, character1[k],l);
}
// WriteCommandLCM(0x01,1); //清屏*/
DisplayListChar(0,0,"V=00.0m/s");
DisplayListChar(0,1,"S=000.0m");
}
/////////////////////////////////////////////////////////////////////////////
void refresh()//刷新屏幕
{
//SU=(TH1*256+TL1)/1000;
SU=(TH1*256+TL1)/100;
if(SU!=0)
V=(TH1*256+TL1)/10-SU3;
//if()
//V=SU*10/C3;
SU3=(TH1*256+TL1)/10;
SU0=SU%10;
SU1=(SU%100)/10;
SU2=SU/100;
V0=(V)%10;
V1=(V)/10;
SU0=SU0|0x30;;
SU1=SU1|0x30;
SU2=SU2|0x30;
V0=V0|0x30;
V1=V1|0x30;
DisplayOneChar(2,0,V1);
DisplayOneChar(3,0,V0);
DisplayOneChar(3,1,SU2);
DisplayOneChar(4,1,SU1);
DisplayOneChar(6,1,SU0);
}
void averrage()
{
V=SU/C3;
V0=(V)%10;
V1=(V)/10;
V0=V0|0x30;
V1=V1|0x30;
DisplayOneChar(2,0,V1);
DisplayOneChar(3,0,V0);
}
/*********************************************************************************************
1602测速模块儿子程序
***************************************************************************************************/
/******************************************************
*名 称:void begin()
*功 能:初始化
*入口参数:无
*出口参数:无
*说 明: 初始化各个参数
*******************************************************/
void begin()
{
key1=1;key2=1;
key3=1;key4=1;
TMOD=0X52;
TH1=0; /* 装入定时器的初值,计数100次溢出 */
TL1=0;
TH0=0x9B; /* 装入定时器的初值,计数100次溢出 */
TL0=0x9B; /*装入时间常数*/
EA=1; /* 开中断 */
ET1=1;
ET0=1;/* 定时器1允许中断 */
ET2=1;
TR2=1;
TR1=1; /* 启动定时器1 */
/* 定时器0允许中断 */
TR0=1;
RCAP2H=(65536-10000)/256;
RCAP2L=(65536-10000)%256;
LH=0;
LL=0;
RH=0;
RL=0;
ENA=0;
ENB=0;
}
/******void scan()
{
if(!key1)
{
delay(15);
if(!key1)
// while(1)
// {
//begin();//初始化
//main1602();
do{turn();
} while(key1&key2&key3&key4);
// }
}
if(!key2)
{
delay(15);
if(!key2)
//while(1)
//{
//begin();//初始化
//main1602();
do{mainbizhang();
}while(key1&key2&key3&key4);
//}
}
if(!key3)
{
delay(15);
if(!key3)
//{
// ET0=0;
// ET1=0;
do{averrage();
}while(key1&key2&key3&key4);
// }
}
if(!key4)
{
delay(15);
if(!key4)
// {
do{stop();
}while(key1&key2&key3&key4);
// ET0=0;
// ET1=0;
//}
}
}*/
/******************************************************
* 名 称:void timer0() interrupt 1
* 功 能: T0中断服务程序
* 入口参数:无
* 出口参数:无
*******************************************************/
void timer1() interrupt 5
{
TF2=0;
C2++;
if((C2%100)==0)
{
C3++;//每秒刷新一次
refresh();
}
//scan();
}
void timer0() interrupt 1 /* T0中断服务程序 */
{
if(count==0)
{
SPEED1=tmp1;
SPEED2=tmp2;
}
if(count<SPEED1) ENA=1;
else ENA=0; /* 产生电机1的PWM信号 */
if(count<SPEED2) ENB=1;
else ENB=0; /* 产生电机2的PWM信号 */
count++;
if(count>=T) count=0; /* 1个PWM信号由100次中断产生 */
}
void count0() interrupt 3
{
TH1=0/256; /* 装入定时器的初值,计数100次溢出 */
TL1=0%256; /*装入时间常数*/
C1++;
//s=x96000/48/4*3*13/2
}
void main()
{
begin();//初始化
main1602();//显示模块儿初始化
while(1)
{
if(key1==0)
{delay(1);
if(key1==0)
do turn();while(key1&key2&key3&key4);}
if(key2==0)
{
delay(1);
if(key2==0)
do mainbizhang();
while(key1&key2&key3&key4);
}
if(key3==0)
{
delay(1);
if(key3==0)
do averrage();while(key1&key2&key3&key4);
}
if(key4==0)
{
delay(1);
if(key3==0)
do stop();
while(key1&key2&key3&key4);
}
}
}
标签:delay,避障,小车,void,IoT,char,sbit,LCM,nop From: https://blog.51cto.com/u_15284384/6030935