TF2
TF2 是 ROS 2 中用來追蹤不同 Coordinate Frame 隨時間變化關係的函式庫。本頁會介紹 Frame、Parent/Child Relationship、Static 與 Dynamic Transform、常用檢查工具,以及在 Mobility 與 Manipulation 應用中最常造成 TF Tree 問題的錯誤。
概述
機器人系統通常同時存在許多具有實際意義的 Coordinate Frame,例如 Camera、LiDAR、Robot Base、各個 Arm Link,以及 Map Origin。
TF2 讓系統中的不同部分都可以詢問:
「在時間 T,Frame A 相對於 Frame B 的位置與方向是什麼?」
而不需要每一個 Node 都自行知道完整的幾何關係。
為什麼重要
幾乎所有 Perception、Navigation 與 Manipulation 功能都依賴正確的 TF。
例如:
- 將偵測到的 Object Pose 轉換到 Robot 的 Planning Frame(Manipulation 的 Pose Estimation)
- 將 Sensor Data 正確放到 Map 上(Mobility)
- 在 RViz 中驅動
RobotModel顯示
如果 TF Tree 中有缺失或重複的 Transform,可能會造成看似與 TF 無關的問題,例如 "planning failed",或 Robot Model 無法移動。
關於 TF 與 Robot Model 的關係,可參考 URDF Robot Model。
核心概念
Frame 與 TF Tree
每一個具有意義的 Coordinate Frame 都有自己的 Frame ID,也就是一個字串,例如 base_link、camera_link 或 map。
TF2 會將這些 Frame 組成一棵 Tree:除了 Root Frame 之外,每一個 Frame 都有且只有一個 Parent,而 Transform 會描述 Child Frame 相對於 Parent 的位置與方向。
map
└── odom
└── base_link
├── camera_link
└── lidar_link
在一棵連通的 TF Tree 中,任意兩個 Frame 之間都只有一條唯一路徑。
因此,即使沒有任何 Node 直接發布那一對 Frame 的 Transform,TF2 仍然可以透過中間的 Frame Chain 計算出兩者之間的關係。
如果兩個 Frame 位於彼此不連通的 TF Tree 區段中,Lookup 就會失敗,因為它們之間不存在 Transform Chain。
Static 與 Dynamic Transform
| 類型 | Topic | 是否隨時間變化? | 常見來源 |
|---|---|---|---|
| Static | /tf_static | 否(在 Node 執行期間固定) | Sensor Mounting Offset、Fixed Link |
| Dynamic | /tf | 是,持續更新 | Robot Odometry、Joint Motion、Localization |
Static Transform 會發布到 /tf_static,並使用 Transient Local Durability,因此較晚加入的 Subscriber 仍然可以收到這些固定 Transform。
它適合描述系統執行期間不會改變的關係,例如剛性固定在 Chassis 上的 Camera。
Dynamic Transform 會持續發布到 /tf,適合描述會隨時間改變的關係,例如由 Odometry 產生的 odom -> base_link,或由 Joint State 計算出的 Robot Link Motion。
robot_state_publisher
對於使用 URDF 描述的 Articulated Robot,通常不需要手動發布每一個 Link 之間的 Transform。
robot_state_publisher 會訂閱 /joint_states,再結合 URDF 中定義的 Kinematic Structure,發布對應的 Link Transform 到 /tf,而 Fixed Joint 則會發布到 /tf_static。
關於 Robot Model 本身的定義方式,可參考 URDF Robot Model。
Timestamp 很重要
Dynamic Transform 都帶有 Timestamp,而 TF2 會保留一段時間的 History Buffer,因此可以查詢特定時間點的 Transform。
TF2 可以在現有 Dynamic Transform 之間進行 Interpolation,但不能無限制地對過去或未來進行 Extrapolation。
Static Transform 代表固定關係,不受一般 Dynamic Transform History 的時間限制。
常見的 Dynamic TF 問題之一,就是要求查詢的 Timestamp 已經早於 TF2 保留的 Buffer,或查詢的是尚未發布 Transform 的未來時間點。
實作步驟
以下指令適用於任何正在發布 TF 的 ROS 2 系統,包括 Publisher/Subscriber 中的 simple_publisher / simple_subscriber 搭配 static_transform_publisher,或任何 Robot Bring-up Launch File。
1. 手動發布 Static Transform
# Translation is in meters; roll, pitch, and yaw are in radians.
ros2 run tf2_ros static_transform_publisher \
--x 0 --y 0 --z 0.1 \
--roll 0 --pitch 0 --yaw 0 \
--frame-id base_link \
--child-frame-id camera_link
不同 Distribution 的語法
static_transform_publisher 的參數格式在不同 ROS 2 Distribution 之間曾經有所變化。
Humble 與 Jazzy 都支援上方使用 Named Argument 的形式:
--x --y --z --yaw --pitch --roll --frame-id --child-frame-id
較舊的 Distribution 則曾使用 Positional Argument:
x y z yaw pitch roll parent child
在正式使用前,可以執行:
ros2 run tf2_ros static_transform_publisher --help
確認目前安裝版本支援的正確格式。
2. 查看兩個 Frame 之間的 Transform
ros2 run tf2_ros tf2_echo base_link camera_link
這個指令會持續輸出兩個 Frame 之間目前的 Transform,並在有新 Transform 時更新。
如果兩個 Frame 之間尚未建立可用路徑,則會顯示錯誤。
3. 產生 TF Tree 視覺化圖
ros2 run tf2_tools view_frames
這個工具會監聽數秒,接著產生一份 PDF(通常是目前目錄下的 frames.pdf,檔名可能因 Distribution 而異)。
內容會顯示每個 Frame、它的 Parent,以及最近一次 Transform 發布時間。
這通常是最快找出斷開 Frame 或異常重複 Transform 的方式。
4. 在 RViz2 中即時顯示 TF
ros2 run rviz2 rviz2
加入 TF Display,就可以即時查看 Frame Tree。
如果系統已經載入 URDF,也可以同時加入 RobotModel Display。
更多操作方式請參考 RViz2。
預期成果
當兩個 Frame 已經連通後,tf2_echo 應該會輸出 Translation 與 Rotation。
view_frames 應該會產生一份沒有孤立 Frame,也沒有任何 Frame 成為自己 Ancestor 的 Tree Diagram。
RViz 的 TF Display 則會顯示各 Frame 的 Axis,並隨底層 Robot 或 Sensor Motion 即時更新。
常用指令
ros2 run tf2_ros tf2_echo <source_frame> <target_frame>
ros2 run tf2_tools view_frames
ros2 topic echo /tf_static
ros2 topic hz /tf
ros2 run tf2_ros static_transform_publisher --help
常見問題
tf2_echo顯示"frame does not exist"— 沒有任何 Node 曾經發布包含該 Frame ID 的 Transform。請檢查拼字是否正確(Frame ID 區分大小寫),並確認負責發布 Transform 的 Node 正在執行。- 出現
"Lookup would require extrapolation into the future/past"— 查詢的 Timestamp 超出 TF2 目前保留的 Buffer 範圍。如果不需要特定歷史時間,可以使用最新可用時間(rclpy.time.Time()/ Zero Timestamp 代表"latest"),或減少 Sensor Data 產生與 TF Lookup 之間的延遲。 - 同一組 Frame Pair 出現重複或互相衝突的 Transform — 有兩個不同 Node 同時發布相同 Parent/Child Relationship,可能產生 Warning 與不可預期結果。最常見的情況是播放已錄製的 TF,同時又啟動 Live
robot_state_publisher。請參考下方說明與 Rosbag。 - RViz 中沒有顯示 RobotModel — TF Tree 可能不完整。使用
view_frames確認 URDF 中的每一個 Link 都能一路連回 Root Frame。
Playback 時不要重複發布 Transform
如果 Rosbag 中已經包含 Robot 的 /tf 與 /tf_static,同時又執行由錄製 /joint_states 驅動的 robot_state_publisher,兩者都會發布相同 Frame 的 Transform。
TF2 不會幫你合併或仲裁同一個 Frame Pair 的兩個 Authority,因此必須選擇其中一種方式:
- 直接回放錄製的
/tf與/tf_static - 或回放
/joint_states,並讓robot_state_publisher根據 URDF 重新計算 TF
不要同時使用兩種方式發布相同 Transform。
完整說明請參考 Rosbag。
重點整理
- TF2 會將 Frame 組成 Tree;任一時間點,每個 Child Frame 都只有一個 Parent。
- Static Transform(
/tf_static)用於固定關係;Dynamic Transform(/tf)用於會隨時間變動的關係。 robot_state_publisher會根據/joint_states與 URDF 計算 Link Transform,因此一般不需要手動發布每一個 Link 的 Transform。- Dynamic Transform 帶有 Timestamp;如果查詢時間超出 TF2 可用 History,就可能造成 Lookup Failure。
- 不要讓兩個來源(例如 Rosbag 與
robot_state_publisher)同時發布相同 Transform。
下一步
接著前往 Rosbag,了解如何記錄與安全回放 ROS 2 資料,包括本頁提到的 TF Playback 規則。