Arduino 自走車教學系列 – 單元(四)使用 Arduino IDE 開發

一、前言

Arduino 自走車教學前三單元教大家從零開始,首先是第一個單元的組裝,接著是第二單元的行進控制,最後是第三單元的避障與循線實現。為了讓初學的使用者可以快速上手,程式的部份是採用圖形化的方式,主要在功能的實現,不在 C++ 語言的學習。

近期陸陸續續有收到不少使用者提出需求,對於已經有一定程度,而且熟練用 Arduino IDE 開發的使用者,或是入門一段時間,想從自走車專案練練手的朋友,許願「是否能提供 C++ 的程式碼做為專案製作參考呢?」。

於是本篇文章就誕生啦!我們將原先的圖形化程式重新構築後,產生了八個範例程式,同樣從基礎的馬達控制,到最終的循線避障。希望可以幫助大家理解,如何用 C++ 開發循線避障自走車。

二、Arduino 程式撰寫說明

若您已經很熟悉 Arduino 可以先跳過這一個段落,直接前往下載範例程式。打開 Arduino IDE 後可以看到畫面內已有一些程式碼,分別是「setup()」以及「loop()」,基本上我們就是將程式碼填入這兩個區塊裡面。以一個導入程式庫執行超音波測距的程式為例子,如下圖所示:

上圖中,我們將程式分為三個區塊,分別是「事前準備區塊」、「初始化區塊」與「重複執行區塊」。

1. 事前準備區塊:通常用來放置一些使用者自訂的程式碼,或是需要導入的程式庫。執行超音波功能時我們需要導入程式庫「NewPing.h」,這個程式庫幫我們把一些超音波測距的邏輯寫完了,我們只需匯入並依照它的規則調用即可,所以我們將它放在「事前準備區塊」。

2. 初始化區塊:這個區塊放置的是一些初始化的設定參數,以及只想在開機時執行的程式(通電後執行一次)。以上圖為例,我們想透過「序列埠監控視窗」將超音波感測器讀取到的數值,列印在視窗上讓我們查看,這時候就需要啟動序列埠的功能 (Serial.begin(9600);),因為啟動只需要做一次,我們就會將這類的程式放在「初始化區塊」。

3. 重複執行區塊:這個區塊顧名思義就是,放置想要一直不停執行的程式。以上圖為例,放置的是讀取超音波感測器的感測數值,並輸出到「序列埠監控視窗」,並以間隔 50 毫秒的時間重複執行上述步驟。

三、範例程式下載

關於自走車控制的原理與相關細節,在第一到第三單元皆已詳細說明,還沒看過的朋友可以參考之前的文章。這個單元我們就直接從程式碼來學習,請大家將所有「範例程式」下載到自己的電腦。

找到下載的範例程式檔案並解壓縮後,如先前所述,總共會有八個範例程式。接下來我們將會一一為大家解說。

四、程式說明

1. 讓馬達轉起來

我們先從第一個程式「01.motor_Test」開始,完整程式如下圖所示:

我們自走車的馬達是連接在馬達驅動板上,透過 Arduino 上面的 PWM 腳位控制驅動板,進而帶動馬達轉動或停止。因為並不是每個腳位都支援 PWM,所以在第一單元組裝時有特別挑選過。依照順序來看,腳位 5、6 控制一顆馬達,腳位 9、10 則控制另一顆馬達。忘記這些腳位控制哪個馬達並無大礙,待會兒轉起來就知道了。

在控制之前,我們需要定義腳位的工作方式,例如我是要讓這個腳位作為輸出用途呢?還是輸入用途?輸入的話通常用來讀取,輸出的話則是用來推動。

我們會用這些腳位來控制馬達轉動,在功能上就是「輸出」的意思,所以我們會透過「pinMode」這個設定腳位功能的程式,來設定我們要使用的腳位狀態。想要腳位做輸出,會在腳位後方填上「OUTPUT」,若是想要輸入,會填上「INPUT」,以下圖為例:

