在第1部分中,我們討論了構建一個探索者機器人的幾個重要步驟。我們透過Eagle設計並創建了我們自己的PCB。在第2部分中,我們將添加其他元件,並對程式進行測試,以確保RF機器人能夠按照預期方式運行。這裡我們所設計的探索者機器人能夠自主行駛,感知周圍環境並無線傳輸收集到的資料。該專案的目標是為探索者機器人製造原型,該原型將配備有一組感測器,如溫度感測器和壓力感測器,這些感測器能夠借助數位無線電通信模組(RF)即時發送所收集的資訊。
遠端控制器將從筆記型電腦的USB埠獲取資訊,並透過第二個nRF24l01+模組進行重定向。遠端控制器由Arduino開發板和帶有NRF連接器和電源的板組成,這個板將被安裝在Uno上方,以避免使用電線。
您還會看到我們安裝的兩個LED燈,用來指示電路的功能和上述的其他元件。我們將會使用一些用於穩壓器和NPN電晶體過熱保護的小冷卻器。電路板將會被放置于電池上方,如圖5所示。
我們正在使用的是nRF24L01+無線收發器模組。nRF24L01+是一款超低功耗的無線射頻收發器。對於此類應用程式,該模組是最佳選擇。它以其出色的性能和低廉的價格成為最受歡迎的型號之一,同時,該模組通用的通信協定使其能夠與全球廣泛使用的軟體相容。
規格:
為了獲取壓力和溫度資料,我們將使用Sparkfun BMP085氣壓感測器。該感測器提供300至110 kPa的測量範圍,誤差為0.03 kPa。BMP085還可以提供0至65 °C範圍內的溫度測量功能。其承受的電壓必須在1.8-3.6V範圍內,並透過l2C直接與微控制器建立連接。
規格:
現在我們將要安裝HC-SR04超聲波感測器。HC-SR04的檢測範圍為2-200釐米。微控制器向感測器發送激發聲波的脈衝。當感測器感知到聲波已返回時,將向微控制器發送回脈衝,計算發送脈衝的時間與接收到脈衝的時間之間的差值即可算出距離。
𝐷=(𝑡2−𝑡1)×170
Arduino平臺提供了預定義的功能,使用者無需對一些寄存器進行配置。可以透過一些庫來實現平臺與其他週邊設備的介面。使用一根Arduino-PC電纜就可以以非常簡單地完成程式設計。該開發板配有USB-UART轉換器。
在本專案中,需要有兩個程式:一個用於機器人的功能,另一個用於遠端控制器。這兩個程式透過nRF24101+模組以無線電的方式相互通信。資料流程圖如圖9所示。有兩種方式。正向傳遞:首先,資訊被應用程式發送到Arduino。然後由收發器接管的資訊將進一步發送到第二個收發器,再返回到微控制器。這將用於控制機器人的機體運動。
反向傳遞:控制器從感測器獲取資訊並進行處理,然後將其發送到OTA(空中下載技術),使其最終到達PC端。
該應用程式是透過Microsoft Visual Studio 2010使用Visual C#程式設計語言來完成的。Visual Studio在編輯部分融入視覺元素,具有非常簡潔直觀的介面。這有助於實現一些複雜的應用程式。該應用程式可以發送指令,接收資料並進行顯示。機器人的控制由鍵盤上的箭頭按鍵來實現。當您按下箭頭按鍵的時候,運動按鈕會亮起,表示指令已經被接收到。這些指令其實是串列傳送的字母,最後會到達機器人控制器進行解碼。例如,當您按下“向上”鍵時,應用程式透過USB在序列埠發送“U”,指令的解碼表如下所示:
字母 | 動作 |
U | 前進 |
R | 右轉 |
D | 向後移動 |
L | 左轉 |
N | 更新 |
B | 打開燈光 |
O | 發送PWM值 |
為了達到該效果,我們應用了以下演算法:應用程式發送LED燈“打開”指令,然後遠端控制器接收指令並將其發送給機器人。一旦機器人接收到指令,就會打開LED並向應用程式發送指令打開指示燈。我們引入該演算法來避免產生不同步的問題(LED熄滅但指示燈亮起的情況)。
另一個重要問題是介面中資訊的導入和顯示。為了使應用程式能夠區分資訊並知道在哪裡顯示,我們實施了以下方法:一旦機器人從遠端控制器接收到資訊,它不僅僅只是進行簡單的傳送,還會對其進行處理並發送類似這樣的指令:“CMD: TE =” + val.marime。應用程式就會在透過序列埠檢索到以“CMD”開頭的資訊時刪除掉“:”並讀取之後的兩個字母,這兩個字母表示的是資訊將會被寫入的區域。“val. marime”會在“=”之後被讀取,對於以上示例,程式將會把讀取到的資料分配到溫度區域中。
為了使應用程式能夠識別同時按下的兩個按鍵,我們已經為每個按鍵初始化了一個變數。當按下一個按鍵時,對應事件就會其變數的值增加到1;當釋放按鍵時,對應事件就會將其變數減少到0。計時器將會持續計算這些變數的值。
機器人使用的是在Arduino IDE中編譯的程式,比遠端控制器的程式要複雜的多:
1. 遠程控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include <SPI.h> #include "nRF24L01.h" #include "RF24.h" char cmd[3]; RF24 radio(9,10); const uint64_t pipes[2] ={ 0xE8E8F0F0E1LL, 0xDEDEDEDEE7LL} ; float bar[7]; char arc[3]; int cifra[3]; int val; int a; void setup(void){ Serial.begin(9600); radio.begin(); radio.openWritingPipe(pipes[0]); radio.openReadingPipe(1,pipes[1]); radio.setDataRate(RF24_250KBPS); pinMode(6, OUTPUT); pinMode(5, OUTPUT); } void loop(void){ if(Serial) { digitalWrite(6, HIGH); digitalWrite(5, LOW); } else { digitalWrite(5, HIGH); digitalWrite(6, LOW); } cmd[0]=Serial.read(); if(cmd[0]=='O') { delay(20); arc[0]=Serial.read(); delay(20); arc[1]=Serial.read(); delay(20); arc[2]=Serial.read(); //Storing the values cifra[0]=arc[0]-'0'; cifra[1]=arc[1]-'0'; cifra[2]=arc[2]-'0'; val=10*cifra[0]+cifra[1]; Serial.println(val); Serial.println(sizeof(cmd)); cmd[0]='X'; cmd[1]=val; cmd[2]=cifra[2]; radio.write(cmd, sizeof(cmd)); } a=1; if(cmd[0]=='N') { radio.write(cmd, sizeof(cmd)); radio.startListening(); delay(1); while(a==1) { if (radio.available()){ a=2; bool done = false; while (!done){ done = radio.read(bar, 28); Serial.print("CMD:X1="); Serial.println(bar[0]); delay(5); Serial.print("CMD:D1="); Serial.println(bar[1]); delay(5); Serial.print("CMD:TE="); Serial.println(bar[2]); delay(5); Serial.print("CMD:PR="); Serial.println(bar[3]); delay(5); Serial.print("CMD:AT="); Serial.println(bar[4]); delay(5); Serial.print("CMD:AL="); Serial.println(bar[5]); Serial.print("CMD:LE="); Serial.println(bar[6]); } } } } else { radio.write(cmd, sizeof(cmd)); } } |
2. 機器人
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
#include <SPI.h> #include "nRF24L01.h" #include "RF24.h" #include #define BMP085_ADDRESS 0x77 // I2C address of BMP085 #define trigPin 2 #define echoPin 4 const unsigned char OSS = 0; int pwm, test1, test2; char cmd[3]; RF24 radio(9,10); const uint64_t pipes[2] ={ 0xE8E8F0F0E1LL, 0xDEDEDEDEE7LL} ; float bar[7]; int ac1; int ac2; int ac3; unsigned int ac4; unsigned int ac5; unsigned int ac6; int b1; int b2; int mb; int mc; int md; float temperature, pressure, atm, altitude,leduri; long b5; void bmp085Calibration() { ac1 = bmp085ReadInt(0xAA); ac2 = bmp085ReadInt(0xAC); ac3 = bmp085ReadInt(0xAE); ac4 = bmp085ReadInt(0xB0); ac5 = bmp085ReadInt(0xB2); ac6 = bmp085ReadInt(0xB4); b1 = bmp085ReadInt(0xB6); b2 = bmp085ReadInt(0xB8); mb = bmp085ReadInt(0xBA); mc = bmp085ReadInt(0xBC); md = bmp085ReadInt(0xBE); } // Calculate temperature in deg C float bmp085GetTemperature(unsigned int ut){ long x1, x2; x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15; x2 = ((long)mc << 11)/(x1 + md); b5 = x1 + x2; float temp = ((b5 + 8)>>4); temp = temp /10; return temp; } // Calculate pressure given up // calibration values must be known // b5 is also required so bmp085GetTemperature(...) must be called first. // Value returned will be pressure in units of Pa. long bmp085GetPressure(unsigned long up){ long x1, x2, x3, b3, b6, p; unsigned long b4, b7; b6 = b5 - 4000; // Calculate B3 x1 = (b2 * (b6 * b6)>>12)>>11; x2 = (ac2 * b6)>>11; x3 = x1 + x2; b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2; // Calculate B4 x1 = (ac3 * b6)>>13; x2 = (b1 * ((b6 * b6)>>12))>>16; x3 = ((x1 + x2) + 2)>>2; b4 = (ac4 * (unsigned long)(x3 + 32768))>>15; b7 = ((unsigned long)(up - b3) * (50000>>OSS)); if (b7 < 0x80000000) p = (b7<<1)/b4; else p = (b7/b4)<<1; x1 = (p>>8) * (p>>8); x1 = (x1 * 3038)>>16; x2 = (-7357 * p)>>16; p += (x1 + x2 + 3791)>>4; long temp = p; return temp; } // Read 1 byte from the BMP085 at 'address' char bmp085Read(unsigned char address) { unsigned char data; Wire.beginTransmission(BMP085_ADDRESS); Wire.write(address); Wire.endTransmission(); Wire.requestFrom(BMP085_ADDRESS, 1); while(!Wire.available()) ; return Wire.read(); } // Read 2 bytes from the BMP085 // First byte will be from 'address' // Second byte will be from 'address'+1 int bmp085ReadInt(unsigned char address) { unsigned char msb, lsb; Wire.beginTransmission(BMP085_ADDRESS); Wire.write(address); Wire.endTransmission(); Wire.requestFrom(BMP085_ADDRESS, 2); while(Wire.available()<2) ; msb = Wire.read(); lsb = Wire.read(); return (int) msb< } // Read the uncompensated temperature value unsigned int bmp085ReadUT(){ unsigned int ut; // Write 0x2E into Register 0xF4 // This requests a temperature reading Wire.beginTransmission(BMP085_ADDRESS); Wire.write(0xF4); Wire.write(0x2E); Wire.endTransmission(); // Wait at least 4.5ms delay(5); // Read two bytes from registers 0xF6 and 0xF7 ut = bmp085ReadInt(0xF6); return ut; } // Read the uncompensated pressure value unsigned long bmp085ReadUP(){ unsigned char msb, lsb, xlsb; unsigned long up = 0; // Write 0x34+(OSS<<6) into register 0xF4 // Request a pressure reading w/ oversampling setting Wire.beginTransmission(BMP085_ADDRESS); Wire.write(0xF4); Wire.write(0x34 + (OSS<<6)); Wire.endTransmission(); // Wait for conversion, delay time dependent on OSS delay(2 + (3<<OSS)); // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB) msb = bmp085Read(0xF6); lsb = bmp085Read(0xF7); xlsb = bmp085Read(0xF8); up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS); return up; } void writeRegister(int deviceAddress, byte address, byte val) { Wire.beginTransmission(deviceAddress); // start transmission to device Wire.write(address); // send register address Wire.write(val); // send value to write Wire.endTransmission(); // end transmission } int readRegister(int deviceAddress, byte address){ int v; Wire.beginTransmission(deviceAddress); Wire.write(address); // register to read Wire.endTransmission(); Wire.requestFrom(deviceAddress, 1); // read a byte while(!Wire.available()) { // waiting } v = Wire.read(); return v; } float calcAltitude(float pressure){ float A = pressure/101325; float B = 1/5.25588; float C = pow(A,B); C = 1 - C; C = C /0.0000225577; return C; } int distance2() { long duration, distance; digitalWrite(trigPin, LOW); // Added this line delayMicroseconds(2); // Added this line digitalWrite(trigPin, HIGH); delayMicroseconds(10); // Added this line digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration/2) / 29.1; if (distance < 4) { // This is where the LED On/Off happens } else { } if (distance >= 200 || distance <= 0){ } else { } return distance; } void setup(void){ pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); bar[0] ='A'; Serial.begin(9600); radio.begin(); radio.openReadingPipe(1,pipes[0]); radio.openWritingPipe(pipes[1]); radio.startListening(); radio.setDataRate(RF24_250KBPS); pwm=255; pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(2, OUTPUT); pinMode(4, INPUT); pinMode(3, OUTPUT); Wire.begin(); bmp085Calibration(); } void mersF(int timp, int pwm) { analogWrite(5, pwm); digitalWrite(6, LOW); digitalWrite(8, LOW); digitalWrite(7, LOW); delay(timp); digitalWrite(5, LOW); } void mersS(int timp, int pwm) { analogWrite(6, pwm); digitalWrite(5, LOW); digitalWrite(8, LOW); digitalWrite(7, LOW); delay(timp); digitalWrite(6, LOW); } void VirareD(int timp, int pwm) { analogWrite(5, pwm); digitalWrite(6, LOW); digitalWrite(8, HIGH); digitalWrite(7, LOW); delay(timp); digitalWrite(5, LOW); digitalWrite(8, LOW); } void VirareS(int timp, int pwm) { analogWrite(5, pwm); digitalWrite(6, LOW); digitalWrite(7, HIGH); digitalWrite(8, LOW); delay(timp); digitalWrite(7, LOW); digitalWrite(5, LOW); } void VirareDspate(int timp, int pwm) { analogWrite(6, pwm); digitalWrite(5, LOW); digitalWrite(8, HIGH); digitalWrite(7, LOW); delay(timp); digitalWrite(6, LOW); digitalWrite(8, LOW); } void VirareSspate(int timp, int pwm) { analogWrite(6, pwm); digitalWrite(5, LOW); digitalWrite(7, HIGH); digitalWrite(8, LOW); delay(timp); digitalWrite(7, LOW); digitalWrite(6, LOW); } void stop(){ digitalWrite(5, LOW); digitalWrite(6, LOW); digitalWrite(7, LOW); digitalWrite(8, LOW); } void trimitere() { temperature = bmp085GetTemperature(bmp085ReadUT()); //MUST be called first pressure = bmp085GetPressure(bmp085ReadUP()); atm = pressure / 101325; // "standard atmosphere" altitude = calcAltitude(pressure); //Uncompensated caculation - in Meters leduri=digitalRead(3); radio.stopListening(); bar[0]=analogRead(A2); bar[1]=distance2(); bar[2]=temperature; bar[3]=pressure; bar[4]=atm; bar[5]=altitude; bar[6]=leduri; radio.write(bar, sizeof(bar)); delay(200); radio.startListening(); } void aprindere() { if(digitalRead(3)) digitalWrite(3,LOW); else digitalWrite(3,HIGH); delay(30); } void loop(void){ if (radio.available()){ bool done = false; while (!done){ done = radio.read(cmd, 3); if (cmd[0] == 'U'){ Serial.println("Exec cmd: Up"); mersF(30,pwm); } else if (cmd[0] == 'E'){ Serial.println("Exec cmd: Right"); VirareD(30,pwm); } else if (cmd[0] == 'C'){ Serial.println("Exec cmd: Right"); VirareDspate(30, pwm); } else if (cmd[0] == 'Z'){ Serial.println("Exec cmd: Right"); VirareSspate(30,pwm); } else if (cmd[0] == 'D'){ Serial.println("Exec cmd: Down"); mersS(30,pwm); } else if (cmd[0] == 'Q'){ Serial.println("Exec cmd: Left"); VirareS(30, pwm); } else if (cmd[0] == 'N'){ delay(30); trimitere(); } else if (cmd[0] == 'B'){ delay(30); aprindere(); } else if (cmd[0] == 'J'){ stop(); delay(30); } else if (cmd[0] == 'X'){ test1=cmd[1]; test2=cmd[2]; pwm=test1*10+test2; delay(30); } else { } } } } |
現在,我們已經構建了一個自主arduino機器人,該機器人能夠自主導航並從周圍環境中收集資料!這是一個非常具有挑戰性又很有意義的項目。這個原型還可以進行進一步的改善,例如添加用於在崎嶇地形/環境中提供保護的外殼。如果您有任何改進的建議,請隨時與我分享!