這篇文章來源于DevicePlus.com英語網站的翻譯稿。

歡迎回到ArduRover系列!在第一部分 和 第二部分中,我們已經構建了一個Arduino六輪驅動漫游車。在最后一部分內容中,我們將致力于編寫一個用于機器人控制的程序。在第二部分中,我們已經介紹了一些代碼,但是那些代碼僅僅是用于控制電機運行的。這一次,我們希望能夠得到所有傳感器的數據,并且能夠通過使用Android手機來實現實際遠程操控!
硬件
? 通過第一部分和 第二部分制作的ArduRover
軟件
? Arduino IDE 1.8.1
? Android Studio 3.0
? Arduino 庫:
? LoRaLib – GitHub
? RohmMultiSensor – GitHub
? JPEGDecoder – GitHub
? Arduino 設計圖 – GitHub
? Android 應用程序 – GitHub
對接線的修正
在本系列的上一部分里,除了部件的制作,我還介紹了邏輯電路(所有傳感器和模塊)的接線。那時,我只測試了電機,因為VNH5019驅動器是其中唯一我沒使用過的子系統,當然,我使用的代碼也只能用來控制它們。由于電機運行得很好,我就以為直接把其他所有函數(例如控制傳感器、相機和伺服器的代碼)添加上去是沒有問題的。所以您可以想象出當我看到所有電機在添加了那些函數后馬上就停止運行時,我有多么驚訝。
當類似這種問題發生的時候,我們必須返回上一步,回顧一下一切運行正常時的情況。我更改了代碼,這很簡單,只需要上傳一個不同的Arduino設計圖就可以了,然后查看一下問題是出在硬件還是軟件上。果然,電機在之前的代碼下恢復了運行。通過對新代碼進行謹慎的添加和刪除,我發現Servo庫在某種程度上導致了該錯誤的發生。在調用servo.attach()函數后,電機就會停止運轉。這似乎很奇怪,直到我意識到了信號傳輸的問題:使用Servo庫的時候,電機控制部分好像完全沒有信號輸出。
VNH5019允許您通過使用脈沖寬度調制(PWM)來控制電機轉速。伺服器也使用PWM來將小型電機設定在某一位置并保持不變。事實證明,在Arduino Mega上,PWM控制具有一些沒有在官方文件中提及的特點。起初,我的伺服器PWM輸入端連接到Arduino Mega的引腳8和9上,電機PWM連接到引腳44和46。根據Arduino官方文件,這些引腳都是支持PWM的。但是,這些文件沒有提及的是,當您通過引腳2和13使用Servo庫時,會失去引腳44和46的PWM功能支持。這是因為Servo庫使用了一個ATmega2560定時器,該定時器同樣用于控制引腳44到46上的PWM,那么自然而然,定時器一次只能執行一個任務。
考慮到這一點,我修正了接線原理圖。如下圖所示,用于電機的PWM移至引腳5和6。經過這次更改,伺服器和電機就可以同時工作了。

圖1:修正后的邏輯端原理圖
Arduino 程序結構
在編寫控制如此復雜的系統的程序時,最好把所有需要考慮的因素都寫下來。對于ArduRover,需要考慮的有以下內容:
? 電機控制 – 這可能是最重要的部分,因為顯然我們需要控制機器人的行進方向。
? 傳感器控制 – 我們需要從連接在機器人上的所有傳感器中獲取一些數據。
? 相機控制 – 這部分包括拍攝、傳輸圖片,以及通過伺服器支架來轉動相機。
? 數據記錄 – 我們有一個板載SD卡,可以通過它來記錄從傳感器和GPS獲取的信息。
一個潛在的問題是回傳圖像數據。正如我們在Arduino教程JPEG解碼中所講的那樣,傳輸圖像數據會花費很長時間。如果機器人突然決定一次把整個圖像都傳送過去,那么它將會在相當長的一段時間內處于無響應狀態。因此,在每次運行主循環時,我們都需要決定是否傳輸圖像中的某些部分,或者是否對上次循環執行期間傳達回來的最終指令作出響應。
以上述要求為要領,我總結出了以下程序邏輯框圖。

