National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 72 24°C PM2.5 13
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 03

節點與 topic

ROS 2 的程式怎麼分工與溝通:發布與訂閱
Interaction 30 minute(s)

Learning Objectives

  • 說明把機器人程式拆成多個節點的好處與代價
  • 用 create_publisher、create_subscription、create_timer 寫節點
  • 說明發布者與訂閱者怎麼靠 topic 名稱配對,名稱不同會發生什麼
  • 用 ros2 指令看節點與 topic 的狀態
  • 寫一個訂閱再發布的轉接節點
  • 說明回呼做太久為什麼會擋住同一個節點的其他工作

光達壞了,不該連馬達都停擺

如果光達驅動、地圖、路徑規劃、馬達控制全部寫在一支程式裡,光達驅動多一個 bug,整台機器人一起當掉;想換一顆不同牌子的光達,也要改到其他部分的程式。

ROS 2 的做法是拆成很多個節點(node),一個節點負責一件事,每個節點可以獨立啟動、重新啟動與替換。節點之間最常用的溝通方式是 topic:

              /scan                       /cmd_vel
 光達驅動 ──────────▶ 避障節點 ──────────▶ 馬達驅動
                         ▲
 路徑規劃 ───────────────┘
              /plan
  • 發布者(publisher)把訊息送到一個 topic;訂閱者(subscriber)收這個 topic 的訊息。
  • 雙方只靠 topic 名稱與訊息型別配對。發布者不知道誰在收、有幾個人在收,甚至不知道有沒有人在收。

這叫鬆耦合:多加一個記錄資料的節點,光達驅動一行都不用改。代價是你不會在程式裡看到「誰接誰」,名字打錯也不會有錯誤訊息,只會安靜地收不到東西,下面就會實際碰到。

互動練習:talker 與 listener

這是 ROS 2 官方範例的結構:兩支程式,也就是兩個獨立的行程。真實環境要開兩個終端機分別執行;這裡按一次「執行」,兩支一起跑。上面的分頁可以切換檔案。

這個練習沒有機器人,只有兩個節點在講話。下面面板顯示的是模擬環境的輸出,時間是虛擬的。

節點裡的三個基本元件

一個節點常用的東西就這三個:

self.pub = self.create_publisher(String, 'topic', 10)          # 型別、名稱、佇列大小
self.create_subscription(String, 'topic', self.on_msg, 10)     # 型別、名稱、回呼、佇列大小
self.create_timer(0.5, self.tick)                              # 週期(秒)、回呼

訊息是一個物件,先建立、填欄位,再 publish。訂閱和計時器都給一個回呼(callback):你不會自己呼叫它,資料來了或時間到了才會被呼叫。

那誰來呼叫?rclpy.spin(node)。它是一個迴圈:等到有事發生,就呼叫對應的回呼,做完再等下一件事。沒有 spin,計時器不會響、訊息也不會進來。 程式寫成這樣的好處是不必自己管「現在該做哪件事」,代價是要習慣程式的順序不是由上往下,而是由事件決定。

佇列大小(那個 10)是訊息來不及處理時最多留幾筆,下一課會看到它的影響。

一步一步看回呼的順序

為了方便看,這個節點自己發布、自己訂閱。注意 publish 那一行執行完,on_msg 並沒有立刻被呼叫:程式會先把 tick 走完,回到 spin,才輪到 on_msg。回呼是排隊一個一個做的。

用 ros2 指令看系統現在的樣子

節點之間的連結不寫在程式裡,所以要靠工具來看。真實環境最常用的是這幾個:

指令 回答什麼問題
ros2 node list 現在有哪些節點
ros2 topic list -t 有哪些 topic,各是什麼型別
ros2 topic echo /scan 這個 topic 現在在傳什麼內容
ros2 topic hz /scan 每秒幾筆,有沒有變慢
ros2 topic info /scan 幾個發布者、幾個訂閱者

除錯的順序通常是:節點有沒有起來 → topic 名稱對不對 → 兩邊數量是不是 0 → 有沒有訊息在跑 → 頻率夠不夠。

下面 robot-lab 的「ros2 指令」面板可以按這些指令,內容由這次模擬的狀態算出,只是格式仿照真實指令,所以有些地方會不一樣。例如真實環境的 ros2 topic list 還會多出系統自帶的 /parameter_events 和 /rosout,模擬環境沒有這兩個。

互動練習:加一個限速節點

planner 只管想走多快,發布到 /cmd_vel_raw。機器人只聽 /cmd_vel。中間的 limiter 訂閱一個 topic、處理、再發布到另一個 topic,這種轉接節點在機器人系統裡很常見。

機器人會在有人的地方走,所以速度上限應該由獨立的節點把關:就算規劃節點算錯,也不會超速。補上 limiter.py 的限速邏輯。planner.py 不能改。

「數值曲線」的藍線是想走的速度,紅線是實際送給機器人的速度。

回呼是排隊做的

預設情況下,一個節點的回呼是一個做完才換下一個,同時只有一個在跑。所以回呼應該短:收到資料、存起來、馬上結束。

如果某個回呼要花 0.5 秒,這 0.5 秒裡:

  • 計時器到了也不會響,控制指令送得更慢
  • 新的感測器資料在佇列裡排隊,你處理的永遠是舊資料
  • 佇列滿了,最舊的資料直接被丟掉

機器人一邊走,這些延遲就是一段「閉著眼睛走」的距離。下面實際看。

互動練習:一個慢回呼讓機器人撞牆

driver.py 的邏輯沒有問題:前方大於 0.6 公尺才走。但 on_scan 裡多了一行要花 0.5 秒的處理,光達每 0.2 秒來一筆。

analyzer.py 是另一個節點,也要花 0.5 秒。先預測會發生什麼,再執行。

確認一下

1.

節點 A 發布到 /scan,節點 B 訂閱 scan(沒有開頭的斜線),兩個節點都沒有設定命名空間。B 收得到嗎?

2.

下面這個節點建立了計時器,但 main 只寫了 node = Talker() 就結束,沒有呼叫 spin。會發生什麼?

3.

你開了 ros2 topic echo /cmd_vel 卻什麼都沒有。下面哪一個是最合理的下一步?

4.

一個節點訂閱 /scan(每 0.2 秒一筆),回呼要花 1 秒,同一個節點還有一個 0.1 秒的計時器發布速度指令。下面哪個描述最接近實際情況?

5.

為什麼限速要做成獨立節點,不寫在規劃節點裡?

第 1 次作答

重點整理

  • 節點一個負責一件事,可以獨立啟動與替換;topic 是節點之間最常用的溝通方式。
  • 發布者與訂閱者只靠名稱與型別配對,彼此不知道對方。名稱不合不會報錯,只會收不到。
  • 回呼由 rclpy.spin() 呼叫。沒有 spin,計時器與訂閱都不會動。
  • 預設一次只跑一個回呼,所以回呼要短;花時間的工作放到別的節點。
  • 除錯先用 ros2 node list、topic list、topic info、topic echo、topic hz 確認事實。

下一課看訊息本身,以及另一種「訂閱了卻收不到」的原因:QoS。

這一課完成了!
Next lesson:訊息與 QoS
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。