
- [前言:为什么 /joint_states 如此重要](#前言:为什么 /joint_states 如此重要)
- [Gazebo 版本演进与 ROS2 兼容矩阵](#Gazebo 版本演进与 ROS2 兼容矩阵)
- [五种发布 /joint_states 的方式](#五种发布 /joint_states 的方式)
- [Gazebo 插件配置详解](#Gazebo 插件配置详解)
- [使用 rqt_plot 实时可视化](#使用 rqt_plot 实时可视化)
- 常见问题排查
- 总结与建议
前言:为什么 /joint_states 如此重要
在 ROS2 机器人系统中,/joint_states 是一个核心话题,它使用 sensor_msgs/msg/JointState 消息类型,承载着机器人所有关节的**位置(position)、速度(velocity)和力矩(effort)**信息。几乎所有上层应用都依赖它:
- robot_state_publisher :订阅
/joint_states,计算并发布 TF 变换树 - RViz2:通过 TF 树实现机器人模型的可视化运动
- 运动规划(MoveIt2):需要实时关节状态来做碰撞检测和轨迹跟踪
- rqt_plot:实时绘制关节曲线,用于调试和分析
然而,/joint_states 不会凭空出现------必须有节点主动发布。本文将系统梳理在不同场景下(有无 ros2_control、不同 Gazebo 版本)如何正确发布这个话题。
Gazebo 版本演进与 ROS2 兼容矩阵
Gazebo 仿真器经历了从 Gazebo Classic (数字编号版本)到 Gazebo(字母编号版本,原名 Ignition)的重大架构变革。理解这个演进是正确配置插件的前提。
Gazebo Classic vs 新版 Gazebo
| 特性 | Gazebo Classic(旧版) | Gazebo(新版) |
|---|---|---|
| 版本号 | Gazebo 9 / 11 | Fortress / Harmonic / Ionic 等 |
| 渲染引擎 | OGRE(较旧) | OGRE 2.x(支持光线追踪) |
| ROS 桥接 | gazebo_ros_pkgs |
ros_gz |
| 插件体系 | SDF + 自定义 C++ 插件 | SDFormat + 模块化架构 |
| 状态 | 已停止新功能开发 1 | 活跃开发,推荐新项目使用 2 |
ROS2 与 Gazebo 版本兼容矩阵
下表列出了当前受支持的 ROS2 与 Gazebo 组合 2:
| ROS2 版本 | Gz Fortress | Gz Garden | Gz Harmonic | Gz Ionic | Gazebo Classic 11 |
|---|---|---|---|---|---|
| Foxy (LTS) | - | - | - | - | Citadel |
| Humble (LTS) | 推荐 | 可用 | 可用 | - | Classic 11 |
| Iron | 推荐 | 可用 | 可用 | - | Classic 11 |
| Jazzy (LTS) | - | - | 推荐 | 可用 | 不支持 |
| Rolling | - | 可用 | 可用 | 推荐 | 不支持 |
注意 :Gazebo Classic 未发布到 Ubuntu Noble(24.04),因此
gazebo_ros2_control从未发布到 Jazzy 和 Rolling 1。如果你使用 Jazzy 或更新版本,必须使用新版 Gazebo(Harmonic/Ionic)。
查看你的 Gazebo 版本
bash
# Gazebo Classic
gazebo --version
# 新版 Gazebo
gz sim --versions
# 通过 apt 查看
dpkg -l | grep gazebo
dpkg -l | grep gz-sim
比如,我这里就是Ubuntu22.04+ ROS2 Humble + Gazebo11。
bash
wu@wu:~$ gazebo --version
Gazebo multi-robot simulator, version 11.10.2
Copyright (C) 2012 Open Source Robotics Foundation.
Released under the Apache 2 License.
http://gazebosim.org
五种发布 /joint_states 的方式
无论你使用哪种 Gazebo 版本,发布 /joint_states 的核心思路一致。下面按复杂度从低到高逐一介绍。
方式一:joint_state_publisher(静态值)
最简单的方式,直接读取 URDF 中定义的关节,发布固定初始值。
bash
ros2 run joint_state_publisher joint_state_publisher
特点:关节值固定不变,适合仅验证 URDF 模型和 RViz2 显示。
方式二:joint_state_publisher_gui(手动滑块)
带 Qt 界面,可以用滑块手动拖动各关节角度,实时发布到 /joint_states。
bash
ros2 run joint_state_publisher_gui joint_state_publisher_gui
特点 :无需真实硬件或仿真,适合调试和快速验证。需要安装 ros-<distro>-joint-state-publisher-gui。
方式三:Gazebo 插件(仿真场景,不用 ros2_control)
这是本文的重点。通过在 URDF 中嵌入 Gazebo 插件,让仿真引擎自动读取关节物理状态并发布。
核心优势 :不需要
ros2_control框架,配置简单,适合轻量级仿真项目。
配置方式因 Gazebo 版本而异,详见下一节。
方式四:ros2_control(工业级方案)
通过 gazebo_ros2_control 插件 + joint_state_broadcaster 控制器实现 3。
bash
# 查看已加载的控制器
ros2 control list_controllers
# 加载并激活 joint_state_broadcaster
ros2 control load_controller joint_state_broadcaster
ros2 control set_controller_state joint_state_broadcaster active
特点:功能最强大,支持轨迹跟踪、力矩控制等,但配置复杂,适合正式项目。
方式五:自定义节点(完全灵活)
自己写 Python 或 C++ 节点,手动构造 sensor_msgs/msg/JointState 消息并发布。
python
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState
class MyJointStatePublisher(Node):
def __init__(self):
super().__init__('my_joint_state_publisher')
self.pub = self.create_publisher(JointState, '/joint_states', 10)
self.timer = self.create_timer(0.02, self.publish) # 50Hz
self.names = ['joint1', 'joint2', 'joint3',
'joint4', 'joint5', 'joint6']
def publish(self):
msg = JointState()
msg.header.stamp = self.get_clock().now().to_msg()
msg.name = self.names
msg.position = [0.0] * 6 # 填入实际关节角度
msg.velocity = [0.0] * 6
msg.effort = [0.0] * 6
self.pub.publish(msg)
特点:最灵活,适合自研驱动、自定义算法等场景。
五种方式对比
| 方式 | 难度 | 数据来源 | 需要 Gazebo | 需要 ros2_control | 适用场景 |
|---|---|---|---|---|---|
| joint_state_publisher | 低 | URDF 静态值 | 否 | 否 | 验证 URDF / RViz |
| joint_state_publisher_gui | 低 | 手动滑块 | 否 | 否 | 调试、无硬件测试 |
| Gazebo 插件 | 中 | 仿真物理引擎 | 是 | 否 | 轻量级仿真 |
| ros2_control | 高 | 仿真/硬件 | 是 | 是 | 正式项目 |
| 自定义节点 | 中 | 自定义 | 否 | 否 | 自研驱动 |
Gazebo 插件配置详解
在不使用 ros2_control 的前提下,通过 Gazebo 插件发布 /joint_states 是最常用的仿真方案。不同 Gazebo 版本的插件配置写法有显著差异。
Gazebo Classic + ROS2(推荐 Humble/Iron 用户)
使用 libgazebo_ros_joint_state_publisher.so 插件,在 URDF 的 <robot> 标签内添加:
xml
<!-- 关节状态发布插件 -->
<gazebo>
<plugin name="joint_state_publisher"
filename="libgazebo_ros_joint_state_publisher.so">
<ros>
<namespace>/</namespace>
<remapping>~/out:=/joint_states</remapping>
</ros>
<update_rate>30</update_rate>
<joint_name>front_left_steering_joint</joint_name>
<joint_name>front_right_steering_joint</joint_name>
<joint_name>front_left_wheel_joint</joint_name>
<joint_name>front_right_wheel_joint</joint_name>
<joint_name>rear_left_wheel_joint</joint_name>
<joint_name>rear_right_wheel_joint</joint_name>
</plugin>
</gazebo>

关键点:
<ros>标签是 ROS2 版gazebo_ros_pkgs的标准格式<remapping>~/out:=/joint_states</remapping>将插件默认输出话题映射到/joint_states<update_rate>设置发布频率(Hz),建议 30-50Hz- 每个需要监控的关节都要通过
<joint_name>声明
Gazebo Classic + ROS1
ROS1 版本写法完全不同,没有 <ros> 标签:
xml
<gazebo>
<plugin name="joint_state_publisher"
filename="libgazebo_ros_joint_state_publisher.so">
<topicName>joint_states</topicName>
<update_rate>30</update_rate>
<joint_name>joint1</joint_name>
<joint_name>joint2</joint_name>
</plugin>
</gazebo>
注意 ROS1 直接用 <topicName> 指定话题名,不需要 <remapping>。
新版 Gazebo(Fortress/Harmonic/Ionic)+ ROS2
新版 Gazebo 使用 ros_gz 包替代 gazebo_ros_pkgs 4。关节状态发布通过 gz_ros2_control 或 ros_gz 桥接实现:
xml
<!-- 新版 Gazebo 使用 SDFormat + ros_gz 桥接 -->
<!-- 方式 A:通过 ros_gz 桥接(轻量级) -->
<gazebo>
<plugin
filename="gz-sim-joint-state-publisher-system"
name="gz::sim::systems::JointStatePublisher">
</plugin>
</gazebo>
<!-- 在 launch 文件中创建桥接 -->
<!-- ros2 run ros_gz bridge /model/{model_name}/joint_states@sensor_msgs/msg/JointState[ignition.msgs.JointState -->
注意 :新版 Gazebo 的插件体系完全不同。如果从 Gazebo Classic 迁移,需要参考官方迁移指南 4,URDF 中的
<gazebo>插件配置需要重写。
版本配置速查表
| 环境 | 插件文件名 | 配置标签风格 | 话题映射方式 |
|---|---|---|---|
| Classic + ROS2 | libgazebo_ros_joint_state_publisher.so |
<ros><remapping> |
<remapping>~/out:=/joint_states</remapping> |
| Classic + ROS1 | libgazebo_ros_joint_state_publisher.so |
<topicName> |
<topicName>joint_states</topicName> |
| 新版 Gazebo + ROS2 | gz-sim-joint-state-publisher-system |
SDFormat + ros_gz 桥接 |
launch 文件中 ros_gz bridge |
使用 rqt_plot 实时可视化
当 /joint_states 成功发布后,可以用 rqt_plot 实时绘制关节数据曲线。
安装
bash
# 安装 rqt_plot 及相关工具
sudo apt install ros-<distro>-rqt ros-<distro>-rqt-common-plugins ros-<distro>-rqt-plot
# 推荐安装更快的绘图后端
pip install pyqtgraph --break-system-packages
命令行启动
由于 position、velocity、effort 是数组,必须指定索引:
bash
# 绘制第 0 个关节的位置
ros2 run rqt_plot rqt_plot /joint_states/position[0]
# 同时绘制多个关节
ros2 run rqt_plot rqt_plot /joint_states/position[0] /joint_states/position[1] /joint_states/position[2]
# 绘制速度
ros2 run rqt_plot rqt_plot /joint_states/velocity[0] /joint_states/velocity[1]
GUI 交互式添加
bash
ros2 run rqt_plot rqt_plot
在界面顶部的 "Topic" 输入框中输入完整字段路径(如 /joint_states/position[0]),点击 + 按钮添加。输入框支持自动补全。
绘图后端选择
点击标题栏齿轮图标可切换后端:
| 后端 | 性能 | 安装方式 |
|---|---|---|
| pyqtgraph | 最快(推荐) | pip install pyqtgraph |
| matplotlib | 较慢 | 系统自带 |
| qwt plot | 快 | python3-qwt |
常见问题排查
ros2 topic echo /joint_states 没有输出
这是最常见的问题,按以下流程排查:
bash
# 1. 确认话题是否存在
ros2 topic list | grep joint_states
# 2. 确认是否有发布者
ros2 topic info /joint_states
# 3. 查看发布频率
ros2 topic hz /joint_states
# 4. 查看消息内容
ros2 topic echo /joint_states
排查思路:
- 话题不存在 → 没有节点发布,需要启动 joint_state_publisher 或加载控制器
- 有 Subscribers 但无 Publishers → 同上,需要启动发布节点
- 话题存在且有 Publishers 但无数据 → 检查 Gazebo 插件配置是否正确
rqt_plot 曲线不显示
- 数组未指定索引 :
/joint_states/position是数组,必须写成/joint_states/position[0] - 字段路径不完整:必须写到具体数值字段的完整路径
- 从 bag 回放 :需要使用仿真时间,加
--clock参数
Gazebo 插件加载失败
- 确认插件文件存在:
rospack find gazebo_ros或检查lib目录 - 确认
<joint_name>与 URDF 中定义的关节名完全一致(区分大小写) - 查看 Gazebo 终端输出的错误日志
总结与建议
如何选择方案
| 你的场景 | 推荐方案 |
|---|---|
| 快速验证 URDF / RViz 显示 | joint_state_publisher |
| 手动调试关节运动 | joint_state_publisher_gui |
| Gazebo Classic 仿真,轻量级项目 | libgazebo_ros_joint_state_publisher.so 插件 |
| Gazebo Classic 仿真,正式项目 | ros2_control + joint_state_broadcaster |
| 新版 Gazebo(Harmonic/Ionic) | ros_gz 桥接 或 gz_ros2_control |
| 自研硬件驱动 | 自定义 Python/C++ 节点 |
最后提醒 :无论选择哪种方案,核心都是确保
/joint_states话题上有数据在发布。养成ros2 topic echo /joint_states和ros2 topic hz /joint_states的排查习惯,可以快速定位 90% 的问题。
参考资料
- Open Robotics, gazebo_ros2_control --- 官方仓库文档。说明 Gazebo Classic 不再支持 Jazzy/Rolling。https://index.ros.org/r/gazebo_ros2_control/
- Gazebo 官方文档, Installing Gazebo with ROS --- ROS2 与 Gazebo 版本兼容矩阵。https://gazebosim.org/docs/ionic/ros_installation/
- ros2_control 官方文档, joint_state_broadcaster --- 通过控制器发布 /joint_states。https://control.ros.org/humble/doc/gazebo_ros2_control/doc/index.html
- Gazebo 官方文档, Migrating ROS 2 packages that use Gazebo Classic --- 从 Classic 迁移到新版 Gazebo 的指南。https://gazebosim.org/docs/latest/migrating_gazebo_classic_ros2_packages/
- ROS Wiki, rqt_plot --- rqt_plot 使用文档与语法说明。http://ftp.osuosl.org/.1/ros/ros_wiki_mirror/rqt_plot.html