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