3、多机器人通信架构:分布式通信原理、DDS在ROS2中的应用、多机网络配置

好,咱们进入正题。多机器人协同,说白了就是让一群机器人能「说上话」。你想想看,如果每个机器人都是信息孤岛,那协同就无从谈起。这一章,我们就来拆解ROS2多机通信的底层逻辑和实战配置。

3.1 分布式通信原理:为什么ROS2抛弃了Master?

用过ROS1的朋友都知道,它有个中心节点叫Master。所有节点都得先找Master注册,才能互相发现。这在单机上没问题,但一旦到了多机器人场景,问题就来了——Master挂了,整个系统就瘫痪了。我在一个仓库物流项目里就吃过这个亏,一台AGV的Master节点崩溃,导致整个车队调度停摆,那叫一个狼狈。

ROS2彻底抛弃了这种中心化架构,改用分布式发现机制。每个节点启动时,会通过DDS(数据分发服务)在网络上广播自己的存在。其他节点收到广播后,直接建立点对点连接。没有单点故障,没有性能瓶颈。

核心变化:

  • ROS1:中心化Master,节点间通信依赖Master路由
  • ROS2:去中心化DDS,节点间直接通信,无中心节点

这种设计的好处很明显:任何一个机器人掉线,不会影响其他机器人之间的通信。而且DDS天生支持QoS(服务质量),你可以为不同话题设置不同的可靠性级别。比如,里程计数据可以丢几帧,但紧急停止指令必须可靠送达。

3.2 DDS在ROS2中的应用:不止是中间件

DDS是什么?你可以把它理解成一个超级智能的「消息快递系统」。它不负责具体业务逻辑,只负责把数据从发送方送到接收方,而且保证「该到的必须到,不该丢的绝不丢」。

ROS2默认使用的DDS实现是Fast DDS(以前叫Fast RTPS)。当然,你也可以换成Cyclone DDS或Connext DDS。我个人习惯用Fast DDS,因为它开源、性能不错,社区支持也最好。

DDS在ROS2中扮演的角色,我总结为三点:

  1. 发现机制:节点启动时,DDS通过Simple Discovery Protocol在局域网内广播自己的存在。其他节点收到后,建立点对点连接。
  2. 数据传输:节点间的所有话题、服务、动作数据,都通过DDS的RTPS协议传输。它支持UDP和TCP两种传输方式。
  3. QoS策略:你可以为每个话题设置可靠性(Reliable vs Best Effort)、持久性(Transient Local vs Volatile)、历史记录深度等参数。

避坑指南:我曾经在一个项目中,发现两个机器人明明在同一个局域网,但就是互相发现不了。排查了半天,发现是防火墙把DDS的组播端口(默认7400-7500)给封了。所以,多机通信前,先检查防火墙规则。

DDS的QoS策略是ROS2多机通信的杀手锏。举个例子:

# 发布者设置
publisher = node.create_publisher(
    String,
    'chatter',
    qos_profile=QoSProfile(
        reliability=ReliabilityPolicy.RELIABLE,  # 可靠传输
        durability=DurabilityPolicy.TRANSIENT_LOCAL,  # 延迟加入的订阅者也能收到最后一条消息
        depth=10  # 历史记录深度
    )
)

这段代码的意思是:这个话题的数据必须可靠送达,而且新加入的订阅者能立刻收到最近的一条消息。这在多机器人协同中非常有用——比如新加入的机器人需要知道当前任务状态。

3.3 多机网络配置:静态IP与主机名解析

好了,理论说完了,咱们来点实际的。要让两个ROS2机器人通信,网络配置是第一步。我建议你给每个机器人设置静态IP,而不是用DHCP。为什么?因为DHCP分配的IP可能会变,一旦变了,其他机器人就找不到它了。

3.3.1 设置静态IP

以Ubuntu 22.04为例,修改Netplan配置文件:

# /etc/netplan/01-netcfg.yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
      addresses:
        - 192.168.1.101/24  # 机器人1的静态IP
      gateway4: 192.168.1.1
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]