接下來,設定好腳位功能後,就可以開始控制腳位來推動馬達了。使用 PWM 控制腳位輸出,會使用「analogWrite」,並於腳位後方填上欲控制的強度,強度範圍是「0~255」。數字越大越強,最小的 0 表示無輸出,馬達是由兩條線控制的,其中一個腳位設定強度,另一個腳位設定無輸出,馬達就會開始轉動。以連接腳位 5、6 控制的馬達為例,如下圖所示:

若是想讓馬達停下來,只要將腳位輸出都設為 0 即可。

說到這裡,聰明的您應該已經知道這個程式在做什麼了吧!將程式燒錄到 Arduino 上,並接上電池看看,自走車是否有動起來了呢?

若是跟您想的不一樣沒關係,讓我來跟大家詳細解說~

說明

程式設定完腳位功能為輸出之後,將左右兩顆馬達都輸出強度為200的馬力,此時馬達會開始轉動,因為「delay」程式的關係,程式會暫停3秒鐘,待3秒鐘一到,接著執行之後的程式。接下來的程式是將馬達強度皆設為0,原先不停轉動的馬達此刻就會停止轉動。所有的程式都是放在「setup()」區塊,所以執行完就不會再執行了。自走車的兩顆馬達將會在送電後開始轉動,等待3秒後轉動會停下來,然後就永遠靜止。

2. 控制馬達轉向

第二個程式「02.motor_setting」與第一個程式沒有不同,唯一不一樣的是更換輸出的腳位,完整程式如下圖所示:

上一個程式說明有提到馬達是由兩個腳位控制的,想要馬達轉動就是一個腳位輸出,另一個腳位不輸出,讓我們來看一下這張圖:

以上圖來看,當 5 號腳輸出強度 200,6 號腳無輸出時,馬達會順時針旋轉。反之,若互換腳位的輸出強度,5 號腳輸出強度 0,6 號腳輸出強度 200 時,馬達變為逆時針旋轉。

不曉得大家執行第一個程式時,自走車是向前進?向後退?還是原地旋轉呢?如果自走車不是向前跑,我們可以用剛剛學習到的知識,調整馬達轉向,讓自走車前進。

像筆者的自走車執行第一個程式時都在原地旋轉,原因是透過 5、6 腳位控制的馬達是向後轉的,9、10 腳位控制的馬達是向前轉的。

所以調整 5、6 腳位的輸出控制,將兩個腳位的輸出強度互換,更改後的程式如「02.motor_setting」所示。重新燒錄程式至 Arduino 後,自走車就變成向前移動了。

3. 控制自走車移動

既然我們已經學會控制馬達轉動,也明白如何調整單顆馬達轉向。第三個範例程式「03.motor_control」將帶大家編寫副程式,並透過呼叫副程式,使自走車移動,完整程式碼如下(因為程式碼太長,所以呈現上用並排的方式):

什麼是副程式呢?副程式就是將一些固定的、常用的程式組成一個群組,當要執行這些程式時,只要呼叫這個群組即可。範例程式中有幾個副程式,分別是「Forward」、「Backward」、「Left」、「Right」與「Stop」。這些副程式如各自名稱所述,功能是讓自走車能前進、後退、左轉、右轉與停止。

副程式基本上可分為兩種,「單純執行編好的程式碼」與「執行完後回傳訊息」兩種,依照種類的不同,當然編寫方式也不同。我們這邊使用的是「單純執行編好的程式碼」,副程式開頭以「void」加上「名稱」與「括弧」,括弧內可以帶入參數(以範例程式裡的 Forward 為例,可帶入格式為 byte,名稱為 sp 的參數),接著標上大括弧,功能都是寫在大括弧內。

同樣以「Forward」為例,如下圖所示:

