1. 功能說明
尋跡機器人是一種能夠跟蹤特定物體或線路的機器人。它們通常具有以下功能和特點:
① 傳感器:尋跡機器人配備了用于感知環(huán)境的傳感器,如攝像頭、灰度傳感器等。這些傳感器可以探測地面上的標記、顏色、紋理或其他特定特征,以確定要跟蹤的目標。
② 自主導航:尋跡機器人通常具備自主導航能力,可以根據(jù)目標物體的位置和運動軌跡進行移動和調(diào)整。它們可能使用輪式、履帶或其他移動機構(gòu)來在地面上移動。
③ 跟蹤精度:尋跡機器人通常被設計為能夠?qū)崟r跟蹤目標物體,并盡可能準確地保持距離和方向。一些高級尋跡機器人還可以通過預測目標物體的運動來提高跟蹤的精度。
④ 應用場景:尋跡機器人可以應用于多種場景,如工業(yè)生產(chǎn)線上的零部件跟蹤、物流倉庫中的貨物識別與追蹤、安防領(lǐng)域中的行人監(jiān)控等。它們在自動化、智能化和效率提升方面具有廣泛的應用前景。
本文示例將實現(xiàn)R023樣機小型雙輪差速底盤機器人沿直線尋跡行走的一個功能。
按照下圖所示方式進行安裝:

