第2章 TF树与坐标帧

说到 TF,很多人第一反应就是「坐标变换」。但在我看来,TF 树才是整个机器人空间认知的骨架。没有这棵树,你连激光雷达的数据该往哪儿投影都搞不清。今天我们就来把这棵树掰开揉碎,聊聊父帧、子帧,以及静态帧和动态帧到底怎么用。

2.1 TF树的概念

TF 树本质上是一个有向无环图,每个节点代表一个坐标帧,每条边代表一个变换关系。说白了,它就是机器人世界里所有「我在哪、你又在哪」的家族谱系。

我习惯把 TF 树想象成家谱:map 是老祖宗,odom 是儿子,base_link 是孙子,laser_frame 是重孙子。每个节点只能有一个父帧,但可以有多个子帧。嗯,这里要注意——不能出现环路,否则 TF 会直接罢工。

map odom odom2 base_link laser camera wheel 根帧 (map) 中间帧 (odom) 叶子帧 (传感器/执行器) 父帧 → 子帧 (有向边)

上面这张图就是一个典型的机器人 TF 树。你会发现 map 是根,下面挂着 odom,再往下才是真正的机器人本体和传感器。我个人习惯把这种结构叫做「三层架构」——全局、里程计、本体。你想想看,如果没有这棵树,导航的时候你连「激光扫描的点相对于谁」都说不清楚。

2.2 父帧与子帧

在 TF 树里,父帧是参考的基准,子帧是相对于父帧定义的。比如 base_link 相对于 odom 的变换,就描述了机器人在地图上的位置。

我记得有一次调试多传感器融合,发现激光雷达的点云总是歪的。查了半天,原来是 laser_frame 的父帧设成了 map,而不是 base_link。这就是典型的父帧选错——激光雷达是固定在机器人身上的,它的父帧应该是 base_link,而不是直接连到地图。

🔑 核心原则: 父帧应该是「更稳定、更全局」的坐标系。子帧是「依附于父帧」的局部坐标系。路径规划中,全局路径通常用 map 帧,局部路径用 odom 或 base_link 帧。

来看一个简单的 TF 广播代码片段,感受一下父子关系:

// 静态TF广播:base_link → laser_frame
geometry_msgs::TransformStamped t;
t.header.frame_id = "base_link";      // 父帧
t.child_frame_id  = "laser_frame";    // 子帧
t.transform.translation.x = 0.2;
t.transform.translation.y = 0.0;
t.transform.translation.z = 0.4;
// ... 四元数设置
static_broadcaster.sendTransform(t);

你看,父帧 base_link 是车体中心,子帧 laser_frame 是激光雷达的安装位置。路径规划里,代价地图需要把激光数据从 laser_frame 变换到 map,中间就要经过 base_link 和 odom 的层层变换。这就是 TF 树存在的意义。

2.3 静态坐标帧 vs 动态坐标帧

搞清楚了父子关系,接下来要区分「动」与「静」。这俩概念在路径规划里特别重要,搞混了轻则警告刷屏,重则导航直接崩溃。

类型特点典型帧更新频率
静态坐标帧相对父帧固定不变,通常只发布一次laser_frame, camera_link, base_footprint仅启动时发布,或极低频率
动态坐标帧随时间变化,需要高频更新odom → base_link, base_link → gripper10~100 Hz

静态帧说白了就是「焊死」的关系。比如激光雷达装在机器人前方 0.2 米,这个偏移量不会变。我一般用 static_transform_publisher 或者写一个节点只发布一次。动态帧则相反,比如里程计到机器人本体的变换,每时每刻都在变。

⚠️ 我曾经踩过的坑: 有一次我把 base_link 到 laser_frame 的变换做成了动态发布,每 10ms 发布一次完全相同的变换。结果 TF 缓存里塞满了冗余数据,导致路径规划延迟飙升。后来改成静态广播,CPU 占用直接降了 30%。记住:能静态就别动态。

你可能会问:路径规划里怎么用?举个例子,全局规划器在 map 帧下规划路径,但机器人实际控制需要 base_link 帧下的速度指令。这中间的变换链就依赖 TF 树——map → odom (动态) → base_link (动态) → laser_frame (静态)。静态帧只参与一次变换,动态帧每周期都要重新计算。

💡 个人小技巧: 在 launch 文件中,我习惯把所有传感器帧都写成静态 TF,用 arg 传参。这样换传感器时只需要改参数,不用动代码。比如:
<node pkg="tf2_ros" type="static_transform_publisher" args="0.2 0 0.4 0 0 0 base_link laser_frame" />

2.4 为什么路径规划离不开TF树?

说白了,路径规划的本质是「在某个参考系下找一条无碰撞的路径」。但机器人身上的传感器、执行器、规划器各自使用不同的坐标系。没有 TF 树,你连「障碍物在机器人左边还是右边」都说不准。

我记得之前做差速小车导航,局部规划器总是往墙上撞。用 tf_echo 一看,原来是 base_link 到 odom 的变换延迟了 200ms,导致规划器以为机器人还在原地。这就是动态帧不及时更新的典型问题。

所以,理解 TF 树、选对父帧、区分静动态,是路径规划稳定的地基。