ROS 2 + RViz2 出现两个机器人模型交替显示:一次由 ROS_DOMAIN_ID 引起的“幽灵模型”排查记录
环境:Ubuntu + ROS 2 Jazzy + RViz2
场景:使用robot_state_publisher加载 URDF,并在 RViz2 中通过RobotModel查看机械臂模型。这次问题非常具有迷惑性:URDF、STL、RViz 配置都没有明显错误,但 RViz2 中却会出现两套不同的机器人模型,甚至一个
RobotModel也会在两套模型之间交替显示。最终发现,真正原因是:局域网中另一台 ROS 2 设备与本机处于同一个 DDS Domain,并且也在发布
/robot_description。这里记录完整排查过程,希望能给遇到类似问题的人一个参考。
1. 问题描述
我使用 robot_state_publisher 加载一个由 SolidWorks 导出的 URDF,并在 RViz2 中显示机械臂。
正常情况下,希望看到的机器人结构大致为:
base_link
└── link_1
└── link_2
└── link_3
└── link_4
└── link_5
└── link_6
但是实际运行过程中出现了非常奇怪的现象:
- 有时 RViz2 显示正确的 SolidWorks 模型;
- 有时却出现另一套带有
shoulder、elbow、forearm等命名的模型; - STL 有时候看起来像是“加载错了”;
- 只有一个
RobotModel时,两套模型甚至会交替出现; - 手动再 Add 一个
RobotModel后,有时两套机器人能够同时出现,但显示结果仍然不稳定。
经过逐项排查后,可以确认以下问题都不是本次异常的原因:
- URDF 中不存在多余的
shoulder、elbow、forearm等 link; - STL 文件本身不会生成额外的 ROS TF 坐标系;
colcon build、--symlink-install和install/中的文件均正常;- RViz2 配置中并没有重复保存两个
RobotModel; - 当前加载的
robot_description也是正确的 URDF; - 问题也不是由 TF tree 本身或 SolidWorks 导出造成的。
排除这些常见原因后,真正值得关注的现象是:只有一个 RobotModel 时,两套模型仍会交替出现。
这说明问题更可能出在 /robot_description 的数据来源,而不是模型文件本身。
2. 检查过程
排除 URDF、STL、RViz 配置和本地 build 问题后,排查过程其实可以很快缩小到 /robot_description。
2.1 检查 /robot_description 的发布者
执行:
ros2 topic info /robot_description --verbose
结果发现:
Publisher count: 2
而且两个 publisher 都叫:
Node name: robot_state_publisher
Node namespace: /
但它们的 GID 不同,说明它们实际上是两个不同的发布端点。
这一步基本解释了为什么一个 RViz2 RobotModel 也会在两套机器人之间切换:它订阅的是同一个 /robot_description,但这个 topic 同时存在两份不同的数据源。
robot_state_publisher A ─┐
├── /robot_description ──> RViz2 RobotModel
robot_state_publisher B ─┘
2.2 检查第二个 publisher 是否在本机
随后检查本机进程:
pgrep -af robot_state_publisher
结果本机只有一个 robot_state_publisher。
于是出现了一个非常关键的现象:
ROS graph:Publisher count = 2
本机进程:robot_state_publisher = 1
因此可以判断:另一个 publisher 很可能来自局域网中的另一台 ROS 2 设备,而不是当前电脑。
2.3 修改 ROS_DOMAIN_ID 验证
为了验证这一判断,临时切换 ROS 2 Domain:
export ROS_DOMAIN_ID=87
ros2 daemon stop
然后重新启动自己的 ROS 2 launch,再次执行:
ros2 topic info /robot_description --verbose
此时结果变为:
Publisher count: 1
同时 RViz2 中只剩下正确的机器人模型,交替显示的问题完全消失。
至此可以确认:
第二个
/robot_descriptionpublisher 来自同一局域网、同一 ROS Domain 中的另一台 ROS 2 设备。
3. 结果
真正的原因是:
局域网中另一台 ROS 2 设备和本机处于相同的 ROS Domain,并且另一台设备也运行着一个名为
/robot_state_publisher的节点,同时发布/robot_description。
原来的情况可以理解为:
同一个局域网
│
ROS_DOMAIN_ID=0
│
┌────────────┴────────────┐
│ │
本机 另一台电脑
│ │
robot_state_publisher robot_state_publisher
│ │
└────────┐ ┌────────┘
▼ ▼
/robot_description
│
▼
RViz2
RobotModel
│
┌───────┴───────┐
▼ ▼
模型 A 模型 B
修改 Domain 后:
本机 另一台电脑
ROS_DOMAIN_ID=87 ROS_DOMAIN_ID=0
│ │
robot_state_publisher robot_state_publisher
│ │
X──────── 无法发现 ────────────X
因此:
/robot_description
只剩下一个 publisher,RViz2 也就稳定了。
永久设置 ROS_DOMAIN_ID
如果希望以后都使用某个固定 Domain,可以修改:
nano ~/.bashrc
加入:
export ROS_DOMAIN_ID=87
然后:
source ~/.bashrc
检查:
echo $ROS_DOMAIN_ID
例如:
87
需要注意:
87并不是 ROS 2 中的特殊数字,只是自己选择的 Domain ID。
同一套 ROS 2 系统中的多台设备应该使用相同 ID。
例如:
机械臂系统 A
视觉 PC:
ROS_DOMAIN_ID=23
轨迹规划 PC:
ROS_DOMAIN_ID=23
控制 PC:
ROS_DOMAIN_ID=23
这样三台机器可以互相发现。
而另外一套实验系统可以使用:
ROS_DOMAIN_ID=42
实现逻辑隔离。
4. 解释
4.1 为什么 ROS 2 会出现这种现象?
这其实并不是 ROS 2 的 bug。
它来自 ROS 2 一个非常重要的设计特性:
ROS 2 是一个支持去中心化、分布式自动发现的通信系统。
ROS 2 通常通过 DDS 进行节点发现和数据通信。
因此,同一个网络中,如果两台机器:
PC A:
ROS_DOMAIN_ID=0
PC B:
ROS_DOMAIN_ID=0
并且 DDS discovery 能够互相发现,那么:
PC A ←──── DDS Discovery ────→ PC B
两台机器上的 ROS 2 节点就可能进入同一个 ROS graph。
这也是为什么:
ros2 node list
不能简单理解成:
“查看本机启动了哪些进程”。
更加准确的理解应该是:
查看当前 ROS Domain 中被发现的 ROS 节点。
4.2 为什么两个节点可以同名?
这次还有一个非常迷惑的地方:
/robot_state_publisher
/robot_state_publisher
两个节点名字完全一样。
但是它们仍然可能对应不同的通信实体。
例如:
Node name: robot_state_publisher
GID: AAAA...
Node name: robot_state_publisher
GID: BBBB...
这里:
GID A != GID B
说明底层存在两个不同的 publisher endpoint。
因此:
节点名字一样
并不能说明:
实际上只有一个 publisher
4.3 为什么一个 Topic 可以有两个 Publisher?
ROS 2 的 publish-subscribe 模型天然允许:
Publisher A ─┐
│
Publisher B ─┼──→ /topic ───→ Subscriber
│
Publisher C ─┘
所以两个节点同时发布:
/robot_description
在 ROS 2 通信机制上并不违法。
问题在于:
这两个 publisher 发布的是两套完全不同的 URDF。
而 RViz2 中的 RobotModel 又订阅:
/robot_description
于是就形成:
Publisher A
│
├─────────┐
│ │
Publisher B │
│ │
└────┬────┘
▼
/robot_description
│
▼
RViz2
RobotModel
当 RobotModel 收到两套不同机器人描述时,就可能表现成:
模型 A
↓
模型 B
↓
模型 A
↓
...
这正是这次看到的“幽灵模型”。
4.4 ros2 daemon 是不是导致问题的原因?
不是。
ros2 daemon 主要是帮助 ROS 2 CLI 获取和维护 graph 信息。
例如:
ros2 node list
ros2 topic list
ros2 service list
会使用相关的 graph 信息。
真正导致另一台电脑进入当前 ROS graph 的核心是:
DDS discovery
+
相同的 ROS_DOMAIN_ID
而不是 daemon。
之所以切换 Domain 后经常执行:
ros2 daemon stop
主要是为了让 CLI 后台进程重新在新的环境配置下工作。
如果已经把:
export ROS_DOMAIN_ID=87
写进:
~/.bashrc
那么以后打开新的终端,通常不需要每次都:
ros2 daemon stop
4.5 一套比较实用的 ROS 2“幽灵节点”排查流程
以后如果再次遇到:
- RViz 莫名出现另一套机器人;
- 不属于自己的 TF;
- 一个 RobotModel 在不同模型之间切换;
- Topic 数据来源不稳定;
- 出现本机根本没有启动的 ROS node;
可以优先按照下面的顺序检查。
① 查看 ROS graph
ros2 node list
② 查看关键 Topic 的发布者
例如:
ros2 topic info /robot_description --verbose
重点关注:
Publisher count
Node name
Node namespace
GID
③ 检查 TF publisher
ros2 topic info /tf --verbose
ros2 topic info /tf_static --verbose
④ 检查本机进程
pgrep -af robot_state_publisher
如果:
ROS graph 中有两个 publisher
但是:
本机只有一个进程
那么就应该高度怀疑:
第二个 publisher 来自其他机器。
⑤ 查看 Domain
echo $ROS_DOMAIN_ID
⑥ 临时切换 Domain 做验证
export ROS_DOMAIN_ID=87
必要时:
ros2 daemon stop
然后重新启动自己的 ROS 2 系统。
如果:
ros2 topic info /robot_description --verbose
从:
Publisher count: 2
变成:
Publisher count: 1
问题基本就确认了。
最后总结
在 ROS 2 中,“本机运行了哪些节点”和“当前 ROS graph 中有哪些节点”是两个不同的问题。
ROS 2 本身就是面向分布式、多机通信设计的。
因此在多人共用局域网的实验室中,如果大家都使用默认 ROS Domain,就可能出现非常隐蔽的跨机器干扰。
上文借助Agent整理完成。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)