
我第一次接触 ROS2 的时候心里是真发怵。网上教程铺天盖地今天讲 DDS明天讲 ament后天又是 colcon、launch、rviz2一打开就是十几篇长文看三天愣是不知道从哪儿下手。后来啃了大半年踩了无数坑回头再看这东西其实说白了就三件事让机器人里各个部件互相通信的一套机制配套的编译调试可视化工具链再加上围绕机器人开发积累起来的一堆现成库。把这三点理顺了ROS2 无非就是这点东西。这篇文章既然叫超细版我就尽量把每个细节摊开讲。从版本选择、安装排错到话题、服务、动作三种通信方式的区别再到功能包怎么写、colcon 怎么编、rviz2 怎么看数据、bag 怎么录数据最后延伸到 SLAM、路径规划和八叉树地图这类进阶方向。全文不整虚的尽量用我实际跑过的命令、踩过的坑还有那些文档里不会明说但真能救命的细节。无论是刚装好双系统的菜鸟还是已经会跑小乌龟但卡在功能包层面的新手应该都能从这里找到能直接抄的作业。1. 别被概念吓住ROS2 说到底是在做三件事1.1 它不是操作系统是一套“机器人专用微信”很多人一听“ROS2”这个名字以为它像 Windows 或者 Ubuntu 一样是个操作系统。真不是。ROS2 是跑在操作系统之上的一层软件框架核心作用就是让机器人上各自为政的程序能互相说话。你可以把 ROS2 理解成给机器人用的微信。摄像头驱动是一个“联系人”激光雷达驱动是一个“联系人”底盘控制是一个“联系人”路径规划算法又是一个“联系人”。每个联系人本身都是独立的小程序不用知道对方具体是谁、代码长什么样只需要按 ROS2 定的规矩发消息、收消息就够了。摄像头节点负责发“我看 到了什么”导航节点订阅这些数据算出“接下来往哪儿走”底盘节点收到指令驱动电机转起来。整个过程里谁挂了都不至于让整个系统瘫掉其他节点该干嘛干嘛——这就是分布式通信带来的好处。这套“微信”底层在 ROS2 里换成了一整套叫 DDSData Distribution Service数据分发服务的中间件。DDS 是工业界相当成熟的通信标准天生支持多对多通信、断线重连、QoS 质量策略甚至跨机器跨网络通信。ROS2 选择 DDS根本原因就是想省掉 ROS1 里那个必须时刻在线的中心节点 master让系统的可靠性和实时性上一个台阶。1.2 为什么全世界都在从 ROS1 往 ROS2 迁ROS1 当年很火但它的通信机制本质上是中心化的。所有节点之间聊天都要先找 master 报个到master 挂了整个系统立刻变哑巴。这在单个机器人上还能凑合可一旦遇到多台机器人协同、或者车规级项目要求“单个节点死掉不能影响全局”ROS1 这套架构就撑不住了。ROS2 的通信是去中心化的。节点之间通过 DDS 直接发现对方、建立连接没有谁是“必须活着”的单点。再加上 ROS2 支持 QoS通信服务质量配置你可以为不同话题设置不同的可靠性策略。比如激光雷达这种丢几帧没关系的传感器数据用 best effort 模式降低成本而控制指令这种丢一条就出大事的消息用 reliable 模式保证必达。这种按需配置的能力是 ROS1 时代想都不敢想的。另外一个现实原因是生态。新的算法库、新的硬件驱动、新的仿真方案基本都优先支持 ROS2。比如导航生态已经全面转向 Nav2机械臂方向 MoveIt2 也早已成熟。你现在新开一个机器人项目除非在维护老代码否则没有任何理由选 ROS1。1.3 ROS2 真正帮你解决的是这三个问题我后来给朋友讲 ROS2 时喜欢把它的价值压缩成三句话通信提供话题、服务、动作三种标准的“对话姿势”覆盖传感器流转、临时请求、长任务执行三类典型场景。工具链提供从编译colcon、调试rqt、rviz2、仿真Gazebo到数据录制回放ros2 bag的一整套流水线让开发调试有据可依。生态库导航定位有 Nav2机械臂有 MoveIt建图有 slam_toolbox、cartographer驱动和算法包成千上万不用从零造轮子。只要把这三块啃下来ROS2 的日常开发基本就不会走偏。接下来我先从最容易让人崩溃的安装环节开始因为这一步卡住了后面全是空谈。2. 装对版本就走通了四分之一版本选择与安装全流程2.1 先看你的 Ubuntu 版本再决定装哪个发行版ROS2 的发行版跟 Ubuntu 绑定得很紧选错版本硬装折腾半天全是依赖冲突。我见过太多人拿着 Ubuntu 20.04 非要装 Humble最后败在各种 python 依赖上。这里放一张对照表按年份来选基本不会错Ubuntu 版本推荐 ROS2 发行版说明20.04 FocalFoxy Fitzroy已 EOL老版本不再推荐新项目使用22.04 JammyHumble Hawksbill当前最稳、资料最多的 LTS 版本24.04 NobleJazzy Jalisco新 LTS功能新教程逐步跟上26.04未定等下一个 LTS 或 Rolling 开发版新系统刚出时依赖匹配不全别急着踩坑我的建议很直接如果你不是有特殊需求一律装 Ubuntu 22.04 Humble。理由很简单社区里的教程、问题反馈、现成 Docker 镜像绝大多数都以 Humble 为基准。等你把 Humble 玩熟了再考虑迁移 Jazzy 也就一天的事。2.2 官方源安装步骤以及那个“error 1”到底怎么破官方安装步骤本身不复杂按顺序敲就行。我在干净系统上完整走一遍大概是这样的# 1. 允许 apt 走 HTTPS 仓库 sudo apt update sudo apt install curl software-properties-common # 2. 下载并安装 ROS2 签名密钥 sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \ -o /usr/share/keyrings/ros-archive-keyring.gpg # 3. 添加 ROS2 软件源 echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] \ http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main | \ sudo tee /etc/apt/sources.list.d/ros2.list /dev/null # 4. 更新索引并安装 sudo apt update sudo apt install ros-humble-desktop python3-colcon-common-extensions # 5. 配置环境变量 echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc但现实往往不会这么顺。很多人在sudo apt update这一步会看到类似这样的报错获取:1 http://packages.ros.org/ros2/ubuntu jammy InRelease [4,682 B] 错误:1 http://packages.ros.org/ros2/ubuntu jammy InRelease我在刚入坑时也卡在这里很久。这个报错的本质是 apt 拉取源列表时要么网络不通要么密钥验证没过要么源地址本身有问题。排查思路我推荐按这个顺序走先确认网络连通性curl -I http://packages.ros.org/ros2/ubuntu如果卡住或者超时那大概率是官方源在你当前网络环境下不好使。换国内镜像源清华源和中科大源都有 ROS2 的镜像这是最省事的办法。以清华源为例把/etc/apt/sources.list.d/ros2.list改成echo deb https://mirrors.tuna.tsinghua.edu.cn/ros2/ubuntu jammy main | sudo tee /etc/apt/sources.list.d/ros2.list注意这里的jammy要和你系统版本对应Humble 对应 jammyJazzy 对应 noble。改完再sudo apt update一次通常就能过去。 3.还是报错就检查密钥如果提示NO_PUBKEY或签名错误重新下载 ros.key 并确认路径写对。 4.最后一个大招直接换用现成的一键脚本。社区里鱼香ROS的脚本用的也是镜像源思路一条命令就能把 ROS2 环境装得七七八八wget http://fishros.com/install -O fishros bash fishros这个脚本的好处是把源切换、密钥下载、依赖安装一次性处理了特别适合网络环境一般的用户。不过脚本毕竟是自动化的装完以后还是建议自己跑一遍小乌龟确认环境可用。2.3 环境变量 source 这件事为什么值得单独说ROS2 装完之后每个新终端都要执行source /opt/ros/humble/setup.bash才能找到ros2命令。很多人就是忘在这一点上一打开新终端就报“command not found: ros2”。我的习惯是装完第一时间把它写进.bashrc一劳永逸echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc另外提醒一句不要直接把 ROS2 的环境变量写进系统的全局 profile除非你确定这台机器一辈子只跑 ROS2。否则后面其他 Python 项目、深度学习环境都可能被 ROS2 的 PYTHONPATH 干扰到时候排查半天都找不到原因。2.4 Docker 跑 ROS2 时网络模式别选错现在很多人喜欢用 Docker 跑 ROS2干净、可复现、不会污染宿主机环境。但这里有个非常隐蔽的坑ROS2 默认基于 DDS 的节点发现依赖 UDP 组播Docker 默认的 bridge 网络会隔离组播报文结果就是两个容器里各自开着节点互相发现不了ros2 node list永远只看到自己。解决办法不是研究什么端口转发而是直接让容器使用宿主机网络docker run -it --nethost ros:humble--nethost是 ROS2 容器环境里的默认选择。在宿主机网络模式下DDS 的发现协议可以正常工作节点间的话题通信也不会有 NAT 和端口映射带来的各种玄学问题。如果你因为某些原因必须用 bridge那就要去配置 Cyclone DDS 的共享发现或指定组播地址复杂度一下子高不少——个人项目里真没必要。3. 话题、服务、动作ROS2 的三大通信方式ROS2 里 90% 以上的通信场景都能用话题、服务、动作这三种方式覆盖。我见过很多新手把三种方式的概念背得滚瓜烂熟真到写代码时却又不知道用哪个。这里我讲点大白话。3.1 话题Topic持续广播的“电台”话题是 ROS2 里最常用的通信方式。它像电台广播一样发送方持续不断地往外发消息接收方按自己的节奏收听。发送方根本不在乎有没有人在听接收方也不在乎发消息的是谁。典型场景就是传感器数据。激光雷达一秒钟扫 10 次每次扫到的点云就往/scan话题上发里程计每转一圈就发一个姿态数据到/odom摄像头更是能以 30 帧、60 帧的速率往外喷图像。这种高频、单向、一对多的数据流话题是唯一合适的姿势。实际操作中最常用到的命令是这几个# 列出当前所有话题 ros2 topic list # 查看某一个话题的实时内容 ros2 topic echo /scan # 查看话题的消息频率确认传感器是否在工作 ros2 topic hz /scan # 查看话题的消息类型和发布订阅方 ros2 topic info /scan调试机器人“某个传感器为什么没数据”时ros2 topic list看一眼话题在不在ros2 topic echo看一眼内容是不是全零ros2 topic hz确认频率正不正常——三步就锁定问题比瞎猜代码高效得多。3.2 服务Service一问一答的“电话”话题解决的是持续流问题但有些场景是“我问你答”我需要知道当前的机器人位置、需要临时设置一个参数、需要触发一次拍照。这种请求量低、需要即时返回结果的操作用话题广播就不合适了服务才是首选。服务的套路像打电话客户端拨过去服务器接起来客户端说“我要 XX 数据”服务器算完把结果返回电话挂断。整个交互是同步的、一次性搞定。举个例子用服务在 turtlesim 里生成一只新乌龟ros2 service list ros2 service type /spawn ros2 service call /spawn turtlesim/srv/Spawn {x: 2, y: 2, theta: 0.2}最后一个命令里的{x: 2, y: 2, theta: 0.2}就是请求参数服务端返回新乌龟的名称。整个过程很干净调用结束连接即断。服务适合的场景还有查询地图当前某个栅格的状态、请求机械臂夹爪开合、触发一次路径规划重算。判断标准很简单如果你需要“调用一下然后马上拿结果”而且频率不高用服务如果数据持续不断流过来永远别用服务。3.3 动作Action能取消、能反馈的“外卖订单”动作是 ROS2 里最“人性化”的通信方式。它解决的是“耗时很长的任务我想随时知道进度还想随时取消”这类问题。拿导航举例让机器人从 A 点走到 B 点可能要走 30 秒。如果走了一半发现路被堵死你得能取消任务执行过程中你也想知道机器人走到哪儿了、还有多远。这些话题和服务都搞不定动作就是为它设计的。动作机制可以拆成三层目标Goal客户端告诉服务端“我要去哪儿”相当于下单。反馈Feedback服务端一边干活一边报告进度“我现在走到 50% 了”相当于外卖小哥实时更新位置。结果Result任务完成或失败后服务端给客户端一个最终结论相当于送达确认。动作在底板上其实就是由多个话题组合实现的但对用户来说它是独立的高级抽象。常用命令ros2 action list ros2 action info /navigate_to_poseNav2 导航栈的核心接口/navigate_to_pose就是一个动作。机器人导航、机械臂抓取、无人机巡检这类任务标准处理方式全是动作。3.4 QoSROS2 比 ROS1 多的那点通信学问QoS 全称是 Quality of Service通信服务质量配置。这个概念在 ROS1 里基本没有地位但在 ROS2 里直接决定消息能不能送达、延迟多少。最核心的两个策略reliable可靠传输保证每条消息都送到适合控制指令、任务目标这类丢一条就出事的场景。best effort尽力传输追求实时性允许丢帧适合点云、图像这类高频大流量数据。新手最容易踩的坑是发布者用 best effort订阅者用 reliable两边 QoS 对不上消息根本收不到。我在项目里排查过不少次“明明在 echo 话题客户端却收不到数据”的问题最后发现就是 QoS 不匹配。最简单的做法是发布和订阅用同样的配置或者都走默认的 reliable。4. 功能包与 colcon写完代码怎么让它跑起来通信模型看懂了接下来最实际的问题就是我自己写的一个节点怎么组织、怎么编译、怎么让系统认识它。这部分是新手从“跑例程”跨到“写自己的程序”的关键门槛。4.1 工作空间的结构与功能包创建ROS2 的项目组织方式是一个“工作空间”通常叫ros2_ws底层分四个目录ros2_ws/ ├── src/ # 存放所有功能包源码 ├── build/ # 编译产物 ├── install/ # 编译完成后安装的可执行文件和环境脚本 └── log/ # 编译日志你平时要碰的就是src剩下的都是 colcon 自动生成的。创建一个功能包可以用官方命令mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src ros2 pkg create my_first_pkg --build-type ament_cmake --dependencies rclcpp std_msgs这里有两个重要选项--build-type指定语言风格ament_cmake是 Cament_python是 Python。--dependencies指定依赖的库这里rclcpp是 ROS2 的 C 客户端库std_msgs提供标准消息类型。如果你的包已经创建好了后面想加依赖可以直接编辑package.xml和CMakeLists.txt不一定非要重新创建。4.2 一个最小 C 发布者代码与 CMakeLists 缺一不可我拿一个最简单的 C 发布者示例来说。在src/my_first_pkg/src/下新建talker_node.cpp#include rclcpp/rclcpp.hpp #include std_msgs/msg/string.hpp using namespace std::chrono_literals; class TalkerNode : public rclcpp::Node { public: TalkerNode() : Node(talker_node) { publisher_ this-create_publisherstd_msgs::msg::String(chatter, 10); timer_ this-create_wall_timer(1s, [this]() { auto msg std_msgs::msg::String(); msg.data hello ros2; publisher_-publish(msg); }); } private: rclcpp::Publisherstd_msgs::msg::String::SharedPtr publisher_; rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedTalkerNode()); rclcpp::shutdown(); return 0; }代码逻辑很简单构造函数里创建了一个发布者和一个定时器每秒往chatter话题发一条hello ros2。rclcpp::spin让节点持续运行处理各种回调事件。但光写代码不够C 包还需要在CMakeLists.txt里把编译规则写清楚。核心这一段add_executable(talker_node src/talker_node.cpp) ament_target_dependencies(talker_node rclcpp std_msgs) install(TARGETS talker_node DESTINATION lib/${PROJECT_NAME})很多人在这里犯迷糊。add_executable声明“我要编译一个叫 talker_node 的可执行文件”ament_target_dependencies告诉编译器“编译时需要链接哪些 ROS2 库”install是“编译完成后把它安装到 install 目录里否则ros2 run找不到它”。这三行少一行后面都会出幺蛾子。4.3 编译、source、运行三步曲以及我踩过的那几种失误写完代码编译运行一条龙cd ~/ros2_ws colcon build --packages-select my_first_pkg source install/setup.bash ros2 run my_first_pkg talker_node--packages-select是有选择地编译某一个包。如果你有几十个包每次全量colcon build会白白等很久只编译改过的那一个能节省大量时间。我在这一步踩过的坑大部分都能归纳成三种没 source install 就 ros2 run。这个步骤只要换一个终端就得重新执行我一度怀疑ros2 run找包是玄学后来才搞明白工作空间的环境脚本本质上是“注册包路径”不 sourceROS2 就不知道你的包在哪儿。改了 CMakeLists 但忘了重新 colcon build。C 不像 Python改了编译规则必须重新编译否则改了个寂寞。CMakeLists 里忘了 install。编译成功、执行也成功但ros2 run就是提示找不到可执行文件。这种情况十有八九是 executable 没有写进install(TARGETS ...)或者 DESTINATION 写错位置。另外如果你用 Python 写节点就不需要 CMakeLists 的编译步骤。Python 包的入口点在setup.py里配置entry_points{ console_scripts: [ talker_node_py my_first_pkg_py.talker_node:main, ], },配置完同样 colcon build、source然后ros2 run就能找到入口了。5. rviz2 和仿真看得见才能调得对代码写好了、节点跑起来了但你总不能全靠ros2 topic echo一行行看日志来判断机器人状态对不对。这时候就轮到可视化工具登场。rviz2 是 ROS2 官方的 3D 可视化工具Gazebo 是官方最常用的物理仿真环境。这一节讲清楚它们各自扮演什么角色。5.1 rviz2 到底能显示什么又该怎么上手rviz2 本质上是一个“数据可视化面板”。它本身不干活但能把 ROS2 里各种话题数据显示成直观的画面。你可以用它看激光雷达点云、摄像头图像机器人 URDF 模型和各关节状态坐标变换TF关系地图、路径、障碍物代价层机器人在地图中的定位粒子启动方法非常简单ros2 run rviz2 rviz2默认启动是一个空界面左边是显示面板右边是 3D 视图。点左下角“Add”选你想要显示的类型再在“Topic”里选对应的话题。新手最常见的困惑是“为什么我 Add 了 PointCloud2 却什么都看不到”——大概率是 Fixed Frame 没设置对。rviz2 里所有数据显示都依赖坐标系转换如果Global Options里的 Fixed Frame 和你的点云所在的坐标系对不上数据会被投影到世界坐标原点附近自然啥也看不见。我的习惯是先确认ros2 topic echo有数据再到 rviz2 里排查显示问题这样可以排除“数据到底有没有发布”这个变量。5.2 Gazebo 仿真与真机实验为什么要先仿真Gazebo 是机器人仿真环境它模拟物理世界中的重力、碰撞、摩擦同时发布传感器数据到 ROS2 话题里。这样你不需要真机就能跑通整个算法流程在 Gazebo 里启动一个带激光雷达的机器人模型雷达数据会和真机一样发布到/scan话题你写的导航代码甚至无法分辨自己面对的是仿真数据还是真实数据。这对算法开发是巨大的便利。我在真机上调试导航时最怕的就是机器人撞墙或者翻车修一次传感器可能几百块。而在 Gazebo 里把机器人撞飞了也就是重启一下仿真的事。安装仿真相关的包sudo apt install ros-humble-gazebo-ros-pkgs启动 Gazebo 和机器人模型的具体方式取决于模型格式但核心套路一致模型描述文件URDF/SDF加载到 GazeboGazebo 启动物理引擎和传感器模拟然后 ROS2 端通过 Gazebo 插件把传感器话题发出来。5.3 turtlesim 和 rqt_graph新手最容易上手的解剖样本如果你还没到用 rviz2 和 Gazebo 的层次也想快速理解 ROS2 的通信机制那就从 turtlesim 开始。小乌龟例子虽然简单但对新手的价值不亚于“Hello World”对 C 语言的价值——它把发布订阅、服务调用、动作目标这些抽象概念全部变成看得见的小乌龟。开两个终端ros2 run turtlesim turtlesim_node ros2 run turtlesim turtle_teleop_key这时你能用方向键控制小乌龟移动。然后在第三个终端跑ros2 topic list ros2 topic echo /turtle1/pose rqt_graphrqt_graph会画出一张节点和话题的关系图你能清清楚楚看到teleop_turtle节点往/turtle1/cmd_vel话题发消息turtlesim节点订阅这个话题同时往/turtle1/pose话题回传自己的位置。这张图就是 ROS2 通信机制最直观的解剖图。我学 ROS2 时最大的顿悟时刻就是第一次在 rqt_graph 里看到自己控制的节点和话题连成一张网——“原来所谓的分布式就是这张网。”6. ros2 bag把现场数据录下来离线复盘调试机器人有个痛苦场景机器人跑了一天某条路径偶尔会出问题可你想抓现场时它又一切正常。真机环境下你不可能让机器人无限重跑同一个动作而传感器数据又极其昂贵。解决办法就是先录数据再回来慢慢分析。这就是ros2 bag存在的价值。6.1 什么时候非用 bag 不可我举两个真实场景。第一个是偶发故障排查。机器人自动导航过程中偶尔会在同一个转角附近抖动一下。现场看半天不一定能复现但你把所有相关话题录下来回去对着数据一条条回放很容易发现其实每次都是同样的障碍物和路径规划冲突造成的。第二个是传感器数据离线测试。你在真机上录的激光雷达数据回家用 bag 回放就能在没有真机的情况下反复调算法参数。这比每次都去实验室跑一圈高效太多。6.2 记录、查看、回放三件套最常用的命令就三个# 记录所有话题-a 表示 all ros2 bag record -a # 记录指定话题 ros2 bag record /turtle1/pose /turtle1/cmd_vel # 查看 bag 信息 ros2 bag info my_bag # 回放 bag ros2 bag play my_bagros2 bag record -a虽然方便但如果你机器人上的话题特别多图像、点云、IMU 全开着录一段时间可能几个 G 就没了。保存空间有限的话还是手动指定要记录的话题比较好。回放的时候有个细节值得注意在回放 bag 的终端里话题照样发出去和真实数据没什么两样。所以你可以同时打开 rviz2 订阅这些话题完整复盘当时的机器人状态。我经常干的事是开三个终端一个跑ros2 bag play一个跑rviz2一个跑rqt_graph把一个小时的现场压缩成十分钟的“电影”反复研究。6.3 bag 的数据格式sqlite3 和 MCAP 是什么关系很多人不知道 bag 文件夹里到底是什么。早期 ROS2 版本默认用 sqlite3 数据库存储数据一个 bag 目录里会有一个.db3文件加几个元数据文件。sqlite3 是单文件关系型数据库用数据库存消息的好处是索引查询很快可以直接按时间范围截取数据。到 Jazzy 版本之后默认格式换成了 MCAP.mcap文件MCAP 是一种更通用的机器人数据容器格式它不绑定某个具体 ROS 版本理论上可以跨 ROS1/ROS2甚至被其他异构工具读取。如果你有一段老的.db3bag想在 Jazzy 里回放可以用ros2 bag convert把它转成 MCAP 格式。我已经连续踩过两次“旧 bag 打不开”的坑所以这里提前提醒一句新项目一律直接用 MCAP 格式省得以后转来转去。7. 上点难度SLAM、路径规划与八叉树地图聊完基础通信和工具链最后这块属于进阶方向。点开搜索引擎和 ROS2 相关的高频词无非四个安装、话题服务动作、rviz2、导航建图。前面几个都讲了导航建图这块我拣最核心的说——尤其是很多人问的“八叉树地图导航”到底是怎么回事。7.1 从传感器到地图SLAM 在做什么SLAMSimultaneous Localization and Mapping的任务是让机器人一边移动一边建图同时确定自己在地图中的位置。可以把它理解为“我在一个陌生房间里一边走动一边在纸上画房间的布局同时标记‘我现在大概在这个位置’”。ROS2 里最常见的 SLAM 工具是slam_toolbox和cartographer。它们吃激光雷达或深度相机的数据输出二维栅格地图和 TF 坐标变换。整体流程是传感器数据/scan → SLAM 节点 → 地图数据 机器人位姿栅格地图把环境划分成小格子每个格子标记为“可走”“障碍”或“未知”。二维导航最常用的就是这种地图Nav2 的代价地图也是在这个基础上叠加障碍物膨胀层形成的。7.2 Nav2 的全局规划与局部规划两条腿走路路径规划是导航的核心。Nav2 内部把规划拆成了两层全局规划在地图上找一条从起点到终点的整体路径。常用的算法是 A* 或 Dijkstra它们在全局地图上搜索最短路径但完全不考虑机器人转动半径、速度上限这类细节。局部规划机器人沿着全局路径走的时候随时根据激光雷达实时数据微调方向躲避动态出现的障碍物。常用算法是 DWA 和 TEB。局部规划器也叫局部代价地图规划器它只看机器人周围一小片区域保证实时性。这种拆分思路和人类开车类似先用导航软件规划一条从北京到上海的高速路线全局再在行驶过程中根据当前车道、前方车辆随时变道转向局部。两层配合才能既保证大方向正确又保证实时安全。坐标变换TF在导航中也非常重要。激光雷达装在机器人哪一侧、离地多高这些都需要 TF 树来描述。用tf2_echo可以查看两个坐标系的变换关系ros2 run tf2_ros tf2_echo map odomTF 树乱了导航表现会非常奇怪——明明地图好好的机器人却认为自己在完全错误的位置然后对着空气规划出一条诡异路径。遇到这种问题先查 TF多半有惊喜。7.3 八叉树地图为什么适合三维导航前面说的栅格地图是二维的对于地面小车够用但无人机、水下机器人需要在三维空间里运动二维地图根本表达不了“头顶有障碍物”这类信息。这时就需要八叉树地图OctoMap。八叉树地图的思路很简洁也很有意思它把三维空间递归地切成八等份如果一个立方体完全被占住或者完全空闲就不再细分只有在边界附近一个立方体内的状态不一致时才继续切成八个更小的子块。这种递归结构让它在表达大范围空旷空间时极其节省内存——空旷区域一个节点就能表示障碍物边界处才精细化。ROS2 中使用 octomap 相关库可以从点云数据直接生成八叉树地图。它和 Nav2 的关系是Nav2 传统上做二维代价地图导航而八叉树地图可以用于三维路径规划、复杂环境避障以及无人机走廊规划。如果你未来做的是无人机巡检、机械臂在堆满物料的货架间抓取这类项目八叉树地图基本是避不开的技术栈。7.4 无人机场景与 mavros 的 IMU 数据流顺着无人机场景多说一句。很多玩无人机的朋友问“怎么把飞控的数据接进 ROS2”答案通常是 mavros——它是 MAVLink 协议和 ROS 之间的桥梁。装上 mavros 之后飞控的 IMU 数据会以标准的sensor_msgs/msg/Imu消息格式发布到/mavros/imu/data话题。拿到 IMU 数据之后配合前面的 ros2 bag 非常实用ros2 bag record /mavros/imu/data /mavros/local_position/odom一次试飞下来把 IMU 和位置估计数据全部录下来回去做姿态解算、传感器融合算法评估都靠它。我在做无人机状态估计时就是靠 bag 回放反复调卡尔曼滤波参数免去了一次又一次试飞的时间成本和炸机风险。回到最初那个问题——ROS2 无非就是这点东西。通信机制、工具链、生态库这三条主线贯穿了从安装到导航的全部路径。只要把这些主线理顺后续啃任何具体算法库你都不会再有无从下手的迷茫感。