---
# System prepended metadata

title: micro-ROS 實作篇
tags: [MCU, ROS 2, micro-ROS, NCKU]

---

# 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、機器人與邊緣運算領域，形成具規模的分散式系統。

---