第1章:通信基础(ROS2)——多机协同的“神经系统”

大家好,我是你们的老朋友,一个在无人机和机器人堆里摸爬滚打多年的工程师。今天咱们开始《多机协同任务执行实战》的第一课。说实话,每次带新人做项目,我最怕的不是飞控算法写不好,而是通信先崩了。你想想看,三架无人机在天上,指令发不出去,状态收不回来,那场面……嗯,咱们今天就把ROS2通信这块地基打牢。

1.1 ROS2核心概念:节点、话题、服务、动作

ROS2说白了,就是一个分布式通信框架。它把整个系统拆成一个个小零件,这些小零件之间怎么说话,就是靠下面这四个核心概念。

1.1.1 节点(Node)

节点是ROS2里最小的执行单元。你可以把它理解成一个独立的小程序,专门干一件事。比如一个节点负责飞控解算,另一个节点负责图像识别。我个人习惯,一个功能就开一个节点,别搞大杂烩。这样哪个节点挂了,其他节点还能继续跑,不至于全军覆没。

小提示: 我在项目中遇到过,有人把所有逻辑写在一个节点里,结果一个bug导致整个系统重启。记住:节点越专一,系统越稳定。

1.1.2 话题(Topic)

话题是节点之间通信的“广播频道”。一个节点发布消息到话题上,其他节点订阅这个话题就能收到。这是ROS2里最常用的通信方式,适合持续的数据流,比如无人机的位置、姿态、速度。

# 发布者示例
self.publisher_ = self.create_publisher(Pose, '/drone/pose', 10)

# 订阅者示例
self.subscription = self.create_subscription(
    Pose, '/drone/pose', self.listener_callback, 10)

这里有个坑:话题名要统一。我曾经见过两个团队,一个用/drone/pose,另一个用/drone/position,结果数据死活传不过去。所以命名规范一定要提前定好。

1.1.3 服务(Service)

服务是“一问一答”的模式。客户端发一个请求,服务端处理完返回一个响应。适合那些需要确认结果的操作,比如“起飞”、“降落”、“切换模式”。

# 服务端
def takeoff_callback(request, response):
    # 执行起飞逻辑
    response.success = True
    return response

# 客户端
future = self.cli.call_async(Takeoff.Request())
注意: 服务是同步阻塞的。如果服务端处理时间太长,客户端会一直等着。多机协同里,我建议把耗时操作放到动作里,别用服务。

1.1.4 动作(Action)

动作是服务的升级版。它支持“长时间任务+实时反馈”。比如“飞到目标点”,你可以一边飞一边收到进度反馈,还能随时取消任务。这在多机协同里太重要了——你总不能让一架无人机卡死在某个任务上吧?

# 动作服务器
self._action_server = ActionServer(
    self, FlyToGoal, 'fly_to', self.execute_callback)

# 动作客户端
goal_handle = self._action_client.send_goal_async(goal)

1.2 DDS中间件原理

ROS2底层用的是DDS(数据分发服务)。说白了,DDS就是一套“谁需要数据,数据就自动送到谁手上”的机制。它不像传统TCP那样需要建立点对点连接,而是基于“发布-订阅”模型,自动发现节点、自动路由数据。

我记得第一次接触DDS时,觉得它太“黑盒”了。后来深入一看,其实核心就三点:

  • 全局数据空间: 所有节点共享一个虚拟的“数据黑板”,你写数据上去,别人就能读到。
  • 自动发现: 节点上线后,DDS会自动找到其他节点,不需要手动配置IP地址。
  • QoS策略: 这是DDS的灵魂,咱们下一节细说。

为什么ROS2选DDS而不是自己造轮子?因为DDS是工业级标准,可靠性、实时性都经过验证。多机协同这种场景,丢一个包可能就撞机了,DDS的可靠性机制能帮你兜底。

1.3 QoS配置——通信质量的“调节旋钮”

QoS(服务质量)是ROS2里最容易被忽视,但也最容易出问题的地方。它决定了数据怎么发、怎么收、丢了怎么办。

QoS策略 说明 典型场景
Reliability 可靠性:RELIABLE(保证送达) vs BEST_EFFORT(尽力而为) 控制指令用RELIABLE,传感器数据用BEST_EFFORT
Durability 持久性:TRANSIENT_LOCAL(保留最新数据给后订阅者) vs VOLATILE(不保留) 地图数据用TRANSIENT_LOCAL,实时状态用VOLATILE
Depth 队列深度:最多缓存多少条历史消息 高频数据设小深度(如10),低频数据设大深度(如100)
避坑指南: 我曾经在项目中,发布者用RELIABLE,订阅者用BEST_EFFORT,结果订阅者经常收不到数据。后来发现,QoS必须两端匹配,否则DDS会降级处理。记住:发布者和订阅者的QoS策略要一致,或者至少兼容。

1.4 多机通信的Namespace与Domain ID设置

多机协同,最头疼的就是“数据打架”。两架无人机都发布/pose话题,到底是谁的?这时候就需要Namespace和Domain ID来隔离。

1.4.1 Namespace(命名空间)

Namespace相当于给每个无人机一个“专属文件夹”。比如无人机1的话题是/drone1/pose,无人机2的是/drone2/pose。这样数据就不会混淆。

# 启动时指定Namespace
ros2 run my_package drone_node --ros-args -r __ns:=/drone1

# 代码里自动获取Namespace
namespace = self.get_namespace()  # 返回 "/drone1"

我建议,每架无人机启动时,都从配置文件里读取自己的ID,然后动态设置Namespace。这样代码可以复用,不用为每架无人机单独写一份。

1.4.2 Domain ID(域ID)

Domain ID是更粗粒度的隔离。不同Domain ID的节点,完全看不到对方。这适合多组无人机在同一物理空间里独立运行。

场景 Domain ID设置 说明
单组多机协同 所有节点用同一个Domain ID(如0) 组内所有节点互相可见
多组独立任务 每组用不同的Domain ID(如1、2、3) 组间完全隔离,互不干扰
地面站与多组通信 地面站加入所有Domain ID 需要特殊配置,默认不支持多Domain
重要提醒: Domain ID范围是0-232,但实际可用的一般是0-101。我遇到过Domain ID设成200,结果DDS发现不了节点的情况。建议用0-100之间的值,稳妥。

1.5 本章知识体系总览

为了让大家更直观地理解这些概念之间的关系,我画了一张图。你看完应该能明白,ROS2通信到底是怎么串起来的。

ROS2多机通信知识体系 节点 (Node) 话题 (Topic) 服务 (Service) 动作 (Action) DDS中间件(数据分发服务) 自动发现 · 全局数据空间 · 发布/订阅模型 QoS配置(可靠性 · 持久性 · 队列深度) Namespace(命名空间) Domain ID(域ID) 节点通过DDS通信,QoS控制质量,Namespace和Domain ID实现多机隔离

这张图把咱们今天讲的内容串起来了。你看,从最底层的节点、话题、服务、动作,到中间的DDS中间件,再到QoS配置,最后到多机通信的Namespace和Domain ID,一层层叠上去,就是一套完整的多机通信方案。

我的经验: 刚开始做多机项目时,别急着写代码。先把Namespace和Domain ID规划好,再定QoS策略。通信架构稳了,后面写代码就是水到渠成的事。

好了,这一章的内容就到这里。通信基础是后面所有实战的基石,别嫌它枯燥。下一章咱们会深入讲ROS2的启动系统,到时候你会感谢今天打下的基础。


公众号:蓝海资料掘金营,微信deep3321