news 2026/9/9 5:11:49

ROS2零基础实战:从环境搭建到话题服务动作通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ROS2零基础实战:从环境搭建到话题服务动作通信

很多零基础的读者,第一次接触 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 引入了amentcolcon作为编译工具链,功能包的组织方式也更规范。加上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 安装过程需要用到curlgnupglsb-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-desktop

ros-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会自动生成buildinstalllog三个目录。此时src目录为空,所以不会产生实际功能包。

之后每次增加或修改源码,都需要在~/ros2_ws下重新执行colcon build,并重新加载安装环境变量:

source install/setup.bash

一个常见错误是只在src目录下执行colcon build,结果提示找不到工程。colcon build必须在工作空间根目录执行,也就是能同时看到srcbuildinstall三个目录的位置。

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 /chatter

ros2 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 buildsource install/setup.bash
turtlesim 启动后按方向键没反应键盘焦点不在终端 B 窗口点击终端 B 窗口后再次按键确认终端 B 保持活动状态
订阅者收不到发布者的消息话题名拼写不一致或 QoS 不匹配使用ros2 topic list查看实际话题名统一话题名,必要时使用相同 QoS 配置

排查这些问题的顺序有一个共性:先看环境变量是否正确加载,再看话题名和节点名是否一致,最后看编译是否成功。绝大多数初学者的问题都能在这三步里解决。

10. 最佳实践与工程建议

10.1 命名规范

功能包名、节点名、话题名和服务名建议统一使用小写字母和下划线,例如demo_pkgtalker/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 管理代码时,必须把buildinstalllog加入.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 环境时,直接编译并运行talkerlistener,几秒钟就能确认环境是否正常。

下一步可以考虑深入几个方向。一是自定义消息接口,创建自己的.msg.srv.action文件,这会解锁真正的项目开发能力。二是掌握 launch 文件,学会用一条命令启动多个节点并传参。三是结合 RViz2 和仿真环境,把通信机制应用到机器人模型和传感器数据上。

ROS2 的内容很多,但核心骨架就是节点加三种通信。骨架稳了,剩下的都是在这张网上挂载各种工具和算法。建议收藏本文,遇到环境问题和概念混淆时,先回到这里对照一遍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 5:10:52

数据分析中的量级失衡:对数变换、可视化与系统设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/9 5:10:04

2026服装PLM怎么选:先看这5个关键维度

很多人在寻找“服装PLM软件公司排名”时&#xff0c;真正关心的并不是一个机械名次&#xff0c;而是这类系统是否贴合自己的研发流程、交付节奏和长期维护要求。对服装PLM来说&#xff0c;款式开发、BOM工艺、面辅料库、样品打版、版本迭代这些环节能否顺畅串联&#xff0c;往往…

作者头像 李华
网站建设 2026/9/9 5:07:06

C#上位机通过Win32 API枚举窗口控件句柄实战

简介&#xff1a;面向C#开发者的Windows控件句柄获取实例&#xff0c;解决通过窗口名遍历并枚举目标程序所有子控件句柄的常见需求。核心利用FindWindow与EnumChildWindows等Windows API&#xff0c;配合P/Invoke技术实现跨进程窗口遍历&#xff0c;并构建控件句柄树结构&#…

作者头像 李华
网站建设 2026/9/9 5:07:02

STM32H725深度解析:550MHz Cortex-M7内核、存储与实时控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/9 5:04:35

蓄电池超级电容混合储能Simulink能量管理仿真建模与策略详解

做混合储能仿真这么多年&#xff0c;我最大的感受是&#xff1a;蓄电池超级电容这套组合&#xff0c;真正难的从来不是把两个模型拖进一张图里&#xff0c;而是母线上的功率到底怎么分配、电容什么时候该出手、电池什么时候必须扛大梁。这套逻辑想不清楚&#xff0c;Simulink模…

作者头像 李华
网站建设 2026/9/9 5:04:14

20款免费降AIGC率工具实测:原理、操作与避坑全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华