第1章:通信基础(ROS2)——多机协同的“神经系统”
大家好,我是你们的老朋友,一个在无人机和机器人堆里摸爬滚打多年的工程师。今天咱们开始《多机协同任务执行实战》的第一课。说实话,每次带新人做项目,我最怕的不是飞控算法写不好,而是通信先崩了。你想想看,三架无人机在天上,指令发不出去,状态收不回来,那场面……嗯,咱们今天就把ROS2通信这块地基打牢。
1.1 ROS2核心概念:节点、话题、服务、动作
ROS2说白了,就是一个分布式通信框架。它把整个系统拆成一个个小零件,这些小零件之间怎么说话,就是靠下面这四个核心概念。
1.1.1 节点(Node)
节点是ROS2里最小的执行单元。你可以把它理解成一个独立的小程序,专门干一件事。比如一个节点负责飞控解算,另一个节点负责图像识别。我个人习惯,一个功能就开一个节点,别搞大杂烩。这样哪个节点挂了,其他节点还能继续跑,不至于全军覆没。
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) |
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 |
1.5 本章知识体系总览
为了让大家更直观地理解这些概念之间的关系,我画了一张图。你看完应该能明白,ROS2通信到底是怎么串起来的。
这张图把咱们今天讲的内容串起来了。你看,从最底层的节点、话题、服务、动作,到中间的DDS中间件,再到QoS配置,最后到多机通信的Namespace和Domain ID,一层层叠上去,就是一套完整的多机通信方案。
好了,这一章的内容就到这里。通信基础是后面所有实战的基石,别嫌它枯燥。下一章咱们会深入讲ROS2的启动系统,到时候你会感谢今天打下的基础。
公众号:蓝海资料掘金营,微信deep3321