# micro-ROS 進階通訊篇
## 一、前言與學習目標(Introduction & Objectives)
### 1. 通訊模型擴展說明
在嵌入式機器人與 ROS 生態系統中,單純的資料發布/訂閱(Publish/Subscribe)模型雖然強大,但已不足以涵蓋所有應用場景。舉例來說:
- 跳過 “一直發送資料” 的持續流(Publish/Subscribe)模式,當你面對「一次性請求/回應」的場景(例如:主機問 MCU 讀取一次感測器數值),就會用到 Service。
- 當任務變為「長時間運行 + 多次回報進度」時(例如:機器手臂執行一組動作、導航器從 A 點移動到 B 點、或是感測器掃描整個房間),那麼 Action 模型就更合適。
- 同時,在資源受限、網路條件不佳(如 Wi-Fi、UDPless、串口連線)或多節點協作的情況下,僅靠預設通訊參數可能產生封包遺失、延遲異常或系統不穩。這就是 Quality of Service(QoS)設定的重要性。
本篇「進階通訊篇」的目的,就是在已熟悉 Publish/Subscribe 的基礎上,進一步補足 Service、Action 與 QoS 這三大進階維度。透過這樣,你將能在 ROS2/µROS 系統中:
- 精準選用最合適的通訊模型
- 調整通訊品質,匹配實務環境(MCU、網路限制、多節點協作)
- 了解 Host ↔ Agent ↔ MCU 三端架構如何實作並優化
### 2. 本篇實作方向
為了使這些理論不只是紙上談兵,我們將採取以下路徑:
1. **概念解析**:簡明剖析 Topic、Service、Action 三大模型的差異與適用場景。
2. **參數升級**:深入 QoS 設定(Reliability、Durability、History、Depth 等)與 µROS/DDS/XRCE-DDS 相關限制。
3. **架構走查**:從通訊協定(DDS/Micro XRCE-DDS)與 Agent 角色講起,理解 Host/MCU 如何互動。
4. **實作骨架**:提供 Host(Python/C++)與 MCU(C、µROS)雙端程式碼範例,涵蓋 Publisher/Subscriber、Service 與 Action 的基本流程。
5. **實務挑戰**:分享 MCU 記憶體、頻率、網路不穩、Agent 負載等「真實世界坑」,並給出優化建議。
6. **延伸練習**:透過練習題或小案例(例如:/reset_device Service、感測器資料可靠傳送、簡單 Action 任務)幫你鞏固理解。
### 3. 章節架構
以下為本篇章節總覽:
| 章節 | 主題 | 重點內容 |
|------|------|---------|
| 二 | 通訊模式比較 | Topic vs Service vs Action |
| 三 | QoS 深入探討 | Reliability, Durability, History 等 |
| 四 | Transport 與協定 | DDS / XRCE-DDS / Agent 架構 |
| 五 | 自訂訊息升級 | .msg / .srv / .action 實作 |
| 六 | Host / MCU 雙端實作 | Python/C++ Host + C Client |
| 七 | 延伸練習與實務應用 | 多節點、IoT、實體移植 |
| 八 | 結語與未來方向 | 系統擴充、性能量測、實務問題 |
### 4. 學習目標
完成本篇後,你將能:
- 辨識並選用最適合的通訊模型(Topic/Service/Action)於 µROS/ROS2 系統中。
- 理解並能設定 QoS 參數,使通訊在資源受限或不理想網路環境中依然穩定。
- 明白 Host ↔ Agent ↔ MCU 三層架構的運作機制,並能撰寫雙端程式碼。
- 使用自訂訊息型別(.msg/.srv/.action),並從概念到實作完成端到端範例。
---
## 二、通訊模式比較(Communication Models)
### 1. Topic
**定義與用途**
在 ROS 2/micro‑ROS 中,Topic 是節點之間用來進行「持續資料傳輸」的主要通道。當你有持續、頻繁、非同步的資料流(例如:感測器讀值、機器人狀態、影像資料)時,就會用 Topic。
**進階關鍵點**
- Topic 為多對多(Many-to-Many)模型:多個 Publisher、或多個 Subscriber 都可以參與。
- Publisher 決定何時發送資料(非 Client 等待模式)
- 在嵌入式/µROS 環境,除了基本發送/訂閱,還要考慮 QoS 設定(稍後章節)與 Agent/Transport 繞過問題。
**適用場景**
- 感測器資料不斷輸出(如溫溼度、LiDAR、IMU)
- 鏡頭影像串流
- 機器人定位/狀態更新
**不要用於**
- 必須立即回應、等待結果的請求(如讀取檔案、觸發執行) → 這部分要用 Service。
- 長時間任務且需要進度回報、可能被取消 → 用 Action。
---
### 2. Service(Request/Response)
**定義與用途**
Service 是 ROS2 中提供「同步請求-回應」模型的通訊方式。當一個節點(Client)向另一個節點(Server)發送請求後,會等待回應。適合「一次性」、「快速結束」的操作。
**結構與流程**
- .srv 定義檔:包含 Request 與 Response 兩部分
- Client 發出 request → Server 處理 → 回傳 response
- 含有同步等待行為(Client 無法繼續直到得到回覆)
**適用場景**
- 查詢機器人當前電量/健康狀態
- 啟動或停止某功能(如:清除地圖、重置設備)
- 請求 MCU 執行「一次性任務」並回報結果
**比較簡單的限制**
- 適合任務短、等待時間可接受的情況
- 如果任務長、或需要進度回報,會造成 Client 阻塞 → 改用 Action。
---
### 3. Action(Goal/Feedback/Result)
**定義與用途**
Action 是用於「長時間任務」且可能需要「進度回報」或「可取消」的模型。Client 發送一個目標(Goal)後,Server 開始執行,並且能回傳 feedback、最後出結果(Result)。
**三段結構簡述**
1. **Goal**:欲達成的任務目標
2. **Feedback**:執行期間回報進度或狀態
3. **Result**:任務結束後的最終輸出(成功/失敗)
**架構與行為特性**
- 支援預設可取消(Cancelling)或中止(Aborting)任務
- 客戶端(Client)不需等待整個任務結束即可繼續其餘邏輯
- 背後實作通常結合 Topic + Service(例如:send_goal Service、feedback Topic、get_result Service)
**適用場景**
- 機器人從 A 點導航至 B 點(任務時間較長)
- 圖像處理任務需掃描整張圖片並回報進度
- 多步驟機械臂動作序列,需要回報動作進度
**在 µROS/嵌入式中的考慮點**
- 因資源受限(MCU 記憶體、RTOS 時間片、Agent 處理能力)使用較少,但理解仍重要
- 考題可能問結構或選擇理由,而非要求你從零實作
---
### 4. 模式選擇指引(When to Use Which)
| 模式 | 適用情境 | 典型範例 |
|-----------|-------------------------------------------|-----------------------------------------------|
| Topic | 資料流大、持續性強、不需回覆/請求 | 感測器數據、狀態廣播 |
| Service | 一次性請求/快速回應、不需進度回報 | 查詢電量、觸發重置、讀取一次數值 |
| Action | 長時間任務、需進度回報或可取消 | 導航、掃描任務、機械臂執行多步驟動作 |
**記住這三句話**
- 想「一直丟資料給大家」 → 用 Topic。
- 想「發一個請求、要一個答案」 → 用 Service。
- 想「下個任務給它、看它進度、結束或取消」 → 用 Action。
---
### 5. 進階提示(對於 µROS/嵌入式環境特別注意)
- 在 µROS 情境中,不要以為「Topic 一定最好」:因為架構跟 Host/Agent/Client(MCU)有額外通路和資源限制。
- Service 若處理時間過長,會阻塞 Client,因此對 MCU 端不要用太複雜的 Service。
- Action 雖然功能齊全,但 MCU 端若資源不足,用 Topic + 自訂同步邏輯有時會更「輕量」。
- 在章節後續會講的 QoS、Transport、Agent 設定,對模式選擇也有影響:Topic/Service/Action 在不同 QoS 下的行為可能差很多。
---
## 三、QoS(Quality of Service)深入探討
### 1. QoS Profile 組成
在 ROS 2/micro‑ROS 系統中,Quality of Service(QoS)設定允許你針對節點間通訊的「可靠性」「延遲」「資源使用」進行調整。
以下是主要政策(policies)與其說明:
- **History(歷史記錄)**:定義 Publisher 存留舊訊息給訂閱者的策略。選項包括 `KEEP_LAST`(僅保留最近 N 筆)或 `KEEP_ALL`(保留所有/受資源限制)。
- **Depth(深度)**:配合 `KEEP_LAST` 使用,定義隊列(緩衝)大小。例如 depth = 5 表示保留最近五筆訊息。
- **Reliability(可靠性)**:定義資料送達保障程度。`RELIABLE` 保證送達(有重傳機制),`BEST_EFFORT` 儘力送達但可能遺失。
- **Durability(耐久性)**:定義新訂閱者加入後是否可看到舊訊息。`VOLATILE`:不保留、只對當下訂閱者;`TRANSIENT_LOCAL`:保留最近一筆/幾筆資料供後加入者讀。
- **Deadline**:定義預期兩次資料發布之間的最大時間間隔,若超過會觸發事件。
- **Lifespan**:定義資料被視為「過期/無效」之前的最大存活時間。
- **Liveliness + LeaseDuration**:定義節點或出版者是否維護「活著」狀態的機制。
> **提醒**:只要 QoS 設定不相容(Publisher/Subscriber 或 Client/Server 之間),即便訊息型別、Topic 名稱都對,通訊仍可能失敗。
---
### 2. micro-ROS 與 QoS 在嵌入式中的限制與考量
在嵌入式/微控制器環境中使用 micro-ROS,雖然 QoS 概念與 ROS2 相同,但**實務限制**必須被納入設計。
- MCU 的記憶體、Flash、CPU 通常比 Host 少很多,因此若設定 `KEEP_ALL` 或 depth 很大,可能導致記憶體不足甚至系統崩潰。
- 在 micro-ROS 所用的底層通訊協定 Micro XRCE‑DDS 中,即便你選 `RELIABLE`,底層仍可能因為代理(Agent)+UDP/串口傳輸造成遺失或延遲。
- 在無線網路、RS-232、或頻率很高的場景,「少許資料遺失比整個系統卡住更好」的情況不少。這時候 `BEST_EFFORT` + 小隊列可能比 `RELIABLE` + 大隊列還更實際。
- 設定 QoS 時,不只是程式碼中填值那麼簡單:你還要考慮通道(Transport)、Agent 負載、MCU 資源、頻率與資料量這些變數。
---
### 3. 各通訊模型與 QoS 的搭配
這裡快速比對「Topic/Service/Action」三種通訊模型在實務上常用的 QoS 設定建議。你在考場看到問 QoS +模型時就能秒答。
| 通訊模型 | 建議 QoS 設定 | 為什麼 |
|---------------|--------------------------------------------------------|------------------------------------------------------|
| **Topic** | 感測器資料流:`BEST_EFFORT` + 小隊列(depth 5-10)<br>關鍵狀態/命令:`RELIABLE` + 適中隊列(depth 10-20) | 資料量大、頻率高、偶有遺失可忍;狀態/命令不能遺失 |
| **Service** | 通常 `RELIABLE` + 預設隊列 | 是同步請求-回應模式,不能丟失結果 |
| **Action** | Goal: 快速觸發 <br>Feedback: 可用 `BEST_EFFORT` <br>Result: 使用 `RELIABLE` | 長任務需回報進度可容錯,結果不能遺失 |
---
### 4. 實作範例(Host C++ + MCU C++)
**Host(C++)範例:**
```cpp
#include <rclcpp/rclcpp.hpp>
#include <my_pkg/msg/SensorData.hpp>
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("sensor_publisher");
auto qos_profile = rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default))
.reliable()
.keep_last(5);
auto publisher = node->create_publisher<my_pkg::msg::SensorData>("/sensor/data", qos_profile);
rclcpp::Rate loop_rate(10); // 10 Hz
while (rclcpp::ok()) {
auto message = my_pkg::msg::SensorData();
// 填充 message 的資料欄位……
publisher->publish(message);
rclcpp::spin_some(node);
loop_rate.sleep();
}
rclcpp::shutdown();
return 0;
}
````
**MCU(C++)範例:**
```cpp
#include <rclcpp/rclcpp.hpp>
#include <my_pkg/msg/SensorData.hpp>
#include "rclc/rclc.h" // 假設使用 rclc 或對應 MCU C++ API
int main()
{
// MCU 通常建立 rclc node, executor 等
rcl_node_t * node = rclc_node_init_default(...);
rcl_publisher_t publisher;
rcl_publisher_options_t opts = rcl_publisher_get_default_options();
opts.qos.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
opts.qos.history = RMW_QOS_POLICY_HISTORY_KEEP_LAST;
opts.qos.depth = 5;
rcl_publisher_init(
&publisher,
node,
ROSIDL_GET_MSG_TYPE_SUPPORT(my_pkg, msg, SensorData),
"/sensor/data",
&opts
);
while (rcl_ok()) {
my_pkg__msg__SensorData msg = my_pkg__msg__SensorData__create();
// 填充資料
rcl_publish(&publisher, &msg, nullptr);
// MCU 需考慮延遲、記憶體、回呼處理
}
return 0;
}
```
> 補充說明:在 MCU 端你還要確保 static memory、stack size、回呼函式效能、串口/通道穩定這些條件。
---
### 5. 測試項目與預期結果
做這些實驗可幫你更理解 QoS 設定如何影響系統。也很適合在考前練一遍。
| 項目 | 設定 | 預期結果 |
| ------------------------- | ------------------- | ---------------------------------------------- |
| Reliability = RELIABLE | depth = 10 | 資料可靠送達但延遲可能稍高 |
| Reliability = BEST_EFFORT | depth = 1 | 延遲低、資源用少,但可能頻繁遺失封包 |
| Depth = 1 vs Depth = 10 | History = KEEP_LAST | 小 depth:舊訊息快速被覆蓋 <br>大 depth:保留較多訊息但記憶體/延遲負擔更高 |
---
### 6. 常見坑與除錯建議
* Publisher 設 `RELIABLE` 而 Subscriber 設 `BEST_EFFORT` → **不相容**,可能無法收到資料。
* 設定過深隊列在 MCU 上直接可能導致記憶體不足或系統卡住。
* 使用 `RELIABLE` 但頻率太高、沒有適當時間片在 MCU 上,很容易造成堆積延遲。
* 在無線/低速通道中硬用 `RELIABLE` 可能比用 `BEST_EFFORT`還差,因為重傳+代理負載加大。
* 忘了 Durability 設定:若系統需要「新訂閱者加入後見到最後狀態」,必須用 `TRANSIENT_LOCAL`。
---
## 四、Transport 與協定(Transport & Protocol)
### 1. Data Distribution Service (DDS) 架構概述
在 ROS 2 中,DDS 是底層通訊的核心中介。DDS 定義了資料中心發佈/訂閱模型(Publish-Subscribe),同時支援 QoS 設定、探索機制(discovery)、跨平台互通。:contentReference[oaicite:2]{index=2}
簡要說明:
- Publisher/Subscriber 模型對應到 DDS 中的 DataWriter/DataReader。
- Nodes(節點)成為 DDS Participants,加入 Global Data Space。
- 透過 RTPS 或其他底層協定進行封包傳輸。
### 2. DDS‑XRCE(µROS)架構與通訊鏈路
當 MCU/微控制器要加入 ROS 2 生態(例如 micro‑ROS)時,不直接用 DDS,而是用 DDS-XRCE(DDS for eXtremely Resource Constrained Environments)。:contentReference[oaicite:5]{index=5}
特色如下:
- 採 Client-Server 架構:MCU 端為 XRCE Client,主機端為 XRCE Agent,Agent 代表 Client 存取 DDS Global Data Space。:contentReference[oaicite:6]{index=6}
- 支援多傳輸通道:UDP、TCP、Serial 等。:contentReference[oaicite:7]{index=7}
- 設計為低資源消耗:典型記憶體為 < 75 KB Flash + ~3 KB RAM。:contentReference[oaicite:8]{index=8}
### 3. Agent/Client/Transport 三層資料流圖與解說
在 micro-ROS 系統中,典型資料流程如下:
- MCU(XRCE Client)透過 Transport(例如:UART、UDP)將資料傳送給 XRCE Agent。
- Agent 接收 XRCE 封包後,轉成 DDS 訊息並發佈至 ROS2 系統內的 Subscriber。
- 反方向亦然:ROS2 系統中的 Publisher 資料可經 Agent 傳至 MCU 端 Subscriber。
**教學提醒**:在考場可能會出「Agent 是什麼角色?」「為什麼 MCU 不能直接用 DDS?」「Transport 層有哪些限制?」這類題。
### 4. 傳輸通道選擇(Transport)與實務考量
在 micro-ROS/嵌入式環境中,「傳輸」比你在 PC 開發時要複雜。你必須考慮通道、頻率、延遲、資源限制。部分常見通道與考量:
- **Serial (UART)**:硬體需求低、能在 MCU 上使用,但頻率低、錯誤率高、可能因通道瓶頸導致資料堆積。
- **UDP**:延遲較低、適合大量資料流,但不保證可靠性。
- **TCP**:提供可靠性,但手續繁複、延遲可能增加、資源消耗較大。
- **Shared Memory/RPMsg/OpenAMP**(當 MCU 與 Host 在同一 SoC 上):延遲最小但硬體依賴強。
**考試常見題**:
> 若 MCU 選用 UART 傳資料,但資料頻率為 1 ms/筆、payload 500 bytes,哪裡最可能出問題?
> → 通道頻寬、記憶體積壓、Agent/MCU 處理能力不足。
### 5. 系統整合與效能瓶頸
實務中你會遇到這些坑,考場也會問:
- MCU → Agent → DDS 的轉換會產生額外延遲:Agent 必須解封包、轉格式、處理錯誤。
- 若有大量 Topic、高頻率、大 payload 資料,MCU 可能因記憶體或 CPU 不夠而掉封包。
- 傳輸通道如無線/低速環境時,要特別注意 QoS+Transport 的配合。
- Agent 需與 Host 網路同步:Topic/Participant 數量多可能造成負載高、處理延遲大。
在考題中你可能被問:「系統延遲比預期高,可能原因是什麼?」你要從 Transport 層分析。
### 6. 實作範例(C++)
以下是簡化的 C++ 範例,描述 Agent 建立 UDP 傳輸與 MCU 端通訊流程(教學示範,實務中可能用 C 或特定 API):
```cpp
// Host (Agent) 側:初始化 UDP Transport
#include <MicroXrceAgent.hpp>
#include <FastDDS/Publisher.hpp>
int main()
{
XRCEAgent agent;
agent.init_transport_udp("0.0.0.0", 7400);
while (agent.running()) {
XRCEMessage msg;
if (agent.receive(msg)) {
ddsPublisher.publish(msg.to_dds());
}
}
return 0;
}
```
```cpp
// MCU (Client) 側:使用 XRCE Client 傳輸 SensorData
#include <MicroXrceClient.hpp>
#include <my_pkg/msg/SensorData.hpp>
int main()
{
MicroXrceClient client;
client.init_transport_udp("192.168.1.100", 7400);
my_pkg::msg::SensorData data{1, 25.0f, 60.0f};
while (true) {
client.publish("/sensor/data", data);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
```
> 補充說明:實務中 Client 端還要設定 Token、Stream、連線保護、錯誤處理、記憶體池管理等。
---
## 五、自訂訊息升級(Custom Message Upgrade)
### 1. `.msg / .srv / .action` 檔案格式說明
在 ROS 2/micro‑ROS 系統中,訊息、服務、動作的介面定義是透過三種檔案格式完成的:
- `.msg` 檔案:用於定義一條資料流(message)。
- `.srv` 檔案:用於定義一次性請求回應的資料型別(service:Request/Response)。
- `.action` 檔案:用於定義一個長時任務(action),內含 Goal、Feedback、Result 三段資料。
這些檔案的格式都有固定語法,如 `.srv` 檔案中,Request 與 Response 之間用 `---` 分隔。
---
### 2. 自訂 `.msg` 範例:`SensorData.msg`
假設你要在 MCU /感測器系統上傳送自訂資料包,以下是一個範例:
```text
int32 id
float32 temperature
float32 humidity
string source
````
這表示:
* `id`:整數型別感測器編號
* `temperature`:單精度浮點數溫度值
* `humidity`:單精度浮點數濕度值
* `source`:字串,描述資料來源或節點名稱
撰寫自訂 `.msg` 的步驟包括:
1. 在 interface package 下創建 `msg/` 資料夾。
2. 新建 `SensorData.msg` 檔案並填寫如上內容。
3. 在 `CMakeLists.txt` 及 `package.xml` 中加入生成指令與依賴設定。
---
### 3. 建置步驟(修改 `package.xml`/`CMakeLists.txt`)
為了讓你的自訂介面檔被編譯並可被其他節點使用(C++/C/Python),你必須做以下設定:
* 在 `package.xml` 中加入:
```xml
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
```
並宣告對其他用到的訊息包(如 `geometry_msgs`)的 `<depend>`。 ([The Robotics Back-End][2])
* 在 `CMakeLists.txt` 中加入:
```cmake
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/SensorData.msg"
"srv/…"
"action/…"
DEPENDENCIES geometry_msgs
)
```
* build workspace:
````bash
colcon build --packages-select <your_interfaces_package>
source install/setup.bash
``` :contentReference[oaicite:8]{index=8}
````
---
### 4. Python/C++ 節點使用自訂訊息範例
假設你已定義 `SensorData.msg` 並 build 成功,以下是在 Host(C++)端使用範例:
```cpp
#include <rclcpp/rclcpp.hpp>
#include <my_interfaces_pkg/msg/sensor_data.hpp>
void callback(const my_interfaces_pkg::msg::SensorData::SharedPtr msg)
{
RCLCPP_INFO(
rclcpp::get_logger("sub"),
"Received id=%d temperature=%.2f humidity=%.2f source=%s",
msg->id, msg->temperature, msg->humidity, msg->source.c_str()
);
}
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("sensor_subscriber");
auto sub = node->create_subscription<my_interfaces_pkg::msg::SensorData>(
"/sensor/data", 10, callback
);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```
使用自訂服務 `.srv` 或動作 `.action` 的流程與此類似,只是你換成 `srv`/`action` 的 API,並注意 Goal/Feedback/Result 的架構。
---
### 5. 延伸:`.srv`/`.action` 用法簡述
* **.srv** 範例:
```text
int32 id
float32 value
---
bool success
```
表示:Client 提交 `id` 與 `value`,Server 回傳 `success`。
* **.action** 範例:
```text
int32 goal_id
---
bool result_ok
---
float32 progress
```
表示:Goal 傳入 `goal_id`,Result 回傳 `result_ok`,Feedback 回傳 `progress`。
你就能在動作執行時回報進度。
---
## 六、Host/MCU 雙端實作(Host & MCU Dual-end Implementation)
### 1. Host(C++)節點架構說明
在 ROS 2(C++)端,我們典型使用 rclcpp 建立節點、Publisher/Subscriber、Service Client 或 Action Client。
- 節點初始化:`rclcpp::init(argc, argv)` → 建立 `rclcpp::Node::make_shared("node_name")`
- 建立通訊物件:publisher, subscription, client or action
- 處理迴圈:使用 `rclcpp::spin(node)` 或者 `rclcpp::spin_some(node)` + `rclcpp::Rate`進行定周期操作
範例流程:Publisher → 發送自訂訊息 → Subscriber 端處理 → MCU 端接收或回應。
### 2. MCU(C/C++)節點架構說明
在 micro‑ROS(MCU)端,因資源受限,我們會選用 rclc (或微控制器支援的 ROS 客户端库)並搭配 XRCE-DDS 通訊。關鍵點:
- 靜態記憶體分配:避免動態 new/delete,使用 static buffer 或 pool。
- 建立節點:`rclc_node_init_default()` 或對應 C++ 封裝。
- 建立 Publisher/Subscriber/Service/Action:使用 `rcl_publisher_init()`、`rcl_subscription_init()` 等低階 API。
- 主迴圈注意:不允許阻塞過久、必須處理 Agent 連線、監控記憶體與時間片。
### 3. 建立 Publisher/Subscriber/Service/Action 程式碼骨架
以下示範 C++ 語法骨架(Host 端)及 MCU 端(C++ 或 rclc)骨架,作為學習範本。
**Host(C++)Publisher 範例**:
```cpp
#include <rclcpp/rclcpp.hpp>
#include <my_interfaces_pkg/msg/sensor_data.hpp>
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("host_sensor_publisher");
auto publisher = node->create_publisher<my_interfaces_pkg::msg::SensorData>("/sensor/data", 10);
rclcpp::Rate rate(10); // 10 Hz
while (rclcpp::ok()) {
auto msg = my_interfaces_pkg::msg::SensorData();
// 填充 msg.id, msg.temperature, msg.humidity, msg.source
publisher->publish(msg);
rclcpp::spin_some(node);
rate.sleep();
}
rclcpp::shutdown();
return 0;
}
````
**MCU(C++/rclc)Publisher 範例**:
```cpp
#include <rclc/rclc.h>
#include <my_interfaces_pkg/msg/sensor_data.h>
int main()
{
rcl_allocator_t allocator = rcl_get_default_allocator();
rclc_support_t support;
rclc_support_init(&support, 0, nullptr, &allocator);
rcl_node_t node;
rclc_node_init_default(&node, "mcu_sensor_node", "", &support);
rcl_publisher_t publisher;
rcl_publisher_options_t opts = rcl_publisher_get_default_options();
opts.qos.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
opts.qos.history = RMW_QOS_POLICY_HISTORY_KEEP_LAST;
opts.qos.depth = 5;
rcl_publisher_init(&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(my_interfaces_pkg, msg, SensorData),
"/sensor/data",
&opts);
while (rcl_ok()) {
my_interfaces_pkg__msg__SensorData msg = my_interfaces_pkg__msg__SensorData__create();
// 填充 msg fields
rcl_publish(&publisher, &msg, nullptr);
rclc_executor_spin_some(&support.executor, RCL_MS_TO_NS(100));
}
return 0;
}
```
### 4. 記憶體、資源與效能限制最佳化
在 MCU 端你必須特別注意以下限制:
* **記憶體池與 stack 大小**:訂閱/回呼數過多、隊列過大容易耗盡 RAM。
* **Publisher/Subscriber 頻率控制**:頻率太高 + 資料量過大 → Agent / MCU 端壓力大、可能遺失訊息或掉線。
* **串口/通道穩定性**:若使用 Serial/WiFi,連線不穩或重連機制不佳會導致通訊中斷。
* **Agent 延遲與 MCU 處理週期**:Host ⇄ Agent ⇄ MCU 的延遲必納入設計(尤其 QoS 選擇、頻率設計、隊列 depth 設定)。
### 5. 移植注意事項:Linux 模擬 vs 實體開發(ESP32/STM32)
* **Linux 模擬**:使用 ROS2 環境模擬 MCU 端,可快速調試 Topic、Service、Action,但無 RTOS/硬體限制,表現比真實 MCU 理想。
* **實體開發**:比如 ESP32 或 STM32,需考慮實體 MCU 規格、電源、通道、藍牙/WiFi、冷啟動、重連機制、代理負載、記憶體與 RTOS 時間片。
* **建議流程**:先在模擬環境完成邏輯,再移植至實體,並逐步測試 MCU 資源使用、連線穩定、延遲狀況。
### 6. 小練習
請你在 Host 端建立一個 Publisher 傳送 SensorData,自訂 .msg 檔案;MCU 端建一個 Subscriber 接收資料並回傳 ack 訊息(透過 Service 模型)。
* 測試 Topic 頻率從 10 Hz 提升到 100 Hz,觀察 MCU 端是否遺失訊息。
* 調整 QoS 設定(如 Reliable vs Best Effort),觀察 效果。
* 測試断線重連:MCU 重啟後訂閱 Topic 是否能恢復。
---
## 七、延伸練習與實務應用(Extended Exercises & Practical Applications)
### 1. 多節點協作示例:感測器網絡與機器人集群
在 micro-ROS/ROS 2 系統中,當你不只是單台 MCU+Host,而是多個 MCU 節點、Host 節點、雲端伺服器協作時,下列為你可實作的架構:
- 多個 MCU 節點(如 ESP32、STM32)分別連接不同感測器、執行不同控制任務;
- 各 MCU 透過 Micro XRCE-DDS Agent 與 ROS 2 網絡連接,將資料匯聚到 Host/伺服器;
- Host 節點中的 ROS 2 節點負責整合資料、策略決策或發布指令給機器人/裝置;
- 將部分節點佈署在雲端/邊緣設備,用於資料儲存、分析、儀表板展示。
你可以設計練習題:
1. 建立 3 台 MCU 節點,每台分別發布不同資料(如溫度、濕度、加速度)至 ROS 2 網絡。
2. Host 節點訂閱這些資料,進行合併與簡單邏輯(如:若溫度>30 °C 且加速度>x,則發出警報指令)。
3. 將指令以 Service / Action 形式回送至 MCU 節點,讓 MCU 執行動作(如:啟動風扇、儲存資料、顯示警示)。
4. 模擬通訊異常(如:斷線、頻率降低、資料遺失)並觀察 QoS/傳輸選擇對系統影響。
### 2. IoT 與雲端整合應用
嵌入式系統+ ROS 2 不再侷限於機器人,亦可擴展至 IoT 與雲端架構。舉例:
- 使用 MQTT / HTTP 與 ROS 2 整合,將 MCU 資料送至雲端平台;例如 EMQX + micro-ROS 整合應用。 :contentReference[oaicite:3]{index=3}
- 在 智慧倉儲、智慧農業、智慧工廠中,部署多個 MCU 感測器+機器人,透過 ROS 2 及 micro-ROS 實現分散式控制與協作。 :contentReference[oaicite:4]{index=4}
- 將 ROS 2 節點、MCU 節點及雲端服務整合,在雲端呈現儀表板、資料分析、遠端監控。
練習題:
- 將 MCU 端資料通過 micro-ROS Agent → ROS 2 節點 → MQTT Broker,再送至雲端資料庫。
- 設計雲端儀表板,監控多個節點的狀態(如:聯網狀態、資料頻率、漏包率),並在異常時發出通知。
### 3. 實體移植與資源限制評估
當你將系統從實驗環境移植至實體設備(如 STM32 + FreeRTOS、ESP32、樹莓派+MCU),需特別注意資源與環境限制:
- 硬體通道/網路品質:WiFi、串口、CAN 總線、4G/5G 模組;
- MCU 記憶體、Flash、CPU 頻率、RTOS/任務設計;
- Agent 負載、節點數量、Topic 數量、頻率、資料大小;
- 系統部署佈景:邊緣-雲架構、節點重啟/恢復機制、時延監控。
練習題:
1. 將 MCU 執行頻率從 10 Hz 提升至 100 Hz 並測量漏包/延遲。
2. 在 WiFi 不穩條件下,改用 串口 / CAN 鏈路 + depth = 1、BEST_EFFORT QoS,觀察通訊品質差異。
3. 部署 ROS 2 節點於邊緣設備,測試 Agent 重啟/ MCU 重啟後系統恢復流程。
### 4. 練習題庫與模擬考題
| 題號 | 題目 | 要點 |
|------|------|------|
| Q7-1 | 當 MCU 節點需要定期傳送資料給 Host/雲端,但通道時常不穩定,你會選哪種 QoS + 通訊模型? | 減少漏包/控制延遲/資源限制 |
| Q7-2 | 在多節點協作系統中,若 Agent 負載過高造成延遲,你可以從哪三個維度著手優化? | 節點數、頻率、傳輸通道 |
| Q7-3 | 將 ROS 2 系統擴展至雲端整合時,資料安全/鑑權/資料一致性需考慮哪些? | Protocol 選擇、Broker 結構、資料同步 |
### 5. 實務應用案例展示
- 在「智慧農業」專案中,多台 MCU 感測器部署於田間,使用 micro-ROS 透過 UDP 傳送資料至 Host,Host 再將資料上傳至雲端分析,並根據分析結果觸發灌溉裝置。
- 在「機器人倉儲」系統中,機器人與 MCU 感測單元協作,透過 ROS 2 控制路徑規劃,同時 MCU 回報感測器資料並接受 Action 指令執行收/放貨流程。