圖2:Arduino程序邏輯框圖
在主循環函數的每次迭代過程中,Arduino會首先更新所有當前的傳感器數據,并把它們記錄到SD卡中,包括ROHM傳感器、GPS以及流經每個電機驅動器的電流。然后,會檢查LoRa模塊是否接收到了新的程序包。如果接收到了,那么程序包中的指令將會被執行,作出的響應將會再次通過LoRa模塊進行傳輸。如果沒有接收到新的指令,程序會檢查是否有圖片需要被傳輸,如果有的話,就會傳輸一個圖片數據包,如果沒有需要傳輸的圖片,這一步會被跳過。最后,程序回到循環的開始,然后重復該過程。
我們必須意識到,使用這個邏輯結構,處理新的指令會比JPEG傳輸擁有更高的優先級。這樣的話,我們就可以巧妙地避免上文中提到的機器人無響應的問題。指令與響應之間的最長延遲時間只會是傳輸一個圖像數據包的時間。
既然已經有了程序邏輯結構的總體思路,那么我們需要關注另一個關鍵的步驟了:這些指令將會是什么樣子?
指令和指令包結構
既然我們使用LoRa來傳輸和接收數據,就必須記住該技術的設計用途:小容量數據的遠距離傳輸。這意味著我們要盡量精簡我們的指令,因為每一個字節都會被計數。我們可以只發送一些字符串,例如“電機左側50”來將左側的電機設置為50%的轉速,但是這其實非常浪費空間。這個字符串占用了14個字節的數據容量,但是僅僅傳輸了三個信息塊:該指令的子系統目標(“電機”)中哪些電機會被影響(“左側”)以及它們將會被設置的轉速所占最大轉速的百分比(“50”)。可以肯定地說,我們可以做得更好。這種類型指令的優點是非常易于閱讀,但其實,再重申一下,只要系統運行正常,在指令執行這一塊是不需要人為干預的。
所以,我決定不使用那些長的字符串,而是開發一種簡單的指令系統,在每個指令包中使用的指令最長僅為4個字節。指令包中的第一個字節叫做首地址,將會被進一步分為兩半:高四位包含來自控制系統的指令分類,低四位用來作為機器人的響應。0表示成功,非零值是具有已知含義的錯誤代碼。
| 第1個字節 | 指令描述 |
| 0x00 | 停止所有電機 |
| 0x10 | 設置所有電機的轉速和轉向 |
| 0x20 | 設置所有左側電機的轉速和轉向 |
| 0x30 | 設置所有右側電機的轉速和轉向 |
| 0x40 | 設置相機傾斜角度 |
| 0x50 | 設置相機平移距離 |
| 0x60 | 使用相機拍攝圖片 |
| 0x70 | 開始JPEG 傳輸 |
| 0x80 | 強制啟動新傳感器測量 |
| 0x90 | 獲取最新傳感器數據 |
| 0xA0 | 重新發送最終指令包 |
| 0xB0 | 設置LoRa調制調節器配置 |
| 0xC0 | 獲取當前所有存在的錯誤信息 |
| 0xD0 | 未被使用 |
| 0xE0 | 未被使用 |
| 0xF0 | 未被使用 |
指令包的下一部分內容是有效負載 – 這也是在有關頭文件中指令的所有附加信息存儲的地方。這部分比首地址更簡單一些,因為它的內容取決于指令類型:有些指令不需要任何附加信息,比如說0x00(停止兩側電機)。所以這些指令的有效負載為空。然而,一些命令要求額外數據不能超過三個字節,比如指令0xD0(LoRa配置),要求每個主要設置,如帶寬、擴頻因子、以及編碼率(有關LoRa細節請參考LoRaLib教程 或者 GitHub wiki)只占一個字節。下表描述了所有指令包的結構。
| 第1個字節 | 第2個字節 | 第3個字節 | 第4個字節 |
| 0x00 | – | – | – |
| 0x10 | 左側PWM控制轉速 | 右側PWM控制轉速 | 轉向 |
| 0x20 | 左側PWM控制轉速 | 轉向 | – |
| 0x30 | 右側PWM控制轉速 | 轉向 | – |
| 0x40 | 傾斜角度 | – | – |
| 0x50 | 平移距離 | – | – |
| 0x60 | – | – | – |
| 0x70 | 圖片編碼 | – | – |
| 0x80 | 傳感器 ID(s) | – | – |
| 0x90 | 傳感器 ID(s) | – | – |
| 0xA0 | – | – | – |
| 0xB0 | 帶寬 | 擴頻因子 | 編碼率 |
| 0xC0 | – | – | – |
| 0xD0 | – | – | – |
| 0xE0 | – | – | – |
| 0xF0 | – | – | – |
當然,這些只是由控制系統發送然后機器人來接收的指令包。我們也希望機器人能夠作出響應。對于某些指令,這種響應可能非常簡單,僅僅是為了讓控制系統知道指令是否被成功執行。如果您仔細看上文中的表格,會注意到所有的指令都只用了第一個指令字節的高四位,這樣剩下的低四位可以用于響應。這種方法有兩個顯著的優勢:首先,始終可以追蹤到每個響應所屬的指令,因為指令位總是作為響應值的一部分返回;其次,對于每一個指令,都有足夠的空間用于16種不同的響應。
讓我們用一個例子來進行說明。假設我們想用相機拍攝一張圖片,那么我們會發送指令0x60。那么機器人有16種不同的方式來響應。如果響應是0x60,這意味指令已經被成功執行。所有其他類型的響應,0x61到0x6F,都意味著出現了錯誤。這樣我們不僅知道指令執行失敗了,還會知道為什么失敗并且進行修正。當然,這些響應不一定總是只有一個字節的長度,有些指令會要求機器發回一些附加信息,比如傳感器數據。下表顯示的是所有針對不同指令的響應包。
| 指令 | 第1個字節 | 第2個字節 | 第3個字節 | 第4~240字節 |
| 0x00 | 0x00 | – | – | – |
| 0x10 | 0x10 | – | – | – |
| 0x20 | 0x20 | – | – | – |
| 0x30 | 0x30 | – | – | – |
| 0x40 | 0x40 | – | – | – |
| 0x50 | 0x50 | – | – | – |
| 0x60 | 0x6_ | – | – | – |
| 0x70 | 0x7_ | 圖像數據 | 圖像數據 | 圖像數據 |
| 0x80 | 0x8_ | 發生錯誤的傳感器 | 傳感器數據 | 傳感器數據 |
| 0x90 | 0x9_ | 發生錯誤的傳感器 | 傳感器數據 | 傳感器數據 |
| 0xA0 | 0xA0 | – | – | – |
| 0xB0 | 0xB0 | – | – | – |
| 0xC0 | 0xC_ | 錯誤標志 | 錯誤標志 | – |
| 0xD0 | 0xD0 | – | – | – |
| 0xE0 | 0xE0 | – | – | – |
| 0xF0 | 0xF0 | – | – | – |
您可以注意到某些指令(主要是關于電機和伺服器的那些指令)只能返回0(成功)。這是因為當前版本中的板載電子設備無法判斷這些指令是否被成功執行。為了達到驗證的目的,我們需要增加新的設備來檢查電機是否在運轉,或者伺服器是否在正確的位置。這應該不太困難,讓我們暫時忽略它,來看看那些可能會有報錯響應的指令:
? 0x60 (用相機拍攝照片)
這項指令有多種報錯響應。如果沒有相機,那么機器人會返回0x61。如果有相機并且能夠正常工作,但是無法拍攝照片,那么會返回0x62。最后,如果成功拍攝了照片,但是不能存儲,那么返回值為0x63。
? 0x70 (開始JPEG 傳輸)
這同樣是一個有多種返回值的指令。如果您能夠回想起我之前的文章 Arduino上的JPEG解碼,就知道第一個數據包里是有關圖片的信息,而非真實像素值。我們需要對這兩種類型進行區分,所以說如果數據包僅包含圖片信息,響應值為0x70,后跟類似寬度、高度和單片機(MCU)計數的信息。如果數據包包含原始像素值,機器人的響應值為0x71,后跟238字節的像素數據。現在我們介紹報錯代碼:如果響應值是0x72,這意味著無法找到SD卡。如果是0x73,就表示SD卡沒問題,但是無法找到請求的圖片。
? 0x80 (強制啟動新傳感器測量)
該指令只有一種失敗模式—所請求的傳感器無法進行測量。這種情況下,響應值為0x81,后跟有包含暗示傳感器故障的一個字節。在這個字節中,位的索引對應下列傳感器的順序(從MSB到LSB):左VNH5019,右VNH5019, BD1020HFV, ML8511A, BM1383GLV, KX022-1020, RPR-0521RS和BM1422GMV。比如,如果響應的第二個字節是0x03 (0b0000 00011),表示傳感器RPR-0521RS 和 BM1422GMV故障。
? 0x90 (獲取最新傳感器數據)
該指令與0x80非常相似—只有當某些傳感器故障時才會顯示報錯。如果那種情況發生,將會返回0x91,后跟有錯誤標志的相同字節。之后,將會有來自傳感器的最多46字節的數據。如果沒有傳感器發生故障,那么返回的第二個字節將會是簡單的0x00,后跟有包含所有46字節的數據。
? 0xB0 (設置LoRa調制調節器配置)
這是目前應用的指令中最后一個會報錯的指令,雖然這項指令本不應該會執行失敗。該指令將設置新的LoRa調制調節器配置,LoRaLib檢驗所提供配置的有效性。如果有一個或者更多的配置值無效,那么響應字節中低四位之一將會被設置為相應值。所以如果提供的帶寬是無效的,那么機器人將返回0xB1 (0b1011 0001)。如果提供的所有配置值都不正確,那么會返回0xB7 (0b1011 0111)。這項指令本不應該會失敗,因為用戶只能從已知設定值中進行選擇。但是,在執行該指令時最好還是再檢查一遍。LoRa模塊是最關鍵的子系統,沒有了它,就無法對機器人進行控制。由于LoRa調制的屬性,兩個模塊上的設置必須相同,否則,接收器就無法對傳輸內容進行解調。
在解釋完所有內容后,讓我們回到最開始的內容。我們想要把左側電機的轉速設置為50%。我們已經知道了指令首地址為0x20。所以根據表格內容,我們可以填寫剩下的字節,從控制器發送的指令包如下所示:
0x20 0x7F 0x00 0x00
第一個字節顯然是首地址—設置左側電機轉速。下一個字節是轉速。電機驅動器通過PWM調制來改變電機轉速,所以數字0xF7(十進制為127)對應50%占空比或50%轉速。第三個字節是轉向—0x00表示正向,0x01表示逆向。最后一個字節僅用來占位—添加該字節來使指令包始終為四字節長。一旦指令被接收并被機器人成功執行,會返回以下指令包:
0x20
對于機器人上所運行的Arduino程序,其指令系統的實現都可以在我的GitHub中查看。通常我在這上面發布實際代碼。不幸的是,代碼文件太大無法放在本文中。所以如果您對于代碼實現的細節有興趣的話,請查看實際代碼中的注釋。現在,我們來繼續看一些有趣的東西吧,那就是實現遠程操控的應用程序!
遠程控制應用程序
在ArduRover系列的最后一部分,我發布了一個機器人在計算機的控制下行駛的視頻。計算機上運行的程序通過一個串行端口將數據發送給Arduino,然后Arduino傳送數據給機器人。但是,這種設置有幾個比較大的問題。
首先,通過鍵盤來控制機器人是非常困難的。下圖是我在視頻中用來控制機器人的應用程序的一個截圖。

