這篇文章來源于DevicePlus.com英語網站的翻譯稿。
點擊此處閱讀本文的第1部分 >
在第1部分中,我們制作了一塊定制的亞克力板底座,把NEMA 17步進電機安裝到了底盤上。然后,我們將Arduino Uno 3和電機開發板也連到亞克力底座上,并安裝了電機開發板庫。在第2部分中,我們將添加機器人工作所需的其他系統部件,比如伺服器和激光測距儀(LRF),并編寫一個程序,讓機器人能夠自主移動。
有關如何利用Arduino UNO R3從零開始搭建輪式機器人的信息,請參考我們在之前的文章《如何制作自己的機器人》 和《如何制作自己的機器人(第2部分)》。在本文中,我們將進一步增加機器人的功能:讓該機器人動起來并為其增加激光測距(LRF)功能,以使該設備能夠檢測物體并測量距離。
該機器人的設計目標如下:
向前、向后移動;向左和向右旋轉90度;向左和向右旋轉45度。
避開障礙物時根據最佳可用路徑朝不同的方向移動。
測量各個方向(前方、左右90度、左右45度)的距離。
根據可用的最長距離,確定朝哪個方向前進(向前、向后、向左或向右)。
硬件
制作此機器人所需的硬件請參見以下硬件明細,這些零部件在許多電商處都可以買到。
Arduino Uno rev 3
適用于Arduino的Adafruit步進電機開發套件
步進電機
底盤、螺絲和尾輪 Parallax.com
車輪、橡膠輪胎和輪轂 Makeblock.com
伺服裝置和安裝套件
Parallax激光測距儀
OLED顯示屏
紅色包裝紙/盒
195 x 195 x 3mm亞克力板
軟件
Arduino IDE
Adafruit Motor Shield Library
LCD Display9696 Library.zip
工具
圓銼
Dremel 電動工具
電烙鐵
迷你鋼鋸
安裝并測試伺服器
下一步是安裝用于平移的伺服器。首先,請用螺釘將兩塊小板擰到伺服基座上,然后用螺釘將其固定到亞克力底座上,如圖1和圖2所示。請用2個螺釘將鋁制安裝架安裝到伺服器的頂部。
圖1.用兩小板將伺服器固定到亞克力底座上
圖2.將鋁制安裝架安裝到伺服器上
如圖3所示,將伺服器的接頭連至電機開發板。電機開發板上有2個伺服器接頭,分別標記為“servo1”和“servo2”。本次連接請使用servo1接頭(外側的那個)。請注意,切勿接反。
圖3.伺服與電機開發板的連接
現在,我們可以運行程序了,請將以下代碼上傳到Arduino。無需安裝新庫,系統所需的庫(Servo.h)都已包含在Arduino IDE程序中。伺服器應使用Digital引腳10(servo 1),或者如果伺服器連接到電機開發板的servo 2,那么應使用引腳11。
//*********************************************************************************************** #include Servo panMotor; // servo for laser range finder (lrf) scanning int pos = 0; // variable to store the servo position const int a = 1000; void setup() { panMotor.attach(10); } // Attach Servo for scanning to pin 10 void loop() { // *************************** Scan Left *********************************** panMotor.write(180); // 90 degree delay(a); panMotor.write(135); // 45 degree delay(a); // ************************** Scan Right ********************************** panMotor.write(45); // 45 degree delay(a); panMotor.write(0); // 90 degree delay(a); // ************************* Neutral position ***************************** panMotor.write(90); delay(a); } //**************************************************************************************************
代碼很簡單,它負責讓機器人從左向右進行掃描并返回原始位置。
伺服器的工作原理請參見以下視頻:
安裝激光測距儀(LRF)
激光測距儀產品文檔– Parallax
Parallax激光測距儀(LRF)模塊是一款利用激光技術計算儀器到目標物體的距離的測量儀器。該設備根據光學三角測量法(激光、攝像頭和物體質心之間的簡單三角函數)來計算儀器到目標物體的距離。其最佳測量范圍為6–48英寸(15–122厘米),測量精度誤差<5%(平均3%)。
硬件安裝很簡單。只需在亞克力板上鉆兩個與LRF位置相匹配的孔,然后用塑料墊片和螺釘將LRF固定到鋁底盤上即可(請參見圖5)。
圖4.將LRF電纜連至電機開發板
圖5.安裝在鋁制安裝架上的LRF
由于LRF的最佳測量值上限為122厘米,我們需要將鋁制安裝架稍微向前彎曲,以使該范圍始終小于120厘米(圖6)。
圖6.向前彎曲鋁制安裝架,使激光到地的距離小于120厘米
請完全按照圖7將電纜接頭連至LRF。GND接地,VCC接5V,SOUT接引腳8,SIN接引腳9。
圖7. LRF與電機開發板的連接
我們已經完成了LRF的安裝和連接,現在就來上傳代碼吧!同樣,無需安裝庫。我們要用的SoftwareSerial.h已經包含在Arduino IDE中。
下面的代碼源自示例代碼,我們只是進行了一些修改,將距離數據從字符串轉換為整數。其作用是測量傳感器到前方物體的距離并打印結果。我們用串行監視器顯示結果。
// ************************************************************************************************************************************************** #include #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output (connects to the LRF's SIN pin) #define ledPin 13 // Most Arduino boards have an on-board LED on this pin #define BUFSIZE 16 // Size of buffer int lrfDataInt; SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); void setup() // Set up code called once on start-up { // *************************************** setup for LRF *********************************************** pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready Serial.println("nnParallax Laser Range Finder"); lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor } // ****************************************** main loop ************************************************ void loop() // Main code, to run repeatedly { lrf(); } // ****************************************** end main loop ********************************************* void lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) // If there are any bytes available to read, then the LRF must have responded { lrfData[offset] = lrfSerial.read(); // Get the byte and store it in our buffer if (lrfData[offset] == ':') // If a ":" character is received, all data has been sent and the LRF is ready to accept the next command { lrfData[offset] = 0; // Null terminate the string of bytes we just received break; } // Break out of the loop offset++; // Increment offset into array if (offset >= BUFSIZE) offset = 0; // If the incoming data string is longer than our buffer, wrap around to avoid going out-of-bounds } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.print("Distance = "); // The lrfData string should now contain the data returned by the LRF, so display it on the Serial Monitor Serial.println(lrfDataInt); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor digitalWrite(ledPin, LOW); // Turn LED off delay(1000); } //*************************************************************************************************************************************************
串行監視器顯示的結果如下所示。所有尺寸的單位均為毫米。
安裝OLED顯示屏
首先,我們將OLED塑料盒安裝到亞克力底座中(請參見圖9),然后再將OLED顯示屏附帶的線纜一端連至顯示屏。要將顯示屏連至電機開發板,請將線纜另外一端jst接頭上的線纜剪下,然后將紅導線焊至5V,將黑導線焊至Ground,將黃導線焊至SDA引腳,將綠導線焊至SCL引腳。請確保OLED顯示屏的背面朝外。
圖8 . OLED與電機開發板之間的連接
將OLED固定在底座上并連接電纜后,我們就可以運行部分軟件了。
首先,請確保已經安裝了SeeedOLED.h。然后,將以下代碼上傳到Arduino。該代碼使用了名為oled1的函數,稍后的最終編碼也會使用該函數。其基本功能就是顯示100到109的數字。
//***************************************************************************************************************************************** #include #include int distanceFwd; void setup() { Wire.begin();} void loop() { int i = 0; for (i; (i < 10); i ++) { distanceFwd = 100 + i; oled1(); delay(1000); } } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //*****************************************************************************************************************************************
程序正常運行時,顯示屏應該會顯示以下視頻中的內容:
安裝最終代碼
現在,我們已經完成了所有硬件的安裝并測試了各個設備。讓我們把所有軟硬件結合起來,構建一個可以自主移動的智能激光機器人吧。最終程序會執行以下功能:
測量前方距離
如果距離超過70厘米,機器人將向前移動500步(大約50厘米);
如果距離大于40厘米小于70cm,機器人將向前移動200步(20厘米);
如果距離小于40厘米,機器人會向左掃描90度,向左掃描45度,向右掃描45度,向右掃描90度;
測量每個方向的距離,然后計算哪個方向的測量距離最長;
轉向距離最長的那個方向;
返回第一步。
請復制以下代碼并將其上傳到Arduino:
//********************************************************************************************************************* #include #include #include "utility/Adafruit_MS_PWMServoDriver.h" #include #include #include #define ledPin 13 #define BUFSIZE 16 #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output(connects to the LRF's SIN pin) SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); // Size of buffer (in bytes) for incoming data // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // Connect a stepper motor with 200 steps per revolution (1.8 degree) Adafruit_StepperMotor *myMotor1 = AFMS.getStepper(200, 1); // motor port #1 (M1 and M2) Adafruit_StepperMotor *myMotor2 = AFMS.getStepper(200, 2); // motor port #2 (M3 and M4) Servo panMotor; // servo for laser range finder (lrf) scanning int leftDistance1; int leftDistance2; int rightDistance1; int rightDistance2; int maxDistance; int angleTurn; int directions; int distanceFwd; const int a = 30; //*********************************************************************************start set up ************************** void setup() { Serial.begin(9600); // set up Serial library at 9600 bps panMotor.attach(10); // Attach Servo for scanning to pin 10 AFMS.begin(); // create with the default frequency 1.6KHz myMotor1->setSpeed(100); // Set stepmotor1 speed at 100 rpm myMotor2->setSpeed(100); // Set stepmotor2 speed at 100 rpm pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); // Input pin for LRF pinMode(txPin, OUTPUT); // Output pin for LRF digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor panMotor.write(90); delay(a); } //******************************************************************* start Loop ***************************************** void loop() { distanceFwd = lrf(); maxDistance = distanceFwd; oled1(); if (distanceFwd > 700) { Motor(500,1);} else if (distanceFwd > 400) { Motor(200,1);} else // if path is blocked { checkTurn(); turn();} } //***************************************************************** check turn function ******************************** void checkTurn() { digitalWrite(ledPin, HIGH); // ************************** Scan Left *********************************** panMotor.write(180); delay(a); leftDistance1 = lrf(); panMotor.write(135); delay(a); leftDistance2 = lrf(); oled(); // *************************** Scan Right ********************************* panMotor.write(45); delay(a); rightDistance2 = lrf(); panMotor.write(0); delay(a); rightDistance1 = lrf(); oled(); panMotor.write(90); digitalWrite(ledPin, LOW); // ************************************ Turn Left ************************ maxDistance = leftDistance1; angleTurn = 100; directions = 0; if (maxDistance <= leftDistance2) {angleTurn = 50; maxDistance = leftDistance2; directions = 0; } //*********************************** Turn Right *********************** if (maxDistance <= rightDistance2) {angleTurn = 50; maxDistance = rightDistance2; directions = 1; } if (maxDistance <= rightDistance1) {angleTurn = 100; maxDistance = rightDistance1; directions = 1; } // ******************************* Turn Back****************************** if ((leftDistance1 < 300) && (rightDistance1 <300) && (distanceFwd <300)) {angleTurn = 200; directions = 3; } } //************************************************ turn function ********************************************************* void turn() { rightDistance1 = 0; rightDistance2 = 0; leftDistance1 = 0; leftDistance2 = 0; if (directions == 0) // turn left { Motor(angleTurn,3);} if (directions == 1) // turn right { Motor(angleTurn,4);} if (directions == 3) // turn back { Motor(angleTurn,4);} } //*************************************** Stepper Motor function **************************************************** void Motor(int x,int y) { int i = 0; for ( i; (i < x); i ++) { if (y == 1) // move forward {myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} if (y == 2) // move backward {myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 3) // move left { myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 4) // move right { myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} } } //*********************************************************************** LRF function ******************************* long lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int lrfDataInt; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) { lrfData[offset] = lrfSerial.read(); if (lrfData[offset] == ':') { lrfData[offset] = 0; break;} offset++; if (offset >= BUFSIZE) offset = 0; } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.flush(); digitalWrite(ledPin, LOW); return lrfDataInt; } //********************************************************* Oled function ************************************************ void oled() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(0,0); SeeedOled.putString("Left 1:"); SeeedOled.setTextXY(0,12); SeeedOled.putNumber(leftDistance1); SeeedOled.setTextXY(2,0); SeeedOled.putString("Left 2:"); SeeedOled.setTextXY(2,12); SeeedOled.putNumber(leftDistance2); SeeedOled.setTextXY(4,0); SeeedOled.putString("Right 1:"); SeeedOled.setTextXY(4,12); SeeedOled.putNumber(rightDistance1); SeeedOled.setTextXY(6,0); SeeedOled.putString("Right 2:"); SeeedOled.setTextXY(6,12); SeeedOled.putNumber(rightDistance2); } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //***********************************************************************************************************************************
圖9. OLED顯示屏安裝在亞克力底座上的最終效果
結論
在之前的文章《如何制作自己的機器人》和《如何制作自己的機器人(第2部分)》中,我們用步進電機制作了一款簡單的輪式機器人。這次,我們對其進行了功能改進:為機器人增加了激光測距儀(LRF)功能,并且安裝了輪子,讓機器人能夠自由移動。我一直想制作一款能夠進行測量的設備。在本例中,憑借激光傳感器,我們的機器人不僅能夠檢測并避開物體,同時還能獲取更準確的距離數據。激光機器人還有許多其他應用場景。您也可以利用該激光傳感器設計自己喜歡的有趣項目。
接下來,我們會做一些更炫酷的事情,敬請期待!
Purnomo Nuhalim
來自墨爾本的Purnomo是一名退休人員,也是電子發燒友。目前,他正使用Arduino和Raspberry Pi從事各種開放式硬件項目的研發。除了電子學,他還對航空建模和天文學充滿熱情。
審核編輯黃宇
-
機器人
+關注
關注
211文章
28468瀏覽量
207357 -
OLED
+關注
關注
119文章
6203瀏覽量
224329 -
激光傳感器
+關注
關注
2文章
152瀏覽量
21409 -
Arduino
+關注
關注
188文章
6471瀏覽量
187241
發布評論請先 登錄
相關推薦
評論