STM32循跡避障小車製作程式碼詳解(簡單實現版)
寫在最前:最近由於需要製作了一個循跡避障小車,製作比較簡單但是還是出現了很多bug,因此在部落格中記錄一下,希望對後期需要製作的能有所幫助,小車由PWM訊號+L293D驅動。(二輪驅動,第三輪為自由輪)
要求:小車要求循跡避障,有兩種避障策略,在循跡黑線上遇到障礙物停車,循跡黑線外遇到障礙物要避障(左轉,右轉,後退均可),循跡過程中要求不能出黑線之外。
循跡原理:(紅外探頭+訊號處理板)
我們在車頭前加有3個紅外探頭板,分別在左中右三個位置,循跡原理很簡單程式碼也十分容易理解好寫,紅外探頭在光滑地板上會收到發射出去後反射的紅外訊號,如果探頭在黑線上,則無法收到(黑線吸收紅外光),當探頭收到時,對應的訊號調整板對應位置的led會亮,同時TTL輸出端會給一個低電平,相反當某個探頭在黑線上,對應位置led會滅,TTL輸出端會給一個高電平,因此通過這種方式我們就知道任意一個探頭是否在線上。
因此我們舉個例子,兩邊燈亮,中間燈滅,說明小車前端中間探頭在線上,這時候是正向,因此我們在程式碼中要求小車直行,若左燈滅,中間燈和右燈都亮,說明左探頭在線上,小車現在是斜向右方,因此我們需要小車左轉來實現方向回正,下面配個圖來說明例子(途中只有左右兩個探頭,但不影響理解)
若僅僅實現循跡,程式碼要求就十分簡單了(這裡我們不再說小車前進等程式碼,只說明策略)
當左燈滅,TTL給1,車身右斜,迴圈左轉至中間燈滅兩邊燈亮(即正向)後直行,否則就一直襬正,右向同樣如此。
/***LED_1,LED_2,LED_3分別對應左中右三個紅外探頭***/ if(LED_1==1&&LED_3==0) //左方黑線亮,左轉直到正向 { while(1) { CarLeft(); if(LED_1==0&&LED_3==0&&LED_2==1) break; } } else if(LED_1==0&&LED_3==1) ////右方黑線亮,右轉直到正向 { while(1) { CarRight(); if(LED_1==0&&LED_3==0&&LED_2==1) break; } } else CarGo();
避障原理:(超聲波探頭)
驅動程式碼是我直接從網上覆制,借鑑的程式碼,在此附上鍊接,講解非常詳細,感謝作者。
但是筆者在使用驅動中也遇到了只收到一個非常小的數,即使已經按照上面連結中的要求更正,但還是收到一個非常小的數字,對此筆者對其測距程式碼進行了小小改動,對定時器測距進行小延時,去除小值資料,請對比上述連結中程式碼~在中間加了一個Delay_Us(10);問題解決。其實筆者試過,Delay_Us(1);同樣可以解決問題。
float Hcsr04GetLength(void ) { u32 t = 0; int i = 0; float lengthTemp = 0; float sum = 0; while(i!=5) { //TRIG_Send = 1; //傳送口高電平輸出 GPIO_SetBits(GPIOA, GPIO_Pin_5); // Alias Delay_Us(15); //TRIG_Send = 0; GPIO_ResetBits(GPIOA, GPIO_Pin_5); // Alias //while(ECHO_Reci == 0); //等待接收口高電平輸出 while(0 == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)) OpenTimerForHc(); //開啟定時器 i = i + 1; //while(ECHO_Reci == 1); while(1 == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)) Delay_Us(10); //延時,去除小數值 CloseTimerForHc(); //關閉定時器 t = GetEchoTimer(); //獲取時間,解析度為1US lengthTemp = ((float)t/58.0);//cm sum = lengthTemp + sum ; } lengthTemp = sum/5.0; return lengthTemp; }
在測距驅動和循跡策略都有的情況下,筆者將程式碼合二為一,實現要求功能(以下為while函式中程式碼)
if(LED_1==0&&LED_3==0&&LED_2==0)
{
length = Hcsr04GetLength();
if(length<=20.00)
{
while(1)
{
for(cnt1=0;cnt1 <= 60;cnt1++)
for(cnt=0;cnt <= 8000;cnt++)
CarBack();
for(cnt1=0;cnt1 <= 60;cnt1++)
for(cnt=0;cnt <= 8000;cnt++)
CarRight();
length = Hcsr04GetLength();
if(length>=40.00)
break;
}
}
else CarGo();
}
else
{
length = Hcsr04GetLength();
if(length<=20.00)
CarStop();
else
{
/***********路徑判斷***********/
if(LED_1==1&&LED_3==0) //左方黑線亮,左轉直到正向
{
while(1)
{
length = Hcsr04GetLength();//這個判斷很重要,否則轉彎過程中
if(length<=20.00) //會被判斷為線外,會後退右轉
CarStop();
else CarLeft();
if(LED_1==0&&LED_3==0&&LED_2==1)
break;
}
}
else if(LED_1==0&&LED_3==1) ////右方黑線亮,右轉直到正向
{
while(1)
{
length = Hcsr04GetLength();
if(length<=20.00)
CarStop();
else CarRight();
if(LED_1==0&&LED_3==0&&LED_2==1)
break;
}
}
else CarGo(); //其實這裡並不嚴謹,筆者後期有改動。
}
}
}
以上程式碼和驅動沒有問題,但是有個很嚴重的問題就是
車子不能走太快,若直行左右轉過程中遇到障礙物,有可能會出現線外避障,後期作者程式碼有改動。
原因是該程式碼中測距是由定時器計數,在此cpu參與計數,微控制器是單核執行,因此大概一半以上時間都帶等資料返回,如果速度太快小車在循跡中燈閃時卻在等待資料,則直接衝出線外,解決方案(定時器捕獲測距),CPU不參與計數,大大提高了檢測速率。
此程式碼為簡單版,並不嚴謹,但完全可以完成任務,筆者後期已經更新一次程式碼,這次小車速度很快而且程式碼比較嚴謹,感謝過程中hjl同學的幫助。
有問題歡迎留言~