圖3:測試電機的C#應用程序
這是在Visual Studio 中的.NET框架下用C#語言來編寫的。雖然它能夠對測試電機起到一些作用,但是對于一次只調整一側電機來控制機器人仍是一個挑戰。這真是令人心煩。用戶唯一可以控制的就是設置電機轉速的滑動按鈕和使機器人立馬停止工作的按鈕。
我遇到的另一個比較大的問題是用來測試的筆記本電腦比較重,不能用一只手拿著隨身攜帶,然后空出另一只手來控制機器人。好吧,一個隨身帶著筆記本電腦和寵物機器人的人看起來可能是有點奇怪。
為了使控制器變得更加簡單和便攜,我需要一些可以連接到Arduino,并且重量輕、便于長時間攜帶的、最好是可以手持的設備。如果隱蔽性比較好那就更好了。起初,我打算使用一個可以安裝在Arduino上的舊RC飛機控制器,但是后來我想到了一個更簡單的解決方案:我的Android手機!它完美地符合所有要求:很輕并且可以手持。我們只需要通過USB端口或藍牙將其與Arduino連接即可。
我提出的下一個應用程序是通過Android Studio用Java編寫的。這比上文中的C#語言應用程序高級得多。它具有多個屏幕,允許用戶控制所有子系統,而不僅僅是電機。讓我們來看一下其中一個屏幕上的布局。

