第2章 TF树的基本概念
各位好,我是老周。今天咱们聊聊TF树——说白了,它就是机器人世界里所有坐标系的“家谱”。你想想看,一个机器人身上有激光雷达、摄像头、轮子、机械臂……每个部件都有自己的坐标系,怎么让它们知道彼此的位置?TF树就是干这个的。
2.1 TF树的定义
TF树是一个有向无环图,每个节点代表一个坐标系,每条边代表从父坐标系到子坐标系的变换关系。我习惯把它想象成一棵倒着长的树——根在最上面,枝干往下分。
在ROS里,TF树的核心作用就两个:
- 存储所有坐标系之间的相对位姿
- 提供任意两个坐标系之间的实时变换
举个例子。你有一个移动底盘,上面装了一台3D激光雷达。底盘坐标系叫base_link,雷达坐标系叫laser_frame。TF树里就会有一条边:base_link → laser_frame,边上挂着平移和旋转。这样,雷达扫描到的一个点,就能换算到底盘坐标系里。
2.2 父坐标系与子坐标系
每个TF变换都有方向:从父到子。父坐标系是“参考系”,子坐标系是“依附系”。比如你的身体是父坐标系,手里的手机是子坐标系——手机的位置是相对于身体描述的。
在ROS里,我们通常这样约定:
| 父坐标系 | 子坐标系 | 典型关系 |
|---|---|---|
base_link | laser_frame | 激光雷达安装在底盘上 |
base_link | camera_frame | 相机固定在底盘上方 |
odom | base_link | 里程计提供底盘位姿 |
map | odom | 地图坐标系下的里程计漂移修正 |
你可能会问:“为什么不能反过来?” 嗯,如果让laser_frame当父,base_link当子,逻辑上就反了——雷达不会背着底盘跑。TF树的设计必须符合直觉,否则调试时会疯掉。
base_link,但IMU实际装在车体顶部。结果每次车体倾斜,IMU数据转换出来都是错的。后来改成base_link → imu_link,一切正常。父与子,千万别搞反。
2.3 TF树的动态更新
机器人不是静止的。轮子转、关节动、相机云台摇头……TF树必须跟着变。动态更新就是TF树的生命力所在。
在ROS里,每个变换都带时间戳。TF树会维护一个时间缓存,默认存储10秒内的所有变换。当你查询“现在”的变换时,TF库会插值或外推,给你最准的结果。
动态更新的典型场景:
- 移动底盘:每帧里程计数据更新
odom → base_link - 机械臂关节:每个关节角度变化更新
link1 → link2 - 云台相机:根据编码器更新
base_link → camera_pan
我见过不少新手,在回调里只广播一次TF,然后就再也不发了。结果TF树里只有孤零零的一帧,一查询就报“Extrapolation Error”。说白了,动态更新就是要持续广播,频率最好和你的控制周期一致。
上面这张图展示了一个典型的机器人TF树。注意看,从map到odom到base_link再到传感器,每一层都是动态更新的。我习惯把map到odom的更新频率放低一些(因为定位修正不需要太快),但base_link到传感器一定要高频——毕竟控制周期在那摆着。
2.4 小结 · 我的一点心得
TF树的概念其实不复杂,但它是整个机器人坐标体系的骨架。我见过太多人栽在父/子关系搞反、忘记动态更新、或者时间戳乱填上。记住三点:
- 父在上,子在下,物理连接决定树结构
- 持续广播,让TF树活起来
- 时间戳要对,否则TF库会给你“惊喜”
嗯,今天就到这儿,咱们下次见。