Learning Objectives
- 說明機器人的感知、決策、行動怎麼形成迴路
- 分辨開迴路與閉迴路控制,說明閉迴路為什麼比較可靠
- 讀懂一個最小的 ROS 2 節點:發布速度指令、讀取光達
- 用感測器的資料改變機器人的行為
機器人和一般程式差在哪裡
一般程式的輸入和輸出都是資料:讀檔案、算一算、印結果。機器人的輸出會改變真實世界,而世界改變之後,下一次的輸入也跟著變:往前走一步,牆就近了一步。
所以機器人程式幾乎都是一個不會結束的迴路:
┌──────────────────────────────────┐
▼ │
感知(sense)→ 決策(plan)→ 行動(act)→ 世界改變
光達、相機 要往哪走 馬達轉動
這條軌道每一課都在這個迴路裡:感測器量到什麼、怎麼判斷、怎麼下指令,以及哪一步可能出錯。
機器人的程式通常不會全部寫在一起。相機、光達、馬達、導航各自是獨立的程式,用訊息互相溝通,這樣某個部分壞了不會全部一起掛,也能換掉其中一塊。把這些程式接起來的軟體框架,最常用的是 ROS 2(Robot Operating System 2)。它不是作業系統,是一組函式庫與工具。
這條軌道的環境。 ROS 2 跑不進瀏覽器,所以
rclpy(ROS 2 的 Python 函式庫)和世界都是課程模擬的。你寫的程式和真實的 ROS 2(以 Lyrical Luth 版本為準)寫法相同;時間是虛擬的,所以每次結果一樣,也可以拖時間軸回看。物理是簡化的:沒有慣性、輪子不會打滑。最後一課會說明怎麼接到真實環境。
開迴路與閉迴路
剛才的程式是開迴路:不看結果,照計畫做。如果事先把一切都算好(走 7 秒、速度 0.4),在沒有干擾時也能停在正確的位置,但輪子打滑、地面不平、有人把東西放在路上,它都不會知道。
閉迴路(closed-loop)則是一邊做一邊看結果來修正,這也是機器人可靠的基礎。代價是要處理感測器的雜訊與延遲,後面的課會一項一項處理。
要讀光達,要多做兩件事:
# 訂閱 /scan:每次收到一筆,就呼叫 on_scan
self.create_subscription(LaserScan, '/scan', self.on_scan, qos_profile_sensor_data)
def on_scan(self, msg):
self.front = msg.ranges[len(msg.ranges) // 2]
msg.ranges 是一圈距離(公尺),on_scan 這種「資料來了才被呼叫」的函數叫回呼(callback)。程式的主流程只有 rclpy.spin():等事情發生,再呼叫對應的回呼。
最後一個參數 qos_profile_sensor_data 第 4 課會解釋;現在先照寫,不寫會收不到光達。
一步一步看一個節點怎麼跑
這是 ROS 2 官方範例的寫法。注意程式先走完 __init__,真正重複做事的 tick 是 spin 在時間到的時候呼叫的。這支範例只模擬 1.6 秒,所以 spin 之後就結束了;真實環境會一直跑到按 Ctrl+C。
確認一下
掃地機器人依照事先算好的路線走,完全不看感測器。下面哪個情況最可能讓它出問題?
在 ROS 2 裡,rclpy.spin(node) 做什麼?
程式裡寫 self.front = None,tick 裡直接寫 if self.front < 0.6。會發生什麼事?
為什麼機器人軟體常常拆成很多個節點,不寫成一支大程式?
重點整理
- 機器人程式是感知、決策、行動的迴路,行動會改變下一次感知到的東西。
- 開迴路不看結果;閉迴路一邊做一邊修正,也要面對雜訊與延遲。
- ROS 2 把機器人軟體拆成節點,節點用 topic 傳訊息。
/cmd_vel下速度指令、/scan是光達。 rclpy.spin()是主迴圈,計時器與訂閱的回呼都由它呼叫。- 這條軌道的 ROS 2 是模擬的,程式寫法和真實相同,物理是簡化的。
下一課先處理一個所有機器人都繞不開的基礎:東西的位置要相對於誰來說。