圖4:基本的應用程序布局
每個屏幕都會被分成幾個部分。在屏幕的兩側,有兩個滑動按鈕(上圖中1和2標記的地方)來分別對左側和右側的電機設定轉向和轉速。這樣的布置可以使用戶能夠以更自然的方式來對機器人的移動進行控制:滑動按鈕向上移動使其前進,向下移動使其后退。按鈕扭矩越大,那一側的電機轉速就越大。很簡單!
接下來是下面(3)的一個大“停止”按鈕。它的作用看起來很好解釋:按下這個按鈕,電機就會停止。當我用C#應用程序測試機器人時,這應該是最有用的按鈕了,所以我決定在該應用程序中保留這個設計。在應用程序的頂部,有一個帶有多個標簽的欄(4)。由于控制內容太多,無法都呈現在一個屏幕上,這個條形欄可以使用戶在不同屏幕間切換。該屏幕中的內容顯示在中心位置(5)。這意味著用戶可以通過任何屏幕上來控制機器人以及停止所有電機!
這些屏幕(按照應用程序頂部條形欄中的排列順序)為:
? “相機”屏。 – 用戶可以更改相機位置,拍攝并保存圖片。目前相機的傾斜角度和水平位移也都顯示在這里。中間的藍色大框中還能顯示接收到的圖像。

