3. ROS2基础

PX4 的外部控制与 Offboard 开发,本质上是在 ROS 2 的计算图(Computation Graph)中,让一个或多个节点与 PX4 的 uXRCE-DDS 桥接节点交换数据。因此,理解 ROS 2 的通信模型,是后续编写 Offboard 控制程序、调试话题频率、排查“指令发不出去”问题的前提。本节把 ROS 2 的核心概念串成一条主线:节点是执行单元,话题/服务/动作是三种通信方式,参数是节点配置,Launch 是启动编排,命令行工具是观察与验证手段。

一、核心原理:节点与三种通信机制

ROS 2 采用分布式节点架构。每个节点(Node)是一个独立进程,通过 DDS 中间件自动发现彼此,无需中心 Master。节点之间通过以下方式交互:

  • 话题(Topic):发布/订阅模型,异步、单向、多对多,适合高频连续数据流,如 /fmu/out/vehicle_odometry、/fmu/in/trajectory_setpoint。
  • 服务(Service):请求/响应模型,同步、双向、一对一,适合一次性命令,如解锁、切换模式、设置参数。
  • 动作(Action):基于话题和服务的组合,支持长时任务、周期反馈与可取消,如“飞往某航点并持续上报进度”。
  • 参数(Parameter):节点的运行时配置项,可动态读写,如控制增益、超时阈值、坐标系选择。

在 PX4 Offboard 场景中,最常用的是话题:PX4 通过 uXRCE-DDS 把 /fmu/out/* 发布出来,外部控制节点订阅状态,并向 /fmu/in/* 发布设定值。服务与动作则用于模式切换、解锁、任务级控制等非连续交互。

ROS 2 计算图:节点与四种交互方式 offboard_control 外部控制节点 发布设定值 / 订阅状态 px4_uxrce_dds PX4 桥接节点 /fmu/in 与 /fmu/out 话题 Topic(发布/订阅) 异步 · 高频 · 多对多 服务 Service(请求/响应) 同步 · 一次性 · 一对一 动作 Action(目标/反馈/结果) 长时任务 · 可取消 · 周期反馈 参数 Parameter(配置项) 运行时读写 · 节点私有 典型 Offboard 数据流 订阅:/fmu/out/vehicle_odometry、/fmu/out/vehicle_status → 获取位姿与状态 发布:/fmu/in/trajectory_setpoint、/fmu/in/offboard_control_mode → 发送设定值与心跳

二、方法流程:从建包到运行的最小闭环

一个可运行的 ROS 2 控制程序,通常遵循“建包 → 写节点 → 配参数 → 写 Launch → 编译运行”的流程。下面以 Python 节点为例:

# 1. 创建功能包
ros2 pkg create --build-type ament_python offboard_demo \
  --dependencies rclpy px4_msgs

# 2. 节点骨架(offboard_demo/offboard_node.py)
import rclpy
from rclpy.node import Node
from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint

class OffboardNode(Node):
    def __init__(self):
        super().__init__('offboard_node')
        self.declare_parameter('setpoint_rate', 20.0)
        rate = self.get_parameter('setpoint_rate').value
        self.pub_mode = self.create_publisher(
            OffboardControlMode, '/fmu/in/offboard_control_mode', 10)
        self.pub_sp = self.create_publisher(
            TrajectorySetpoint, '/fmu/in/trajectory_setpoint', 10)
        self.timer = self.create_timer(1.0 / rate, self.tick)

    def tick(self):
        mode = OffboardControlMode()
        mode.position = True
        self.pub_mode.publish(mode)
        sp = TrajectorySetpoint()
        sp.position = [0.0, 0.0, -5.0]
        self.pub_sp.publish(sp)

def main():
    rclpy.init()
    node = OffboardNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

参数通过 declare_parameter 声明后,可在 Launch 文件中覆盖,也可用命令行动态修改。Launch 文件负责把桥接节点、控制节点、参数配置一次性拉起:

# offboard_demo/launch/offboard.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(package='offboard_demo', executable='offboard_node',
             name='offboard_node',
             parameters=[{'setpoint_rate': 20.0}]),
    ])

三、常用命令行工具:观察与验证

调试 Offboard 时,命令行工具是“眼睛”。下表列出最常用的命令及其用途:

命令 作用 Offboard 场景示例
ros2 node list 列出运行中的节点 确认 offboard_node 与 px4_uxrce_dds 是否在线
ros2 topic list 列出所有话题 检查 /fmu/in 与 /fmu/out 是否出现
ros2 topic hz <topic> 统计发布频率 验证设定值是否稳定在 20 Hz
ros2 topic echo <topic> 打印话题内容 查看 vehicle_odometry 的位姿数值
ros2 interface show <type> 查看消息/服务定义 确认 TrajectorySetpoint 字段含义
ros2 param list / get / set 查看与修改参数 运行时调整 setpoint_rate
ros2 service call 调用服务 触发解锁或模式切换
ros2 launch <pkg> <file> 启动 Launch 文件 一键拉起控制节点与参数

四、案例与误区复盘

案例:Offboard 模式无法进入。常见原因是只发布了 trajectory_setpoint,却没有持续发布 offboard_control_mode 心跳。PX4 要求进入 Offboard 前必须先收到稳定的控制模式消息,且频率足够。用 ros2 topic hz /fmu/in/offboard_control_mode 即可确认心跳是否存在。

误区一:把服务当话题用。服务是同步阻塞的,若在回调中长时间等待,会拖垮执行器。高频控制必须走话题。

误区二:忽略 QoS 配置。PX4 的 uXRCE-DDS 话题通常使用 Best Effort 或特定 Reliability,若订阅端 QoS 不匹配,会“看不到数据”。用 ros2 topic info -v 可查看 QoS 详情。

误区三:参数改了却没生效。参数修改后需确认节点是否重新读取,或是否被 Launch 文件中的默认值覆盖。建议用 ros2 param get 复核实际值。

建议在每次 Offboard 调试前,先执行 ros2 node list、ros2 topic list、ros2 topic hz 三步检查,确认桥接节点在线、关键话题存在、频率稳定,再启动控制逻辑,可显著减少“指令无响应”的排查时间。

掌握节点、话题、服务、动作、参数与 Launch 的协作方式,并熟练使用命令行工具观察数据流,就具备了后续编写 PX4 Offboard 控制程序的基础设施能力。下一阶段将把这些概念落到具体的 PX4 消息接口与坐标系约定上。