參數的用意是,我們可以在呼叫的時候填上想要的控制強度,今天我想要以強度 200 來前進,就在呼叫時於括弧內填上 200,想以強度 100 則在括弧內填上 100。

說到這裡,完整程式執行邏輯就是:

送電後首先執行各腳位輸出設定,接著讓自走車開始不停重複前進、後退、左轉與右轉的動作。在每個動作切換前,會先執行停止的指令。

請大家參考範例程式,編寫自己自走車的副程式,並將程式燒錄到 Arduino 上,送電驗證看看。若是您的自走車沒有像程式撰寫般移動,例如明明要左轉,實際卻是向後移動,請依照調整馬達轉向的方法,修正各個副程式,直到動作正常。

4. 讀取循線感測器數值

經過前幾個程式的學習,您應該已經學會如何控制自走車移動,第四個範例程式「04.ir_sensor_read」,會開始進到循線的主題。在進行循線之前,我們要先學會如何讀取感測器的數值,經由感測器來判斷,車子該如何修正行進的路線,完整程式碼如下:

程式中保留了我們原先設定馬達控制的腳位 (5、6、9、10),額外新增了 A0 與 A1 腳位,分別用來讀取兩個循線感測器。為了讓 Arduino讀取感測器的訊號,所以我們要將其設定為「INPUT」,確保這兩個新加入的腳位做為輸入用途。

為了查看讀取到的訊號,我們會透過序列埠監控視窗來查看,使用「Serial.begin(9600)」啟動序列埠功能,括弧內的數字則是傳輸的鮑率。

讀取訊號前我們會依照感測器的類型做選擇,感測器以使用方法區分,有分為「數位訊號類型」、「類比訊號類型」與「特殊類型」。在購買感測器的時候可以詢問店家或查看相關說明,在 Google 上搜尋也是一個辦法。我們自走車使用的循線感測器是屬於「數位訊號類型」的,程式中會使用「digitalRead(A0)」來讀取訊號,括號內放入的是要讀取的腳位。這個程式會取得感測器當下讀取的狀態並回傳,通常我們會設定一個變數將這個資料儲存下來,方便之後做程式比對。

取得感測器的訊號後,就能使用序列埠監控視窗列印出來,程式會使用「Serial.print()」與「Serial.println()」,「Serial.print()」不換行,而「Serial.println()」會換行,括弧內放入要印出的資訊。

完整的程式流程:

將程式燒錄到 Arduino 後,請點開序列埠監控視窗,並設定相同的鮑率 9600,即能正常查看到印出來的資訊囉!

5. 實現循線功能

依照上一個程式做衍伸,取得感測器資訊後,我們就能用來控制自走車移動。在Arduino 自走車教學系列 – 單元(三)中,我們可以知道循線感測器的原理,以及自走車在行進中會遇到的四種狀態,如下圖:

我們會依照兩個循線感測器讀取到的訊息,得出上面四種結果。依照得到的結果做出相對應的控制,像是遇到狀態 1 時,我們會用程式控制車子前進,而遇到狀態 3 時,我們會控制車子向左偏移,讓車子可以回到線上。

完整程式碼「05.line_follower」:

詳細概念可以參考上個單元的文章,首先我們先撰寫一個控制自走車前進的副程式,這個副程式可以填入四個參數,分別用來控制左、右馬達轉動的強度,短短一行即可設定四支腳位的狀態。

接下來使用「if…else…」,將每個狀態對應的馬達控制動作寫出來。

完整的程式流程:

首先在「setup()」區塊內,設定馬達與感測器的腳位狀態。接著在「loop()」區塊內,依照感測器讀取到的狀態,控制馬達轉動。

將程式燒錄到 Arduino 後,於白色桌子上黏貼黑色膠帶路線,接上電池試試看自走車有沒有沿著線跑。

6. 讀取超音波感測器數值

