很多零基础的读者,第一次接触 ROS2 时并不是被机器人算法劝退的,而是倒在环境搭建和概念迷宫里。装完一个 Humble 桌面版,打开终端却发现ros2命令不存在;好不容易装好,又不知道工作空间、功能包、节点到底是什么关系;去搜教程,一半是 ROS1 的老文章,另一半把各发行版混在一起讲。这篇文章就是为了解决这三个问题。
我的核心判断是:ROS2 零基础学习,不要按教科书顺序先啃 DDS 和通信中间件,而应该先建立一个完整可运行的最小系统,再逐个理解节点、话题、服务、动作。你不需要在第一天明白 DDS 的分发协议细节,但一定要亲手看到两个节点之间有一条消息在流动。只要这条链路建立起来,后面接触机器人导航、视觉、SLAM,都是在同一个框架上堆模块。
接下来,我会从 ROS2 解决的问题开始,带你把环境搭起来,创建自己的工作空间和功能包,然后用话题、服务、动作三种通信方式,让不同节点真正“说上话”。整个过程以 Ubuntu 22.04 + ROS2 Humble 为例,代码和命令都可以直接复制验证。
1. ROS2 解决了什么问题:从 ROS1 到 ROS2 的本质变化
1.1 ROS1 时代的痛点
ROS1 是一套面向机器人的通信框架,它让不同模块可以像多个小程序一样互相发消息。但在真实项目里,ROS1 暴露了三个非常明显的问题。
第一个问题是中心化依赖。ROS1 默认依赖一个叫作 master 的中心节点,所有话题、服务、参数的注册和发现都要经过它。一旦 master 进程异常退出,整个机器人系统就失去联系,这在稳定运行的机器人上非常致命。
第二个问题是通信实时性不够。ROS1 的 TCPROS 基于 TCP 封装,适合局域网传输,但面对机械臂、移动底盘这类需要确定性延迟的工业场景,它的实时性和可靠性都不够理想。
第三个问题是多语言和跨平台支持不统一。ROS1 的 C++ 和 Python 客户端库维护节奏不同,功能支持也经常不一致,团队协作时很容易出现“C++ 可以,Python 不行”的尴尬情况。
这些痛点在 ROS2 里被集中处理。ROS2 抛弃了 master 中心节点,改用 DDS 作为底层通信中间件,节点之间通过 DDS 的发现机制直接建立连接,这也是 ROS2 从“中心化架构”走向“去中心化架构”的关键一步。
1.2 ROS2 的关键变化
ROS2 在架构上的变化,直接影响了我们后续的编程方式和调试手段,这里挑三个最核心的点说明。
第一,真正的分布式通信。ROS2 不再有 master,节点启动后通过 DDS 的 discovery 协议自动发现网络中的其他节点。这使得多机协同、机器人集群部署更自然,也提升了单个节点异常时的系统容错能力。
第二,接口层标准化。ROS2 沿用了 ROS1 的 topic、service、action 三大通信接口,但底层数据序列化和传输完全交给 DDS。开发者只需要关注消息类型和 QoS 策略,不需要关心底层是 TCP 还是 UDP。
第三,工程工具链更完整。ROS2 引入了ament和colcon作为编译工具链,功能包的组织方式也更规范。加上ros2命令行工具全面统一,无论是查看节点、监听话题还是调用服务,都有一套清晰的命令。
这些变化带来的直接结果就是:ROS2 的入门门槛表面上变高了,因为它涉及 DDS、QoS、生命周期、launch 文件等新概念;但只要先搭好环境、跑通第一个节点,后面再回头理解这些概念,反而会觉得顺理成章。
1.3 零基础学习的正确路径
我给零基础读者的建议是:把学习分成两个阶段。
第一阶段,什么都不用管,先把 ROS2 环境跑起来。安装、创建功能包、运行一个发布者节点,让一条字符串消息能在终端里打印出来。这个阶段的目标是建立“我能跑通 ROS2”的自信。
第二阶段,按“节点、话题、服务、动作”的顺序建立心智模型。不要试图一次性搞懂 DDS 的消息匹配细节,只需理解:节点是进程,话题是异步数据流,服务是同步请求响应,动作是带反馈的长任务。基于这几个抽象概念,你就能看懂绝大多数机器人项目代码。
本文就是按照这个路径写的,先装环境,再讲概念,然后用三种通信协议做实战验证。
2. 核心概念:用“快递站和外卖平台”理解节点、话题、服务、动作
2.1 节点:一个程序就是一个节点
在 ROS2 中,节点是最基本的运行单元,可以简单理解为一个独立运行的程序模块。
一个摄像头驱动是一个节点,一个路径规划模块是一个节点,一个底盘速度控制器也是一个节点。每个节点负责一类职责,并通过 ROS2 的通信机制与其他节点交互。
为什么要拆成这么多节点?直接原因是为了解耦。机器人系统非常复杂,如果把所有功能写进一个可执行文件,代码会越来越难维护。拆成节点之后,每个节点可以独立开发、独立调试、独立重启,也支持用不同语言编写。比如视觉节点用 Python 写,底盘控制节点用 C++ 写,两者通过 ROS2 消息通信,互不影响。
在命令行中查看节点很方便:
ros2 node list ros2 node info /节点名ros2 node info会展示节点下所有话题、服务、动作的发布订阅情况,是排查通信问题时最常用的命令之一。
2.2 话题、服务、动作的本质区别
初学者最容易混淆的就是话题、服务和建议三者。这里用一个贴近生活的场景来解释。
话题(Topic)就像一个大喇叭广播。广播员只负责不停地发布信息,收听者可以随时加入,也可以随时离开,双方都不知道对方是否存在。它适合视频流、传感器数据、状态信息这类持续更新的数据流。注意,话题是异步的、单向的,发布者发布消息后不会等待接收者反馈,本身带有“发出后不管”的性质。
服务(Service)就像两个人打电话。客户端发起请求,服务端处理后返回响应,整个交互是同步的、一次性的。它适合“请计算两个数相加”“请查询当前机器人位置”这种短时间请求-响应模式。
动作(Action)更像一份外包任务。呼叫方发出目标,执行方持续反馈进度,任务完成后返回最终结果。更关键的是,双方可以在任务执行过程中取消它,这非常适合导航到某个点、机械臂抓取物体这类耗时长、可中断的任务。
三者的关系可以用下面这张表快速记忆。
| 通信方式 | 方向 | 是否同步 | 是否长任务 | 是否有反馈 | 是否可取消 | 典型场景 |
|---|---|---|---|---|---|---|
| 话题 Topic | 单向流 | 异步 | 适合持续流 | 无 | 不适用 | 传感器数据、图像、状态发布 |
| 服务 Service | 请求-响应 | 同步 | 短任务 | 无 | 不适用 | 计算、查询、一次触发 |
| 动作 Action | 目标-反馈-结果 | 异步+同步结合 | 长任务 | 有 | 有 | 导航、机械臂执行、复杂任务 |
2.3 工作空间与功能包
工作空间(Workspace)是 ROS2 项目代码的根目录,它把所有功能包、编译产物、日志统一组织起来。
一个标准的工作空间通常包含四个目录:
src:存放功能包源码。build:编译过程中的中间文件和缓存。install:编译后的可执行文件、库、接口文件存放位置。log:编译日志和测试日志。
功能包(Package)是 ROS2 代码组织和发布的最小单元。一个功能包里可以有一个或多个节点,也可以只包含自定义消息接口或启动文件。功能包必须包含package.xml描述文件,以及基于 Python 的setup.py或基于 CMake 的CMakeLists.txt。
理解工作空间和功能包的关系,是理解 ROS2 工程结构的第一步。后面第 5 章会亲手创建,这里先建立一个整体印象。
2.4 常用命令行工具
ROS2 的命令行工具设计得很统一,基本规则是ros2 功能 子命令。零基础阶段记熟下面这几组就够了。
ros2 node list # 查看节点 ros2 node info <node> # 查看节点详情 ros2 topic list # 查看话题列表 ros2 topic echo <topic> # 打印话题消息 ros2 service list # 查看服务列表 ros2 service call <service> <type> <args> # 调用服务 ros2 action list # 查看动作列表 ros2 pkg create # 创建功能包 ros2 run <pkg> <node> # 运行功能包中的节点遇到不记得的参数,直接加--help即可:
ros2 topic --help命令行工具的价值在实战调试中会体现得非常明显,这也是 ROS2 比 ROS1 更好上手的地方之一。
3. 环境准备:操作系统、发行版与依赖
3.1 为什么推荐 Ubuntu 22.04 + ROS2 Humble
ROS2 虽然支持 Windows、macOS 和多个 Linux 发行版,但绝大多数教程、学习资料和机器人工程代码都集中在 Ubuntu 系统上。原因有两方面。
一方面,Ubuntu 是机器人社区事实上的标准系统,X86 和 ARM 平台支持都比较完善,官方文档默认示例也以 Ubuntu 为主。另一方面,ROS2 每个 LTS 版本都对应特定的 Ubuntu LTS 版本,长期支持的组合能减少很多环境兼容问题。
目前最稳妥的组合是:
- 操作系统:Ubuntu 22.04 LTS(Jammy Jellyfish)
- ROS2 发行版:Humble Hawksbill
如果你用的是 Ubuntu 24.04,对应的是更新的 Jazzy 发行版,当前很多第三方库和教案还没有完全跟进。零基础学习阶段,Ubuntu 22.04 + Humble 的社区资料最丰富,遇到问题最容易搜到答案。
3.2 检查系统环境
在开始之前,先确认系统版本是否满足要求。
打开终端执行:
cat /etc/os-release如果输出中包含VERSION_ID="22.04",就可以继续。如果你使用的是 Windows,可以先安装虚拟机,分配至少 4GB 内存和 20GB 磁盘空间,再安装 Ubuntu 22.04。不建议零基础阶段使用双系统或 Docker 环境,因为容易出现额外的显示和权限问题。
3.3 准备常用工具
ROS2 安装过程需要用到curl、gnupg、lsb-release等工具,提前装好可以避免后续卡壳。
sudo apt update sudo apt install -y curl gnupg lsb-release如果你的 Ubuntu 是最小安装版,可能没有安装software-properties-common,建议一并补上:
sudo apt install -y software-properties-common这一步看似简单,却是很多新手在安装过程中卡住的原因,因为缺少工具会导致后面的密钥导入命令执行失败。
4. 环境搭建:一步步安装 ROS2 Humble 桌面版
4.1 配置软件源
ROS2 不在 Ubuntu 默认软件源中,需要先添加 ROS2 官方 apt 源,并导入 ROS 软件源的签名密钥。
先将 ROS2 软件源的密钥下载到系统位置:
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg然后写入软件源配置:
echo "deb [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这里使用的$(lsb_release -cs)会自动替换为当前系统的代号。在 Ubuntu 22.04 上就是jammy。
如果你所在网络访问官方源下载速度较慢,可以将命令中的packages.ros.org替换为可用的国内镜像地址,之后再执行sudo apt update验证是否生效。
4.2 安装 ROS2 桌面版
执行下面的命令刷新软件源并安装 ROS2 Humble 桌面版。
sudo apt update sudo apt install -y ros-humble-desktopros-humble-desktop会安装 ROS2 核心库、常用的机器人演示包、可视化工具 RViz2,以及 turtlesim 小海龟仿真器。这个组合对零基础学习完全够用。如果之后需要开发完整的机器人应用,再按需安装导航、规划等组件。
安装完成后,不要急着关闭终端,还要配置环境变量。
4.3 初始化环境变量
ROS2 的可执行文件和一些环境依赖需要通过setup.bash脚本加载。为了让每次打开终端都自动生效,把它追加到~/.bashrc:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc然后验证 ROS2 是否安装成功:
ros2 --help如果能看到命令帮助信息,说明安装成功。如果提示找不到命令,大概率是source没有执行成功,或者安装过程中出现中断。
4.4 用 turtlesim 验证安装
环境是否真正可用,最简单的方法是启动 turtlesim 小海龟仿真器。
打开终端 A:
ros2 run turtlesim turtlesim_node如果安装正常,会弹出一个蓝色背景的窗口,中间显示一只小乌龟。打开终端 B:
ros2 run turtlesim turtle_teleop_key之后在终端 B 中按方向键,可以控制窗口中这只乌龟移动。这一步可以验证节点发现、话题通信、按键控制模块三个关键链路都正常工作。
如果按方向键没反应,先检查两个终端是否都在运行、窗口是否获得键盘焦点,再看终端 B 有没有因为按键模式被占用。这个现象很常见,不是环境问题。
5. 创建工作空间与功能包:理解 colcon 构建流
5.1 工作空间目录结构
ROS2 使用 colcon 作为统一的构建工具,它会把源码编译到独立目录,避免污染系统环境。
创建一个标准工作空间:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build第一次构建时,colcon build会自动生成build、install、log三个目录。此时src目录为空,所以不会产生实际功能包。
之后每次增加或修改源码,都需要在~/ros2_ws下重新执行colcon build,并重新加载安装环境变量:
source install/setup.bash一个常见错误是只在src目录下执行colcon build,结果提示找不到工程。colcon build必须在工作空间根目录执行,也就是能同时看到src、build、install三个目录的位置。
5.2 创建最小功能包
ROS2 功能包分不同构建类型,Python 功能包使用ament_python构建类型。输入下面命令创建一个功能包:
cd ~/ros2_ws/src ros2 pkg create --build-type ament_python --node-name talker demo_pkg这条命令会创建:
demo_pkg:功能包根目录。demo_pkg/demo_pkg/talker.py:默认节点源码文件。demo_pkg/package.xml:功能包描述文件。demo_pkg/setup.py:Python 功能包的入口脚本和安装配置。demo_pkg/setup.cfg:可执行文件安装目录配置。
其中package.xml定义了功能包名称、版本、依赖和许可协议。setup.py里最关键的是entry_points,它把命令行名字映射到具体的 Python 函数。ROS2 的ros2 run <package> <node>本质上是根据入口点找到对应的 Python 函数并执行。
5.3 编译并用 ros2 run 运行
先回到工作空间根目录编译:
cd ~/ros2_ws colcon build --packages-select demo_pkg--packages-select demo_pkg表示只编译这一个功能包,可以明显减少大型项目中的重复编译时间。
编译完成后,加载安装环境并运行节点:
source install/setup.bash ros2 run demo_pkg talker如果一切正常,终端会持续输出 ROS2 生成的默认日志。再打开一个终端,执行:
ros2 node list列表中会出现talker,说明这个节点已经被 ROS2 发现。到这一步,工作空间和功能包的基础流程就完全跑通了。
6. 话题通信实战:发布者与订阅者
6.1 编写发布者 talker
现在我们把默认的talker.py替换成自己的发布者节点。它负责周期性发布字符串消息。
文件路径:~/ros2_ws/src/demo_pkg/demo_pkg/talker.py
import rclpy from rclpy.node import Node from std_msgs.msg import String class Talker(Node): def __init__(self): super().__init__('talker') self.publisher = self.create_publisher(String, 'chatter', 10) self.timer = self.create_timer(1.0, self.timer_callback) self.count = 0 def timer_callback(self): msg = String() msg.data = f'Hello ROS2: {self.count}' self.publisher.publish(msg) self.get_logger().info(f'Publishing: {msg.data}') self.count += 1 def main(args=None): rclpy.init(args=args) node = Talker() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()这里的核心点有三个。
super().init('talker')给节点命名为talker,这个名称会出现在ros2 node list中。create_publisher(String, 'chatter', 10)表示节点在话题chatter上发布std_msgs/msg/String类型消息,第三个参数10是消息队列深度,不是发送频率。create_timer(1.0, self.timer_callback)表示每秒钟回调一次timer_callback`。
6.2 编写订阅者 listener
订阅者节点负责监听话题,并在收到消息时打印出来。
文件路径:~/ros2_ws/src/demo_pkg/demo_pkg/listener.py
import rclpy from rclpy.node import Node from std_msgs.msg import String class Listener(Node): def __init__(self): super().__init__('listener') self.subscription = self.create_subscription( String, 'chatter', self.listener_callback, 10 ) def listener_callback(self, msg): self.get_logger().info(f'Received: {msg.data}') def main(args=None): rclpy.init(args=args) node = Listener() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()订阅者通过create_subscription注册回调函数,收到消息后由listener_callback处理。这里不需要写任何显式循环,rclpy.spin(node)会持续监听并分发到达的消息。
6.3 注册节点入口
在 setup.py 的entry_points中,需要把listener注册为控制台脚本,否则无法用ros2 run demo_pkg listener运行。
文件路径:~/ros2_ws/src/demo_pkg/setup.py
entry_points={ 'console_scripts': [ 'talker = demo_pkg.talker:main', 'listener = demo_pkg.listener:main', ], },这段配置的意思很直白:命令行中的talker对应demo_pkg/talker.py文件中的main函数,listener同理。修改 setup.py 之后,需要重新编译并 source,才能让入口点生效。
6.4 编译运行与验证
回到工作空间根目录,重新编译并加载环境:
cd ~/ros2_ws colcon build --packages-select demo_pkg source install/setup.bash打开终端 A 运行发布者:
ros2 run demo_pkg talker打开终端 B 运行订阅者:
ros2 run demo_pkg listener正常输出如下:
[INFO] [1700000000.123456789] [listener]: Received: Hello ROS2: 0 [INFO] [1700000000.123456789] [listener]: Received: Hello ROS2: 1这说明发布者和订阅者已经通过话题chatter完成了点对点通信。
还可以打开第三个终端,用命令行直接观察话题:
ros2 topic list ros2 topic echo /chatter ros2 topic hz /chatterros2 topic echo会实时打印话题内容,ros2 topic hz会统计消息发布频率。如果hz显示接近 1.0 Hz,说明通信频率正常。这组命令在后续调试真实机器人时非常实用,建议熟练掌握。
7. 服务通信实战:同步请求与响应
7.1 服务通信适用场景
话题通信适合持续数据流,但很多场景需要“问一句,答一句”。比如客户端请求“计算 2 和 3 的和”,服务端返回结果5。这就是标准的服务通信。
ROS2 的服务接口由两部分组成:客户端发送Request,服务端返回Response。请求和响应是成对出现的,整个过程是同步的,所以不适合长时间运行的任务,也不存在进度反馈。
为了让示例足够简单,直接使用 ROS2 内置的example_interfaces/srv/AddTwoInts接口,它定义了两个整数相加的服务。
7.2 服务端实现
服务端节点创建服务并等待请求到达。
文件路径:~/ros2_ws/src/demo_pkg/demo_pkg/service_server.py
import rclpy from rclpy.node import Node from example_interfaces.srv import AddTwoInts class AddTwoIntsServer(Node): def __init__(self): super().__init__('add_two_ints_server') self.srv = self.create_service( AddTwoInts, 'add_two_ints', self.add_two_ints_callback ) def add_two_ints_callback(self, request, response): response.sum = request.a + request.b self.get_logger().info( f'Incoming request: a={request.a}, b={request.b}, sum={response.sum}' ) return response def main(args=None): rclpy.init(args=args) node = AddTwoIntsServer() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()在 ROS2 Python 服务端中,回调函数必须以return response结尾,否则客户端会一直等待结果。这是一个细节,很容易被忽略。
7.3 客户端实现
客户端节点创建服务代理,发送请求并等待响应。
文件路径:~/ros2_ws/src/demo_pkg/demo_pkg/service_client.py
import sys import rclpy from rclpy.node import Node from example_interfaces.srv import AddTwoInts class AddTwoIntsClient(Node): def __init__(self): super().__init__('add_two_ints_client') self.client = self.create_client(AddTwoInts, 'add_two_ints') while not self.client.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting...') def send_request(self, a, b): req = AddTwoInts.Request() req.a = a req.b = b future = self.client.call_async(req) rclpy.spin_until_future_complete(self, future) return future.result() def main(args=None): rclpy.init(args=args) node = AddTwoIntsClient() if len(sys.argv) == 3: response = node.send_request(int(sys.argv[1]), int(sys.argv[2])) node.get_logger().info(f'Result of {sys.argv[1]} + {sys.argv[2]} = {response.sum}') else: node.get_logger().info('Usage: ros2 run demo_pkg service_client 2 3') node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()wait_for_service的作用是等待服务端就绪,避免客户端启动时服务还没注册,导致请求失败。call_async是异步调用方式,配合spin_until_future_complete可以简单地把异步调用转变成同步等待,适合入门学习。
7.4 运行验证
把服务端和客户端入口注册到 setup.py:
文件路径:~/ros2_ws/src/demo_pkg/setup.py
entry_points={ 'console_scripts': [ 'talker = demo_pkg.talker:main', 'listener = demo_pkg.listener:main', 'service_server = demo_pkg.service_server:main', 'service_client = demo_pkg.service_client:main', ], },重新编译并加载环境:
cd ~/ros2_ws colcon build --packages-select demo_pkg source install/setup.bash终端 A 运行服务端:
ros2 run demo_pkg service_server终端 B 运行客户端:
ros2 run demo_pkg service_client 2 3客户端输出:
[INFO] [1700000000.123456789] [add_two_ints_client]: Result of 2 + 3 = 5同时,服务端终端会打印收到的请求参数。这说明服务通信已经成功。
实际项目中,不一定需要自己写客户端,可以用命令行直接调用服务接口,这对调试非常方便:
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 10, b: 20}"如果一切正常,命令行会直接返回sum: 30。
8. 动作通信实战:带反馈的长任务
8.1 动作通信解决什么问题
话题通信和服务通信都无法很好地表达“任务执行中”。比如机器人要从当前位置导航到目标点,这可能需要十几秒甚至更久,执行过程中用户希望看到进度,也能够随时取消任务。
动作通信专门解决这类问题。动作接口由三部分组成:
- Goal:客户端发出的目标。
- Feedback:服务端在执行过程中回传的进度反馈。
- Result:服务端完成任务后返回的最终结果。
动作的执行是异步的,但客户端可以监听反馈,也可以中途取消目标。理解了反馈和可取消这两个特性,就抓住了动作通信的核心。
8.2 用 turtlesim 体验 action
turtlesim 自带一个绝对旋转动作,非常适合用来体验动作通信。先启动小海龟:
ros2 run turtlesim turtlesim_node在新终端查看系统当前有哪些动作:
ros2 action list -t输出中应该包含:
/turtle1/rotate_absolute [turtlesim/action/RotateAbsolute]查看动作详情:
ros2 action info /turtle1/rotate_absolute然后发送一个旋转 180 度的动作目标:
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 3.14}" --feedback执行这条命令时,你会看到终端持续打印反馈信息,小海龟在窗口中缓缓转向。这就是动作通信中 Feedback 机制最直观的体现。如果没有加--feedback,命令行只会在动作最终完成时返回结果。
8.3 定义自定义 action 的方向
要编写自己的动作通信节点,需要先定义.action接口文件。一个典型的接口文件长这样:
文件路径:~/ros2_ws/src/demo_pkg/action/Count.action
int32 target --- int32 current --- int32 result这三段分别对应 Goal、Feedback、Result。定义接口后,还需要在功能包中配置编译规则让 ROS2 生成对应的 Python 消息代码,然后才能实现动作服务端和客户端。
这个流程比话题和服务略复杂,零基础阶段建议先用 turtlesim 把动作的通信过程跑通,理解 Goal、Feedback、Result 的流转节奏,再深入自定义接口和代码实现。动作通信的完整实战值得单独写一篇,这里先把概念和体验链路建立起来。
9. 常见问题与排查方法
以下是零基础阶段最高频的几类问题,值得收藏备用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
ros2: command not found | 没有加载 ROS2 环境变量 | 检查~/.bashrc是否包含 source 命令 | 执行source /opt/ros/humble/setup.bash |
E: Unable to locate package ros-humble-desktop | 没有添加 ROS2 apt 源,或软件源配置错误 | 检查/etc/apt/sources.list.d/ros2.list是否存在且内容正确 | 重新按 4.1 节配置软件源后sudo apt update |
colcon: command not found | 缺少 colcon 构建工具 | 执行which colcon | 安装sudo apt install python3-colcon-common-extensions |
运行colcon build提示找不到包 | 没有在工作空间根目录执行构建 | 确认当前目录在~/ros2_ws下 | 切换到工作空间根目录后重新构建 |
使用ros2 run demo_pkg talker提示 package 不存在 | 没有加载工作空间的 install 环境 | 检查是否执行了source install/setup.bash | 重新 source 后再运行 |
ModuleNotFoundError: No module named 'rclpy' | 使用python3直接运行节点,而不是ros2 run | 确认是否加载了 ROS2 环境变量 | 用ros2 run demo_pkg talker运行节点 |
| 两个节点互相看不到对方 | ROS_DOMAIN_ID不一致或网络问题 | 在两个终端执行echo $ROS_DOMAIN_ID对比 | 保持ROS_DOMAIN_ID一致,默认都为 0 |
| 修改 setup.py 新增节点后无法运行 | 没有重新编译或没有重新 source | 检查 setup.py 中 entry_points 是否写对 | 重新colcon build并source install/setup.bash |
| turtlesim 启动后按方向键没反应 | 键盘焦点不在终端 B 窗口 | 点击终端 B 窗口后再次按键 | 确认终端 B 保持活动状态 |
| 订阅者收不到发布者的消息 | 话题名拼写不一致或 QoS 不匹配 | 使用ros2 topic list查看实际话题名 | 统一话题名,必要时使用相同 QoS 配置 |
排查这些问题的顺序有一个共性:先看环境变量是否正确加载,再看话题名和节点名是否一致,最后看编译是否成功。绝大多数初学者的问题都能在这三步里解决。
10. 最佳实践与工程建议
10.1 命名规范
功能包名、节点名、话题名和服务名建议统一使用小写字母和下划线,例如demo_pkg、talker、/chatter、/add_two_ints。不要使用大写字母和特殊符号,因为 ROS2 对命名有严格限制,一些工具链在解析这些名字时会产生错误。
每个节点启动时都会分配一个全局唯一的节点名,建议在代码中显式命名,而不是依赖默认值。清晰的节点名能直接提升ros2 node list的可读性。
10.2 构建与源码管理
编译大型工作空间时,建议按需编译,而不是每次都全量构建。使用下面的命令只编译修改过的包:
colcon build --packages-select demo_pkg如果多个包之间互相依赖,可以加上--symlink-install参数,Python 代码修改后不需要重新编译即可生效,这对 Python 开发尤其高效:
colcon build --symlink-install --packages-select demo_pkg使用 git 管理代码时,必须把build、install、log加入.gitignore,不要提交编译产物。团队协作时,其他人克隆仓库后只需要执行colcon build就能重新生成这些目录。
10.3 通信方式选择标准
很多新手写代码时不知道该用话题还是服务,可以按下面标准判断。
如果数据是持续产生的,比如激光雷达扫描、里程计、相机图像、温度状态,用话题。如果数据是查询式的,比如“当前电量是多少”“计算结果是什么”,用服务。如果任务是耗时的、需要进度上报、可能需要被取消,比如导航到目标点、执行机械臂动作序列,用动作。
选错通信方式会导致系统可维护性下降。例如用服务实现一个高频状态查询,每次请求都会阻塞客户端,这不利于系统扩展。
10.4 多节点启动与管理
真实机器人项目通常有几十个节点,逐个运行不现实。ROS2 提供 launch 文件支持同时启动多个节点,并可以为每个节点设置参数。零基础阶段可以先不深入 launch 文件,但要有这个概念。
此外,善用这三个调试命令能解决大部分问题:
ros2 doctor ros2 topic hz /话题名 ros2 node info /节点名ros2 doctor会检查系统环境是否存在常见配置问题,ros2 topic hz能验证消息频率,ros2 node info能看出节点发布和订阅的完整通信关系。
10.5 环境变量与多机通信提醒
ROS2 通过环境变量控制节点发现范围。默认情况下ROS_DOMAIN_ID=0,同一个局域网内所有共享相同 Domain ID 的节点可以互相发现。
如果你想在同一台电脑上运行多套互不干扰的 ROS2 系统,可以为每个系统设置不同的 Domain ID。但要注意,一旦设置了非 0 值,启动所有相关节点时都要保持同一个值,否则节点之间无法通信。
export ROS_DOMAIN_ID=1多机通信还涉及网络发现和防火墙配置,零基础阶段不建议一开始就尝试多机互联,先把单机环境跑稳定,再逐步扩展。
11. 总结与学习路线
到这里,你已经完成了 ROS2 零基础最核心的一段路径:装好 Humble 环境、创建了自己的工作空间和功能包,并亲手用话题、服务、动作三种方式让节点完成通信。掌握这些内容之后,再去看机器人导航、机械臂控制、视觉识别相关的 ROS2 项目,至少不会再对着名词发懵。
建议你把demo_pkg保留下来,它相当于一个环境自检工具。以后换电脑、换系统、需要快速验证 ROS2 环境时,直接编译并运行talker和listener,几秒钟就能确认环境是否正常。
下一步可以考虑深入几个方向。一是自定义消息接口,创建自己的.msg、.srv、.action文件,这会解锁真正的项目开发能力。二是掌握 launch 文件,学会用一条命令启动多个节点并传参。三是结合 RViz2 和仿真环境,把通信机制应用到机器人模型和传感器数据上。
ROS2 的内容很多,但核心骨架就是节点加三种通信。骨架稳了,剩下的都是在这张网上挂载各种工具和算法。建议收藏本文,遇到环境问题和概念混淆时,先回到这里对照一遍。