改完后执行 sudo netplan apply 生效。每个机器人都要设置不同的IP,但必须在同一个网段。

3.3.2 主机名解析

IP地址不好记,对吧?我建议你用主机名代替IP。这样,在代码里你只需要写 robot1robot2,而不是一串数字。

修改 /etc/hosts 文件,把每个机器人的IP和主机名对应起来:

# 在机器人1上
192.168.1.101   robot1
192.168.1.102   robot2

# 在机器人2上
192.168.1.101   robot1
192.168.1.102   robot2

注意,每个机器人的 /etc/hosts 文件都要包含所有机器人的映射。这样,任何机器人都能通过主机名找到其他机器人。

重要提醒:ROS2的DDS发现机制依赖组播。如果你的网络环境不支持组播(比如某些云服务器或VPN环境),你需要配置DDS的发现服务器(Discovery Server)模式。这个我们后面会讲。

3.4 ROS2多机通信实战:从零搭建多机器人系统

好,现在我们来动手。假设你有两台机器人,分别叫 robot1robot2,都运行Ubuntu 22.04和ROS2 Humble。

3.4.1 环境准备

首先,确保两台机器人都能互相ping通:

# 在robot1上
ping robot2

# 在robot2上
ping robot1

如果ping不通,检查网络配置和防火墙。我记得有一次,我折腾了半天发现是网线没插紧……嗯,这种低级错误谁都会犯。

3.4.2 设置ROS_DOMAIN_ID

ROS2使用 ROS_DOMAIN_ID 来隔离不同的机器人系统。如果你在同一个局域网里有多个团队在开发,设置不同的Domain ID可以避免互相干扰。

# 在两台机器上都设置
export ROS_DOMAIN_ID=42

建议把这一行加到 ~/.bashrc 里,省得每次都要手动设置。

3.4.3 编写多机通信节点

我们来写一个简单的发布-订阅示例。在 robot1 上运行发布者,在 robot2 上运行订阅者。

发布者(robot1):

# talker.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Talker(Node):
    def __init__(self):
        super().__init__('talker')
        self.pub = self.create_publisher(String, 'chatter', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = 'Hello from robot1!'
        self.pub.publish(msg)
        self.get_logger().info('Publishing: "%s"' % msg.data)

def main(args=None):
    rclpy.init(args=args)
    node = Talker()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

订阅者(robot2):

# listener.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Listener(Node):
    def __init__(self):
        super().__init__('listener')
        self.sub = self.create_subscription(String, 'chatter', self.callback, 10)

    def callback(self, msg):
        self.get_logger().info('Received: "%s"' % msg.data)

def main(args=None):
    rclpy.init(args=args)
    node = Listener()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

3.4.4 运行测试

robot1 上运行发布者:

ros2 run my_package talker

robot2 上运行订阅者:

ros2 run my_package listener

如果一切正常,你应该能在 robot2 的终端上看到 Received: "Hello from robot1!" 的消息。

调试技巧:如果收不到消息,可以用 ros2 topic list 查看话题列表,用 ros2 topic echo /chatter 直接监听话题内容。这能帮你快速定位是发布端还是订阅端的问题。

3.5 多机通信的常见问题与解决方案

实战中总会遇到各种坑。我把自己踩过的几个典型问题列出来,你遇到了可以直接对照排查。

问题现象 可能原因 解决方案
节点互相发现不了 防火墙阻止了DDS组播 开放UDP端口7400-7500,或使用Discovery Server模式
消息时有时无 QoS设置不匹配 确保发布者和订阅者的QoS配置一致
跨网段通信失败 DDS默认不支持跨网段 配置DDS的发现服务器,或使用TCP隧道
主机名解析失败 /etc/hosts文件配置错误 检查所有机器人的hosts文件是否一致

嗯,这一章的内容就到这里。多机通信是ROS2协同开发的基石,你花时间把网络配置和DDS原理搞透,后面写分布式算法时会轻松很多。下一章,我们会深入DDS的QoS配置,看看如何用不同的服务质量策略来优化多机器人系统的通信效率。