3. 多机系统命名空间概述
在单机 PX4 ROS2 开发中,节点名和话题名往往可以“随手起”,因为整个计算图里只有一套 /fmu/... 接口。但一旦进入多机场景,例如三架无人机同时通过 MicroXRCEAgent 接入同一个 ROS2 域,如果每架飞机的节点都发布 /fmu/out/vehicle_odometry,那么地面站、控制节点和日志系统将无法区分数据来自哪一架飞机。命名空间(Namespace)正是解决这一问题的核心机制:它把原本扁平的名称空间切分成多个逻辑分区,使同名话题、服务、动作和节点可以共存而不冲突。
一、命名空间的作用:从“唯一名称”到“可寻址路径”
ROS2 中的名称本质上是一个层级路径,而不是一个孤立字符串。命名空间的作用可以归纳为三点:
- 隔离:不同机器人使用不同前缀,避免话题、服务、参数重名冲突。
- 寻址:通过前缀可以明确指定“我要访问 uav1 的里程计”还是“uav2 的里程计”。
- 复用:同一套启动文件、同一套控制节点,只需传入不同命名空间即可部署到多台机器人。
在 PX4 多机系统中,典型做法是为每架飞机分配一个命名空间,例如 /uav1、/uav2、/uav3。这样,原本的 /fmu/out/vehicle_odometry 会变成 /uav1/fmu/out/vehicle_odometry、/uav2/fmu/out/vehicle_odometry,地面站只需订阅对应前缀即可区分数据来源。
二、ROS2 命名空间机制:节点、话题与重映射
ROS2 的命名空间不是独立于节点存在的“文件夹”,而是通过节点启动时的 namespace 参数、名称重映射(remap)以及相对名称解析共同实现的。一个节点在创建时,会拥有一个完全限定名(Fully Qualified Name),其结构通常为:
/<namespace>/<node_name>
/<namespace>/<topic_name>
/<namespace>/<service_name>
当节点内部使用相对名称(如 fmu/out/vehicle_odometry)时,ROS2 会将其与节点所在命名空间拼接,形成最终名称。若节点启动时指定了 __ns:=/uav1,则相对名称会被解析为 /uav1/fmu/out/vehicle_odometry。若使用绝对名称(以 / 开头),则命名空间不会参与拼接,直接使用绝对路径。私有名称(以 ~ 开头)则相对于节点自身名称解析,常用于节点内部参数或私有话题。
核心记忆:相对名称 = 命名空间 + 名称;绝对名称 = 忽略命名空间;私有名称 = 节点名 + 名称。
在 PX4 ROS2 多机部署中,通常使用 MicroXRCEAgent 为每架飞机建立独立的 DDS 参与者,并通过 px4_ros_com 或自定义启动文件为每个 micro_ros_agent 和对应节点设置命名空间。例如:
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 -n uav1
ros2 run px4_ros_com vehicle_odometry_listener --ros-args -r __ns:=/uav1
上述命令中,-n uav1 为 agent 指定命名空间,__ns:=/uav1 为监听节点指定命名空间。两者配合后,节点订阅的话题会自动解析到 /uav1/fmu/out/vehicle_odometry。
三、相对、绝对与私有话题命名规则
ROS2 名称解析规则是本节最容易混淆的部分。下表给出三类名称在命名空间 /uav1、节点名 /uav1/odom_listener 下的解析结果:
| 名称类型 | 书写形式 | 解析规则 | 解析结果示例 |
|---|---|---|---|
| 相对名称 | fmu/out/vehicle_odometry |
命名空间 + 名称 | /uav1/fmu/out/vehicle_odometry |
| 绝对名称 | /fmu/out/vehicle_odometry |
直接使用,忽略命名空间 | /fmu/out/vehicle_odometry |
| 私有名称 | ~/status |
节点名 + 名称 | /uav1/odom_listener/status |
从表中可以看出,绝对名称会“穿透”命名空间,因此在多机系统中应谨慎使用。若某个节点内部硬编码了绝对话题名,那么即使启动时指定了 __ns:=/uav2,它仍然会发布到全局 /fmu/out/vehicle_odometry,导致多机数据混叠。相对名称和私有名称才是多机部署中推荐的方式。
四、典型场景与常见误区复盘
假设我们有三架飞机 uav1、uav2、uav3,每架飞机运行一个 vehicle_odometry_listener 节点,并希望将里程计数据分别转发到地面站。正确的启动方式如下:
# uav1
ros2 run px4_ros_com vehicle_odometry_listener --ros-args -r __ns:=/uav1
# uav2
ros2 run px4_ros_com vehicle_odometry_listener --ros-args -r __ns:=/uav2
# uav3
ros2 run px4_ros_com vehicle_odometry_listener --ros-args -r __ns:=/uav3
此时使用 ros2 topic list 可以看到:
/uav1/fmu/out/vehicle_odometry
/uav2/fmu/out/vehicle_odometry
/uav3/fmu/out/vehicle_odometry
地面站只需分别订阅这三个话题,即可获得三架飞机的独立里程计。若发现某个话题仍然显示为 /fmu/out/vehicle_odometry,通常说明该节点内部使用了绝对名称,或者启动时没有正确传入 __ns 参数。
常见误区:把命名空间当成“重命名工具”,只在启动命令里加 __ns,却忽略了节点代码中写死绝对名称。结果就是命名空间看似生效,实际话题仍然冲突。排查时应先用 ros2 topic list 和 ros2 node info 确认完全限定名,再检查代码中的名称写法。
另一个误区是混淆“节点命名空间”和“话题命名空间”。在 ROS2 中,话题的最终名称由节点命名空间和话题名称共同决定,而不是由话题单独拥有命名空间。因此,同一个话题名在不同命名空间的节点中会解析成不同路径,这正是多机系统能够复用同一套代码的根本原因。
总结来看,命名空间是多机 ROS2 系统的“地址系统”。理解相对、绝对、私有三种名称的解析规则,并在 PX4 多机启动时正确设置 __ns 与节点内部名称写法,才能让每架飞机的数据在同一个 DDS 域中互不干扰、可被独立寻址。后续在配置多机通信、QGC 连接和 offboard 控制时,所有话题路径都将建立在本节所述的命名空间规则之上。