3 · 静态TF发布器
用 static_transform_publisher 发布固定坐标变换,比如激光雷达和底盘中心的关系。这玩意儿是导航里最基础的 TF 节点之一,但很多人一开始会忽略它的细节。
为什么需要静态TF?
说白了,机器人身上很多传感器和底盘之间的位置是固定的。激光雷达拧在支架上,IMU 焊在主板上,摄像头锁死在结构件上——这些变换不会随时间改变。你想想看,如果每次启动都要重新计算一遍,那多浪费算力。
我个人习惯把静态变换单独拎出来,用专门的发布器管理。这样 TF 树干净,排查问题也快。
核心用法:命令行一把梭
ROS 自带的 tf2_ros 包提供了 static_transform_publisher 节点。我最常用的就是命令行启动,一行搞定:
rosrun tf2_ros static_transform_publisher x y z yaw pitch roll frame_id child_frame_id
举个例子,激光雷达装在底盘前方 0.3 米,高度 0.15 米,完全水平:
rosrun tf2_ros static_transform_publisher 0.3 0 0.15 0 0 0 base_link laser
注意顺序:平移在前,旋转在后。旋转用的是 RPY(绕固定轴),单位是弧度。我见过有人把角度填成度数,结果雷达数据直接飞到天上去……
node 标签启动。这样团队里其他人一看就知道传感器是怎么装的。
launch 文件里的标准姿势
在实际项目中,我更推荐用 launch 文件来管理。下面这个例子是我在巡检机器人上用的:
<launch>
<node pkg="tf2_ros" type="static_transform_publisher" name="lidar_to_base"
args="0.28 0.0 0.18 0.0 0.0 0.0 base_link laser" />
<node pkg="tf2_ros" type="static_transform_publisher" name="imu_to_base"
args="0.0 0.0 0.05 0.0 0.0 0.0 base_link imu_link" />
</launch>
每个 node 的 name 要唯一,不然 ROS 会默默覆盖。嗯,这里要注意:如果你用相同的 name 启动两次,后面的会踢掉前面的,而且不会有任何报错。
用 Python 或 C++ 发布静态TF
有时候你需要更灵活的控制,比如从参数服务器读取偏移量。我一般用 Python 写个简单节点:
import rospy
import tf2_ros
import geometry_msgs.msg
if __name__ == '__main__':
rospy.init_node('my_static_tf_broadcaster')
broadcaster = tf2_ros.StaticTransformBroadcaster()
t = geometry_msgs.msg.TransformStamped()
t.header.frame_id = "base_link"
t.child_frame_id = "laser"
t.transform.translation.x = 0.3
t.transform.translation.y = 0.0
t.transform.translation.z = 0.15
t.transform.rotation.w = 1.0 # 无旋转
broadcaster.sendTransform(t)
rospy.spin()
这段代码发布一次就挂起等待。实际上 StaticTransformBroadcaster 内部会按一定间隔重发,确保后来的监听器也能收到。
静态TF vs 动态TF
很多新手会问:为什么不用普通的 TF 发布器?我简单对比一下:
| 特性 | static_transform_publisher | 普通 TF 发布器 |
|---|---|---|
| 发布频率 | 自动定期重发(约 100s) | 按需发布,通常 10-100 Hz |
| 资源占用 | 极低,几乎不占 CPU | 每次发布都要序列化 |
| 适用场景 | 传感器固定安装、车轮位置 | 关节运动、SLAM 位姿 |
| TF 树表现 | 静态边,不会跳动 | 动态边,随时间变化 |
说白了,如果你的变换是固定的,就别用动态发布器。我见过有人用 tf2_ros::TransformBroadcaster 在循环里发同样的变换,白白浪费带宽。
验证你的静态TF
发布之后怎么确认对不对?我最常用的三板斧:
tf_echo base_link laser—— 看平移旋转数值是否和预期一致。view_frames—— 生成 PDF 图,检查树结构是否正确。- 在 Rviz 里添加 TF 显示,看坐标系箭头是否在正确的位置。
有一次我同事说雷达数据总是偏左,我让他跑了一下 tf_echo,发现 y 偏移填了 0.1 而不是 0.0。这种低级错误,用工具一眼就能揪出来。
--clock,记得确保时间同步。否则 TF 可能会因为时间戳混乱而被忽略。
总结一下
静态 TF 发布器是导航调试的基石。你只要记住三点:
- 用
static_transform_publisher处理所有固定传感器。 - launch 文件里把 name 写清楚,方便以后维护。
- 发布完一定用
tf_echo或 Rviz 验证。
我自己的习惯是,每装一个新传感器,第一件事就是写一个静态 TF 并验证。这一步做扎实了,后面导航调参才能少踩坑。