2. ROS2基础回顾与Nav2依赖:ROS2核心概念、TF树、URDF与Launch系统

各位同学,欢迎来到第二章。在正式开始折腾Nav2仿真之前,咱们得先把地基夯实。很多新手一上来就急着跑导航,结果遇到问题一脸懵,回头一看,原来是ROS2的基本功没打牢。

这一章,我会带你快速过一遍ROS2里跟Nav2最相关的几个核心概念。说白了,Nav2就是建立在ROS2这些基础组件上的一个复杂应用。你搞懂了节点、话题、动作、TF树这些玩意儿,后面排查问题才能有的放矢。

2.1 ROS2核心概念:节点、话题、服务、动作

ROS2的通信机制,我习惯把它理解成一个“消息工厂”。不同的零件(节点)通过不同的传送带(话题、服务、动作)来传递包裹(消息)。

2.1.1 节点(Node)

节点是ROS2中最基本的执行单元。一个机器人系统通常由几十上百个节点组成,每个节点负责一个特定的功能。比如,激光雷达驱动是一个节点,里程计发布是一个节点,Nav2的控制器又是一个节点。

我的经验: 我个人习惯把节点设计得“小而专”。一个节点只干一件事,干好它。这样出了问题,你只需要盯着那一个节点看,排查起来快得多。我曾经见过有人把定位、规划、控制全写在一个节点里,那调试起来简直是噩梦。

2.1.2 话题(Topic)

话题是ROS2里最常用的通信方式,用于连续、单向的数据流。比如,激光雷达数据、里程计数据、速度指令,这些都是通过话题发布的。

  • 发布者(Publisher):发送数据的节点。
  • 订阅者(Subscriber):接收数据的节点。
  • 消息类型(Message Type):定义了数据的结构,比如 sensor_msgs/msg/LaserScan。

在Nav2里,最重要的几个话题包括:

话题名称 消息类型 说明
/scan sensor_msgs/msg/LaserScan 激光雷达数据,Nav2感知环境的核心输入
/odom nav_msgs/msg/Odometry 里程计数据,提供机器人位姿估计
/cmd_vel geometry_msgs/msg/Twist 速度指令,Nav2最终输出的控制命令

2.1.3 服务(Service)

服务用于短连接、有应答的通信。你发一个请求,服务器处理完给你一个响应。它适合那些不需要持续更新的操作,比如“加载地图”、“获取当前位姿”。

在Nav2中,服务常用于生命周期管理。比如,启动导航时,Nav2节点会通过服务请求来切换状态。

2.1.4 动作(Action)

动作是ROS2里最强大的通信机制,专门用于处理长时间执行的任务。它结合了话题和服务的特点:

  • 客户端发送一个目标(Goal)。
  • 服务端在执行过程中,持续发布反馈(Feedback)。
  • 任务完成后,返回结果(Result)。

Nav2的核心——导航行为树,就是基于动作服务器实现的。你给Nav2一个导航目标,它会持续反馈当前状态(比如“正在规划路径”、“正在绕障”),最后告诉你“到达目标”或“任务失败”。

避坑指南: 我曾经在调试时,发现机器人导航到一半突然停了,但没有任何报错。后来排查了半天,发现是动作服务器的超时时间设置得太短。记住,动作通信的timeout参数一定要根据你的实际场景来调整,尤其是在仿真环境里,计算资源有限,很容易超时。

2.2 TF树与坐标变换

TF(Transform)树,是机器人学里最核心的概念之一。没有它,机器人就是个“瞎子”。它描述了机器人各个部件之间的空间关系。

你想想看,激光雷达扫描到的点,是在激光雷达坐标系下的。但机器人要导航,需要知道这些点相对于机器人底盘(base_link)的位置。TF树就是用来做这个转换的。

2.2.1 常见的坐标系

  • map:世界坐标系,固定不变,是全局定位的基准。
  • odom:里程计坐标系,用于局部一致性估计,会随时间漂移。
  • base_link:机器人底盘坐标系,通常位于机器人中心。
  • laser_link:激光雷达坐标系。

Nav2正常工作,必须保证以下TF变换是正确发布的:

  • map → odom:由定位模块(如AMCL)发布。
  • odom → base_link:由里程计发布。
  • base_link → laser_link:由机器人模型(URDF)发布。
警告: 如果TF树断掉了,Nav2会直接罢工。最常见的错误是base_link到laser_link的变换没有发布。你可以在终端用ros2 run tf2_tools view_frames命令来查看当前的TF树结构,这是排查定位问题的第一步。

2.2.2 TF树结构图

下面这张图,展示了Nav2仿真中一个典型的TF树结构。你可以看到,所有坐标系最终都关联到base_link上。

Nav2 典型TF树结构 map odom base_link laser_link left_wheel_link right_wheel_link ● 全局定位 ● 里程计 ● 机器人本体 ● 传感器/执行器

2.3 URDF/SDF模型基础

URDF(Unified Robot Description Format)和SDF(Simulation Description Format)都是用来描述机器人模型的。在Nav2仿真里,我们主要用URDF。

URDF文件定义了机器人的:

  • 连杆(link):机器人的刚体部分,比如底盘、轮子、激光雷达外壳。
  • 关节(joint):连接两个连杆的方式,比如固定关节、旋转关节。
  • 几何与碰撞属性:用于可视化和物理仿真。

一个最简单的URDF文件长这样:

<robot name="my_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.5 0.4 0.2"/>
      </geometry>
    </visual>
  </link>
  <joint name="laser_joint" type="fixed">
    <parent link="base_link"/>
    <child link="laser_link"/>
    <origin xyz="0.2 0 0.1" rpy="0 0 0"/>
  </joint>
</robot>
我的建议: 在写URDF时,origin标签里的xyz和rpy一定要仔细核对。我曾经因为把激光雷达的安装高度写错了,导致导航时机器人总是撞到低矮的障碍物。记住,URDF里的坐标关系,直接决定了TF树的正确性。

2.4 Launch文件系统

Launch文件是ROS2里启动多个节点的“总指挥”。你不需要一个一个地开终端去运行ros2 run,一个Launch文件就能搞定一切。

在Nav2仿真中,一个典型的Launch文件会做以下几件事:

  1. 启动机器人模型(URDF)发布节点。
  2. 启动Gazebo仿真环境。
  3. 启动Nav2的核心节点(导航行为树、控制器、规划器等)。
  4. 启动RViz2可视化界面。

一个简单的Launch文件示例:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='robot_state_publisher',
            executable='robot_state_publisher',
            name='robot_state_publisher',
            output='screen',
            parameters=[{'robot_description': open('my_robot.urdf').read()}]
        ),
        Node(
            package='nav2_bringup',
            executable='bringup_launch.py',
            name='nav2_bringup',
            output='screen'
        ),
    ])
注意: Launch文件里的参数传递很容易出错。比如,robot_description这个参数,必须是一个字符串,而不是文件路径。我见过有人直接把文件路径传进去,结果节点启动后一直报错说找不到机器人模型。记住,要用open().read()把文件内容读成字符串。

好了,这一章的内容就到这里。这些基础概念,是后面所有仿真联调工作的基石。你花时间把它们搞透,后面遇到问题才能快速定位。下一章,我们就要开始搭建实际的仿真环境了。