圖5:相機屏
? “GPS”屏。 – 在當前版本應用程序中還未實現。這個想法是希望有一個地圖,可以顯示最近一次接收到的機器人位置。

圖6:GPS屏
? “圖形”屏。 – 同樣在最新版本中尚未實現。在這個屏幕中,傳感器的輸出將以圖形顯示,類似于Arduino IDE中的串行繪圖儀。

圖7:圖形屏
? “傳感器”屏。 – 該頁面顯示傳感器的最近一次輸出,同時也包含一些按鈕來強制啟動傳感器進行新的測量,或更新顯示值。

圖8:傳感器屏
? “監視器”屏。 – 基本上與Arduino IDE中的串行監視器相同:用戶打字輸入,監視器顯示輸出。該屏幕也顯示所有發送的指令(黑色文本),機器人的所有響應(藍色文本),所有報錯(紅色文本),或者來自應用程序的常規信息(灰色文本)。

圖9:監視器屏
? “設置”屏。 – 包含多種設置,大部分與USB、藍牙和LoRa通信相關。此外,屏幕上有一個按鈕,我們可以用它來從機器人獲取當前存在的所有錯誤。

圖10:設置屏
結論
像往常一樣,我們呈現一些試駕的照片!






顯然,在許多地方仍然有很大的改進空間。比如說,測試機器人的時候,我注意到抑制器會有向下垂的趨勢。

圖11:機器人移動時的懸架細節可以注意到車身前部離地面較近
盡管這不是一個很大的問題,但它表明出抑制器內部的彈簧設置得有點松。彈簧設置更緊一些,機器人的車體就會更高一些,這樣在崎嶇地形中被困住的可能性會更小。
另一個可以改進的是應用程序。顯然,其中某些部分現在只是個占位符而已:GPS和圖形選項不包含任何內容,所以如果能夠實現這些功能就更好了。不幸的是,在編寫程序的時候,我還沒想到該怎么做,所以我可能還需要更多的Android Studio經驗才能找到方法。
最后,傳感器和相機的位置布局也是可以改善的部分。雖然通常的習慣是將相機放在機器人的前方,但是這樣還是有些暴露。可能通過使用某種桿來放在中間會更好一些,就像你在所有火星探測器上看到的那樣。既然說到這里,添加一些太陽能電池板并將整個設備用于某些太空任務可能也是個不錯的注意!如果您有幾億美元的閑置資金并且愿意用來資助這項驚人的太空冒險計劃,就跟我聯系吧!
言歸正傳,非常感謝您閱讀這篇文章以及對整個系列的關注。如果您對機器人有任何想法請在下面討論區留言!如果您決定要親自構建屬于您自己的ArduRover,請記得與全世界分享您的成果!

Jan Gromes
Jan 目前在布爾諾工業大學學習電氣工程。他有多年使用Arduino和其他微控制器來構建項目的經驗,對于機器人系統的機械設計十分感興趣。
審核編輯黃宇
-
編程
+關注
關注
90文章
3716瀏覽量
97203 -
Arduino
+關注
關注
190文章
6526瀏覽量
196974
發布評論請先 登錄
esp32-S3支持arduino編程嗎?
玩轉Arduino手機編程
Exxelia元件助力毅力號漫游車
Exxelia元件助力毅力號漫游車
基于Arduino UNO的自主導航漫游車
用 樹莓派 Zero 打造的智能漫游車!
Arduino探索漫游車3—編程
評論