1. 先把ROS2的底层逻辑摸清楚,再动手装环境
我见过太多人在入门ROS2的第一周就卡死:网上复制一行命令,终端报一堆红字,然后开始怀疑是不是自己系统装坏了。其实问题往往不在系统,而在于没搞明白 ROS2 的运行模型和依赖关系,就去照抄别人的安装流程。这份记录写给三类人:刚接触ROS2的在校学生、从 ROS1 迁移过来的工程师、以及需要把机器人产品快速做出原型的开发者。读完你应该能自己判断“我的 Ubuntu 该配哪个发行版”“为什么我的节点互相看不见”“QoS 到底该选哪个策略”,而不是只会背命令。
1.1 ROS2和ROS1的差距不只是版本号
ROS1 时代最让人头疼的是单点依赖:roscore 一挂,整个系统全瘫。而且它跨机器通信要靠 ROS_MASTER_URI 手工配置,网络环境稍微复杂一点就各种连不上。ROS2 把这一层彻底重写了,去掉了中心节点,每个节点自己就是一个独立的进程,节点之间直接建立点对点连接。
这件事的直接影响是:你可以启动第二个节点而不需要关心第一个节点是否还在运行,也可以让两个机器人子系统各自独立启停,互不影响。ROS1 里的参数服务器是一个全局字典,所有节点都往里面读写,很容易出现命名冲突;ROS2 把参数变成节点自身的属性,每个节点管理自己的参数,需要外部配置时通过命令行或者 YAML 文件注入。
还有一个经常被忽略的差别是构建系统。ROS1 用 catkin,本质上是 CMake 的一层包装;ROS2 换成 colcon,它是一个通用的多包构建工具,不绑定 ROS,能同时管理 Python 包、C++ 包和纯 CMake 包。这意味着你可以在同一个工作空间里放不同语言、不同构建类型的包,colcon 会按依赖顺序自动编排。
1.2 DDS是ROS2真正的地基
很多人学 ROS2 学了很久,依然不知道DDS(Data Distribution Service)才是它通信的核心。ROS2 只是定义了一套接口规范,真正的发现、序列化、传输全部由底层的 DDS 实现完成。常见的实现有 Fast DDS、Cyclone DDS、Fast-DDS 的封装层等,通过 RMW(ROS Middleware)接口对接。
这个设计带来的好处是解耦:你想要低延迟就换一个实现,想要跨平台就换另一个。坏处是排查问题时多了一层迷雾——节点不能通信,可能是 ROS2 层的问题,也可能是 DDS 层的域配置问题,还可能是网络组播被限制。
DDS 的工作方式可以类比成“广播电台加订阅杂志”:每个节点先通过发现机制(默认是组播)喊一嗓子“我在这个域里,我叫什么名字,我发什么话题”,其他节点听到之后,如果发现话题匹配,就建立点对点连接。这解释了为什么在同一个局域网里,两个不同用途的 ROS2 系统可能莫名其妙互相串扰——只要域 ID 一样,它们就在同一个“电台频道”里。
提示:多台机器、多套系统同时调试时,务必给每个系统分配不同的 ROS_DOMAIN_ID,否则会互相发现、互相干扰。
1.3 这份入门记录覆盖哪些内容
我准备按真实上手顺序来讲:先解决系统与发行版配对,再装环境、建工作空间,然后吃透节点、话题、服务、动作四类通信原语,接着讲最容易踩坑的 QoS 与 DDS 调优,之后是 launch 文件和 TF 变换怎么把节点组织成一个系统,再往后是仿真、SLAM、导航、机械臂、传感器接入和嵌入式这几种典型实战场景,最后给一套报错速查表和面试考点梳理。
整个过程我用的是“先能跑起来,再搞明白为什么”的思路,但每个关键选择我都会解释背后的原因,让你不至于变成只会复制粘贴的搬运工。
2. Ubuntu与ROS2发行版怎么配对,装错一步全白干
ROS2 的发行版和 Ubuntu 版本是强绑定的,装之前先确认系统版本,这一步错了后面全是坑。常见组合是 Ubuntu 22.04 配 Humble,Ubuntu 24.04 配 Jazzy,再往后的新系统对应更新的发行版。
2.1 发行版对应关系与选型建议
| Ubuntu 版本 | 推荐 ROS2 发行版 | 支持状态 | 适用场景 |
|---|---|---|---|
| 20.04 | Foxy | 已停止维护 | 老项目维护,不建议新学 |
| 22.04 | Humble | 长期支持 | 企业项目、教程资料最丰富 |
| 24.04 | Jazzy | 长期支持 | 新项目首选,配套仿真版本新 |
| 24.10 及以后 | Rolling / 新版本 | 滚动更新 | 追新特性,不建议生产使用 |
我的建议很直接:新学就上 Ubuntu 24.04 + Jazzy,因为 Gazebo 等仿真组件的版本更新跟得上;如果你要跟着大量中文教程做,Humble 的资料密度仍然是最高的,遇到问题更好查。别去追 Rolling,它每天都在变,今天能编过的代码下周可能就报错,对新手极其不友好。
还有个容易被忽略的点:如果你用的是树莓派 5 这类 ARM 平台,需要先确认官方是否为该架构提供了对应发行版的二进制包。很多包在 x86 上有预编译版本,在 ARM 上只能源码编译,编译时间和内存占用会成倍增加。
2.2 从软件源安装的完整流程
以 Ubuntu 22.04 安装 Humble 为例,先准备基础工具:
sudo apt update && sudo apt install -y curl gnupg lsb-release software-properties-common接着导入软件源的签名密钥并把源地址写入系统:
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \ -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \ http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" \ | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null然后更新索引并安装桌面完整版:
sudo apt update sudo apt install -y ros-humble-desktop这里选desktop而不是ros-base,原因是新手需要 RViz2、演示示例、以及一部分调试工具,desktop把这些都带上了。如果你是在服务器上做无图形界面的部署,那用ros-base更省空间。
安装完之后立刻做一次自检:
source /opt/ros/humble/setup.bash ros2 run demo_nodes_cpp talker另开一个终端,同样 source 之后跑:
ros2 run demo_nodes_cpp listener看到 talker 不断打印 “Hello World”,listener 收到同样的消息,说明通信链路是通的。这一步很重要,别跳过。
2.3 国内网络下的软件源与一键脚本
官方源在国内访问速度不稳定,很多人卡在apt update转圈。解决办法是把源地址替换成国内镜像站,例如清华的 ROS2 镜像:
sudo sed -i 's|http://packages.ros.org/ros2/ubuntu|https://mirrors.tuna.tsinghua.edu.cn/ros2/ubuntu|g' \ /etc/apt/sources.list.d/ros2.list sudo apt update国内社区还有一些封装好的一键安装脚本,比如鱼香 ROS 社区提供的安装工具,它做的事情本质上是自动判断系统版本、替换镜像源、安装对应发行版、配置环境变量。用这类脚本能省事,但你要知道它替你改了什么文件,否则出问题时无从下手。我一般会先看一遍脚本内容,确认它没有做多余的事情再用。
注意:一键脚本不是万能药。如果你之前手工配过源,脚本可能和你已有的配置冲突,导致重复行或者优先级混乱。用之前最好先
cat /etc/apt/sources.list.d/ros2.list看一眼现状。
2.4 把环境变量固化下来
每开一个新终端都手动 source 一次,很快就会烦。常规做法是把这行写进~/.bashrc:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc如果你经常在多个工作空间之间切换,可以装一个colcon-argcomplete之类的补全工具,或者自己写个简单函数按需切换。我更推荐后者,因为全局 source 会让所有终端都指向同一个环境,做多版本对比实验时容易搞混。
验证环境是否生效:
printenv | grep -i ros正常情况下会看到ROS_DISTRO、ROS_VERSION、AMENT_PREFIX_PATH等变量。如果ROS_DISTRO是空的,说明 source 没生效,后面所有命令都会报command not found,这就是热词里那个经典报错的根源。
3. 工作空间与功能包:从零建起自己的代码仓库
装好环境只是第一步,真正写代码要在工作空间里组织功能包。ROS2 的工作空间结构比 ROS1 清晰,但初次接触的人容易被install、build、log三个目录搞晕。
3.1 colcon工作空间的目录约定
标准结构长这样:
ros2_ws/ ├── src/ # 放源码包 ├── build/ # 中间编译产物,可删 ├── install/ # 安装结果,source 的就是这里 └── log/ # 构建日志,排查编译失败看这里src是唯一需要你手工维护的目录。build和install都可以删掉重新生成,所以千万别把源码或者配置文件放在这两个目录里,这是新手最容易犯的错误之一。
初始化命令很简单:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build第一次构建空空间也会生成目录结构。构建完成后,source 一下安装环境:
source install/setup.bash这个顺序不能颠倒:先建空间、再放包、再 build、再 source。有人先 source 了再 build,结果新包加载不出来,白白折腾半小时。
3.2 创建C++和Python功能包的差别
创建 C++ 包:
ros2 pkg create --build-type ament_cmake my_cpp_pkg --dependencies rclcpp std_msgs创建 Python 包:
ros2 pkg create --build-type ament_python my_py_pkg --dependencies rclpy std_msgs两者的差别不只是语言。ament_cmake的包需要你手工维护CMakeLists.txt,包括可执行文件的声明、依赖查找、安装规则;ament_python的包靠setup.py和setup.cfg描述安装路径,写得少但灵活性也低。
--dependencies这个参数很实用,它会自动把依赖写进package.xml,省得你手工拼字段。常见依赖包括rclcpp、rclpy、std_msgs、sensor_msgs、geometry_msgs、tf2_ros、nav2_msgs等,按需添加即可。
3.3 package.xml 与 CMakeLists.txt 的关键字段
package.xml里最容易搞错的是依赖类型。运行时需要的库要用<depend>,只在编译时需要的用<build_depend>,只在测试时需要的是<test_depend>。搞错类型会导致编译通过但运行时找不到动态库,报出类似 “error while loading shared libraries” 的错误。
CMakeLists.txt里几个必看的点:
find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) add_executable(my_node src/my_node.cpp) ament_target_dependencies(my_node rclcpp std_msgs) install(TARGETS my_node DESTINATION lib/${PROJECT_NAME})install这段极其关键。很多人写了节点、编译成功,但ros2 run找不到可执行文件,就是因为忘了把目标安装到lib/${PROJECT_NAME}目录。ros2 run查找的是 install 目录下的可执行文件,不是 build 目录里的。
3.4 构建、验证与增量编译
在工作空间根目录执行:
colcon build --symlink-install --packages-select my_cpp_pkg--symlink-install对 Python 包特别有用,它用符号链接代替复制,改完代码不用重新 build 就能生效,能省下大量时间。--packages-select只编译指定包,包多了之后这个参数能显著提速。
构建之后验证节点能否被找到:
ros2 pkg executables my_cpp_pkg如果没有输出,先回头检查install规则,再看log/latest_build下的日志。日志里会明确告诉你哪个包失败、失败在编译还是链接阶段,比在终端里盲目翻滚屏高效得多。
4. 节点、话题、服务、动作:四类通信原语吃透
ROS2 的通信机制说到底就这四类,把它们的适用边界搞清楚,写代码时就不会选错。
4.1 节点与执行器
节点是 ROS2 的最小运行单元,一个进程里可以跑多个节点,也可以一个节点独占一个进程。C++ 里通常用rclcpp::spin让节点阻塞等待回调,底层靠执行器(Executor)调度。Python 里对应rclpy.spin。
常见误区是把耗时操作直接写在回调里。回调是串行执行的,一个回调卡住,其他话题的消息就会堆积,最终导致延迟越来越高。正确做法是把重活儿丢给单独的线程,或者用多线程执行器。
实操心得:调试时用
ros2 node list看节点清单,用ros2 node info /节点名看它订阅、发布了哪些话题,以及提供了哪些服务和动作。这个命令是我排查问题的第一站。
4.2 话题与消息类型
话题是发布订阅模型,适合高频、单向、允许丢包的数据流,比如激光雷达点云、图像、里程计。发布方和订阅方互不知道对方存在,只靠话题名和消息类型匹配。
查看话题列表和消息内容:
ros2 topic list ros2 topic echo /chatter --once ros2 topic hz /scan ros2 topic info /scan --verbose--verbose能列出具体的发布者和订阅者,以及它们各自的 QoS 配置,排查通信问题时非常有用。
消息类型遵循接口定义语言(IDL),常见的有std_msgs/msg/String、sensor_msgs/msg/Image、geometry_msgs/msg/Twist。自定义消息需要单独建一个xxx_interfaces包,里面放.msg文件,然后在包的CMakeLists.txt里调用rosidl_generate_interfaces生成代码。自定义消息能大幅提升代码可读性,但会引入编译依赖,别滥用。
4.3 服务与动作的真正区别
服务是请求响应模型,同步调用,一问一答,适合配置类操作,比如“切换模式”“查询状态”。服务的问题是如果执行时间长,调用方就会一直阻塞等待,界面直接卡死。
动作(Action)就是为解决这个问题设计的。它由三部分组成:目标(Goal)、反馈(Feedback)、结果(Result)。客户端发送目标,服务端在执行过程中持续推送反馈,完成后返回结果,整个过程可以取消。典型场景是导航到某个坐标、机械臂运动到指定位姿。
ros2 action list ros2 action info /navigate_to_pose用ros2 action send_goal可以手工测试一个动作,加上--feedback能实时看到执行进度。很多人在做导航调试时不知道机器人走到哪一步了,其实就是没打开这个反馈。
选型原则很简单:短、快、要求即时返回用服务;长、可中断、需要过程反馈用动作;高频流式数据用话题。
4.4 参数系统的使用与陷阱
参数挂在节点上,通过命令行可以查询和修改:
ros2 param list ros2 param get /my_node my_param ros2 param set /my_node my_param 42 ros2 param dump /my_nodeparam dump能把当前参数导出成 YAML,正好可以拿来做 launch 文件的参数输入。这个闭环很实用:先手工调参调出满意结果,导出 YAML,再写进 launch 文件固化下来。
要注意的是,通过命令行set的参数通常只在当前运行期间有效,节点重启就恢复默认值。想让参数持久生效,必须写进 launch 文件或者 YAML 配置。另外不是所有参数都支持运行时修改,这取决于节点回调里是否注册了参数变更处理逻辑。
5. QoS与DDS调优:新手最容易栽跟头的地方
ROS2 相比 ROS1 增加的最大复杂度就是 QoS。默认配置下大部分场景能用,但一旦遇到“话题明明有发布者,订阅者却收不到数据”,九成是 QoS 不兼容。
5.1 QoS策略的六个关键维度
| 策略 | 可选值 | 影响 |
|---|---|---|
| Reliability | RELIABLE / BEST_EFFORT | 是否保证送达,传感器常用 BEST_EFFORT |
| Durability | TRANSIENT_LOCAL / VOLATILE | 是否给后加入的订阅者补发历史数据 |
| History | KEEP_LAST / KEEP_ALL | 缓存策略,KEEP_LAST 需配合深度 |
| Depth | 整数 | 缓存队列长度 |
| Deadline | 时间 | 承诺的最大发布间隔 |
| Lifespan | 时间 | 消息的有效期 |
Reliability 是最关键的一项。发布者设为 RELIABLE,订阅者设为 BEST_EFFORT 时,两者通常能协商成功,实际按较弱的那个执行;反过来发布者是 BEST_EFFORT、订阅者是 RELIABLE,则不兼容,直接收不到数据。
5.2 三个真实的QoS不兼容案例
第一个案例:用雷达驱动发点云,驱动默认 BEST_EFFORT,自己写的订阅节点用了默认 QoS(RELIABLE),结果一条数据都收不到。解决方案是把订阅端改成 BEST_EFFORT,或者用 SensorDataQoS 预设。
第二个案例:静态地图发布用 TRANSIENT_LOCAL,让后启动的导航节点也能拿到地图。如果订阅端是 VOLATILE,通常仍能兼容并正常工作,但订阅端如果也要求 TRANSIENT_LOCAL 而发布端是 VOLATILE,就会失败。
第三个案例:多机通信时域 ID 不一致。两个节点看起来都在运行,话题名也完全一样,就是互相看不见。检查echo $ROS_DOMAIN_ID是否一致,是最快的排查手段。
代码里设置 QoS 的方式:
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy qos = QoSProfile( reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=10 ) self.subscription = self.create_subscription(Image, '/camera/image', self.cb, qos)提示:调试阶段先统一用默认 QoS,确认链路通了再针对具体话题调整。反过来做会让你分不清是代码问题还是 QoS 问题。
5.3 RMW选择与域隔离实践
切换底层 DDS 实现靠环境变量:
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp不同实现在延迟、内存占用、多播行为上表现不同。做高频率大数据量传输时值得实测对比,但入门阶段不用纠结,默认的 Fast DDS 已经够用。
多套系统共存时,用域 ID 隔离:
export ROS_DOMAIN_ID=7域 ID 的取值范围一般在 0 到 232 之间,避开系统占用的几个值即可。如果你在实验室里同时有好几台机器人,给每台分配一个固定域 ID,能省掉大量“为什么我的话题被别人的数据污染了”的困惑。
6. launch文件与TF变换:把散装节点拼成一个系统
单跑一个节点没问题,但真实系统往往要同时启动十几个节点、配好参数、设置命名空间和重映射,这时候必须靠 launch 文件。
6.1 Python版launch文件的写法
ROS2 主流用 Python 写 launch,因为它支持逻辑判断和循环,比 XML 灵活得多:
from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='demo_nodes_cpp', executable='talker', name='my_talker', remappings=[('/chatter', '/my_chatter')], parameters=[{'rate': 5}], output='screen' ), ])几个参数值得记住:name可以覆盖节点默认名,避免同名冲突;remappings做话题重映射,这在集成第三方包时几乎必用;output='screen'让节点日志直接打印到终端,调试时很关键,不加的话日志被吞掉,你会以为节点没启动。
启动方式:
ros2 launch my_pkg my_launch.py6.2 TF2坐标变换的正确姿势
TF 是机器人里最抽象也最容易出错的模块。它的核心思想是把整个系统组织成一棵坐标树,每个坐标系只能有一个父节点。常见的树根是map,往下是odom、base_link,再往外是各个传感器和连杆。
发布静态变换的命令行方式:
ros2 run tf2_ros static_transform_publisher 0 0 0.1 0 0 0 base_link laser这行表示 laser 相对 base_link 平移了 z 方向 0.1 米,姿态无旋转。参数顺序是 x y z roll pitch yaw 父坐标系 子坐标系,写反了整棵树就乱了。
排查 TF 问题:
ros2 run tf2_tools view_frames ros2 run tf2_ros tf2_echo map base_linkview_frames生成一张坐标系关系图,能一眼看出是否有多个父节点或者断链。tf2_echo实时打印两个坐标系之间的变换,如果一直等不到输出,说明这两个坐标系之间没有连通的路径。
常见坑:有人把
map到odom的变换和odom到base_link的变换都放在同一个节点里发布,结果定位算法和里程计互相打架。正确分工是里程计发布odom到base_link,定位模块发布map到odom。
6.3 URDF建模与RViz2可视化
机器人模型用 URDF 描述,包含连杆、关节、视觉、碰撞等标签。写完之后用robot_state_publisher加载,它会根据关节角度自动计算并发布各连杆之间的 TF。
ros2 launch my_description display.launch.py配合 RViz2,添加 RobotModel 显示模型,添加 TF 显示坐标系。RViz2 的配置可以保存成.rviz文件,然后在 launch 里加载,避免每次重开都要手工添加显示项。
RViz2 里最常加的显示项包括:RobotModel、TF、LaserScan、PointCloud2、Image、Map、Path、MarkerArray。插件方面,如果要做交互式目标点选择,需要装 2D Goal Pose 工具。
7. 从仿真到真机:几类典型实战场景
理论讲完,谈几个落地场景,这些也是招聘里最常被问到的方向。
7.1 Gazebo仿真环境搭建
Ubuntu 24.04 + Jazzy 对应的仿真器是 Gazebo Harmonic,通过ros_gz桥接包和 ROS2 通信。核心是ros_gz_bridge,它负责在 Gazebo 话题和 ROS2 话题之间搬运数据:
ros2 run ros_gz_bridge parameter_bridge \ /cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist \ /scan@sensor_msgs/msg/LaserScan@gz.msgs.LaserScan桥接的参数格式是话题名@ROS2消息类型@Gazebo消息类型,方向用@还是]、[区分,写错了桥接就不通。这个语法比较反直觉,建议直接抄官方示例再改。
启动常用仿真:
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=falseheadless:=false表示带图形界面。如果你在远程服务器上跑,用headless:=true然后通过话题观察数据,能省下大量显存。
7.2 SLAM建图与导航栈
SLAM 的典型流程是:启动雷达和里程计,跑 SLAM 节点,遥控机器人绕一圈,保存地图,再用导航栈加载地图做路径规划。
ros2 launch slam_toolbox online_async_launch.py ros2 run nav2_map_server map_saver_cli -f my_map保存后会得到.pgm和.yaml两个文件,前者是栅格图像,后者记录分辨率、原点、阈值等元信息。导航时加载 yaml 即可。
对于三维环境,常配合八叉树地图(OctoMap)做三维占据栅格表示,再投影成二维代价地图用于规划。三维地图的优点是能表达悬空障碍和地面高度差,缺点是内存和计算开销明显更大。我的经验是室内平地场景用二维栅格足够,多层建筑或者地形起伏明显的场景才上三维。
导航调参的重点在代价地图的膨胀半径和机器人半径。膨胀半径设小了,规划出的路径会贴着墙走,实际跑起来容易剐蹭;设大了,窄通道直接过不去。
7.3 机械臂仿真与控制
机械臂场景一般涉及三块:模型描述、运动规划(MoveIt2)、仿真或真机驱动。以 UR5e 为例,加载模型和规划组之后,可以在 RViz2 里用交互式标记拖动末端目标位姿,然后点 Plan & Execute 观察轨迹。
逆运动学求解失败是最常见的问题,通常是因为目标位姿超出工作空间或者接近奇异位形。解决办法是先检查目标点是否可达,再调节求解器容差,必要时换个初始关节角度重试。
7.4 传感器接入:深度相机与固态激光雷达
深度相机以 D435i 为例,安装驱动后一条命令启动:
ros2 launch realsense2_camera rs_launch.py默认会发布彩色图、深度图、点云、IMU 等话题。注意分辨率和帧率设置,开太高会导致 USB 带宽吃满,出现丢帧。降低分辨率通常是第一选择。
固态激光雷达以 Livox Avia 为例,需要编译官方 ROS2 驱动,配置好网络参数后启动。这类雷达的输出格式和机械式雷达不同,点云是非重复扫描的,做建图时要注意点云累积方式。配置文件里的 IP 和端口必须和雷达实际设置一致,否则话题里一条数据都没有。
7.5 micro-ROS与嵌入式打通
把微控制器接入 ROS2 系统,micro-ROS 是目前最成熟的方案。典型组合是 ESP32 + PlatformIO 写固件,宿主机跑 micro-ROS agent 做协议转换。
宿主机侧用 Docker 跑 agent:
docker run -it --rm --net=host microros/micro-ros-agent:humble udp4 --port 8888固件侧配置好传输层,指向宿主机的 IP 和端口。接通之后,微控制器上的节点会出现在ros2 node list里,和普通节点一样参与通信。这种方案常用于把底层电机控制、传感器采集下沉到嵌入式端,上层只做决策和规划。
实操心得:micro-ROS 的传输层对网络抖动很敏感,用 Wi-Fi 时容易出现连接断开。能上串口就上串口,稳定性提升非常明显。
8. 常见报错速查与排查思路
这一节基本是踩坑记录,按类别整理,建议收藏备用。
8.1 环境与安装类问题
ros2: command not found是最高频的问题,原因几乎都是环境变量没 source。检查三件事:/opt/ros/<发行版>/setup.bash是否存在、.bashrc里是否写入了 source 语句、当前终端是不是 source 之后打开的。
另一个常见情况是装完 ROS2 之后又装了别的东西,把AMENT_PREFIX_PATH覆盖了。执行printenv | grep AMENT看路径是否指向正确的 install 目录。
多个工作空间叠加时,顺序很重要。先 source 底层发行版,再 source 自己的工作空间,反过来会导致系统包被覆盖。
8.2 通信类问题排查顺序
遇到“节点在跑但收不到数据”,我一般按这个顺序查:
ros2 topic list确认话题是否存在。ros2 topic info /话题名 --verbose看发布者和订阅者是否都在列表里。- 对比双方的 QoS,重点看 Reliability 和 Durability。
- 检查
ROS_DOMAIN_ID和RMW_IMPLEMENTATION是否一致。 - 多机场景下检查防火墙是否放行了组播和对应端口。
这套顺序能覆盖百分之九十以上的通信故障。
8.3 构建与编译类问题
| 报错关键词 | 常见原因 | 解决方向 |
|---|---|---|
| package not found | 依赖没装或没 source | 检查 package.xml,安装缺失依赖 |
| undefined reference | 链接库缺失 | 补 ament_target_dependencies |
| no executable found | install 规则缺失 | 补 install(TARGETS ...) |
| duplicate symbol | 头文件重复定义 | 检查 include guard 和 inline |
| out of memory | 并行编译太多 | 加 --parallel-workers 1 |
编译报错时先看log/latest_build下对应包的日志尾部,那里通常有最直接的错误行,比在终端翻几百行输出快得多。
8.4 速查表
| 命令 | 用途 |
|---|---|
ros2 node list | 列出运行中的节点 |
ros2 node info /xxx | 查看节点接口 |
ros2 topic hz /xxx | 查看话题频率 |
ros2 topic echo /xxx | 打印话题内容 |
ros2 interface show xxx/msg/Yyy | 查看消息字段 |
ros2 param dump /xxx | 导出参数 |
ros2 doctor | 一键体检环境 |
ros2 bag record -a | 录制全部话题 |
ros2 bag play xxx | 回放数据包 |
ros2 doctor这个命令值得单独提一句,它会检查发行版、网络、平台等多项配置,很多时候能直接指出问题所在,比手工一项项查省事。
9. 学习路径与笔试面试考点梳理
最后说下学习节奏和面试准备。ROS2 的知识点比较散,按层次推进效率最高。
9.1 一条相对靠谱的学习路线
第一周搞定环境和工作空间,能独立创建包、写节点、跑通话题通信。第二周吃透服务、动作、参数,能自己设计一个包含三类通信的小系统。第三周上手 launch、TF、URDF、RViz2,让机器人在仿真里动起来。第四周做一遍完整的建图和导航流程。之后按兴趣分化,选视觉、机械臂、嵌入式或者多机协同深挖。
每学一个概念,都动手写一个小例子,别只看教程。ROS2 的很多坑只有自己踩过才有印象。
9.2 笔试面试常考方向
从近几年的题型看,考察重点集中在这几块:ROS1 与 ROS2 的架构差异、DDS 的作用与发现机制、QoS 策略的具体含义与不兼容场景、话题与服务的选型、动作的三段式结构、TF 树的组织规则与常见错误、launch 文件的参数传递方式、以及一个综合场景设计题。
综合设计题常问“设计一个移动机器人系统,包含哪些节点、如何组织通信”。回答时把感知、定位、规划、控制分层说清楚,每层用什么通信原语、为什么这么选,比罗列一堆包名更能体现理解深度。
我在实际用 ROS2 做项目的过程中体会最深的一点是:它把很多原本隐藏在框架里的东西暴露到了台面上,QoS、域 ID、执行器模型这些概念一开始确实增加学习成本,但一旦理解,排查问题时心里就有谱。我个人建议新手别急着堆功能,先把通信这块的基本盘打牢,后面无论转向哪个方向,都会顺很多。