TF/TF2 là gì? Theo dõi vị trí tương đối giữa các khung toạ độ
Atlasbot · 16/8/2026 · 11 phút đọc
Một robot AMR có rất nhiều "khung toạ độ" (coordinate frame) khác nhau cùng tồn tại: khung của LiDAR (gắn trên đầu robot, cách tâm robot 20cm), khung của chính robot (base_link, di chuyển trên sàn), khung bản đồ tổng thể (map, cố định). Khi LiDAR đo được một vật cản "cách LiDAR 2m về phía trước", câu hỏi thực sự cần trả lời là: vật cản đó nằm ở đâu trên bản đồ? — đây chính xác là bài toán TF2 giải quyết.
Khái niệm chính
TF2 (Transform Library, phiên bản 2) theo dõi vị trí và hướng tương đối giữa các khung toạ độ theo thời gian, tổ chức chúng thành một cây (tree) — mỗi khung có đúng một khung cha, không có vòng lặp. Cấu trúc phổ biến trên một AMR:
map
└── odom
└── base_link
├── lidar_link
├── imu_link
└── camera_link
- map → odom — do hệ định vị toàn cục (AMCL, SLAM) cập nhật, sửa lại sai số trôi (drift) của odometry theo thời gian
- odom → base_link — do node tính odometry cập nhật liên tục, dựa trên dữ liệu encoder/IMU
- base_link → lidar_link/imu_link/camera_link — thường cố định, xác định bởi vị trí lắp đặt vật lý thật của từng cảm biến trên khung robot (khai báo trong URDF, không đổi theo thời gian)
Broadcaster và Listener
Node nào biết quan hệ giữa hai khung sẽ đóng vai broadcaster, liên tục publish transform lên hệ thống TF2. Node nào cần biết "điểm A ở khung X đang nằm đâu trong khung Y" đóng vai listener, gọi hàm tra cứu — TF2 tự động nhân dồn (chain) các transform trung gian nếu hai khung không nối trực tiếp, ví dụ tính từ lidar_link ra tới map phải đi qua đủ lidar_link → base_link → odom → map.
Tóm lại: Không có TF2, mỗi node sẽ phải tự làm phép tính hình học quy đổi toạ độ thủ công giữa mọi cặp khung nó cần — rất dễ sai và không thể tái sử dụng. TF2 chuẩn hoá việc này thành một dịch vụ tra cứu chung cho toàn hệ thống.
Nguyên lý hoạt động
Phát transform tĩnh giữa base_link và lidar_link (LiDAR lắp cố định, cách tâm robot 0,1m theo trục X):
ros2 run tf2_ros static_transform_publisher 0.1 0 0.15 0 0 0 base_link lidar_link
Tra cứu transform giữa hai khung bất kỳ, kể cả khi không nối trực tiếp — TF2 tự tính chuỗi trung gian:
from tf2_ros import Buffer, TransformListener
tf_buffer = Buffer()
listener = TransformListener(tf_buffer, self)
transform = tf_buffer.lookup_transform('map', 'lidar_link', rclpy.time.Time())
# TF2 tự nhân dồn: lidar_link → base_link → odom → map
Xem trực quan toàn bộ cây TF hiện tại của hệ thống — công cụ debug gần như bắt buộc khi robot "hành xử lạ":
ros2 run tf2_tools view_frames # xuất ra file PDF vẽ toàn bộ cây TF
Kiến thức kỹ thuật
Công thức
Mỗi transform trong TF2 gồm 1 phép tịnh tiến (translation) + 1 phép xoay (rotation, biểu diễn bằng quaternion) — về bản chất toán học là 1 ma trận biến đổi đồng nhất 4×4. Nhân dồn nhiều transform liên tiếp trong chuỗi tương đương nhân các ma trận đó lại với nhau:
T(A→C) = T(A→B) × T(B→C)
VD đơn giản hoá (chỉ tịnh tiến theo trục X, bỏ qua xoay):
base_link → lidar_link: dịch +0.1m
odom → base_link: robot đang ở vị trí +2.0m
→ odom → lidar_link: 2.0 + 0.1 = 2.1m
Đây là lý do TF2 luôn cần đi đúng chuỗi các khung trung gian — không thể "nhảy cóc" tính trực tiếp từ khung này sang khung kia nếu chưa biết đủ các transform trên đường đi.
Thông số
Tần suất publish khác nhau tuỳ loại transform trong cây TF — transform động cần tần suất đủ cao để tránh nội suy sai lệch:
| Loại transform | Tần suất publish điển hình |
|---|---|
odom → base_link (từ odometry) | 20 – 50Hz |
map → odom (từ AMCL/SLAM) | 1 – 10Hz (chậm hơn nhiều, vì tính toán nặng hơn) |
base_link → cảm biến (tĩnh, từ URDF) | Publish 1 lần, giữ nguyên |
Protocol
Ở tầng dưới, TF2 chỉ đơn giản là 2 topic ROS2 thông thường: /tf (cho transform động, publish liên tục) và /tf_static (cho transform tĩnh, dùng QoS Transient Local đã học ở bài QoS — đảm bảo node khởi động sau vẫn nhận được ngay lập tức thay vì phải chờ, vì transform tĩnh thường chỉ publish đúng 1 lần lúc khởi động). Đây là ví dụ thực tế rõ ràng nhất cho lý do tồn tại của Durability QoS: nếu /tf_static dùng Volatile như mặc định, mọi node khởi động sau sẽ không bao giờ biết được vị trí lắp đặt của cảm biến.
Ứng dụng thực tế
Khi camera phát hiện một vật cản ở toạ độ cục bộ trong khung camera_link (VD: "vật thể cách camera 1.5m, lệch trái 0.3m"), node xử lý vật cản cần dùng TF2 để quy đổi toạ độ đó sang khung map toàn cục trước khi đưa vào bản đồ chi phí (costmap) của Nav2 — nếu không quy đổi đúng, vị trí vật cản trên bản đồ sẽ sai lệch theo đúng độ lệch giữa vị trí lắp camera và tâm robot, khiến robot né tránh sai chỗ hoặc không né vật cản thực sự đang ở đó.
Ví dụ thực tế
$ ros2 run tf2_ros tf2_echo base_link lidar_link
Output:
At time 1723000000.123456789
- Translation: [0.100, 0.000, 0.150]
- Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]
Translation: [0.100, 0.000, 0.150] xác nhận đúng transform tĩnh đã publish ở ví dụ trên — LiDAR cách tâm robot 0.1m theo X và 0.15m theo Z (cao hơn), rotation là quaternion đơn vị (không xoay). tf2_echo là công cụ nhanh nhất để xác nhận 1 transform cụ thể có tồn tại và đúng giá trị hay không, trước khi nghi ngờ code phức tạp hơn.
Lỗi thường gặp
- Lỗi ngoại suy thời gian (extrapolation error) — listener hỏi transform tại 1 mốc thời gian mà broadcaster chưa kịp publish (hoặc đã publish quá lâu trước, vượt "cửa sổ" bộ đệm TF2 lưu) — thường do 2 node chạy lệch tần suất lớn hoặc đồng hồ hệ thống không đồng bộ — dùng
rclpy.time.Time()(thời gian mới nhất có sẵn) thay vì thời gian cụ thể nếu không thực sự cần đồng bộ chính xác theo timestamp. - Quên publish transform tĩnh cho một cảm biến — node cố tra cứu
lookup_transformtới 1 khung chưa từng được broadcaster nào publish sẽ luôn thất bại (không có transform để tính) — kiểm tra bằngros2 run tf2_tools view_framesđể xác nhận khung đó có tồn tại trong cây TF hay không. - Một khung có nhiều hơn 1 khung cha — vi phạm cấu trúc cây bắt buộc của TF2 (mỗi khung chỉ 1 cha) — thường xảy ra khi 2 package khác nhau vô tình cùng publish transform cho cùng 1 khung con, gây lookup mơ hồ/không xác định.
- Đồng hồ hệ thống lệch nhau giữa nhiều máy tính cùng đóng góp vào 1 cây TF — nếu robot có nhiều máy tính onboard (VD: Jetson và máy tính công nghiệp) không đồng bộ thời gian (qua NTP/chrony), timestamp trên các transform từ các máy khác nhau có thể lệch nhau, gây lỗi ngoại suy dù tần suất publish hoàn toàn bình thường.
Kết luận
TF2 là hệ thống nền tảng giải quyết bài toán "điểm này ở đâu trong khung toạ độ kia" cho toàn bộ hệ thống robot — dựa trên cấu trúc cây và cơ chế nhân dồn transform tự động, chạy trên chính 2 topic ROS2 quen thuộc (/tf, /tf_static). Gần như mọi package nhận thức không gian (SLAM, Nav2, xử lý point cloud) đều phụ thuộc trực tiếp vào TF2 hoạt động đúng. Đây cũng là bài cuối cùng của chuyên mục ROS2 Communication — đủ nền tảng kỹ thuật để chuyển sang chuyên mục ROS2 Package, nơi học cách đóng gói các node và transform này thành package hoàn chỉnh.