2. ROS2基础与AUV软件架构

ROS2 是 AUV 水下仿真与控制系统的软件骨架。理解它,关键不是记住几个术语,而是弄清楚“一个节点如何通过话题、服务、参数与 Launch 文件,与其他节点协同完成一次推进、一次姿态调整或一次任务下发”。本节围绕这五个基础概念展开,并落到 AUV 控制系统的典型 ROS2 架构上。

节点:功能的最小运行单元

节点(Node)是 ROS2 中可独立启动、独立通信的进程单元。在 AUV 系统中,通常按“职责单一”原则拆分节点:一个节点负责读取仿真中的 IMU 与深度计,一个节点负责推进器分配,一个节点负责航向/深度控制律计算,一个节点负责任务调度。每个节点通过 rclcpp(C++)或 rclpy(Python)创建,拥有唯一名称,例如 /auv/imu_driver、/auv/thruster_allocator、/auv/depth_controller。节点名是后续话题、服务、参数寻址的基础,命名应体现设备与功能层级。

一个最小节点示例(Python)如下,它发布深度数据:

import rclpy
from rclpy.node import Node
from std_msgs.msg import Float64

class DepthSensor(Node):
    def __init__(self):
        super().__init__('depth_sensor')
        self.pub = self.create_publisher(Float64, '/auv/depth', 10)
        self.timer = self.create_timer(0.05, self.tick)  # 20 Hz

    def tick(self):
        msg = Float64()
        msg.data = 12.5  # 示例:来自仿真模型的深度值
        self.pub.publish(msg)

rclpy.init()
rclpy.spin(DepthSensor())

话题:持续数据流的发布与订阅

话题(Topic)是异步、多对多的数据通道,适合传感器流、控制指令流这类高频、持续的数据。发布者与订阅者通过话题名和消息类型匹配,彼此不感知对方存在。AUV 中典型话题包括:/auv/imu(sensor_msgs/Imu)、/auv/depth(std_msgs/Float64)、/auv/cmd_vel(geometry_msgs/Twist)、/auv/thruster_cmd(自定义 Float64MultiArray)。

设计话题时要关注三点:一是频率与队列深度,传感器流用 10~50 Hz、队列 10 较常见;二是QoS 策略,仿真中传感器数据可用 SensorDataQoS(best effort),控制指令宜用 reliable;三是坐标系约定,AUV 的 cmd_vel 通常约定为机体坐标系:x 向前、y 向右、z 向下,线速度与角速度均在该系下表达,全文与图示保持一致。

服务:请求—响应的同步交互

服务(Service)用于“发一次请求、等一次响应”的场景,如切换控制模式、请求上浮、查询电池状态。与话题不同,服务是同步的、一对一的。AUV 中可定义 /auv/set_mode(请求字段 mode: "manual"|"auto"|"hold",响应字段 success: bool),或 /auv/calibrate_imu。服务不宜承载高频数据,也不宜在控制回路中阻塞调用,否则会拖慢实时性。

参数:运行时可调的配置

参数(Parameter)是节点的配置项,可在启动时声明、运行时查询与修改。AUV 控制器的 PID 增益、限幅、采样周期都适合做成参数。例如深度控制器声明 kp、ki、kd、max_thrust,通过 ros2 param set /auv/depth_controller kp 1.8 在线调整。参数修改后节点需读取新值并生效,这为水下调试提供了不必重启节点的便利。

Launch 文件:多节点的统一编排

Launch 文件把节点启动、参数注入、话题重映射、命名空间组织在一起。一个 AUV 仿真启动文件通常同时拉起 Gazebo、机器人状态发布、传感器桥接、控制器与任务节点。示例(Python Launch):

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(package='auv_control', executable='depth_controller',
             name='depth_controller', namespace='auv',
             parameters=[{'kp': 1.5, 'ki': 0.2, 'kd': 0.05,
                          'max_thrust': 40.0}]),
        Node(package='auv_control', executable='thruster_allocator',
             name='thruster_allocator', namespace='auv'),
        Node(package='auv_bringup', executable='mission_manager',
             name='mission_manager', namespace='auv'),
    ])

AUV 控制系统的典型 ROS2 架构

把上述概念串起来,AUV 控制系统可划分为四层:感知层(IMU、深度计、DVL 驱动节点,发布传感器话题)、估计层(状态估计节点,融合出位姿与速度)、控制层(航向/深度/速度控制器,订阅设定值与估计状态,输出推力指令)、执行层(推力分配节点,把合力/力矩映射到各推进器转速)。任务管理层通过服务与参数对控制层下发模式与目标。下图给出这一数据流与调用关系。

AUV 控制系统典型 ROS2 架构(数据流 + 服务/参数调用) 感知层 imu_driver depth_sensor dvl_driver 发布 /auv/imu 等 估计层 state_estimator 融合 IMU/深度/DVL 输出 /auv/odom 控制层 depth_controller heading_controller speed_controller 参数 kp/ki/kd 执行层 thruster_allocator 合力/力矩 → 转速 /auv/thruster_cmd 任务管理层 mission_manager 下发目标深度/航向、切换模式、设定任务航路点 服务 /auv/set_mode 参数 /auv/depth_controller 服务/参数 状态反馈 /auv/odom 话题数据流 服务/参数调用 状态反馈

这张架构图对应到实际调试:启动后先用 ros2 node list 确认各节点在线,用 ros2 topic hz /auv/imu 检查传感器频率是否达到 20 Hz,用 ros2 topic echo /auv/odom 观察估计位姿是否随仿真运动更新。若深度控制器输出异常,先确认它订阅的 /auv/depth 与 /auv/odom 是否有数据,再检查参数 kp 是否被 Launch 文件覆盖。任务管理层通过服务切换模式时,若响应 success=false,应检查当前状态是否允许切换(例如手动模式下不允许直接进入自动定深)。

命名空间 /auv 统一了所有节点与话题前缀,便于多机器人或多仿真实例并存;Launch 文件中用 namespace='auv' 即可批量生效,避免逐个重映射。