基于 ROS2 通信机制的局域网通信
目标: 基于 ROS2 通信机制设计并实现一个局域网通信系统,包括:
- 局域网广播通信(同一子网内所有机器互相发现)
- 点对点通信(精确隔离,仅指定机器之间互相发现)
教程级别: 中等
时间: 45 分钟
1. 什么是局域网通信
局域网(LAN, Local Area Network)指覆盖范围较小的计算机网络,例如学校机房、实验室或家庭网络,所有设备共享同一个 IP 子网(如 192.168.1.0/24)。
局域网内的通信主要有两种模式:
| 模式 | 说明 | 典型场景 |
|---|---|---|
| 广播(Broadcast) | 一台机器发送,局域网内所有机器均可收到 | 服务发现、多机器人状态同步 |
| 点对点(Unicast / P2P) | 一台机器发送,只有指定的另一台机器能收到 | 远程控制指令、隐私数据传输 |
在多机器人系统中,两种模式各有用途:广播适合"集群通知",点对点适合"一对一任务分配"或隔离秘密频道,避免其他机器人干扰。
2. 业界通常如何进行局域网通信
局域网通信有多种技术方案,以下是工业和学术界常见的几种:
| 方案 | 底层协议 | 是否需要中间件 | 典型场景 | 优缺点 |
|---|---|---|---|---|
| UDP Socket 广播 | UDP Broadcast | 否 | 简单设备发现、游戏局域网 | 简单快速,但不可靠,无连接管理 |
| TCP Socket | TCP | 否 | 文件传输、指令传输 | 可靠,但需手写协议和发现机制 |
| MQTT | TCP + Broker | 需 Broker 进程 | IoT 设备消息分发 | 轻量,但需独立 Broker,单点故障风险 |
| DDS(ROS2 底层) | UDP Multicast/Unicast | 需 RMW 中间件 | 机器人分布式系统 | 自动发现、QoS 丰富,与 ROS2 原生集成 |
ROS2 基于 DDS(Data Distribution Service) 标准构建通信层,因此它天然支持局域网通信,只需配置少量环境变量即可实现广播和点对点两种模式,无需手写 Socket 代码。
3. ROS2 实现局域网通信的基本思路
3.1 DDS 中间件简介
DDS 是一套"以数据为中心"的发布/订阅通信标准。ROS2 通过 RMW(ROS Middleware) 层抽象出不同的 DDS 实现,目前内置支持以下几种:
| RMW 实现 | DDS 实现 | 安装方式 | 备注 |
|---|---|---|---|
rmw_fastrtps_cpp | eProsima FastDDS | ROS2 默认安装 | ROS2 Kilted 默认 RMW |
rmw_cyclonedds_cpp | Eclipse CycloneDDS | 需额外安装 | 性能优秀 |
rmw_zenoh_cpp | Zenoh | ROS2 Kilted 内置 | 新协议,跨网络友好 |
3.2 广播通信:ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET
ROS2 默认仅在本机(localhost)范围内做 DDS 发现。要打开局域网广播,需要设置两个环境变量:
export ROS_DOMAIN_ID=42 # 逻辑隔离 ID,同一局域网内相同才能互通
export ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET # 将发现范围扩展到整个子网
ROS_DOMAIN_ID:取值 0–232,相当于"频道编号",不同 ID 的节点互相不可见。ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET:启用 DDS 多播(Multicast),局域网内所有设置了相同DOMAIN_ID的节点自动互相发现,无需知道对方 IP。
3.3 点对点通信:FastDDS Discovery Server
默认的 DDS 广播发现意味着局域网内任何人都能看到你的节点。当需要精确控制哪些机器之间可以互相通信时,可以使用 FastDDS Discovery Server。
工作原理:
- 一台机器(机器 A)启动 Discovery Server 进程,监听指定端口。
- 其他机器设置
ROS_DISCOVERY_SERVER=<机器A的IP>:11811,只向该 Server 注册自己。 - 未注册到同一 Server 的机器无法发现这些节点,实现物理隔离。
# 机器A:指向本机 Server
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
# 机器B:指向机器A的 Server
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DISCOVERY_SERVER="192.168.1.100:11811"
3.4 四种 P2P 方案横向对比
ROS2 生态中实现点对点/隔离发现有以下几种方案,供参考:
| 方案 | RMW 要求 | 额外进程 | 配置方式 | CLI 友好度 |
|---|---|---|---|---|
| FastDDS Discovery Server(本课选用) | rmw_fastrtps_cpp(默认) | 需要 1 个 Server 进程 | ROS_DISCOVERY_SERVER | ★★★★ |
| CycloneDDS Unicast 配置 | rmw_cyclonedds_cpp | 不需要 | XML 文件 | ★★★ |
| Zenoh Router | rmw_zenoh_cpp | 需要 1 个 Router 进程 | 自动 | ★★★★ |
| ROS_STATIC_PEERS | 与 RMW 无关 | 不需要 | 环境变量 | ★★★★★ |
本课程选用 FastDDS Discovery Server,因为它是官方文档最完整的方案,且使用 ROS2 默认 RMW,无需额外安装。
猜猜我为啥没有使用 ROS_STATIC_PEERS 方案?
4. 实验:局域网通信
4.1 实验准备
实验架构:
局域网 192.168.1.0/24
│
├── 机器A 192.168.1.100 ← 广播发布者 / P2P Discovery Server
├── 机器B 192.168.1.101 ← 广播订阅者 / P2P 客户端
└── 机器C 192.168.1.102 ← 旁观者(用于验证 P2P 隔离性)
如果你只有两台机器,可以跳过机器 C 的隔离验证步骤,或用单机的两个终端模拟广播实验。
步骤 1:验证三台机器在同一子网
在每台机器上运行:
ip addr show | grep "inet "
确认三台机器的 IP 均在 192.168.1.x 段,子网掩码 /24。
步骤 2:测试网络连通性
从机器 B ping 机器 A:
ping -c 4 192.168.1.100
从机器 C ping 机器 A:
ping -c 4 192.168.1.100
全部 ping 通后再继续。
步骤 3:验证多播可达(广播实验前置条件)
在机器 B 开一个终端,等待接收多播包:
source /opt/ros/kilted/setup.bash
ros2 multicast receive
在机器 A 开一个终端,发送多播包:
source /opt/ros/kilted/setup.bash
ros2 multicast send
如果机器 B 的终端显示收到消息,说明局域网多播正常,可以进行广播通信实验。
如果 ros2 multicast receive 没有收到消息,原因通常是:
- 防火墙阻断了 UDP 多播(
sudo ufw disable临时关闭测试) - 路由器禁止多播(更换交换机直连或同一交换机下的机器)
- 网络接口不同(机器有多块网卡时,用
ros2 multicast send --transient指定接口)
步骤 4:检查当前 ROS2 环境变量
在每台机器上运行:
printenv | grep -i ROS
确保没有残留的旧 ROS_DOMAIN_ID、ROS_DISCOVERY_SERVER 变量。如有,重新开一个终端或用 unset 清除。
4.2 广播通信实验
广播通信让局域网内所有机器都能互相发现和订阅话题,无需知道对方 IP。
步骤 1:创建广播环境配置脚本
在每台机器上创建文件 setup_broadcast.sh,内容如下:
#!/usr/bin/env bash
source /opt/ros/kilted/setup.bash
export ROS_DOMAIN_ID=42
export ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET
echo "[广播模式] DOMAIN_ID=$ROS_DOMAIN_ID RANGE=$ROS_AUTOMATIC_DISCOVERY_RANGE"
将此脚本放在家目录 ~/setup_broadcast.sh,每次实验时用 source ~/setup_broadcast.sh 载入当前终端,不要直接执行(./setup_broadcast.sh),否则 export 的变量不会保留在当前 shell 中。
步骤 2:两台机器分别 source 配置脚本
机器 A 和 机器 B 各开一个终端:
source ~/setup_broadcast.sh
步骤 3:机器 A 发布广播话题
ros2 topic pub /lan_broadcast std_msgs/msg/String \
'{data: "Hello from Machine-A!"}' \
--rate 1
步骤 4:机器 B 订阅广播话题
ros2 topic echo /lan_broadcast
成功时,机器 B 终端会每秒打印一条消息:
data: Hello from Machine-A!
---
data: Hello from Machine-A!
---
步骤 5:验证节点互相可见
在机器 B 依次运行以下三条命令:
ros2 node list
ros2 node list --all
ros2 topic list
ros2 topic hz /lan_broadcast
ros2 node list 与 ros2 node list --all 的区别ros2 topic pub 和 ros2 topic echo 在内部创建节点时,节点名以下划线开头(如 /_ros2cli_12345)。ROS2 约定首字符为 _ 的节点为隐藏节点:
ros2 node list(不加参数):只显示普通节点,隐藏节点被过滤掉 → 输出为空是正常的。ros2 node list --all(或-a):显示所有节点,包括 CLI 工具创建的隐藏节点 → 可以看到机器 A 和机器 B 各自的匿名节点。
ros2 topic list 没有这个过滤逻辑,话题只要有发布者/订阅者就可见,因此即使 ros2 node list 输出为空,话题也能正常显示。
ros2 node list --all 应同时列出机器 A 和机器 B 的匿名节点;ros2 topic hz /lan_broadcast 应显示约 1 Hz 的频率。
步骤 6(可选):机器 C 验证广播可接收
机器 C 同样 source setup_broadcast.sh 后运行:
ros2 topic echo /lan_broadcast
机器 C 也能收到消息,说明广播对整个子网开放。
4.3 点对点通信(FastDDS Discovery Server)
提示:点对点通信需要额外的 Discovery Server 进程,且配置更复杂一些。建议先完成广播实验,熟悉环境变量和基本通信后再进行 P2P 实验。
点对点通信让只有连接到同一 Discovery Server 的机器才能互相发现,机器 C 虽在同一局域网,但不向该 Server 注册,因此无法看到 A/B 的节点。
步骤 1:机器 A 启动 Discovery Server
在机器 A 的**专用终端(终端 1)**运行:
source /opt/ros/kilted/setup.bash
fastdds discovery -i 0 -l 0.0.0.0 -p 11811
参数说明:
-i 0:ServerH ID,从 0 开始编号-l 0.0.0.0:监听所有网络接口(局域网可达)-p 11811:监听端口(默认端口,可自定义)
成功后终端会持续打印 Server 运行日志,保持此终端开启。
步骤 2:创建 P2P 配置脚本
为了让机器 A 和机器 B 都能连上同一个 Discovery Server,我们需要在两台机器上创建相同的配置脚本。
在机器 A 和机器 B 上分别创建文件 setup_p2p.sh,内容如下(请将 192.168.1.100 替换为 机器 A 的实际局域网 IP):
#!/usr/bin/env bash
source /opt/ros/kilted/setup.bash
# 清理可能残留的广播环境变量
unset ROS_AUTOMATIC_DISCOVERY_RANGE
# 设置 P2P 所需环境变量,指向机器 A 的局域网 IP
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DISCOVERY_SERVER="192.168.1.100:11811" # 修改成注册主机的实际地址
export ROS_DOMAIN_ID=44
echo "[P2P 模式已生效] 当前指向 SERVER: $ROS_DISCOVERY_SERVER"
步骤 3:机器 A 发布 P2P 话题
在机器 A 上新开一个终端(终端 2),载入脚本并开始发布:
source ~/setup_p2p.sh
ros2 topic pub /p2p_channel std_msgs/msg/String \
'{data: "Private message via P2P"}' \
--rate 1
步骤 4:机器 B 订阅 P2P 话题
在机器 B 上开一个新终端(终端 3),载入脚本并订阅:
source ~/setup_p2p.sh
printenv | grep -E "RMW|DISCOVERY|DOMAIN" # 先确认变量注入成功
ros2 topic list # 确认能看到 /p2p_channel
# 显式指定类型订阅,跳过等待 DDS 图就绪的时间
ros2 topic echo /p2p_channel std_msgs/msg/String
成功时,你能看到:
data: Private message via P2P
---
std_msgs/msg/String?如果不带类型直接 ros2 topic echo /p2p_channel,CLI 会先向 Discovery Server 查询这个话题的消息类型。因为节点注册有几秒延迟,如果查得太早会报错 WARNING: topic does not appear to be published yet。显式传类型可以绕过查询直接建立订阅通道。
步骤 5:机器 C 验证隔离性
机器 C 作为一个"路人"节点,不 source setup_p2p.sh,仅保持默认的 ROS2 状态或广播状态:
source /opt/ros/kilted/setup.bash
export ROS_DOMAIN_ID=42
export ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET # 开启广播
ros2 node list
机器 C 的 ros2 node list 不显示通过 P2P 机制通信的 A 和 B 的节点。因为 A 和 B 取消了局域网多播广播,只向 Discovery Server(终端 1)汇报存活状态,成功实现了"秘密通信"。
步骤 7:ros2 service call 跨机测试(扩展)
确认 P2P 连接后,还可以在机器 B 调用机器 A 上由 turtlesim 等提供的服务,进一步验证 P2P 的双向通信能力:
# 机器 A 开一个终端(source P2P 脚本后)
ros2 run turtlesim turtlesim_node
# 机器 B 查看跨机服务列表
ros2 service list
# 机器 B 调用服务
ros2 service call /clear std_srvs/srv/Empty {}
ros2 service call /reset std_srvs/srv/Empty {}
8. 拓展思考
- 如果用 ROS2 的通信机制实现一个"聊天群",需要哪些功能?
受保护的内容
请输入密码以查看此内容
- 仅依靠 ROS2 能否完整实现聊天群?其局限性是什么?
受保护的内容
请输入密码以查看此内容
9. 课程总结与作业
总结
| 对比项 | 广播通信(SUBNET 模式) | 点对点通信(Discovery Server 模式) |
|---|---|---|
| 关键环境变量 | ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET | ROS_DISCOVERY_SERVER=<IP>:<PORT> |
| 节点发现方式 | DDS 多播,自动广播 | 集中注册,按需发现 |
| 是否需要额外进程 | 否 | 是(fastdds discovery) |
| 隔离性 | 弱(同 Domain ID 均可见) | 强(未注册 Server 则不可见) |
| 配置复杂度 | 低 | 中 |
| 典型用途 | 多机器人状态共享、调试 | 隐私通道、定向控制 |
两种模式可以共存:在同一个系统中,部分节点走广播(状态广播),部分节点走 Discovery Server(控制通道),按需选择。
作业
作业 1(必做):三机广播通信
在 3 台机器上配置广播通信,使机器 A 发布 /team_status 话题,机器 B 和 机器 C 同时订阅并接收消息。
提交内容:
- 三台机器上
printenv | grep -i ROS的截图(证明环境变量配置正确) - 机器 B 和 机器 C 上
ros2 topic echo /team_status的截图 - 在机器 B 上运行
ros2 topic hz /team_status的截图
作业 2(选做):P2P 通信与隔离验证
配置机器 A 和 机器 B 进行 P2P 通信,同时验证机器 C 无法发现 A/B 的节点。
提交内容:
- 机器 A 上
fastdds discovery运行截图 - 机器 B 上
ros2 topic echo /p2p_channel收到消息的截图 - 机器 C 上
ros2 node list输出为空(或不含 A/B 节点)的截图,并附上机器 C 当时的环境变量截图证明它确实在同一局域网和相同ROS_DOMAIN_ID