在這個示例中,我們采用了以下硬件,請大家參考:
按照下圖所示方式進行電路連接:
編程環(huán)境:Arduino 1.8.19
① 下面提供一個控制輪子轉(zhuǎn)動方向和速度的參考例程(Test1.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include//調(diào)用舵機庫 ------------------------------*/ Servo myservo;//聲明一個舵機類void setup()//Arduino的設置函數(shù){myservo.attach(4);//綁定控制舵機的引腳}void loop()//Arduino的循環(huán)函數(shù){for(int i=0; i<180; i++){//通過調(diào)節(jié)i值控制舵機的運行參數(shù) myservo.write(i);//輸出控制舵機的運行參數(shù) delay(500);//延時 } }
② 下面提供一個控制輪子前進、停止、左轉(zhuǎn)、右轉(zhuǎn)、左微調(diào)、右微調(diào)的參考例程(Test2.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include------------------------------*/ #define middle1 88//定義對應舵機的中間值,即停止轉(zhuǎn)動的值#define middle2 88//此值需要測量,各個舵機不一定相同void setup()Servo myservo[2];//定義一個舵機類數(shù)組{myservo[0].attach(4);delay(1000);myservo[1].attach(3);}void loop(){Right();Left();//調(diào)用左轉(zhuǎn)函數(shù)void Left()//左轉(zhuǎn)函數(shù)delay(1000);delay(1000);Forwards();delay(1000);stop();}{{myservo[0].write(middle1);myservo[1].write(middle2 + 20);}void Right()//右轉(zhuǎn)函數(shù)myservo[0].write(middle1 - 20);myservo[0].write(middle1 - 20);myservo[1].write(middle2);}}void Forwards()//前進函數(shù){myservo[1].write(middle2 + 20);}void stop()//后退函數(shù){myservo[1].write(middle2);myservo[0].write(middle1);
③ 下面提供一個將灰度傳感器數(shù)據(jù)顯示到LED點陣上的參考例程(Test3.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include//調(diào)用點陣庫函數(shù) ------------------------------*/ #include LedControl lc=LedControl(12,11,13,1);//聲明點陣類,并設置對應的引腳int pin[3] = {A0, A4, A3};//設置傳感器的對應的三個引腳byte value;//聲明傳感器值變量void setup(){/************************************************************************************LedInit();//初始化點陣}具體解析:for循環(huán)中使用了位處理,這樣的結(jié)果就是value的一個數(shù)據(jù)位對應一個傳感器的狀態(tài),此程序用到了for與switch的配合框架,可用于多傳感器的實時處理,請細細體會!傳感器觸發(fā)時返回值為0,因此value值與傳感器觸發(fā)的狀態(tài)對應關(guān)系以A0觸發(fā)為例:此程序value的類型為byte,則可支持8個傳感器,如果要使用更多傳感器可定義int等。所以這樣做的好處就是當傳感器的狀態(tài)發(fā)生改變時程序可以快速的到達指定的處理方式A0傳感器觸發(fā)-->二進制:00000110-->十六進制:0x06-->對應case 0x06;{**************************************************************************************/void loop()for(int i=0; i<3; i++){//通過循環(huán)檢測,讀取傳感器的狀態(tài)值 value |= (digitalRead(pin[i]) << i);//通過位處理得到結(jié)果值,digitalRead()用于讀取數(shù)字值 } switch (value) {//根據(jù)結(jié)果值進行相應的事件處理 case 0x00://全部觸發(fā) LedOn(0);//點亮相應的點陣 LedOn(1); LedOn(2); break; case 0x01://觸發(fā)右邊兩個 LedOn(1); LedOn(2); break; case 0x03://觸發(fā)右邊一個 LedOn(2); break; case 0x04://觸發(fā)左邊兩個 LedOn(0); LedOn(1); break; case 0x05://觸發(fā)中間一個 LedOn(1); break; case 0x06://觸發(fā)左邊一個 LedOn(0); break; default: ; } } void LedOn(int key)//根據(jù)參數(shù)點亮相應的點陣LED { lc.clearDisplay(0); for(int i=0; i<2; i++){ for(int j=3*key; j<3*key+2; j++){ lc.setLed(0, i, j, true); } } } void LedInit() //點陣初始化函數(shù) { lc.shutdown(0,false); lc.setIntensity(0,8); lc.clearDisplay(0); }value = 0;
④ 下面提供一個小車行走直線的參考例程(Test4.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include------------------------------*/ #define middle1 88#define middle2 88Servo myservo[2];int pin[3] = {A0, A4, A3};byte value;byte value_his = 0;//記錄上一次的傳感器值myservo[1].attach(3);void setup(){myservo[0].attach(4);}for(int i=0; i<3; i++){ value |= (digitalRead(pin[i]) << i); } if(value == 0x07){//當傳感器都沒有觸發(fā)時默認為上一次的值 value = value_his; } switch (value) { case 0x00://全部觸發(fā) Forwards(); break; case 0x01://觸發(fā)右邊兩個 while(digitalRead(pin[1])){//通過while循環(huán)使小車回到跑道中間 Right(); } break; case 0x03://觸發(fā)右邊一個 while(digitalRead(pin[1])){ Right(); } break; case 0x04://觸發(fā)左邊兩個 while(digitalRead(pin[1])){ Left(); } break; case 0x05://觸發(fā)中間一個 Forwards(); break; case 0x06://觸發(fā)左邊一個 while(digitalRead(pin[1])){ Left(); } break; default: stop(); } value_his = value; } void Left() { myservo[0].write(middle1); myservo[1].write(middle2 + 20); } void Right() { myservo[0].write(middle1 - 20); myservo[1].write(middle2); } void Forwards() { myservo[0].write(middle1 - 20); myservo[1].write(middle2 + 20); } void stop() { myservo[0].write(middle1); myservo[1].write(middle2); }void loop(){value = 0;
⑤ 下面提供一個小車識別十字路口的參考例程(Test5.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include------------------------------*/ #include #define middle1 88#define middle2 88Servo myservo[2];LedControl lc=LedControl(12,11,13,1);int pin[3] = {A0, A4, A3};byte value;void setup()byte value_his = 0;int time[3];//用于記錄傳感器的觸發(fā)時間{void loop()LedInit();myservo[0].attach(4);myservo[1].attach(3);for(int i=0; i<3; i++){ value |= (digitalRead(pin[i]) << i); if(!digitalRead(pin[i])){ time[i] = millis();//調(diào)用mills函數(shù)可以得到此時單片機的運行時間 } } if(TimeDeal()) { if(millis() > 1000){//用于排除剛開機時的誤判}{value = 0;LedDis();//十字路口顯示}}if(value == 0x07){//當傳感器都沒有觸發(fā)時默認為上一次的值value = value_his;}while(digitalRead(pin[1])){switch (value) {case 0x00://全部觸發(fā)break;Forwards();while(digitalRead(pin[1])){case 0x01://觸發(fā)右邊兩個Right();}case 0x03://觸發(fā)右邊一個break;}Right();break;break;case 0x04://觸發(fā)左邊兩個while(digitalRead(pin[1])){Left();}while(digitalRead(pin[1])){case 0x05://觸發(fā)中間一個Forwards();case 0x06://觸發(fā)左邊一個break;break;Left();}myservo[0].write(middle1);default:stop();}value_his = value;lc.clearDisplay(0);myservo[1].write(middle2 + 20);}void Left(){ }myservo[0].write(middle1 - 20);void Right(){myservo[0].write(middle1 - 20);myservo[1].write(middle2);}myservo[1].write(middle2 + 20);void Forwards(){ }if((abs(time[1] - time[0]) < 100) && (abs(time[1] - time[2]) < 100)){//當中間傳感器與另外兩個傳感器觸發(fā)的時間小于100毫秒時判定為十字路口 return true; } else return false; } } void LedDis()//十字路口顯示函數(shù) { for(int i=3; i<5; i++){ for(int j=0; j<8; j++){ lc.setLed(0, i, j, true); } } for(int i=3; i<5; i++){ for(int j=0; j<8; j++){ lc.setLed(0, j, i, true); } } } void LedInit() { lc.shutdown(0,false); //start the 8*8 led lc.setIntensity(0,8); lc.clearDisplay(0); }void stop(){myservo[0].write(middle1);myservo[1].write(middle2);}bool TimeDeal()//十字路口識別函數(shù){if(millis() > 500){
⑥ 下面提供一個小車實現(xiàn)尋跡的完整程序(TrackingCar.ino):
/*------------------------------------------------------------------------------------版權(quán)說明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.https://opensource.org/licenses/MITDistributed under MIT license.See file LICENSE for detail or copy atby 機器譜 2023-07-14 https://www.robotway.com/#include------------------------------*/ #include #define middle1 88#define middle2 88LedControl lc=LedControl(12,11,13,1);Servo myservo[2];int time[3];int pin[3] = {A0, A4, A3};byte value;int times = 0;byte value_his = 0;int flag = 0;myservo[0].attach(4);void setup(){LedInit();Serial.begin(9600);//串口,用于調(diào)試myservo[1].attach(3);for(int i=0; i<3; i++){ value |= (digitalRead(pin[i]) << i); if(!digitalRead(pin[i])){ time[i] = millis(); } } if(TimeDeal()) { times++; Serial.print(times); } else { if(times > 1){}void loop(){Serial.println();value = 0;flag += 1;while(1){Serial.println(flag);}times = 0;}stop();if(flag == 3){} }if(value == 0x07){value = value_his;}switch (value) {LedOn(0);case 0x00://全部觸發(fā)LedOn(1);while(digitalRead(pin[1])){LedOn(2);Forwards();//delay(500);break;case 0x01://觸發(fā)右邊兩個LedOn(2);LedOn(1);Right();Right();}case 0x03://觸發(fā)右邊一個break;while(digitalRead(pin[1])){LedOn(2);}break;break;case 0x04://觸發(fā)左邊兩個LedOn(0);while(digitalRead(pin[1])){LedOn(1);while(digitalRead(pin[1])){Left();}case 0x05://觸發(fā)中間一個LedOn(1);break;Forwards();LedOn(0);case 0x06://觸發(fā)左邊一個if((abs(time[1] - time[0]) < 100) && (abs(time[1] - time[2]) < 100)){ return true; } else return false; } } void Left() { myservo[0].write(middle1); myservo[1].write(middle2 + 20); } void Right() { myservo[0].write(middle1 - 20); myservo[1].write(middle2); } void Forwards() { myservo[0].write(middle1 - 20); myservo[1].write(middle2 + 20); } void stop() { myservo[0].write(middle1); myservo[1].write(middle2); } void LedOn(int key) { for(int i=0; i<2; i++){ for(int j=3*key; j<3*key+2; j++){ lc.setLed(0, i, j, true); } } } void LedInit() { lc.shutdown(0,false); //start the 8*8 led lc.setIntensity(0,8); lc.clearDisplay(0); }Left();}default:break;stop();}value_his = value;lc.clearDisplay(0);}bool TimeDeal(){if(millis() > 500){