一、 綜述: a) 項目名稱:小車自動走迷宮; b) 小組成員: i. 周杰:負責總體規劃,及ARM編程; ii. 宋大成:負責車輪驅動; iii. 陳瀟:負責紅外驅動; c) 小車圖片: MicroMouse615: MicroMouse120: 二、 項目介紹: 電腦鼠走迷宮競賽的目的是制作一個微型機器人,它能在最短的時間內穿越迷宮到達終點。參賽的機機器人稱為“電腦鼠”,將電腦鼠放入迷宮并啟動操作的人稱為“操作員”。 電腦鼠的基本功能是從起點開始走到終點,這個過程稱為一次“運行”,所花費的時間稱為“運行時間”。從終點回到起點所花費的時間不計算在運行時間內。從電腦鼠的第一次激活到每次運行開始,這段期間所花費的時間稱為“迷宮時間”。如果電腦鼠在比賽時需要手動輔助,這個動作稱為“碰觸”。競賽使用這三個參數,從速度﹑求解迷宮的效率和電腦鼠的可靠性三個方面來進行評分。器人稱為“電腦鼠”,將電腦鼠放入迷宮并啟動操作的人稱為“操作員”。 在G02小組的工作中,基本完成了電腦鼠的驅動部分和算法部分:電腦鼠可以進行直線行走、90度左轉、90度右轉和180度后轉;能準確監測左前右三個方向的擋板,能準確判斷左右間距是否恰當;能進行調距運動,以保持與左右擋板的距離;能簡單判斷通路,并計算記憶合適通路;并按照通路完成迷宮行走。 但是,由于某些原因,小車的沒有取得完美的效果:由于電機轉速控制不當,小車直線行走效果不佳(參看第條);左轉右轉不夠準確,只能依靠調整函數補償;算法不夠優秀,正在探究更好的算法。這些問題都在努力克服中,我們不會因為檢查完畢就放棄小車的調試。 三、 項目整體結構: MicroMouse102 電腦老鼠,采用美國LuminaryMicro 公司生產的32 位ARM CortexM3處理器LM3S102,控制和檢測紅外傳感器;主CPU 根據檢測到的傳感信號,控制電機驅動電路調整行走路徑,直到到達終點。 四、 硬件部分介紹: LED 電路 電腦鼠有5 個獨立的LED,通過LM3S 系統單片機的GPIO 口直接控制,如圖 1.6 所示。電路采用了I/O 口灌電流的驅動方式來驅動LED,LM3S 系統單片機的灌電流為2~8mA(可配置),所以不需要驅動就可以點亮LED。GPIO 引腳輸出高電平時LED 熄滅,低電平時LED 點亮。 電機驅動電路 電機采用直流減速電機,最高輸出轉速為800 轉/分鐘,工作電壓為DC3V。電機驅動 電路采用專用的單相直流電動機橋式驅動芯片。 車速檢測電路 車速檢測用于檢測并記錄車體運行的路徑,通過車速檢測記錄車體做迷宮的坐標,同 時也起到控制車速和保持左右雙輪的速度一致。 檢測原理:在左輪和右輪的內則都貼有的光電碼盤,碼盤由兩種顏色組成白色和黑色。 紅外發射管安裝在車輪光電檢測碼盤的檢測區域,當紅外發射與接收管正對著黑色邊時, 紅外線沒有被反射,接收管的電阻很大;當紅外發射與接收管正對著白色邊時,紅外線被 反射,接收管的電阻很小。 紅外檢測電路 紅外檢測電路是用于迷宮擋板的檢測,分為左側、右側、前方三個方向,三個方向的 檢測原理相同,某一個方向的檢測電路。 CPU 及晶振電路 電腦鼠的單片機、晶體振蕩器和LDO輸出原理如圖所示。該單片機選用LM3S102 微處理器。 五、 軟件部分介紹: 一體化紅外接收頭工作原理 一體式紅外線接收傳感器IRM8601S,它內部集成自動增益控制電路、帶通濾波電路、 解碼電路及輸出驅動電路。當連續收到38KHz 的紅外線信號時,將產生脈寬10ms 左右的 低電平。如果沒有收到信號,便立即輸出高電平。Send 為發射控制端,高 電平時發射38KHz 的紅外信號。Out 為接收輸出端,低電平表示收到信號。 檢測障礙物的軟件設計 根據接收頭是否檢測到經過反射的紅外線信號,就可以判斷是否存在障礙物。由于接 收頭檢測到信號時只產生一個負脈沖,所以只需要在檢測時使能紅外線發射,一次檢測結 束后使能無效,程序設計參考流程圖如圖2.5 所示。 接收頭有一定的 響應時間 開始發送38KHz 的紅外線 迷宮擋板檢測 調制信號產生 本設計中采用定時器1 產生38KHz 的調制信號,由PB5 輸出,該端口連接到圖2.3 中 的Pulse 端口。在中斷中翻轉PB5 輸出信號,所以要產生頻率為f 的脈沖,定時器的頻率 要為2f。在本設計中要產生38KHz 的頻率,定時器中斷頻率為76KHz。 程序清單 3.1 為定時器1 的初始化函數,程序清單 3.2 為中斷服務函數,在這里翻轉 PB5 口輸出狀態。 程序清單 3.1 定時器1 初始化 void PULSEIni(void) { GPIODirModeSet(GPIO_PORTB_BASE, SEND | PULSE, GPIO_DIR_MODE_OUT); // 設置為輸 出 GPIOPinWrite( GPIO_PORTB_BASE,SEND | PULSE,0); // 紅外線初始時停止發射 SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER1 ); // 使能定時器1 外設 TimerConfigure(TIMER1_BASE, TIMER_CFG_32_BIT_PER); // 設置定時器1 為周期觸發 TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet()/76000); // 設置定時器裝載值 TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT); TimerEnable(TIMER1_BASE, TIMER_A); IntEnable(INT_TIMER1A); } 程序清單 3.2 定時器1 服務函數 void Timer1A_ISR(void) { TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT); // 清除定時器1 中斷 GPIOPinWrite(GPIO_PORTB_BASE, PULSE,GPIOPinRead(GPIO_PORTB_BASE, PULSE) ^ PULSE); // 翻轉GPIO B5 端口 } 抗干擾處理 紅外線在空氣中傳播和反射受外界的干擾,如果測量距離剛好處在能夠檢測到信號的 臨界狀態,保持距離不變,傳感器輸出信號也可能不確定。這樣就需要在軟件中進行抗干 擾處理。參考程序如程序清單3.3 所示。 程序清單3.3 抗干擾處理程序 GPIOPinWrite( GPIO_PORTB_BASE,SEND , SEND); // 發送脈沖 Delay(150); // 延時 for(i=0,j=0;i<10;i++) // 檢測接收信號 { if(GPIOPinRead(GPIO_PORTA_BASE, OUT_L)==0) j++; } GPIOPinWrite( GPIO_PORTB_BASE,SEND , ~SEND); // 停止發送 if(j>5) // 左邊存在擋板 { } else // 左邊存在支路 { } 圖3.1 為抗干擾程序在Micromouse 中運行后用邏輯分析儀抓到的波形圖,Pulse 為 38KHz 的輸出信號,Send 高電平有效,有效時發送紅外線脈沖,OUT 為一體化接收頭輸 …… …… 出端,該圖所示為接收頭探測到障礙物,軟件在Send 信號無效(下降沿)前完成檢測OUT 輸出信號,從圖中可以看出,此時正處于OUT 有效信號的中間,所以軟件里延時參數能 保證正確檢測到信號。 圖3.1 傳感器檢測波形圖 軟件設計參考 為用一組紅外實現兩組參數(是否存在擋板和是否太接近擋板)的檢測流程圖。在Micromouse 中,用到了三組(左、前、右)反射式紅外檢測傳感器,左邊和右邊的傳感器各自都需要檢測兩組參數,而前方的傳感器只需要探測有無擋板,存在擋板就必須根據策略轉換行進方向,若不存在就可以繼續前進。如圖3.3 所示為Micromouse 紅外檢測的程序設計流程圖。紅外檢測參考程序見程序清單 3.4 所示,該程序中使用了五個LED 用來指示傳感器檢測的狀態,由于這幾個LED 硬件上連接到JTAG,關于如何切換GPIO和JTAG功能參見6 使用JTAG 引腳作GPIO。 此頻率僅作為參考,要根據實際檢測距離來確定。結合可調電阻R1 變可以實現擋板和防碰撞的檢測。 程序清單 3.4 Micromouse 紅外檢測函數(見附錄) 電機的調速 電機的調速 直流電機的轉速控制在本設計中通過PWM來控制,LM3S102 單片機則剛有兩路PWM 輸出,非常適合用于控制兩個電機的轉速。 兩路PWM 是LM3S102 通用定時器0(Timer0)的三種工作模式之一,16 位PWM 模 式。該模式是將一個32 位的定時器,折分成兩個16 位的定時器TimerA 和TimerB。這些 定時器為計數寄存器(GPTMTnR)遞減計數,遞減到0 時自動加載預裝載值(GPTMTnILR)。 當然預裝載值也是由用戶設定,該直也就決定了定時周期,也即PWM 的輸出周期。 當計數器的值與預裝載值相等時,輸出PWM 信號有效,當計數器的值與匹配寄存器 (GPTMnMATCHR)的值相等時,輸出PWM 信號失效。通過軟件可以設定PWM 輸的信 號有效和信號無效的電平狀態。當GPTMCTL 寄存器的TnPWML 位值為0 時,信號有效 為高電平,信號無效為低電平;TnPWML 位值為1 時,則反之。如圖 4.1 所示。 輸出信號 計數 0x411A 0xC350 TnPWML=0 TnPWML=1 TnEN置位 GPTMTnR=GPTMnMR GPTMTnR=GPTMnMR 時間 圖 4.1 16 位PWM 模式輸出 占空比的約定:占空比為在一個周期內,輸出有信號有效電平占整個周期時間的比率。 在這里為以統一軟件控制的約定,用戶API 函數輸入的占空比值越大,電機轉速越快,正 向運行和反向運行都一樣。 為了簡化占空比輸出的計算,將計數寄存器與匹配寄存器值相等時,輸出的電平信號 為驅動電機的有效信號。例如將PWM 周期時間設定為60000 個時鐘節拍,需要輸出驅動 電機的占空比為75%,則設置匹配寄存器值為75*6000。 由于電機的轉向不一樣,所以電機驅動的有效電平也需要調整,通過控制TnPWML 實現。 2 程序設計 Timer0 的兩路16 定時器TimerA 和TimerB 的PWM 輸出引腳分別為PB0 和PB6,PB0 和PB6 分別控制左輪和右輪驅動器TA7291S 的IN1 引腳,而它們的IN2 引腳分別由GPIO 輸出的PA4 和PA5 控制。 左輪的控制函數如程序清單 4.1 所示。 該函數的第1 個參數sel 為選擇輪子的控制方式:0 為停止,1 為輪子向前,2 為輪子 向后;percen 參數為占空比,其最大值為99,最小值為1,對于輪子的停止控制該參數無 效。 程序清單 4.1 左輪控制函數 void LeftWheelRun(int sel,unsigned char percen) { switch(sel) { /*輪子停止轉動*/ case 0: TimerDisable(TIMER0_BASE,TIMER_A); // 禁止定時器 GPIOPinWrite(GPIO_PORTA_BASE,LWC2,0xff); // 控制引腳輸出高電平 GPIODirModeSet(GPIO_PORTB_BASE, LWC1, GPIO_DIR_MODE_OUT); // GPIO 輸出 GPIOPinWrite(GPIO_PORTB_BASE,LWC1, 0xff); // GPIO 輸出高電平 break; /*左輪向前*/ case 1: GPIOPinWrite(GPIO_PORTA_BASE,LWC2, 0xff); // PA4 輸出高電平 TimerControlLevel(TIMER0_BASE,TIMER_A,true); // PWM 有效電平方向 GPIODirModeSet(GPIO_PORTB_BASE, LWC1, GPIO_DIR_MODE_HW); // PWM 輸出 TimerMatchSet(TIMER0_BASE,TIMER_A,percen*600); // 設置占空比 TimerEnable(TIMER0_BASE,TIMER_A); // 使能定時器 break; /*左輪向后*/ case 2: GPIOPinWrite(GPIO_PORTA_BASE,LWC2, 0); // PA4 輸出低電平 TimerControlLevel(TIMER0_BASE,TIMER_A,false); // PWM 有效電平方向 GPIODirModeSet(GPIO_PORTB_BASE, LWC1, GPIO_DIR_MODE_HW); // PWM 輸出 TimerMatchSet(TIMER0_BASE,TIMER_A,percen*600); // 設置占空比 TimerEnable(TIMER0_BASE,TIMER_A); // 使能定時器 break; } } 右輪的控制函數如程序清單 4.2 所示。 該函數的第1 個參數sel 為選擇輪子的控制方式:0 為停止,1 為輪子向前,2 為輪子 向后;percen 參數為占空比,其最大值為99,最小值為1,對于輪子的停止控制該參數無 效。 程序清單 4.2 右輪控制函數 void RightWheelRun(int sel,unsigned char percen) { switch(sel) { /*輪子停止轉動*/ case 0: TimerDisable(TIMER0_BASE,TIMER_B); // 禁止定時器 GPIOPinWrite(GPIO_PORTA_BASE,RWC2,0xff); // 控制引腳輸出高電平 GPIODirModeSet(GPIO_PORTB_BASE, RWC1, GPIO_DIR_MODE_OUT); // GPIO 輸出 GPIOPinWrite(GPIO_PORTB_BASE,RWC1,0xff); // GPIO 輸出高電平 break; /*右輪向后*/ case 2: GPIOPinWrite(GPIO_PORTA_BASE,RWC2, 0xff); // PA4 輸出高電平 TimerControlLevel(TIMER0_BASE,TIMER_B,true); // PWM 有效電平方向 GPIODirModeSet(GPIO_PORTB_BASE, RWC1, GPIO_DIR_MODE_HW); // PWM 輸出 TimerMatchSet(TIMER0_BASE,TIMER_B,percen*600); // 設置占空比 TimerEnable(TIMER0_BASE,TIMER_B); // 使能定時器 break; /*右輪向前*/ case 1: GPIOPinWrite(GPIO_PORTA_BASE,RWC2, 0); // PA4 輸出低電平 TimerControlLevel(TIMER0_BASE,TIMER_B,false); // PWM 有效電平方向 GPIODirModeSet(GPIO_PORTB_BASE, RWC1, GPIO_DIR_MODE_HW); // PWM 輸出 TimerMatchSet(TIMER0_BASE,TIMER_B,percen*600); // 設置占空比 TimerEnable(TIMER0_BASE,TIMER_B); // 使能定時器 break; } } 需要注意的是,當PWM 信號禁止后,其輸出引腳的電平狀態是保持靜止時的狀態(可 能為低電平也可能為高電平),導致電機可能不能停止,所以在制停電機時,需要將PWM 引腳改為GPIO 輸出,并且出高電平,使電機剎車停止。 定時器PWM 初始化函數如程序清單 4.3 所示。 程序清單 4.3 定時器PWM 初始化 void PWMTimer0AIni(void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); // 使能定時器0 模 塊 GPIODirModeSet(GPIO_PORTB_BASE, LWC1|RWC1 , GPIO_DIR_MODE_OUT); /* 控制引腳輸出*/ GPIOPinWrite(GPIO_PORTB_BASE, LWC1|RWC1 , 0xff); // GPIO 輸出高電 平 GPIODirModeSet(GPIO_PORTA_BASE, LWC2|RWC2 , GPIO_DIR_MODE_OUT); // 控制引腳輸出 GPIOPinWrite(GPIO_PORTA_BASE, LWC2|RWC2 , 0xff); // GPIO 輸出高電 平 /* 定時器配置*/ TimerConfigure(TIMER0_BASE,TIMER_CFG_16_BIT_PAIR |TIMER_CFG_A_PWM|TIMER_CFG_B_PWM); // 16 位PWM 輸出 TimerControlLevel(TIMER0_BASE,TIMER_A,false); //有效信號為低電 平 TimerControlLevel(TIMER0_BASE,TIMER_B,false); TimerLoadSet(TIMER0_BASE,TIMER_A,60000); // 設定PWM 頻率 TimerLoadSet(TIMER0_BASE,TIMER_B,60000); } Micromouse 車速檢測 車速檢測程序設計 本設計中選用了LM3S102 的PA0 和PB1 分別檢測左輪和右輪的下降沿的脈沖個數, 為了快速向應檢測信號,使用了下降沿觸發中斷。LM3S102 單片機的特點,任何一個GPIO 引腳都可以配置為中斷輸入,并且可以作任意設定為高電平觸發、低電平觸發、下降沿觸 發、上升沿觸發和上升或下降沿觸發5 種模式。本應用中使用下降沿觸發,其初始化如程 序清單 5.1 所示。 程序清單 5.1 輪子脈沖檢測初始化 void WheelPulseIni(void) { // 配置引腳為輸入 GPIODirModeSet(GPIO_PORTA_BASE, PULSE_R, GPIO_DIR_MODE_IN); GPIODirModeSet(GPIO_PORTB_BASE, PULSE_L, GPIO_DIR_MODE_IN); // 配置引腳下降沿觸發中斷 GPIOIntTypeSet(GPIO_PORTA_BASE,PULSE_R,GPIO_FALLING_EDGE); GPIOIntTypeSet(GPIO_PORTB_BASE,PULSE_L,GPIO_FALLING_EDGE); // 使能引腳輸入中斷 GPIOPinIntEnable(GPIO_PORTA_BASE,PULSE_R); GPIOPinIntEnable(GPIO_PORTB_BASE,PULSE_L); // 使能GPIO PA 口和GPIO PB 口中斷 IntEnable(INT_GPIOA); IntEnable(INT_GPIOB); } 左右輪檢測脈沖中斷處函數如程序清單 5.2 所示。 程序清單 5.2 左右輪檢測脈沖中斷處函數 //------------------------------------------------------------------------------------ // 函數名稱: GPIO_Port_A_ISR // 函數功能: 右輪檢測脈沖中斷處函數 //------------------------------------------------------------------------------------ void GPIO_Port_A_ISR (void) { unsigned char IntStatus; IntStatus = GPIOPinIntStatus(GPIO_PORTA_BASE,true); // 讀PA 口中斷狀態 if(IntStatus&PULSE_R) // 是否為左輪脈沖中斷 { PulCount_R++; if(PulCount_R >= RightPulse) { RightWheelRun(0, 1); WheelStop_R= 1; } GPIOPinIntClear(GPIO_PORTA_BASE,PULSE_R); // 清中斷 } } //------------------------------------------------------------------------------------ // 函數名稱: GPIO_Port_B_ISR // 函數功能: 左輪檢測脈沖中斷處函數 //------------------------------------------------------------------------------------ void GPIO_Port_B_ISR (void) { unsigned char IntStatus; IntStatus = GPIOPinIntStatus(GPIO_PORTB_BASE,true); // 讀PA 口中斷狀態 if(IntStatus&PULSE_L) // 是否為右輪脈沖中斷 { PulCount_L++; if(PulCount_L>= LeftPulse) { WheelStop_L= 1; LeftWheelRun(0, 1); } GPIOPinIntClear(GPIO_PORTB_BASE,PULSE_L); // 清中斷 } } 六、 系統DV:參看附件: a) 直線行走3格+右轉: 該運動中,小車首先直線行走3格(50CM),然后右轉,并在碰到障礙物后右轉,直線行走一格,再右轉一次; b) 圍繞小桌運動: 該運動中,小車圍繞方形小桌繞圈,判斷安全距離、檢測是否存在通路,并自動修正方向。 七、 測試情況: 經檢測,小車能較好的完成給定的運行任務,較為準確地直走、左右轉,及180度轉。小車完全可以完成探究迷宮、自動尋路等任務,并以較短的時間完成迷宮行走。 但是,小車依舊存在些問題:直走中有擺動現象,左右轉也做不到90度(大概85度到95度之間),180度更不好;算法不夠優秀,時間依舊較長。雖然這些問題更為瑣碎更為難處理,但我們有信心,暑假間會完全克服。 八、 曾遇到的問題:
1.車輪行進速度不一:
同一函數中,小車的左右轉速相差比較大,我們嘗試采取了以下幾種方式解決
1)。調占空比:無效,原因不明。在與別的小組交流之后,我們嘗試改寫了原先的驅動函數,使速度函數與正反轉函數區分開:
(原函數 & 改進后的函數 請見附錄)
還不太明白是為什么,把兩個函數分開寫了之后,發現有一定效果。
2)。脈沖補償:
這也是演示程序的方法,在執行完一次運行任務之后,把相差的脈沖補償到下一次任務的設定中:
PWMTimer0AIni(); // PWM初始化
PULSEIni(); // 調制信號初始化
WheelPulseIni(); // 測速初始化
while(1)
{
LeftPulse = 10; // 設定電機運行任務
RightPulse = 10;
WheelStop_L = 0; // 清零電機停止標志位
WheelStop_R = 0;
LeftWheelRun(1, 99); // 啟動左右電機
RightWheelRun(1, 99);
while(!(WheelStop_L && WheelStop_R))// 等待運行結束,狀態在中斷中改變
Check_Infrared(0); // 等待過程中進行紅外檢測
PulCount_L -= LeftPulse; // 誤差補償到下一次運動中
PulCount_R -= RightPulse;
}
想法很好,實際行不通。
以最低誤差來算,假設左輪10個脈沖,右輪9個,小車明顯走出一條弧線;而當下次補償到下次之中時,小車還是先走同樣的路程,然后左輪停轉,右輪轉2個脈沖(上次誤差+ 這次誤差),表現出了明顯的“一瘸一拐”的情況;而且,在我們的調試過程中,出現了不穩定的左右擺動,這是由于“半個脈沖”的問題,小車不能有效識別比較小的距離差,在最好的狀態下,小車也存在計數的問題,比如,左輪剛轉就開始向下的觸發,右輪快轉一圈才有第一次的向下觸發。而且,這種誤差是隨機的,與小車車輪的起始位置有關,不好避免。
3).從硬件下手:
在左右電機同加固定電壓時,清楚地看到轉速不一樣,因此,最好的辦法還是從硬件上下手,通過串聯電阻來解決問題。這是我們的一個想法,還沒開始實施,準備在暑假時再想想軟件解決辦法,不行就改電路。(電機內阻,運行時電流)
2。檢測信號的波形:
受燈光的影響,可以看到,紅外接受得到的波形不時很好。檢測初速的還好,因為幅度比較大,所以沒有誤判的問題;檢測擋板的波形振動比較大。按照電路圖,嘗試著變換的電阻,取得不錯的效果,以下函數足夠完成判斷:
( 源程序詳見附錄)
3。編譯器的問題:
大量鐵的事實證明,crosswork不好用,推薦使用Keil for ARM。前期使用的是crosswork,很多不明就里的問題,換到Keil for ARM就解決了,說明crosswork對小車的支持不夠好。很多函數,在crosswork上,完全起不到效果,跑出來都知道是怎么回事。我們不是在責怪crosswork不好,但至少說明,不易于上手。
總結了一些發現的問題:
1)。不能出現漢字:還好,無非就是存盤載入時麻煩點。
2)。只能裝C盤:剛開始裝在D盤,整天藍屏。
3)。頭文件載入:很多頭文件需要手動一一載入,還是查百度知道的,說明文件上沒有。
4)。編譯顯示錯誤,不顯示為什么錯:開始時,發現錯誤就在Keil上找錯。。。。。。
5)。脫機運行問題:想脫機運行?需要改很多。。。。。。
因此,不推薦用crosswork。
|