本文介紹了一種基于51單片機(jī)的小車尋跡系統(tǒng)。該系統(tǒng)采用四組高靈敏度的光電對(duì)管,對(duì)路面黑色軌跡進(jìn)行檢測(cè),并利用單片機(jī)產(chǎn)生PWM波,控制小車速度。測(cè)試結(jié)果表明,該系統(tǒng)能夠平穩(wěn)跟蹤給定的路徑。整個(gè)系統(tǒng)基于普通玩具小車的機(jī)械結(jié)構(gòu),并利用了小車的底盤、前后輪電機(jī)及其自動(dòng)復(fù)原裝置,能夠平穩(wěn)跟蹤路面黑色軌跡運(yùn)行。
一、設(shè)計(jì)任務(wù)
1、 要求
(1)車輛從起跑線出發(fā),開始直線行走一段時(shí)間。當(dāng)遇到黑線時(shí),小車自動(dòng)檢測(cè)黑線并沿著黑線自動(dòng)行進(jìn)。黑線走完之后,直線行走。然后遇到障礙物,通過(guò)檢測(cè)障礙物離自己的距離來(lái)判斷自己是前進(jìn)、后退還是左轉(zhuǎn)、右轉(zhuǎn),最終達(dá)到繞過(guò)障礙物的目的。
(2)小車能夠自動(dòng)記錄,自動(dòng)顯示行駛時(shí)間,從而判斷是否符合要求。(1602必須安裝在小車上)。
二、方案論證與比教
1、小車控制單元的選擇
主控部分是整個(gè)小車的大腦,是整個(gè)小車運(yùn)行的核心部件,起著控制小車所有運(yùn)行狀態(tài)的作用。通常選用單片機(jī)作為小車的核心控制單元,本文以STC公司的STC89c52單片機(jī)為例予以介紹。STC89c52是一款擁有8K RAM、32 個(gè)I/O 口的單片機(jī),它還擁有豐富的處理功能,為小車的功能擴(kuò)展提供了相當(dāng)大的空間,只要按照該單片機(jī)的要求對(duì)其編制程序就可以實(shí)現(xiàn)很多不同的功能。此外,STC是國(guó)產(chǎn)單片機(jī),價(jià)格便宜,性價(jià)比更高。
2、小車驅(qū)動(dòng)單元的選擇
小車驅(qū)動(dòng)電機(jī)一般不使用現(xiàn)成的玩具小車上的配套直流電機(jī),而使用減速電機(jī),因?yàn)槠潋?qū)動(dòng)力更強(qiáng),優(yōu)于普通直流電機(jī)。考慮到小車必須能夠前進(jìn)、倒退、停止,并能靈活轉(zhuǎn)向,在左右兩輪各裝一個(gè)電機(jī)分別進(jìn)行驅(qū)動(dòng)。當(dāng)左輪電機(jī)轉(zhuǎn)速高于右輪電機(jī)轉(zhuǎn)速時(shí)小車向右轉(zhuǎn),反之則向左轉(zhuǎn)。為了能控制車輪的轉(zhuǎn)速,可以采取PWM調(diào)速法,即由單片機(jī)的IOB8、IOB9輸出一系列頻率固定的方波,再通過(guò)功率放大來(lái)驅(qū)動(dòng)電機(jī),在單片機(jī)中編程改變輸出方波的占空比就可以改變加到電機(jī)上的平均電壓,從而可以改變電機(jī)的轉(zhuǎn)速。左右輪兩個(gè)電機(jī)轉(zhuǎn)速的配合就可以實(shí)現(xiàn)小車的前進(jìn)、倒退、轉(zhuǎn)彎等功能。用單片機(jī)控制電機(jī)的時(shí)候,需要加驅(qū)動(dòng)電路,為電機(jī)提供足夠大的驅(qū)動(dòng)電流。使用不同的電機(jī),其驅(qū)動(dòng)電路也不同,我們應(yīng)該根據(jù)實(shí)際需求選擇合適的驅(qū)動(dòng)電路,通常有以下幾種方案:
方案一:
用三極管電流放大驅(qū)動(dòng)電路。三極管是一種常見(jiàn)的電子元器件,價(jià)格便宜,也很實(shí)用。但是,它僅能驅(qū)動(dòng)單個(gè)單片機(jī)并且要求所驅(qū)動(dòng)電流不大,而且用三極管搭建驅(qū)動(dòng)電路也很麻煩。
方案二:
用電機(jī)的專用芯片來(lái)驅(qū)動(dòng)電機(jī)。專用驅(qū)動(dòng)芯片雖說(shuō)價(jià)格稍微貴了一點(diǎn),但是,專用芯片可同時(shí)操作多可電機(jī),且電路連接簡(jiǎn)單,穩(wěn)定性好。是一種比較好的驅(qū)動(dòng)電路。
考慮到電機(jī)的穩(wěn)定性及便捷性,我們選擇了L298芯片作為驅(qū)動(dòng)電機(jī)的驅(qū)動(dòng)電路。
3、 軌跡檢測(cè)模塊選擇:
方案一、使用簡(jiǎn)易光電傳感器結(jié)合外圍電路探測(cè)。
光電傳感器實(shí)際效果并不理想,對(duì)行駛過(guò)程中的穩(wěn)定性要求很高,且誤測(cè)幾率較大、易受光線環(huán)境和路面介質(zhì)影響。在使用過(guò)程極易出現(xiàn)問(wèn)題,而且容易因?yàn)?該部件造成整個(gè)系統(tǒng)的不穩(wěn)定。
方案二、利用兩只光電開關(guān)。
分別置于軌道的兩側(cè),根據(jù)其接受到白線的先后來(lái)控制小車轉(zhuǎn)向來(lái)調(diào)整車向,但測(cè)試表明,如果兩只光電開關(guān)之間的距離很小,則約束了速度,如果著重于小車速度的提升,則隨著車速的提升,則勢(shì)必要求兩只光電開關(guān)之間的距離加大,從而使得小車的行駛路線脫離軌道幅度較大,小車將無(wú)法快速完成準(zhǔn)確的導(dǎo)向從而有可能導(dǎo)致尋跡失敗。
方案三、用四只光電開關(guān)。
一只置于軌道中間,兩只置于軌道外側(cè),當(dāng)小車脫離軌道時(shí),即當(dāng)置于中間的一只光電開關(guān)脫離軌道時(shí),等待外面任一只檢測(cè)到黑線后,做出相應(yīng)的轉(zhuǎn)向調(diào)整,直到中間的光電開關(guān)重新檢測(cè)到黑線(即回到軌道)再恢復(fù)正向行駛�,F(xiàn)場(chǎng)實(shí)測(cè)表明,雖然小車在尋跡過(guò)程中有一定的左右搖擺(因?yàn)樗?gòu)小車的內(nèi)部結(jié)構(gòu)決定了光電開光之間的距離到達(dá)不了精確計(jì)算值1厘米),但只要控制好行駛速度就可保證車身基本上接近于沿靠軌道行駛。綜合循跡的準(zhǔn)確性和行車的速度,我們選擇了方案三。并采用了ST168光電對(duì)管。因?yàn)镾T168采用高發(fā)射功率紅外光、電二極管和高靈敏光電晶體管組成,采用非接觸式檢測(cè)方式。ST168的檢測(cè)距離很小,一般為8~15毫米,因?yàn)?毫米以下是它的檢測(cè)盲區(qū),而大于15毫米則很容易受干擾。經(jīng)過(guò)多次測(cè)試、比較,發(fā)現(xiàn)把傳感器安裝在距離檢測(cè)物表面10毫米時(shí),檢測(cè)效果最好。
傳感器的安裝
正確選擇檢測(cè)方法和傳感器件是決定循跡效果的重要因素,而且正確的器件安裝方法也是循跡電路好壞的一個(gè)重要因素。從簡(jiǎn)單、方便、可靠等角度出發(fā),同時(shí)在底盤裝設(shè)4個(gè)紅外探測(cè)頭,進(jìn)行兩級(jí)方向糾正控制,將大大提高其循跡的可靠性,具體位置分布如圖1所示。圖中循跡傳感器全部在一條直線上。其中X1與Y1為第一級(jí)方向控制傳感器,X2與Y2為第二級(jí)方向控制傳感器,并且黑線同一邊的兩個(gè)傳感器之間的寬度不得大于黑線的寬度。小車前進(jìn)時(shí),始終保持(如圖3中所示的行走軌跡黑線)在X1和Y1這兩個(gè)第一級(jí)傳感器之間,當(dāng)小車偏離黑線時(shí),第一級(jí)傳感器就能檢測(cè)到黑線,把檢測(cè)的信號(hào)送給小車的處理、控制系統(tǒng),控制系統(tǒng)發(fā)出信號(hào)對(duì)小車軌跡予以糾正。第二級(jí)方向探測(cè)器實(shí)際是第一級(jí)的后備保護(hù),它的存在是考慮到小車由于慣性過(guò)大會(huì)依舊偏離軌道,再次對(duì)小車的運(yùn)動(dòng)進(jìn)行糾正,從而提高了小車循跡的可靠性。
圖1紅外探頭的分布圖
4、 顯示模塊選擇
方案一:
采用LED顯示,LED常見(jiàn)且價(jià)格便宜,但是僅能顯示數(shù)字和簡(jiǎn)單的字母,且外部電路比較復(fù)雜,需要專門的驅(qū)動(dòng)電路實(shí)現(xiàn)。
方案二
采用LCD1602顯示,1602是市面上價(jià)格適中的顯示器件,不僅僅能顯示數(shù)字還能顯示字母和特殊符號(hào),且支持背光解決了一半LCD顯示不夠清晰的問(wèn)題,且有3.3低電壓產(chǎn)品,更大的解決了功耗問(wèn)題。
方案三
采用LCD12864顯示,12864顯示效果清晰,且可以顯示更多信息,支持中文字庫(kù),有很好的人及交換功能,能顯示更多更清楚的信息。但是12864顯示器市場(chǎng)上價(jià)格比較昂貴,且功耗很大,在開背光的情況下耗電非常之大,不易于使用在移動(dòng)設(shè)備上。
方案選擇
使用LCD1602不僅價(jià)格適中,且功耗很低,用擁有較好的人機(jī)交互價(jià)值。且在此方案中僅顯示小車需要采集的數(shù)據(jù)即可,所以本系統(tǒng)采用了低功耗的1602顯示器,即方案二。
三、硬件設(shè)計(jì)
1、 最小系統(tǒng)的設(shè)計(jì),如圖2。
單片機(jī)最小系統(tǒng)能夠運(yùn)行的必要條件是:(1)電源,(2)晶振電路,(3)復(fù)位電路。
圖2
2、 LCD顯示電路
圖3
本次選用的1602液晶顯示屏最主要的是顯示小車運(yùn)行時(shí)間。我們將1602的液晶的D0~D7數(shù)據(jù)口接到單片機(jī)的P0口;然后將讀寫選擇端(R/W)直接接地;液晶顯示對(duì)比度調(diào)節(jié)端(VO)接一個(gè)10K的電位器,最后與電源正極相連;數(shù)據(jù)/命令選擇端(RS)P3^5口,是能信號(hào)(E)接P3^4口。如圖3。
3、 循跡模塊,如圖4
圖4
4、 避障模塊
采用超聲波模塊壁障。
四、軟件設(shè)計(jì)
軟件流程圖:
其程序控制框圖如圖5。小車進(jìn)入循跡模式后,即開始不停地掃描與探測(cè)器連接的單片機(jī)I/O口,一旦檢測(cè)到某個(gè)I/O口有信號(hào),即進(jìn)入判斷處理程序(switch),先確定4個(gè)探測(cè)器中的哪一個(gè)探測(cè)到了黑線,如果InfraredML(左面第一級(jí)傳感器)或者InfraredSL(左面第二級(jí)傳感器)探測(cè)到黑線,即小車左半部分壓到黑線,車身向右偏出,此時(shí)應(yīng)使小車向左轉(zhuǎn);如果是InfraredMR(右面第一級(jí)傳感 器)或InfraredSR(右面第二級(jí)傳感器)探測(cè)到了黑線,即車身右半部壓住黑線,小車向左偏出了軌跡,則應(yīng)使小車向右轉(zhuǎn)。在經(jīng)過(guò)了方向調(diào)整后,小車再繼續(xù)向前行走,并繼續(xù)探測(cè)黑線重復(fù)上述動(dòng)作。由于第二級(jí)方向控制為第一級(jí)的后備,則兩個(gè)等級(jí)間的轉(zhuǎn)向力度必須相互配合。第二級(jí)通常是在超出第一級(jí)的控制范圍的情況下發(fā)生作用,它也是最后一層保護(hù),所以它必須要保證小車回到正確軌跡上來(lái),則通常使第二級(jí)轉(zhuǎn)向力度大于第一級(jí),即level2>level1(level1、level2為小車轉(zhuǎn)向力度,其大小通過(guò)改變單片機(jī)輸出的占空比的大小來(lái)改變),具體數(shù)值在實(shí)地實(shí)驗(yàn)中得到。
圖5
五、作品總結(jié)
這個(gè)避障循跡小車使我們自己設(shè)計(jì)硬件、自己編輯軟件制作的。在制作的過(guò)程中,我們遇到了許多困難,在困難中也學(xué)到了許多知識(shí),亦得到了許多樂(lè)趣。在這個(gè)過(guò)程中,我懂得了,遇到困難不要退縮,只有迎難而上,方能成就大事。同時(shí),在學(xué)習(xí)的過(guò)程中,要注意細(xì)節(jié)。所謂細(xì)節(jié)決定成敗,便是如此。就像我們編輯軟件的時(shí)候,出現(xiàn)過(guò)這樣一個(gè)問(wèn)題,在現(xiàn)實(shí)時(shí)間這個(gè)模塊中,我們用定時(shí)器來(lái)定時(shí),50ms一次中斷,同時(shí)定義一個(gè)變量count,每次中斷count都自加一次,然后用if(count==20)來(lái)判斷時(shí)間,但結(jié)果卻是幾十秒才加一次,我們花了近一天的時(shí)間沒(méi)有想出結(jié)果,最后在學(xué)長(zhǎng)的幫助下將判斷時(shí)間的語(yǔ)句改為if(count>=20),結(jié)果與自己想的一模一樣了。所以我們?cè)谝院蟮膶W(xué)習(xí)過(guò)程中一定要注重理論知識(shí),不要把事情想得太簡(jiǎn)單了,這樣才可以提高我們的能力。
六、附錄
參考文獻(xiàn):《51單片C語(yǔ)言教程》 郭天祥
《C程序設(shè)計(jì)》 譚浩強(qiáng)
《全國(guó)大學(xué)生電子設(shè)計(jì)競(jìng)賽培訓(xùn)系列教程》 高吉
#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);
/*延時(shí)函數(shù)*/
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=120;y>0;y--);
}
/*10us的延時(shí)函數(shù)*/
void delay50us()
{
uchar i;
for(i=50;i>0;i--);
}
/*寫指令函數(shù)*/
void write_com(uchar com)
{
E=0;
RS=0;
delay(5);
E=1;
P0=com;
E=0;
}
/*寫數(shù)據(jù)函數(shù)*/
void write_data(uchar date)
{
E=0;
RS=1;
delay(5);
E=1;
P0 = date;
E=0;
RS=0;
}
/*初始化函數(shù)*/
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;
}
/*所的距離函數(shù)*/
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;
}
/*測(cè)得運(yùn)行時(shí)間的函數(shù)*/
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);
}
/*顯示離障礙物的距離函數(shù)*/
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);
}
/*小車前進(jìn)函數(shù)*/
void forward()
{
dj1 = 1;
dj2 = 0;
dj3 = 1;
dj4 = 0;
}
/*小車后退函數(shù)*/
void back()
{
dj1 = 0;
dj2 = 1;
dj3 = 0;
dj4 = 1;
}
/*小車左轉(zhuǎn)函數(shù)*/
void turnleft()
{
dj1 = 0;
dj2 = 1;
dj3 = 1;
dj4 = 0;
}
/*小車右轉(zhuǎn)函數(shù)*/
void turnright()
{
dj1 = 1;
dj2 = 0;
dj3 = 0;
dj4 = 1;
}
/*顯示時(shí)間和路程函數(shù)*/
void xianshi()
{
if(count>=20)
{
count = 0;
miao++;
write_data2(2,miao);
if(miao%3==0)
{
bell = 1;
delay(20);
bell = 0;
}
}
}
/*避障函數(shù)*/
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;
}
}
/*指示燈函數(shù) */
void zhishideng(uint p)
{
if(p>300)
{
C1 = 0;
B1 = 1;
}
else
{
B1 = 0;
C1 = 1;
bell = 1;
}
}
/*循跡函數(shù)*/
/*
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++;
}