[Robotics Studio] 自走型機器車 -- Day4
之前寫的程式只能算是遙控器而已啊~
現在我們要開始寫一個自己會動作的機器車子, 這樣才符合 Robotics 這樣的主題啊~
從簡單的寫起, 先寫一個由自己的所在開始, 探索自己周圍的簡單機器車子吧, 假設車子的行走路徑如下:
就是一個越來越大的 L 型路徑, 所以我們只要把 L 型路徑寫成一個 Activity, 然後由外部輸入這個 L 的大小,
最後這個 Activity 要能夠在完成行走路徑時吐出更大的 L 大小, 就大致完成這樣的程式了.
根據這個構想, 拖一個 Activity 來, 改名成為 RunL , 然後把輸入定為 size , 類別是 double, 至於輸出,
考慮這個情境的輸出應該算是 Notification , 為什麼是通知而不是輸出呢?
最主要是因為機器完成時都是使用通知來告訴我們, 而我們的 L 型路徑的完成也是靠通知才知道的,
所以這個例子的輸出是 Notification, 如下
現在開始構思該如何寫裡面的流程, 根據 GenericDifferentialDrive 可以接受的指令, 只有前進一段距離, 以及旋轉一個角度,
所以我們的 L 型路徑應該由 1.前進 , 2.旋轉 90 度, 3 前進, 4.旋轉 90 度 (為了下一次的 L) 四個部分來組成.
而每一個部分的完成都是靠通知來完成, 並不是靠當下 GenericDifferentialDrive 的輸出, 所以當我們收到通知時, 有必要知道
目前我們處於哪一個狀態, 而決定應該如何進行下一個步驟, 這個在離散數學上就算是 Finite State Machine , 在 VPL 處理 FSM
的問題就是靠變數 (Variables) , 所以, 除了 1. 前進是靠外部輸入驅動以外, 2,3,4 都是靠 GenericDifferentialDrive 的完成通知囉.
然後我們再靠一個 CurrentState 的變數, 來決定我們該進行哪一個動作.
當我們拖拉一個 Variable 到 RunL 裡面時, 看右邊的屬性表, 可以從那裏新增一個變數, 我們新增一個 CurrentState, 類別是 int, 如下
然後在輸入資料時, 就把一個 Data 值為 1 的作為該變數的 SetValue,
再把輸入的 size 輸入給 GenericDifferentialDrive 的 DriveDistance, 設定如下:
意思是以 100% 的速度前進 value 這樣的距離.
因為等一下還需要知道使用者輸入的 size, 所以也順便把 size 儲存為 CurrentSize (用 Variable 的 屬性表當中的 Add 就可以新增一個變數)
現在 RunL 的內容應該如下:
然後我們在 Digram 輸入一個 0.5 的值給這個 RunL 的 size, 如下
現在請記得把 RunL 裡面的 GenericDifferentialDrive 設定為 use a Manifest, 我們仍然可以使用 LEGO.NXT.Tribot.Simulation.Manifest.xml 這個設定,
你可以試跑看看, 是否會跑一個小直線呢?
現在我們要接著完成 2. 旋轉, 3. 前進, 4. 旋轉, 5. 通知完成 這樣的步驟,
對於每一個使用者自訂的 Activity, 都有一個特定的稱為 "Start" 的 Activity, 這個的目的就是用來做為通知, 或是啟動等等的流程撰寫,
在 Start 裡面的流程都沒有輸出以及輸入, 但是裡面的元件可以有 Notification, (Diagram 裡面的也可以有).
為了要收到 GenericDifferentialDrive的通知, 所以我們要把 GenericDifferentialDrive 的元件放到 Start 這個 Activity 當中,
你可以在活動編輯紅色按鈕左邊看到 Action 下拉按鈕, 當中就有 "Start" , 如下:
建議先從原本 (Action) 當中複製 GenericDifferentialDrive 元件, 切換到 Start Action 後, 貼上 GenericDifferntialDrive 元件 (當然也可以拖拉一個 GenericDifferentialDrive 元件上來, 但是要注意設定要選為 use a Manifest, LEGO.NXT.Tribot.Simualtion.Manifest.xml ), 你會注意到元件右邊有紅色圈圈, 那個就是 Notification (通知),
由於我們要打算只收到兩種通知, 分別是完成前進 (DriveDistance) 動作以及完成旋轉 (RotationDegrees)動作, 所以我們放兩個 If 來判斷這兩種通知.
接收 DriveDistance 的 If 要寫 value.DriveDistanceStage == DriveStage.Completed , 而
接受 RotationDegrees 的 If 就寫 value.RotateDegreesStage == DriveStage.Completed , 然後將這兩個 If 的判斷 Merge 以後
輸出給一個 Calculate, 當中填入 state.CurrentState + 1 , 再把這個 Calculate 的結果餵給 CurrentState 變數, 如下:
這樣我們就完成了 "收到通知以後, 將狀態 + 1 " 這件事情.
但我們還要完成一件事情就是 "將狀態 + 1 以後, 根據目前狀態決定該做甚麼事"
所以, 設完變數以後, 再餵給另一個 Calculate, 此次我們填入 state.CurrentState , 然後後面再加上 Switch 判斷,
2 的時候就餵給 GenericDifferentialDrive 一個全力 (power=1)旋轉 90 度,
3 的時候就餵給 GenericDifferentialDrive 一個全力 (power=1)前進 (Distance= state.CurrentSize)
4 的時候就餵給 GenericDifferentialDrive 一個全力 (power=1)旋轉 90 度,
5 的時候餵給 RunL (建議可以去 Diagram 複製一個來用) 一個 size = -1 的值, 當作結束.
現在程式如下:
因為我們設定了 size=-1 為完成, 所以還要去 Action 裏頭多做一個判斷, 當 Size=-1 就發一個通知 (Notification)
值可以設為 state.CurrentSize , 如下:
最後, 我們再去 Diagram 設定 RunL 的通知再透過 Calculate 將原本的 Size + 0.3 再餵給 RunL 如下:
終於趕在午夜 12:00 以前完成今天的程式 (我又不是灰姑娘)
等一下可以緊接著在明天做更好的修正囉^ ^