返回专辑
·Johan·4 分钟阅读

robot_state_publisher:模型 TF 的单一真相源

RSP 专责 URDF 运动学;别与定位/Gazebo 抢边;joint 名与 sim_time 必须对齐。

robot_state_publisher:模型 TF 的单一真相源

1. RSP 管什么,不管什么

robot_state_publisher 吃 URDF(或 xacro 展开结果)+ sensor_msgs/JointState,发布模型运动学 TF——从 base_link 到各 link 的关节链。它该发 map→odomodom→base;那是定位栈的职责。与 Gazebo 插件、应用节点抢同一 child,树会出现多父或跳变,Nav2 表现为「代价地图偶尔整体平移」。

先画边归属:模型运动学归 RSP,全局定位归 SLAM/AMCL,外参静态归 static TF 或 URDF fixed joint。应用节点再广播 RSP 已有边「以防万一」,是结构性错误。

2. 启动与话题契约

典型 launch 参数:

bash
ros2 run robot_state_publisher robot_state_publisher \
  --ros-args -p robot_description:="$(xacro model.urdf.xacro)" \
  -p use_sim_time:=true

RSP 订阅 joint_states(默认可 remap)。没有 joint_states 时,非 fixed 关节停在 URDF 零位——costmap 仍可能规划,但几何是错的。bringup 应保证控制器或仿真桥在 RSP 之后持续发 joint 状态。

bash
ros2 topic hz /joint_states
ros2 run tf2_tools view_frames

3. joint 名称:集合必须相等

URDF 里的 joint 名与 joint_states.name 必须一致——拼写漂移(joint_1 vs joint1)会变成「树缺边」,看起来像导航坏了。CI 应用 xacro 与 joint_states 名称集做集合差:

python
urdf_joints = parse_movable_joints("model.urdf")
msg_joints = set(last_joint_state.name)
assert urdf_joints == msg_joints, urdf_joints ^ msg_joints

漏发某个关节:对应 link 冻在零位,机械臂碰撞检测会系统性漏障。多 DOF 系统还要检查 joint_states.position 长度与 name 等长——长度不匹配时 RSP 会忽略整条消息或部分关节,日志级别不够时完全静默。

4. 静态、fixed 与活动关节

URDF 里 type="fixed" 的关节由 RSP 在启动时作为静态 TF 发布(或通过 internal static 路径)。活动关节完全依赖 joint_states 的 position。传感器安装若用 fixed joint 表达,不要再 duplicate static broadcaster。

零位(URDF limit 内的 default)与标定零位不一致时,应回 URDF/标定修,别在导航膨胀参数里补几何——那是把 kinematics 债转成 planning 债。

5. use_sim_time 与 stamp 一致

仿真下 joint_states.header.stamp 必须跟 /clock,否则 RSP 与控制器时间域分裂,查找表现为间歇 future/past extrapolation。真机若有人把仿真 stamp 策略拷过来,同样症状。

yaml
# ros2_control 桥接示例:stamp 来自硬件读数时间
joint_state_broadcaster:
  ros__parameters:
    use_sim_time: true  # 仅仿真

ros2_control 的 joint_state_broadcaster 读出状态后发布 joint_states;controller manager 未 active 时,广播可能停或冻在旧值。launch 若让 Nav2 先于 controller 进入 active,会出现「前几秒臂还在零位但已在规划」的窗口——这不是 RSP bug,是 bringup 顺序债。

6. 与 Gazebo / 真机的差异

Gazebo Harmonic/Ignition 插件可能自己发部分 TF;真机由 RSP + 驱动栈发。bringup 文档写清「仿真谁发、真机谁发」,避免双源。切换仿真/真机不应改 frame 名——只改发布者归属。

xacro 参数化(多套臂长/轮距)时,RSP 读的 robot_description 必须与硬件清单一致;车队用 overlay yaml 选型号,不要手工改 URDF 片段。launch 里 $(find pkg) 与安装空间不一致时,RSP 可能加载旧 URDF——表现为「改了 xacro 但 TF 不变」,先用 ros2 param get /robot_state_publisher robot_description | head 确认实际加载内容。

7. bringup 顺序与验收

顺序:加载 robot_description → 启动 RSP → controller manager + joint_state_broadcaster active → 定位边就绪 → 导航激活。Composable 容器里 RSP 与 ros2_control 同进程时,仍要用 lifecycle 或事件保证 joint 源已 hz>0 再激活 Nav2。

bash
ros2 control list_controllers
ros2 topic hz /joint_states
  • view_frames 无环、无多父;活动关节运动时对应边更新,静止时不无故抖动。
  • joint_states 时诊断可见(hz 为零或 lifecycle 未 active),而不是静默零位继续规划。
  • 重复广播检测:同 child 多源,联调脚本应失败。
  • sim_time 回放 bag:RSP 与 joint 源 stamp 同域,TF 查询成功率达标。

8. 常见问题分诊

现象先查
缺整条 link 链URDF 是否加载、joint_states 是否 hz>0
关节冻在零位joint 名集合差、控制器是否 active
间歇 TF 跳变是否与定位/Gazebo 双源同一 child
sim 里 past extrapolationjoint_states stamp 是否跟 /clock

9. 案例:零位漂移进导航参数

某项目机械零位与 URDF 差 2°,现场在 costmap 膨胀上「微调」补碰撞——换型号后全失效。正确做法是改 URDF 零位或 joint 校准 offset,让 RSP 输出真实几何。导航参数不能偿还 kinematics 债。

10. 启动门禁

bringup 在导航 active 前检查:URDF 已加载、joint_states hz 达标、view_frames 无多父。任一项失败应阻断激活,而不是带着零位树规划。RSP 是模型几何的单一真相源——边界清晰,TF 问题少一半;边界模糊,录包也难抓双源那一帧。

← 全部文章

johan's blog