# micro-ROS 實作篇
## 一、前言與學習目標(Introduction & Objectives)
### 1. micro-ROS 的核心概念
micro-ROS 讓微控制器(MCU)能成為 ROS 2 架構的一部分,
透過 **micro-ROS Agent** 在輕量協定 Micro XRCE-DDS 與 DDS 之間轉換,
低功耗裝置得以與 ROS 2 節點進行資料交換。
整體結構可分為三個角色:
| 角色 | 功能 | 執行環境 |
| ------ | ----------------- | --------------------- |
| Host | ROS 2 節點(發送或接收資料) | 一般電腦(Ubuntu / Docker) |
| Agent | 協定轉換、封包橋接 | ROS 2 主機端 |
| Client | micro-ROS 節點 | MCU / 嵌入式平台 |
這三者構成完整的資料通訊鏈,讓主機與感測端共享相同 Topic 架構。
---
### 2. 實作方向
為了理解 micro-ROS 的實際開發流程,本篇將以「Ping-Pong 通訊系統」作為範例:
* **Host 節點(Python)** 負責發送 Ping 訊息。
* **Client 節點(C++)** 接收 Ping 並回傳 Pong。
* **Agent** 居中轉換協定,完成雙向通訊。
此範例覆蓋從環境建置、API 使用、封包驗證、到除錯的完整步驟。
---
### 3. 章節架構
| 章節 | 主題 | 重點內容 |
| -- | -------------- | ---------------------------------------------- |
| 二 | 開發環境設定 | 建立 ROS 2 Humble + micro-ROS Agent,說明模擬與實體開發差異。 |
| 三 | Python Host 實作 | 建立 Publisher/Subscriber,定時傳送 Ping 並接收 Pong。 |
| 四 | C++ Client 實作 | 實作 micro-ROS 節點,回傳相同訊息型別。 |
| 五 | 通訊測試與驗證 | 啟動 Agent、觀察 Topic 傳輸與封包往返。 |
| 六 | 自訂訊息與 QoS | 新增 .msg、自訂 interface 與 QoS 測試。 |
| 七 | 除錯與常見問題 | 說明常見錯誤訊息與修正方式。 |
| 八 | 延伸練習 | 擴充至多節點或 IoT 應用。 |
---
### 4. 學習目標
完成本篇後,應能:
1. 使用 `rclpy` 與 `rclcpp` 建立並運行節點。
2. 讓 Host 與 Client 透過 Agent 傳遞 `std_msgs/Header` 訊息。
3. 理解主要 API(`init`、`create_node`、`create_publisher`、`publish`、`spin`)的邏輯。
4. 區分模擬環境與實體開發流程的差異。
5. 建立自訂 .msg 並調整 QoS 以符合需求。
---
### 5. 教學結構
每個章節皆包含:
1. 核心概念與結構圖說明。
2. 完整範例程式碼。
3. 逐行解說與執行結果。
4. 常見錯誤與除錯方法。
---
### 6. 小結
micro-ROS 的價值在於讓感測端與運算端共享一個通訊生態。
本篇將從基礎環境建置出發,逐步構築可運行的通訊範例,
並在過程中掌握 micro-ROS 的開發流程與 API 邏輯。
---
## 二、開發環境設定與建置流程(Environment Setup & Build Process)
### 1. 系統需求
| 元件 | 建議版本 | 說明 |
| ----------- | ---------------- | --------------------- |
| 作業系統 | Ubuntu 22.04 LTS | ROS 2 Humble 官方支援版本 |
| ROS 2 | Humble Hawksbill | 作為 micro-ROS 的主機端框架 |
| Python | 3.11+ | Host 端節點開發語言 |
| GCC / CMake | 9.4.0 / 3.22+ | 用於編譯 micro-ROS Client |
| Docker(可選) | 最新版 | 用於快速建置與模擬 Agent |
---
### 2. ROS 2 與 micro-ROS 安裝
#### 2.1 安裝 ROS 2 Humble(Host 端)
```bash
sudo apt update && sudo apt install locales -y
sudo locale-gen en_US en_US.UTF-8
sudo apt install software-properties-common -y
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt update
sudo apt install ros-humble-desktop python3-colcon-common-extensions -y
````
設定環境變數:
```bash
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
---
#### 2.2 安裝 micro-ROS Agent
micro-ROS Agent 是 Host 與 MCU 溝通的橋樑,負責轉換協定(XRCE-DDS ↔ DDS)。
```bash
sudo apt install ros-humble-micro-ros-agent
```
啟動測試(UDP 模式):
```bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
```
當出現以下訊息表示成功啟動:
```
[INFO] [micro_ros_agent]: UDP agent initialized. Waiting for clients...
```
---
### 3. 建立工作空間
建議使用 `colcon` 管理 ROS 2 專案:
```bash
mkdir -p ~/microros_ws/src
cd ~/microros_ws
colcon build
source install/setup.bash
```
---
### 4. 建立 Host 端套件(Python)
建立一個 Python 專案作為 Host 端節點:
```bash
cd ~/microros_ws/src
ros2 pkg create --build-type ament_python host_ping
```
結構如下:
```
host_ping/
├── package.xml
├── setup.py
├── resource/
│ └── host_ping
└── host_ping/
├── __init__.py
└── ping_node.py
```
---
### 5. 建立 Client 端套件(C++)
在同一工作空間下新增 C++ 專案作為 micro-ROS Client:
```bash
cd ~/microros_ws/src
ros2 pkg create --build-type ament_cmake client_pong --dependencies rclcpp std_msgs
```
結構如下:
```
client_pong/
├── CMakeLists.txt
├── package.xml
└── src/
└── pong_node.cpp
```
---
### 6. 模擬與實體開發差異
| 比較項目 | Linux 模擬環境 | 實體 MCU(ESP32 / STM32) |
| ---------- | --------------------- | ----------------------- |
| 編譯流程 | colcon build(本機) | 需交叉編譯工具鏈(cross-compile) |
| Agent 連線方式 | UDP / TCP | UART / Serial / Wi-Fi |
| 執行方式 | 直接啟動節點 | 燒錄韌體後自動執行 |
| Debug 方法 | `ros2 topic echo`、`log` | 串列埠輸出或 GDB 監控 |
| 時間同步 | 使用系統時間 | 需在應用層補償時間誤差 |
> 💡 小訣竅:建議先在 Linux 模擬環境完成開發,再移植到實體板,以確保通訊流程正確。
---
### 7. Docker 快速啟動(可選)
若不想安裝所有依賴,可使用 micro-ROS 官方 Docker 映像:
```bash
docker pull microros/micro-ros-agent:humble
docker run -it --net=host microros/micro-ros-agent:humble udp4 --port 8888
```
這樣主機端的 Agent 即可立即啟動。
---
### 8. 環境驗證
完成設定後可用下列指令確認 ROS 2 功能正常:
```bash
ros2 node list
ros2 topic list
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp listener
```
若能看到節點互傳資料,代表 ROS 2 與 DDS 架構運作正常,
接下來即可進入 Host 節點的程式實作。
---
### 9. 小結
1. ROS 2 Humble 與 micro-ROS Agent 建置完成後,
Host 與 Client 即可透過 Topic 互通。
2. Linux 模擬能在無硬體時測試封包傳輸;
實體開發則需額外處理交叉編譯與序列連線。
3. 完成此章後,環境即可支援後續的 Python 與 C++ 節點實作。
---
## 三、Python Host 節點實作(Python Host Node Implementation)
### 1. 節點角色與資料流
Host 節點是 ROS 2 系統中的主要發送端,
負責產生「Ping」訊息並接收「Pong」回覆。
資料流如下:
```
[Python Host Node] → [micro-ROS Agent] → [C++ Client Node]
↑ ↓
←------------------- Pong -------------------←
```
Host 節點使用 `rclpy` 函式庫進行初始化、建立 Publisher 與 Subscriber,
並定時發布訊息到 `/microROS/ping`,
同時監聽 `/microROS/pong` 以接收回傳資料。
---
### 2. 程式架構概覽
主要由三個部分組成:
| 區塊 | 功能 |
| ---------- | -------------------- |
| 初始化 | 啟動 ROS 2,建立節點與 Topic |
| Publisher | 每秒傳送一筆 Ping 封包 |
| Subscriber | 接收 Pong 並計算往返延遲 |
---
### 3. 範例程式:`ping_node.py`
```python
import rclpy
from rclpy.node import Node
from std_msgs.msg import Header
class PingNode(Node):
def __init__(self):
super().__init__('ping_node')
self.publisher = self.create_publisher(Header, '/microROS/ping', 10)
self.subscriber = self.create_subscription(
Header, '/microROS/pong', self.pong_callback, 10)
self.timer = self.create_timer(1.0, self.timer_callback)
self.get_logger().info('Ping node initialized.')
def timer_callback(self):
msg = Header()
msg.stamp = self.get_clock().now().to_msg()
msg.frame_id = 'ping_from_host'
self.publisher.publish(msg)
self.get_logger().info(f'Sent Ping at {msg.stamp.sec}.{msg.stamp.nanosec}')
def pong_callback(self, msg):
recv_time = self.get_clock().now().to_msg()
latency_ms = (recv_time.sec - msg.stamp.sec) * 1000 \
+ (recv_time.nanosec - msg.stamp.nanosec) / 1e6
self.get_logger().info(f'Received Pong | Latency: {latency_ms:.2f} ms')
def main(args=None):
rclpy.init(args=args)
node = PingNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
```
---
### 4. 逐段說明
#### (1)匯入模組
```python
import rclpy
from rclpy.node import Node
from std_msgs.msg import Header
```
* `rclpy`:ROS 2 的 Python 介面。
* `Node`:所有 ROS 2 節點的基底類別。
* `Header`:內含時間戳與 frame id 的標準訊息型別。
---
#### (2)建立 Publisher 與 Subscriber
```python
self.publisher = self.create_publisher(Header, '/microROS/ping', 10)
self.subscriber = self.create_subscription(
Header, '/microROS/pong', self.pong_callback, 10)
```
* 第一個參數:訊息型別(`Header`)。
* 第二個參數:Topic 名稱。
* 第三個參數:接收訊息的 callback。
* 第四個參數:Queue 大小(緩衝數量)。
---
#### (3)使用 Timer 週期性傳送訊息
```python
self.timer = self.create_timer(1.0, self.timer_callback)
```
每 1 秒觸發一次 `timer_callback()`,以固定頻率送出 Ping。
---
#### (4)建立與發送訊息
```python
msg = Header()
msg.stamp = self.get_clock().now().to_msg()
msg.frame_id = 'ping_from_host'
self.publisher.publish(msg)
```
* `stamp`:時間戳記,用於計算往返延遲。
* `frame_id`:自訂識別字串。
* `publish()`:將訊息送至 `/microROS/ping` topic。
---
#### (5)接收 Pong 並計算延遲
```python
recv_time = self.get_clock().now().to_msg()
latency_ms = (recv_time.sec - msg.stamp.sec) * 1000 \
+ (recv_time.nanosec - msg.stamp.nanosec) / 1e6
```
以兩次時間戳差計算通訊延遲,並顯示在終端。
---
### 5. 執行方式
1. 啟動 micro-ROS Agent:
```bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
```
2. 在另一終端執行 Host 節點:
```bash
ros2 run host_ping ping_node
```
3. 若 Client 節點(C 端)已連線,終端會顯示:
```
[INFO] Sent Ping at 1701.234567890
[INFO] Received Pong | Latency: 25.31 ms
```
---
### 6. 驗證通訊
可使用以下指令檢查 Topic 狀態:
```bash
ros2 topic list
ros2 topic echo /microROS/ping
ros2 topic echo /microROS/pong
```
若能看到資料流動,代表 Host 節點運作正常。
---
### 7. 延伸練習
* 將傳送頻率改為 0.5 秒,觀察封包間隔變化。
* 增加自訂欄位(如節點 ID)以辨識多個 Host。
* 在 callback 中記錄延遲並畫出折線圖(matplotlib)。
---
### 8. 小結
1. Host 節點透過 `rclpy` 以 Publisher/Subscriber 方式運作。
2. `Header` 型別提供時間戳記,能計算封包延遲。
3. 整個節點可在 Linux 模擬環境執行,不需 MCU。
4. 這個範例是後續 C 端 Pong 節點的對應基礎。
---
## 四、C++ 端(micro-ROS Client)實作(C++ Client Node Implementation)
### 1. 節點角色與資料流
Client 節點是運行在 MCU 或模擬環境上的 **micro-ROS Node**,
負責接收 `/microROS/ping` 訊息,並回傳 `/microROS/pong` 封包。
資料流:
```
[Python Host Node] → /microROS/ping → [C++ Client Node]
[C++ Client Node] → /microROS/pong → [Python Host Node]
````
此節點透過 `rclcpp` 函式庫(micro-ROS C++ client library)建立 publisher 與 subscriber,
並使用 `rclcpp::spin` 處理 callback 函式。
---
### 2. 程式架構概覽
| 區塊 | 功能 |
| -------- | ---------------------------------- |
| 初始化 | 建立節點、publisher、subscriber |
| callback | 收到 Ping 後組出 Pong 訊息 |
| 執行迴圈 | 使用 spin 處理通訊事件 |
---
### 3. 範例程式:`pong_node.cpp`
```cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/header.hpp"
using std::placeholders::_1;
class PongNode : public rclcpp::Node
{
public:
PongNode()
: Node("pong_node")
{
publisher_ = this->create_publisher<std_msgs::msg::Header>("/microROS/pong", 10);
subscription_ = this->create_subscription<std_msgs::msg::Header>(
"/microROS/ping", 10, std::bind(&PongNode::ping_callback, this, _1));
RCLCPP_INFO(this->get_logger(), "[Client] Pong node started. Waiting for Ping...");
}
private:
void ping_callback(const std_msgs::msg::Header::SharedPtr msg)
{
RCLCPP_INFO(this->get_logger(),
"[Client] Received Ping at %d.%u",
msg->stamp.sec, msg->stamp.nanosec);
auto pong = std_msgs::msg::Header();
pong.stamp = msg->stamp;
pong.frame_id = "pong_from_client";
publisher_->publish(pong);
RCLCPP_INFO(this->get_logger(), "[Client] Sent Pong");
}
rclcpp::Publisher<std_msgs::msg::Header>::SharedPtr publisher_;
rclcpp::Subscription<std_msgs::msg::Header>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<PongNode>());
rclcpp::shutdown();
return 0;
}
````
---
### 4. 逐段說明
#### (1)匯入標頭
```cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/header.hpp"
```
* `rclcpp`:ROS 2 的 C++ client library。
* `std_msgs/msg/header.hpp`:與 Host 相同的訊息型別。
---
#### (2)初始化節點
```cpp
class PongNode : public rclcpp::Node
{
PongNode() : Node("pong_node") { ... }
};
```
建立名為 `pong_node` 的節點,並在建構子內設定通訊介面。
---
#### (3)建立 publisher/subscriber
```cpp
publisher_ = this->create_publisher<std_msgs::msg::Header>("/microROS/pong", 10);
subscription_ = this->create_subscription<std_msgs::msg::Header>(
"/microROS/ping", 10, std::bind(&PongNode::ping_callback, this, _1));
```
* Publisher:發送 Pong。
* Subscriber:接收 Ping。
* Topic 型別同樣為 `std_msgs/Header`。
---
#### (4)callback 函式
```cpp
void ping_callback(const std_msgs::msg::Header::SharedPtr msg)
{
auto pong = std_msgs::msg::Header();
pong.stamp = msg->stamp;
pong.frame_id = "pong_from_client";
publisher_->publish(pong);
}
```
* 收到 Ping 時複製時間戳。
* 發布 Pong 封包。
* 使用 `rclcpp::Publisher::publish()` 送出。
---
#### (5)執行迴圈
```cpp
rclcpp::spin(std::make_shared<PongNode>());
```
`spin()` 會持續監聽事件並觸發 callback。
---
### 5. 編譯與執行
```bash
colcon build
source install/setup.bash
ros2 run client_pong pong_node
```
若 Agent 與 Host 節點皆啟動,終端應顯示:
```
[Client] Pong node started. Waiting for Ping...
[Client] Received Ping at 1701.234567890
[Client] Sent Pong
```
---
### 6. 測試與驗證
Host 端會同步印出:
```
[INFO] Sent Ping at 1701.234567890
[INFO] Received Pong | Latency: 23.44 ms
```
表示封包往返成功。
---
### 7. 延伸練習
* 在 callback 中加入傳輸計數器。
* 嘗試加入 QoS 設定:
```cpp
rclcpp::QoS qos_profile(10);
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
```
* 在實體板(如 ESP32)上交叉編譯執行,透過 UART 或 Wi-Fi 連線至 Agent。
---
### 8. 小結
1. Client 節點使用 `rclcpp` 管理 callback 與通訊事件。
2. 對應 Host 的 `/microROS/ping` 與 `/microROS/pong` topic。
3. 架構輕量,適合嵌入式與模擬環境。
4. 為後續 QoS 與自訂訊息擴充打下基礎。
---
## 五、Ping-Pong 通訊測試與封包驗證(Communication Test & Packet Verification)
### 1. 測試目的
本章目標是驗證 **Host (Python)** 與 **Client (C)** 節點能否透過 **micro-ROS Agent** 正確收發資料。
測試項目包含:
1. Agent 啟動與連線確認
2. Topic 對應關係檢查
3. 訊息往返與延遲測量
4. 封包層級驗證(rqt_graph、Wireshark)
---
### 2. 測試前環境確認
| 元件 | 狀態檢查指令 | 成功條件 |
| --------------- | ----------------------------------------------------------- | --------------------------- |
| ROS 2 Humble | `ros2 doctor` | 無錯誤訊息 |
| micro-ROS Agent | `ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888` | 顯示 `Waiting for clients...` |
| Host Node | `ros2 run host_ping ping_node` | 顯示「Sent Ping」 |
| Client Node | `ros2 run client_pong pong_node` | 顯示「Sent Pong」 |
---
### 3. 啟動順序
#### (1)啟動 micro-ROS Agent
```bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
```
Agent 會監聽 UDP 8888 埠口,等待 Client 連線。
#### (2)啟動 Client 節點
```bash
ros2 run client_pong pong_node
```
終端輸出:
```
[Client] Pong node started. Waiting for Ping...
```
#### (3)啟動 Host 節點
```bash
ros2 run host_ping ping_node
```
若系統正常,Host 會每秒送出一筆 Ping,Client 立即回 Pong。
---
### 4. 驗證 Topic 與節點
列出目前所有節點:
```bash
ros2 node list
```
預期結果:
```
/ping_node
/pong_node
```
列出 Topic:
```bash
ros2 topic list
```
預期結果:
```
/microROS/ping
/microROS/pong
```
檢查資料流:
```bash
ros2 topic echo /microROS/ping
ros2 topic echo /microROS/pong
```
可看到 `Header` 訊息的時間戳與 frame_id。
---
### 5. 延遲量測與封包驗證
Host 節點會在 callback 計算封包延遲:
```
[INFO] Sent Ping at 1710.142331
[INFO] Received Pong | Latency: 24.37 ms
```
#### 使用 rqt_graph 可視化
開啟節點關聯圖:
```bash
rqt_graph
```
結果應顯示:
```
[ping_node] → (/microROS/ping) → [pong_node]
[pong_node] → (/microROS/pong) → [ping_node]
```
#### Wireshark 封包觀察(選用)
1. 開啟 Wireshark,選擇網卡。
2. 設定篩選條件:
```
udp.port == 8888
```
3. 可觀察 micro-ROS Agent 收發封包內容:
* XRCE-DDS Header
* Timestamp Payload
* Topic 名稱 `/microROS/ping`、`/microROS/pong`
---
### 6. 常見問題與排除
| 問題 | 可能原因 | 解法 |
| ------------- | ------------------ | ---------------------------------------- |
| Host 無法連線 | Agent 未啟動或埠號錯誤 | 確認 `--port` 一致 |
| 無 Pong 回覆 | Client 未訂閱正確 Topic | 檢查 topic 名稱拼字 |
| latency 顯示為 0 | 時間戳格式不一致 | 同步 `Clock`、重新轉換為 ROS Time |
| Agent 關閉時節點掛起 | DDS session 中斷 | 捕捉例外或使用 `try...except KeyboardInterrupt` |
---
### 7. 延伸測試
#### (1)多節點互通
可同時啟動多個 Client:
```bash
ros2 run client_pong pong_node --ros-args -r __node:=pong_node_2
```
Host 端會收到多筆 Pong 回應,可觀察 Topic 併發行為。
#### (2)QoS 測試
在 Host 與 Client 加入 QoS 參數:
```python
qos = QoSProfile(depth=10, reliability=QoSReliabilityPolicy.RELIABLE)
self.create_publisher(Header, '/microROS/ping', qos)
```
觀察資料遺失率與延遲變化。
#### (3)封包遺失模擬
關閉 Agent 後重新啟動,檢查節點是否能自動重新連線。
---
### 8. 小結
1. Ping-Pong 範例驗證了整個 micro-ROS 通訊鏈。
2. 封包延遲可作為系統效能量測依據。
3. 透過 `rqt_graph` 或 Wireshark 可觀察 DDS 與 XRCE-DDS 封包內容。
4. 此章完成後,開發者即可嘗試自訂訊息與 QoS 測試。
---
## 六、自訂訊息與 QoS 測試(Custom Message & QoS Experiments)
### 1. 章節目的
前面章節中,我們使用標準訊息 `std_msgs/Header` 完成 Ping-Pong 通訊。
本章將示範如何:
1. 新增自訂 `.msg` 檔,建立自定義資料結構。
2. 修改建置設定,使其能被 Python 與 C++ 端同時使用。
3. 測試不同 QoS(Quality of Service)參數對通訊穩定性的影響。
---
### 2. 建立自訂訊息
#### (1)建立 `msg` 資料夾與檔案
在 Host 或 Client 專案目錄內新增 `msg/SensorData.msg`:
```bash
cd ~/microros_ws/src/host_ping/
mkdir msg
nano msg/SensorData.msg
````
內容:
```text
int32 id
float32 temperature
float32 humidity
string source
```
---
#### (2)修改 `package.xml`
在 `<build_depend>` 與 `<exec_depend>` 中加入:
```xml
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
```
---
#### (3)修改 `CMakeLists.txt`
在最底部新增:
```cmake
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/SensorData.msg"
)
ament_export_dependencies(rosidl_default_runtime)
```
---
#### (4)重新建置
```bash
cd ~/microros_ws
colcon build
source install/setup.bash
```
建置成功後可驗證:
```bash
ros2 interface show host_ping/msg/SensorData
```
輸出:
```
int32 id
float32 temperature
float32 humidity
string source
```
---
### 3. Python 端使用自訂訊息
修改 `ping_node.py`:
```python
from host_ping.msg import SensorData
def timer_callback(self):
msg = SensorData()
msg.id = 1
msg.temperature = 25.6
msg.humidity = 73.2
msg.source = "Host"
self.publisher.publish(msg)
self.get_logger().info(f'Sent SensorData: T={msg.temperature}, H={msg.humidity}')
```
建立 Publisher:
```python
self.publisher = self.create_publisher(SensorData, '/sensor/data', 10)
```
---
### 4. C++ 端使用自訂訊息
在 Client 節點中匯入相同型別:
```cpp
#include "host_ping/msg/sensor_data.hpp"
```
建立 Publisher:
```cpp
publisher_ = this->create_publisher<host_ping::msg::SensorData>(
"/sensor/data_feedback", 10);
```
建立 Callback:
```cpp
void data_callback(const host_ping::msg::SensorData::SharedPtr msg)
{
RCLCPP_INFO(this->get_logger(),
"Received SensorData: T=%.2f H=%.2f from %s",
msg->temperature, msg->humidity, msg->source.c_str());
}
```
---
### 5. QoS(Quality of Service)設定
QoS 影響資料傳輸的可靠性與佇列行為。
可在 Publisher 與 Subscriber 建立時指定。
#### Python 範例:
```python
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy
qos_profile = QoSProfile(
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=5
)
self.publisher = self.create_publisher(SensorData, '/sensor/data', qos_profile)
```
#### C++ 範例:
```cpp
rclcpp::QoS qos_profile(5);
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
publisher_ = this->create_publisher<host_ping::msg::SensorData>(
"/sensor/data_feedback", qos_profile);
```
---
### 6. QoS 測試建議
| 測試項目 | 設定 | 預期結果 |
| ------------------------------ | ---------------- | ------------------- |
| Reliability = RELIABLE | 封包不遺失,延遲略升 | 高穩定性 |
| Reliability = BEST_EFFORT | 延遲極低但可能遺失封包 | 適合即時感測 |
| Depth = 1 vs 10 | 影響緩衝區大小 | 小 depth 時高頻率封包可能被覆蓋 |
| History = KEEP_LAST / KEEP_ALL | KEEP_ALL 可完整紀錄封包 | 消耗更多記憶體 |
---
### 7. 驗證與觀察
可使用下列指令觀察 QoS 效果:
```bash
ros2 topic info /sensor/data --verbose
```
輸出中會顯示:
```
Reliability: RELIABLE
Durability: VOLATILE
History: KEEP_LAST (depth=5)
```
再用 `ros2 topic echo /sensor/data` 驗證實際封包。
---
### 8. 延伸練習
* 嘗試建立多欄位資料(如光照度、氣壓)並傳輸。
* 模擬封包遺失(關閉 Client)觀察 QoS 是否保留訊息。
* 結合第七章主題,將自訂訊息應用於感測器網路或自走車架構中。
---
### 9. 小結
1. 自訂 `.msg` 檔可讓 micro-ROS 與 ROS 2 傳輸複合型資料。
2. QoS 設定是確保通訊穩定性與效能的核心。
3. 透過 RELIABLE、BEST_EFFORT、depth 等參數可調整資料傳輸特性。
4. 此章結束後,整個 micro-ROS Ping-Pong 架構已具備彈性延伸能力。
---
## 七、常見錯誤與除錯方法(Debugging & Common Issues)
### 1. 章節目的
在 micro-ROS 開發過程中,錯誤訊息常出現在「Agent 連線」、「Topic 命名」、「Callback 執行」或「節點初始化」等部分。
本章整理常見錯誤類型與對應解法,幫助快速定位問題來源。
---
### 2. 常見啟動錯誤
| 錯誤訊息 | 原因 | 解決方式 |
| ------------------------------------------- | --------------- | ---------------------------------------- |
| `Connection refused` | Agent 未啟動或埠號錯誤 | 確認 `--port` 一致並重新啟動 Agent |
| `Entity not found` | 節點尚未完成初始化 | 延遲啟動 Publisher 或使用 Timer 初始化 |
| `No DDS participants matched` | Topic 名稱或訊息型別不符 | 檢查 Topic 拼字與 message type 是否一致 |
| `rclpy.executors.ExternalShutdownException` | 節點被外部強制關閉 | 使用 `try...except KeyboardInterrupt` 捕捉例外 |
| `Segmentation fault (core dumped)` | 節點物件提早釋放或記憶體操作錯誤 | 檢查 Publisher、Subscription 是否在節點生命週期內建立 |
---
### 3. 通訊異常
#### (1)Ping 無法傳送
檢查:
```bash
ros2 topic list
````
若無 `/microROS/ping`,代表 Publisher 未建立成功。
→ 檢查 Python 節點的:
```python
self.publisher = self.create_publisher(Header, '/microROS/ping', 10)
```
是否遺漏 `import` 或未呼叫 `rclpy.init()`。
---
#### (2)Pong 無回覆
Client 沒有收到 Ping:
```bash
ros2 topic echo /microROS/ping
```
若資料有正常流出但無回覆,請檢查 C++ Client 的 callback 綁定是否正確:
```cpp
subscription_ = this->create_subscription<std_msgs::msg::Header>(
"/microROS/ping", 10,
std::bind(&PongNode::ping_callback, this, std::placeholders::_1));
```
若 `ping_callback()` 未被觸發,請確認:
1. Topic 名稱與 Host 端完全一致。
2. 節點是否已呼叫 `rclcpp::spin()`。
3. QoS 設定是否導致資料未送達(例如 reliability 不匹配)。
---
#### (3)延遲顯示異常
Host 顯示:
```
Latency: 0.00 ms
```
原因:
* Host 與 Client 時鐘不同步。
* `msg.stamp` 未更新。
解法:
* 在 callback 中使用 `this->get_clock()->now()` 重新取時間。
* 確認 C++ 端複製時間戳時未被覆蓋。
---
### 4. 編譯與建置錯誤
| 錯誤 | 原因 | 修正 |
| -------------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------- |
| `colcon build` 無法找到自訂訊息 | 忘記在 `package.xml` 加入 `rosidl_default_generators` | 新增相依性並重建 |
| `undefined reference to rosidl_typesupport_cpp` | 未正確匯入 type support | 確保在 CMake 使用 `ament_target_dependencies(... rclcpp std_msgs ...)` |
| `class ... has no member named 'create_publisher'` | 未繼承 `rclcpp::Node` 類別或未呼叫 `Node()` 初始化 | 確認類別繼承結構正確 |
| `rclcpp::spin` 未執行 | 主程式未進入事件迴圈 | 在 main() 結尾加上 `rclcpp::spin(node)` |
---
### 5. Agent 連線異常
#### 狀況一:Agent 已啟動但無回應
原因:UDP 封包被防火牆阻擋。
修正:
```bash
sudo ufw disable
```
或允許 UDP 8888 埠。
#### 狀況二:Client 重啟後無法重新連線
原因:DDS session 未重設。
解法:重新啟動 Agent 或在節點重啟前呼叫:
```cpp
rclcpp::shutdown();
rclcpp::init(0, nullptr);
```
---
### 6. 記憶體與穩定性問題(嵌入式)
#### (1)堆疊不足
症狀:Client 執行數秒後當機。
修正:
在 FreeRTOS 或 RTOS 設定中增加 stack size。
#### (2)過度建立物件
頻繁建立與釋放 Publisher/Subscription 導致記憶體碎片化。
修正:
在建構子中建立一次,重複使用物件。
#### (3)QoS 參數過大
過多緩衝導致記憶體不足。
修正:
將 QoS depth 調低,使用 `BEST_EFFORT` 模式。
---
### 7. 除錯技巧
| 方法 | 指令 / 範例 | 功能 |
| ---------- | ------------------------------ | ---------------- |
| ROS 2 日誌模式 | `--ros-args --log-level DEBUG` | 顯示完整通訊與初始化過程 |
| topic 檢查 | `ros2 topic info <topic>` | 顯示 QoS、訂閱者與發布者資訊 |
| 節點連線圖 | `rqt_graph` | 可視化節點通訊關係 |
| 封包層驗證 | Wireshark + `udp.port==8888` | 觀察 XRCE-DDS 封包內容 |
| ROS bag 錄製 | `ros2 bag record -a` | 記錄所有封包以供離線分析 |
---
### 8. 效能優化建議
1. 將頻繁的 log 輸出改為統計後批次列印,降低 I/O 負擔。
2. 在 C++ 端使用 `rclcpp::Rate` 控制迴圈頻率,避免 spin 阻塞。
3. 若資料量大,使用 QoS `KEEP_LAST` 搭配適中 depth。
4. 對多節點系統,採用 UDP 取代 Serial,可顯著降低延遲。
---
### 9. 小結
1. micro-ROS 常見錯誤多源於初始化順序、Topic 命名或 QoS 設定不符。
2. 使用 ROS 2 CLI 工具可快速檢查節點與封包狀態。
3. 對嵌入式平台而言,記憶體與堆疊配置尤為關鍵。
4. 熟悉 Agent log、QoS 與事件迴圈行為,是維持穩定通訊的基礎。
---
## 八、實作篇總結與延伸練習(Summary & Extended Practice)
### 1. 實作回顧
micro-ROS 讓微控制器(MCU)能以與 ROS 2 相同的通訊架構運作。
整個 Ping-Pong 實驗過程中,我們完成了:
| 階段 | 任務 | 主要成果 |
| -- | --------- | ------------------------------------------------------ |
| 二 | 環境建置 | 建立 ROS 2 Humble 與 micro-ROS Agent,可支援 Host / Client 通訊 |
| 三 | Host 節點 | 使用 Python `rclpy` 發送與接收 Header 訊息 |
| 四 | Client 節點 | 使用 C++ `rclcpp` 接收 Ping 並回傳 Pong |
| 五 | 通訊驗證 | 以 `rqt_graph`、Wireshark 驗證封包正確往返 |
| 六 | 自訂訊息與 QoS | 建立 `.msg` 自訂資料結構,測試 QoS 穩定性 |
| 七 | 除錯 | 收錄常見錯誤、記憶體問題與最佳化技巧 |
此流程完整展示 micro-ROS 在實際系統中「從理論到執行」的路徑。
---
### 2. 技術收穫
1. 理解 **Host / Agent / Client** 三層結構與資料流。
2. 能使用 Python 與 C++ API 建立雙向通訊節點。
3. 掌握 `.msg` 定義與 QoS 調校方法。
4. 能讀懂常見錯誤並快速除錯。
5. 具備將 micro-ROS 移植至實體硬體的基礎。
---
### 3. 延伸練習方向
#### (1)多節點協作
建立多個 Client 節點:
```
/sensor/temp
/sensor/hum
/sensor/light
```
Host 負責聚合資料並上傳雲端。
可練習同步多節點傳輸與 Topic 管理。
#### (2)Service 與 Action
micro-ROS 也支援 Service/Action 模式:
Client 可透過 Service 向 Host 要求設定或回報狀態。
延伸目標:
* 建立 `/reset_device` service。
* 由 Host 發出指令,Client 重設感測器。
#### (3)整合 IoT 與資料視覺化
將 Host 資料透過 `rosbridge` 或 MQTT 傳入雲端,
並在網頁上以 WebSocket 實時顯示。
可使用 `rqt_plot` 或自建 dashboard。
#### (4)移植至實體開發板
以 ESP32 或 STM32 為平台:
* 使用 micro-ROS build system 交叉編譯。
* 以 UART 或 Wi-Fi 連線至 Agent。
* 實測延遲與資料遺失率。
---
### 4. 實驗指標與進階觀察
| 項目 | 建議觀察指標 |
| ----- | -------------------- |
| 延遲 | 封包往返時間(ms) |
| 穩定度 | 封包遺失率 (%) |
| 可擴展性 | 節點數量增加下的效能變化 |
| 資料完整性 | QoS RELIABLE 模式下的正確率 |
這些指標能作為撰寫報告或競賽專案時的量化依據。
---
### 5. 延伸應用構想
| 領域 | 應用範例 | 說明 |
| -------- | -------- | ----------------------- |
| IoT | 環境監測系統 | 多個 micro-ROS 感測節點回傳資料 |
| Robotics | 自走車底層控制 | Host 做導航,Client 控制馬達與避障 |
| 工控 | 智慧工廠感測 | 微控制器作為 ROS 節點上報狀態 |
| 教育 | ROS 實驗課程 | 學生以低成本板學習分散式控制架構 |
---
### 6. 專案擴充建議
1. **整合容器化部署**:以 Docker Compose 同時啟動 Agent、Host 與資料視覺化服務。
2. **自動化測試**:撰寫 pytest 腳本,檢查 Topic 資料正確性與通訊延遲。
3. **系統監控**:結合 Prometheus/Grafana,監測 ROS 節點狀態。
4. **安全性測試**:研究 micro-ROS 封包加密與驗證機制。
---
### 7. 小結
1. 本系列從環境建置到通訊驗證,完整實作 micro-ROS 架構。
2. 以 Python 與 C++ 節點組合建立雙向資料流,符合 ROS 2 標準架構。
3. 通過 QoS 與自訂訊息的測試,展示 micro-ROS 的彈性與穩定性。
4. 往後可延伸至 IoT、機器人與邊緣運算領域,形成具規模的分散式系統。
---