超音波感測器的原理與操作,同樣可以參考Arduino 自走車教學系列 – 單元(三)中的內容。為了方便量測超音波感測器的數值,我們需要安裝適合的程式庫,請開啟 Arduino IDE,並於選單中啟動「程式庫管理員」。在搜尋欄位中,輸入「NewPing」搜尋,找到下圖中的程式庫後請安裝該程式庫。

接著讓我們來看看,讀取超音波感測器的完整程式碼「06.ultrasonic」:

開頭我們會匯入程式庫「NewPing」,並依照它的使用規則設定連接的腳位,將功能賦予給名為「sonar」的物件。

小提示:

程式庫的使用規則可以參考它本身提供的範例程式作為參考,可以在選單中的「檔案>範例>NewPing」中查看相關範例程式,在這裡就不贅述了。

接著與方才讀取循線感測器一樣,在「setup()」區塊內我們會設定序列埠功能並設定鮑率,「loop()」區塊內,則是放上列印資訊。程式「sonar.ping_cm()」會回傳超音波感測器偵測到的距離,單位是公分。

完整程式流程大致上分為三個步驟:

第一步:匯入程式庫並完成相關設定。

第二步:啟動序列埠功能。

第三步:不停讀取感測器數值並列印至序列埠監控視窗。

將程式燒錄至 Arduino,完成後開啟序列埠監控視窗,試著用手遮擋超音波感測器,並觀察感測數值。

7. 實現避障功能

可以取得與前方障礙物的距離後,我們就能設定一個距離作為判斷依據,例如 20 公分。自走車移動的過程中,當超音波偵測到的距離小於 20 公分時,讓自走車做出轉彎的動作來閃避。若偵測大於 20 公分,則自走車繼續前進。

完整程式碼「07.avoid_the_wall」如下:

我們將範例程式「05.line_follower」,裡面控制馬達的程式複製到此。對比循線自走車的程式,避障自走車的程式較為單純,只要一組「if…else…」就能完成判斷。當偵測距離大於 20 公分,自走車以強度 150 前進,反之則以強度 150 轉彎。

完整程式流程大致上分為二個步驟:

第一步:匯入程式庫並完成相關設定。

第二步:不停讀取感測器數值並控制馬達移動。

將程式燒錄到 Arduino 後,接上電池試試看,自走車前進過程中,有沒有看到障礙物就轉彎。

8. 結合避障功能與循線功能

既然已經學會如何使用超音波感測器,來製作避障功能,同時也完成了讀取循線感測器,實現了循線自走車的功能。那麼接下來就將這兩個功能接合在一起吧!

完整程式碼「08.combine」如下:

我們將以避障功能作為車子移動中,判斷停止的最優先,只要前方在設定的距離內有障礙存在,就下達停止的指令,否則車子繼續循線移動。

首先,我們將看起來較為複雜的循線程式碼,包成副程式「line_following」,方便之後取用。

接下來就像撰寫避障自走車一樣,當前方沒有障礙物時,就下達循線指令,若是遇到障礙物,則自走車停止。

完整程式流程大致上分為二個步驟:

一步:匯入程式庫並完成相關設定。

第二步:不停讀取感測器數值,並以超音波的讀值為優先,控制自走車停止或繼續循線。

將程式燒錄到 Arduino 後,接上電池試試看,觀察自走車是否如程式撰寫般移動。

五、結語

綜合上述八種範例程式,大家是不是已經會使用 C++ 來控制自走車了呢?其實只要知道原理與操作方法,用 Arduino IDE 編輯程式並不會太複雜。相較於圖形化語言,雖然使用上較不直覺,且需要多加練習並熟悉語法。但就擴充的彈性,以及撰寫的自由度而言,用Arduino IDE 可說是相當地方便。

除了循線與避障,自走車也能外接其他感測器,進而衍生不同的主題。至於還可以製作出什麼樣不同的內容,就留給大家發揮創意,自行嘗試,我們自走車的教學就到這邊結束,期待下一篇文章再與大家分享更多的內容喔!