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 多机命名空间解析:相对 / 绝对 / 私有名称 命名空间 /uav1 节点 /uav1/odom_listener 相对名称话题 fmu/out/vehicle_odometry 私有名称话题 ~/status 名称解析规则 相对名称 命名空间 + 名称 绝对名称 忽略命名空间 私有名称 节点名 + 名称 最终完全限定名 /uav1/fmu/out/ vehicle_odometry /fmu/out/ vehicle_odometry /uav1/odom_listener/ status 多机部署建议:优先使用相对名称或私有名称,避免绝对名称导致跨机话题冲突。

总结来看,命名空间是多机 ROS2 系统的“地址系统”。理解相对、绝对、私有三种名称的解析规则,并在 PX4 多机启动时正确设置 __ns 与节点内部名称写法,才能让每架飞机的数据在同一个 DDS 域中互不干扰、可被独立寻址。后续在配置多机通信、QGC 连接和 offboard 控制时,所有话题路径都将建立在本节所述的命名空间规则之上。