rosbag2:紀錄與重播
- ros2
- rosbag2
- recording
- replay
問題定義
除錯機器人系統最痛苦的情境之一,是「問題只在特定條件下發生,而且很難重現」——可能是某次感測器資料剛好有雜訊、某個時間點兩個主題剛好同時觸發。如果每次都要真的把機器人或感測器架起來重跑一次,效率非常低。rosbag2 讓你把整段資料流原封不動錄下來,之後可以無限次重播,離線分析問題,不需要每次都依賴實體硬體。
核心概念說明
bag 檔案的實際結構
執行 ros2 bag record 之後,會產生一個資料夾(不是單一檔案),裡面包含:
my_bag/
├── my_bag_0.db3 # 實際的訊息資料(sqlite3 資料庫格式)
└── metadata.yaml # 記錄了有哪些主題、訊息型別、數量、時間範圍等中繼資料
metadata.yaml 是快速了解一個 bag 檔案內容的第一站,不需要真的重播就能知道裡面錄了什麼、錄了多久、每個主題各有幾則訊息。
序列化格式與儲存外掛
訊息內容以序列化後的二進位格式存在 .db3 檔案裡,這代表訊息型別的定義必須跟錄製當下一致,重播時系統才能正確反序列化。如果你錄製之後又修改了自訂 message 的欄位定義(例如在 Detection.msg 裡新增一個欄位),舊的 bag 檔案在重播時可能會出現欄位對應錯誤,這是實務上維護長期資料集時要特別注意的地方。
除了預設的 sqlite3,rosbag2 也支援 MCAP 作為儲存後端,兩者可以透過 --storage 參數切換,這裡不深入比較,但知道有這個選項存在,之後遇到效能或跨語言工具鏈整合需求時可以評估。
實作範例
1. 錄製所有主題
ros2 bag record -a -o my_bag
-a 代表錄製當下所有可見的主題,-o my_bag 指定輸出資料夾名稱。錄製期間讓第 2.2 節的 talker 持續發布訊息,錄個幾秒後按 Ctrl+C 停止。
預期輸出
[INFO] [rosbag2_storage]: Opened database 'my_bag/my_bag_0.db3' for READ_WRITE.
[INFO] [rosbag2_recorder]: Listening for topics...
[INFO] [rosbag2_recorder]: Subscribed to topic '/chatter'
[INFO] [rosbag2_recorder]: Subscribed to topic '/rosout'
[INFO] [rosbag2_recorder]: Subscribed to topic '/parameter_events'
^C[INFO] [rclcpp]: signal_handler(signum=2)
2. 只錄製特定主題
實務上很少真的需要 -a,多半是針對性地只錄要分析的主題,避免檔案過大:
ros2 bag record -o chatter_only /chatter
3. 依檔案大小自動切分
長時間錄製時,單一 .db3 檔案可能會變得很大,--max-bag-size 可以指定超過大小後自動切成新檔案(單位為位元組):
ros2 bag record -a -o my_bag --max-bag-size 104857600
這會在每個 .db3 檔案接近 100MB 時自動開新檔案,metadata.yaml 會記錄所有切分後的檔案清單,重播時你只需要指向資料夾,不需要自己處理多檔案的串接。
4. 檢視 bag 內容摘要
ros2 bag info my_bag
預期輸出
Files: my_bag_0.db3
Bag size: 48.2 KiB
Storage id: sqlite3
Duration: 8.312s
Start: Jul 18 2026 14:32:10.123 (1752813130.123)
End: Jul 18 2026 14:32:18.435 (1752813138.435)
Messages: 19
Topic information:
Topic: /chatter | Type: std_msgs/msg/String | Count: 16 | Serialization Format: cdr
Topic: /rosout | Type: rcl_interfaces/msg/Log | Count: 3 | Serialization Format: cdr
這份摘要在你拿到別人交接的 bag 檔案時特別有用——不需要真的重播,就能快速確認裡面有沒有你需要的主題、訊息數量是否合理(例如預期應該有雷射掃描資料,結果 Count 是 0,代表錄製當下那個主題其實沒有真的在發布)。
5. 重播
ros2 bag play my_bag
預期輸出
[INFO] [rosbag2_storage]: Opened database 'my_bag/my_bag_0.db3' for READ_ONLY.
[INFO] [rosbag2_player]: Set rate to 1
重播期間,開另一個終端機用 ros2 topic echo /chatter 就能看到跟原本錄製時一樣的訊息內容與間隔陸續重現。
6. 調整重播速率、只重播特定主題
ros2 bag play my_bag --rate 2.0 --topics /chatter
--rate 2.0 讓重播速度變成兩倍快,--topics /chatter 則只重播 /chatter,即使 bag 裡還錄了其他主題也會被忽略——這在你只想針對某個子系統的行為做重現測試時很實用。
常見錯誤與除錯技巧
錯誤一:重播時終端機顯示找不到 message 型別
ERROR: Failed to deserialize message: unknown type my_package_msgs/msg/Detection
原因:這通常發生在「錄製時的環境」跟「重播時的環境」不一致——例如錄製時工作空間裡有 my_package_msgs 這個自訂介面套件,但重播時換了一台機器或新開的環境,忘記編譯、source 這個介面套件。
排除方式:重播前確認自訂介面套件已經編譯並 source:
source ~/ros2_ws/install/setup.bash
ros2 interface list | grep my_package_msgs
錯誤二:重播出來的資料,時間看起來完全對不上
原因:如果你的節點邏輯依賴 use_sim_time 搭配訊息裡的時間戳記做運算(例如計算兩則訊息的時間差),但重播時沒有正確設定 use_sim_time 為 true,節點會用「牆上時鐘」(wall clock)的時間,而不是 bag 裡記錄的模擬時間,導致運算結果跟預期不符。
排除方式:重播時加上 --clock 讓 rosbag2 發布時鐘訊號,並確保消費資料的節點有把 use_sim_time 設為 true:
ros2 bag play my_bag --clock
ros2 run my_package listener --ros-args -p use_sim_time:=true
小結
rosbag2 把「重現問題」這件事從「祈禱它再發生一次」變成「錄下來、事後隨時重播」。實務上比較常用的是針對性錄製特定主題(而非無腦全錄)、搭配 ros2 bag info 快速確認內容,以及重播時注意介面套件版本一致與時間軸設定。下一節會介紹另一個提升開發效率的工具:一次啟動多個節點、統一管理參數的 launch 檔案系統。
延伸閱讀
常見問題
- 錄製的 bag 檔案可以直接用文字編輯器打開看內容嗎?
- 不行,rosbag2 預設用 sqlite3 資料庫格式儲存(副檔名 .db3),訊息內容是序列化過的二進位資料,需要透過 ros2 bag 系列指令或程式化讀取(rosbag2_py)才能還原成可讀內容,不能直接用文字編輯器開啟。
- 重播時可以只重播其中幾個主題嗎?
- 可以,ros2 bag play 支援 --topics 指定只重播特定主題,其餘記錄在 bag 裡但沒被指定的主題會被忽略。這在你只想針對某個子系統重現問題、又不想同時把所有節點都餵一次舊資料時很有用。
- 重播的訊息時間戳記還是原本錄製時的時間嗎?
- 預設情況下,重播出來的訊息會沿用錄製當下的時間間隔(也就是原本錄製時兩則訊息間隔多久,重播時也會間隔多久),但訊息內容裡如果有自己填的 header.stamp 欄位,除非額外處理,仍然會是錄製當下的時間值,不會自動對齊到重播當下的系統時間。