寻迹小车开发日记
這幾天實驗室有一個關于大一的比賽,說是讓開發一個智能尋跡小車。但是作為大三的我,怎能不給他們做個榜樣呢?于是我就默默的自己試著開發了一下這個智能車。經過了十多天的時間(中間一個星期在等原件),第四版尋跡小車制作完成。但是現在我還沒有告訴我們實驗室大一的學弟我在做這個,讓他們自己先琢磨一會。
賽道如下圖:
比賽項目的大致情況就是這樣。
下面就是我?每一次開發的過程記錄,我就直接把當時的筆記摘抄在下面了。
硬件資源:STC89C52RC,12M晶振,LM298N兩路驅動,TT馬達,四路尋跡模塊,整車采用前面兩輪驅動,后面一個萬向輪結構。
2017.11.15?晚
資料調查:
紅外探測傳感器由于發出的是紅外光,常見光對它的干擾極小,且由于價格便宜,而被廣泛應用于智能小車的循線、避障以及其它機器人中物料檢測、灰度檢測等系統中。
紅外光電管有兩種:
一種是無色透明的LED,此為發射管,它通電后能夠產生人眼不可見紅外光;
另一種為黑色不透明LED,為接收部分,它內部的電阻會隨著接收到紅外光的多少而變化。
檢測原理:
由于黑色吸光,當紅外發射管照射在黑色物體上時反射回來的光就較少,接收管接收到的紅外光就較少,表現為電阻大,經施密特觸發器整形后輸出高電平;
同理,當照射在白色表面時發射的紅外線就比較多,表現為接收管的電阻較小,經施密特觸發器整形后輸出低電平。
此時再將此電平狀態送到單片機的I/O口,單片機就可以判斷是黑白路面,進而完成相應的功能,如循跡、避障等。
總結:
黑線------------高電平
白線------------低電平
暫時還沒有足夠的原件來實際實驗,只能通過示波器來查看端口輸出的pwm波形是否符合邏輯。
2017.11.18 下午
修改了程序,第二版test_2,示波器輸出pwm波很好,經過黑線也能停車,但是現在還沒有最小系統版,不能實際實驗,只有等到原件全部到了實驗一下。感覺還是C語言基礎薄弱,在一個文件中知道該怎么寫,但是實際中大工程都是分文件編寫的,分文件編寫的時候就容易出錯,變量的調用了就容易搞混掉!!加強C語言的練習!!
2017.11.24?上午
今天終于可以進行實際實驗了,但是發現小車很不穩定,主要有一下幾點的問題。
1、尋跡的時候會沖出跑道。特別時轉向的時候。
2、由于尋跡模塊安裝的位置不合適,導致中間直線位置,三個紅外對管都沒有檢測到黑線,中間停車。
3、還有不知道哪里一直在響?電容?(現在知道了,其實是電機在響,當輸出的pwm占空比不足以驅動電機轉動時,電機會一直有很小的響聲,是電機一直在抖動狀態導致的,可增占空比)
我覺得關鍵是紅外對管安裝不合理。另外單片機響應太慢?
2017.11.25 上午
剛開始用兩節18650驅動L298N,能提供大概7.9V的電壓,實測輸入7.9V,輸出給單片機的5V,但是一直出現L298N一直響的問題,電機轉動很慢,響應也很慢,可能是驅動電壓不夠,因為L298N上面也是寫了要求輸入12V電壓。為什么輸入12V還是響?(我當時竟然以為是驅動芯片在響)
其實是中斷處理越快越好。比如,當右側紅外對管檢測到黑線時,說明車體偏左,此時急需右轉彎,芯片的處理速度能保證右側對管一檢測到黑線立馬給響應的變量賦予響應的值,就這種情況來說,程序立馬執行:
right_duty = 0; //3,占空比太低。,基本不轉 eft_duty = low_speed+1;這段話,給這兩個變量賦值。但是真正執行小車轉彎的程序段在中斷入口函數里面:
right_cnt++;//右輪pwm if(right_cnt <= right_duty){ right_go_ahead; } else{ right_stop; } if(right_cnt==cycle) //只允許20次中斷 { right_cnt=0; } 在這段程序里面完成pwm波的輸出 。
但是如果定時器定時時間過長的話,就需要比較長的時間才能進入中斷函數,才能比較right_cnt和right_duty的大小,進而才能決定電機的前進和停止。那么問題就來了,在定時器的這段時間里,小車有沒有已經跑出軌道了呢?這就要看“運氣”了。因為定時器是與程序主循環分開的,主循環的運行和定時器的計數是同時進行的。如果運氣好的話,當檢測到右側黑線的時候,剛好定時器快要溢出,那么right_duty 和left_duty 一旦被賦值,正好這時定時器產生溢出,進入中斷函數,執行響應的動作,也許能修正偏離的軌跡。但是如果你運氣不好的話,正好上一個中斷剛剛結束,下一個中斷剛剛開始,這時你就需要等待一個定時器的周期才能執行相應的動作。在單片機執行這個動作之前,他會延續上一個狀態運行。關鍵就是在等待的這段時間里,小車有沒有偏離軌道。偏離了,失敗;沒偏離,修正回來,繼續尋跡。其他情況同理可分析。
但是在一個程序里面,我們不允許有“運氣”成分的存在,我們要的是百分之百的按照我們設想的方法去執行。那么就考慮能不能縮短定時器的時間來減小“運氣”的存在。但這同時也帶來了另一個問題,定時器頻繁的打斷主程序的運行。導致運行效率的變慢。(在我的這個程序里,定時器設置的50us,一次主循環里面要被中斷打斷3次,可以說比較頻繁了)
那么此時就要靠自己的統籌規劃了,找到兩者之間的平衡點,既要保證動作的及時響應,又要不能過多的影響主程序的運行。
其實有時候也要考慮一下物理因素,不要總是從程序上找毛病。我這個小車的輪胎很滑,基本沒有什么抓地力,轉向的時候,很容易就沖出軌道,換了摩擦力比較大的輪胎之后,效果明顯好轉,但是程序還是不夠好,還是會出現“出軌現象”,比之前好了一點點。
2017.11.26 下午
到了第三版,現在小車終于能穩定尋跡了。之前是三個尋跡口,可能是由于主程序循環時間過長,導致不能很及時的轉向進而導致一直過沖,也讓我煩惱了一天。今天去圖書館查了資料,看到 別人都是 用的兩個尋跡口,剛剛抱著試試看的心態去掉中間一個尋跡口之后,沒想到效果很好。能很穩定的尋跡。
接下來在能穩定尋跡的基礎上,在兩邊加上兩個紅外對管,作為對轉彎的判斷。
反思:
剛剛我又寫了一個程序,使用兩個尋跡口,發現還是會過沖!這就非常值得反思了。同樣的設備,同樣的思想,為什么一個可以尋跡,一個不能尋跡呢?這就是寫程序的功力了。
尋跡可以,但是45度角直接沖了過去。速度太快。其實能看到在45角位置,左輪給了一個力,但是由于慣性,小車沖了過去。然后就進行后面的判斷,直行
在二次停車的時候,又感覺單片機的處理速度太快了。。。。。經過黑線的一瞬間,四個尋跡口全黑,這時flag標志加一。但是在經過的那瞬間單片機 主循環循環了兩次,導致flag一下加到了2,停車了。
得在全黑的時刻來一個延時。但是這個延時的長短需要反復測試,直到經過黑色線的時候正好加一。
因為中間兩個用來尋直線的紅外對管安裝在相對比較考前的位置,(這是為了在第一個45角地方轉彎用的)所以在小車遇到起點的時候,總是中間兩個傳感器先檢測到黑先,按照先前的設定,這是小車會快速向右轉彎,所以會出現在起點向右轉彎的情況。
2017.11.27 下午
目前第四代小車基本能完成題目要求,(穩定尋跡,在岔路口識別選擇轉向。兩圈自動停車)但是還不是很穩定。
缺陷:
1、偶爾會出現沖出跑道的現象,但是很少。
2、有時會出現第二次無法停車的情況,會直接向右轉彎,這是由于尋跡時小車左右晃動,導致在終點橫著的黑線處誤以為轉彎。如果能保持直行的話,就可以準確停車。
中間遇到了這個錯誤,記錄一下。
RAMSIZE(256)?
*** FATAL ERROR L211: I/O ERROR ON OUTPUT FILE:
EXCEPTION 0029H: ACCESS TO FILE DENIED
FILE: test_5_finally
Target not created
出現這個錯誤估計你就是新建工程的時候和其他工程放在了一起,然后你這個工程里面的文件名和其他工程的文件名重名了。這時,你只需要新建一個文件,然后在這個空文件里面重新建立剛才的那個工程,就可以了。養成好習慣,每個工程和所需的源文件和頭文件都打包放在一起。不要把多個工程都放在一個文件下面。路徑很容易出錯。
2017.11.27 晚
經過斷斷續續12天的開發,其實中間有一個星期的時間在等原件,當時沒有51系統板了。總共用來開發的時間大概也就一個星期。從剛開始半天寫了尋跡程序,信心滿滿的開始測試,但是第一版小車很容易沖出跑道很不穩定。反思一下就是剛開始沒有考慮中斷對主程序的影響,沒有考慮尋跡時的先后邏輯,if-else嵌套的時候設計的不合理。最重要的一點是當時還不會用keil4仿真程序,不知道用仿真來看程序運行時占用的時間。經過這個項目的開發之后,雖說功能很簡單,但是還是在這個過程中學會了很多東西。所以我想開放自己的一點點代碼。共同分享。如果大家發現程序的不足和可以改進的地方,歡迎在博客下面留言。
第四版尋跡小車源碼:
#include<reg52.h> #include <intrins.h> #include "main.h" #include "delay.h"/* 電機I/O口定義,500hz, 檔位20 檔 */ sbit left_en = P2^5; //電機使能 sbit Left_Motor_P1 = P2^3; sbit Left_Motor_P2 = P2^4;sbit right_en = P2^2; sbit Right_Motor_P1 = P2^1; sbit Right_Motor_P2 = P2^0;#define left_go {Left_Motor_P1 = 1;Left_Motor_P2 = 0;} #define left_back {Left_Motor_P1 = 0;Left_Motor_P2 = 1;} #define left_stop {Left_Motor_P1 = 1;Left_Motor_P2 = 1;}#define right_go {Right_Motor_P1 = 1;Right_Motor_P2 = 0;} #define right_back {Right_Motor_P1 = 0;Right_Motor_P2 = 1;} #define right_stop {Right_Motor_P1 = 1;Right_Motor_P2 = 1;}/*紅外對管I/O口定義*/ sbit Sensor_Right = P1^1; sbit Sensor_Left = P1^2; sbit turn_flag = P1^0;//作為45度角轉向標志 sbit turn_flag_2 = P1^3;//第一個45度角轉彎之后的矯正,防止沖出跑道/* 占空比變量定義 */ uint Low_Speed = 5; //經測試還是5最合適 uint Left_Forward_Duty = 5; /* 左電機 正轉 */ uint Right_Forward_Duty = 5; /* 右電機 正轉 *//* 占空比計數變量定義和 預設占空比 進行比較 */ uint Left_Forward_Cnt = 0; /* 左電機 正轉占空比計數 */ uint Right_Forward_Cnt = 0; /* 右電機 正轉占空比計數 */static flag = 0;sbit time_flag = P1^4; //---------------------------- void main() {pwm_init(); time_flag = 1;while(flag < 2) //768us{traing(); }while(1) //兩圈停車{time_flag = 0;car_stop(); } }//---------------- void pwm_init(void) {TMOD=0x02; /* 模式設置,*/TH0=0X9C; /* 定時器設置,100us一次中斷*/TL0=0X9C;ET0=1; /* 開定時器0 中斷*/EA=1; /* 開總中斷*/TR0=1; /* 打開定時器*/ }//------------------ void traing() {if(turn_flag == 0) //最右側尋跡口為白線{if((Sensor_Right==0)&&(Sensor_Left==1))//左邊有黑線,往左轉{right_go;left_stop;Right_Forward_Duty = Low_Speed;//+2的話跑的太快,單片機處理速度無法勝任, Left_Forward_Duty = 0; }else{if((Sensor_Right==1)&&(Sensor_Left==0)) //右轉{left_go;right_stop;Left_Forward_Duty = Low_Speed;Right_Forward_Duty = 0; }else if((Sensor_Right==0)&&(Sensor_Left==0)) //全白,尋跡的最佳情況,慢速。 {if(turn_flag_2 == 0) //增加判斷是否在經過第一個45度角地方,防止過沖{ left_go; //正常走,能否在這里保存上一次執行的動作?【0000】right_go; Left_Forward_Duty = Low_Speed-1;Right_Forward_Duty = Low_Speed-1;}else //經過45度角地方,防止過沖,【1000】{left_back;right_go; Left_Forward_Duty = Low_Speed+1;Right_Forward_Duty = Low_Speed+1; //快轉 }}else //中間兩個尋跡端口全黑,此時注意結合在起點時的情況 【0110】【1110】{ if(turn_flag_2 == 1) //只有在起點,車體稍微傾斜時,可能【1110】,第二個45度角也可能出現,但是很短暫{left_stop;right_go; //稍微轉向 Left_Forward_Duty = Low_Speed; //全黑。直線尋跡的時候不會出現這種情況,只有在轉彎處才會。Right_Forward_Duty = Low_Speed; }else //【0110】轉角遇到{left_go;right_go; //原地轉向。在第一個45度角地方 Left_Forward_Duty = Low_Speed;Right_Forward_Duty = Low_Speed; }}}}//下面是turnflag = 1的情況else if((Sensor_Right==1)&&(Sensor_Left==1)&&(turn_flag_2==1)) //四個尋跡口都是黑線,此時經過起點,記錄圈數//780us{ //【1111】 // delay_10us(50); // flag++;//圈數標志left_go;right_go;Right_Forward_Duty = Low_Speed+2;//慢走 Left_Forward_Duty = Low_Speed+2;delay_1ms(100); //50ms不行,當100ms時,只有當在這一段準確的直線行駛才能保證二次停車,就是不能保證100%停車flag++;//圈數標志 }else //最右側傳感器檢測到黑線,【(1),(10,01,00),(1,0)】【(1),(11),(0)】{left_go;right_back;Left_Forward_Duty = Low_Speed+2; //剛開始就運行到這里的話,到364us Right_Forward_Duty = Low_Speed+2;}}//---停車,失能電機------ void car_stop(void) {left_en = 0; right_en = 0;Left_Forward_Duty = 0;Right_Forward_Duty = 0; }//--------------------- void time0() interrupt 1 { /*左輪前進PWM輸出*/ Left_Forward_Cnt++;if(Left_Forward_Cnt <= Left_Forward_Duty){ // Left_Motor_P1 = 1; // Left_Motor_P2 = 0;left_en = 1;//高電平,使能電機}else { // Left_Motor_P1 = 1; // Left_Motor_P2 = 1;left_en = 0;}if(Left_Forward_Cnt==20){Left_Forward_Cnt = 0;}/*右輪前進PWM輸出*/Right_Forward_Cnt++;if(Right_Forward_Cnt <=Right_Forward_Duty){ // Right_Motor_P1 = 1; // Right_Motor_P2 = 0;right_en = 1; }else { // Right_Motor_P1 = 1; // Right_Motor_P2 = 1;right_en = 0;} if(Right_Forward_Cnt==20){Right_Forward_Cnt = 0;} }
總結
- 上一篇: windows自带的文件校验工具MD5,
- 下一篇: Java垃圾收集学习笔记