news 2026/9/3 23:37:32

ROS2零基础入门:从环境搭建到三种通信机制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ROS2零基础入门:从环境搭建到三种通信机制实战

在机器人开发领域,ROS 早已不是新鲜词。过去几年,ROS1 在学术研究和原型验证中占据绝对主流,但到了真实产品落地阶段,它的通信实时性、多机协同、安全机制等短板越来越明显。ROS2 正是在这种背景下开始逐步取代 ROS1,成为新项目、新课程、新工具链的首选。不过对于零基础读者来说,ROS2 的上手门槛并不低:环境安装涉及大量系统依赖,工作空间和功能包概念又和传统软件开发不一样,话题、服务、动作三种通信方式也容易混淆。

这篇文章会围绕 ROS2 零基础入门最常见的几个环节展开:从环境搭建开始,到创建工作空间和功能包,再通过可运行的代码案例把话题通信、服务通信、动作通信全部串起来。无论你是刚接触机器人的学生,还是准备转行机器人开发的工程师,都可以按照本文步骤从零跑通。最终你会理解 ROS2 的节点如何组织、消息如何在节点之间传递,并且能独立创建自己的功能包。

1. 为什么选择 ROS2?

1.1 ROS2 与 ROS1 的本质区别

ROS1 的通信架构依赖一个 Master 节点来管理所有节点之间的连接。如果 Master 挂了,整个系统就会失去协调能力。这一点在实验室单机场景下问题不大,但一旦进入多机器人协作、车间部署、车机环境,稳定性就会成为致命伤。

ROS2 则去掉了中心化 Master,底层通信改为 DDS(Data Distribution Service)标准。DDS 是一种工业级的数据分发中间件,支持实时通信、自动发现、QoS 控制,节点之间可以直接发现对方,不需要中心节点转达。简单说,ROS2 把“通信可靠性”从上层应用下沉到了通信中间件层,这让整个系统更适合真实机器人项目。

另一个重要变化是 ROS2 支持 Python、C++ 双语言平等开发。ROS1 对 Python 的支持虽然也存在,但很多核心工具链和通信接口对 C++ 更友好。ROS2 中 Python 节点和 C++ 节点可以混用,通信完全透明,这对新手非常友好。

1.2 本文学习目标与适合人群

本文适合以下三类读者:

  • 没有 ROS 基础,但会一点 Python,想快速入门 ROS2 的初学者。
  • 用过 ROS1,想看 ROS2 环境、工作空间、功能包和通信方式有哪些变化的老开发者。
  • 已经安装过 ROS2,但对话题、服务、动作三种通信机制理解不深,想通过代码加深理解的同学。

读完这篇文章后,你应该能独立完成以下事情:

  • 在 Ubuntu 上安装 ROS2 Humble。
  • 创建自己的工作空间和功能包。
  • 用 Python 写出话题发布者、订阅者。
  • 用 Python 写出服务端、客户端。
  • 用 Python 实现动作通信的 server 与 client。
  • 掌握 ROS2 常用调试命令。

文章示例基于 Python,因为 Python 的代码结构更适合讲清楚通信原理。掌握概念后,转写 C++ 会容易很多。

2. ROS2 环境搭建

2.1 版本选择:ROS2 Humble 还是别的?

ROS2 的发行版本很多,常见的有 Foxy、Galactic、Humble、Iron、Jazzy。对零基础学习者,最推荐的是ROS2 Humble Hawksbill,因为 Humble 是长期支持版本,支持周期较长,社区资料多,网上遇到的坑基本都能搜到解决方案。

需要特别注意,ROS2 每个版本对应的 Ubuntu 版本是固定的。Humble 官方推荐 Ubuntu 22.04(Jammy)。如果你的系统是 Ubuntu 20.04,则需要安装 Foxy;如果是 Ubuntu 24.04,则对应 Jazzy。版本不匹配会出现大量依赖冲突,这是新手最容易踩的坑。

本文以 Ubuntu 22.04 + ROS2 Humble 为例。如果你的环境不同,安装步骤中的 apt 源、包名需要对应调整。

2.2 Ubuntu 22.04 下安装 ROS2 Humble

在安装开始之前,建议先更新系统软件源和软件包索引:

sudo apt update sudo apt upgrade -y

ROS2 官方安装方式是把安装源添加到 apt 中,然后通过 apt 安装。首先安装基础工具:

sudo apt install curl gnupg lsb-release -y

接着导入 ROS2 的 GPG 公钥:

sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg

然后添加 ROS2 apt 源。这里使用系统自动识别的 Ubuntu 版本代号,不需要手动填:

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

再次更新 apt 索引:

sudo apt update

接下来安装桌面版 ROS2。桌面版包含了常用的 rviz2、demo 程序、示例等功能,学习和调试非常方便:

sudo apt install ros-humble-desktop python3-argcomplete -y

安装过程比较长,取决于网络速度。安装完成后,可以安装 colcon 构建工具。colcon 是 ROS2 官方推荐的编译工具,相当于 ROS1 中的 catkin:

sudo apt install python3-colcon-common-extensions -y

这里解释一下为什么要用 colcon:工作空间中有多个功能包时,colcon 可以批量编译,并且会生成buildinstalllog目录,方便管理。

2.3 配置环境变量与验证安装

ROS2 安装完成后,还需要在每次打开终端时加载环境变量。你可以手动输入:

source /opt/ros/humble/setup.bash

为了避免每个新终端都要手动 source,建议写入~/.bashrc

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc

验证是否安装成功:

ros2 --help

如果能看到命令帮助,说明 ROS2 已经安装成功。更直观的验证方式是运行小海龟程序:

sudo apt install ros-humble-turtlesim -y

打开第一个终端,启动海龟仿真器:

ros2 run turtlesim turtlesim_node

打开第二个终端,启动键盘控制节点:

ros2 run turtlesim turtle_teleop_key

此时应该能看到一只小海龟出现在窗口里,按方向键可以控制它移动。这是一个非常经典的 ROS2 运行验证方式,虽然简单,但它已经用到了“节点”和“话题”的概念,后面会详细解释。

如果你的终端提示找不到ros2命令,优先检查环境变量是否加载成功,执行echo $ROS_DISTRO,如果输出为空或错误,则说明 source 没有生效。

2.4 Windows 用户:WSL2 环境说明

如果你使用的是 Windows 系统,最稳妥的方法不是原生安装 ROS2,而是通过 WSL2 安装 Ubuntu 22.04。在 Windows 中安装 WSL2 后,在 Ubuntu 子系统内部按照上面的步骤安装 ROS2 Humble。

WSL2 下 ROS2 的图形界面工具(如 rviz2、turtlesim)需要 WSLg 支持,较新的 Windows 10/11 版本默认自带。如果你在使用过程中看不到图形窗口,可以先更新 WSL 内核,或者检查 Windows 版本是否满足要求。

另外,在 WSL2 中运行硬件相关的机器人驱动可能会遇到 USB 设备透传问题,这一点在实际项目阶段需要单独处理。对于学习通信机制和写功能包来说,WSL2 已经足够。

3. 工作空间与功能包

3.1 工作空间目录结构

在 ROS2 中,工作空间是一个存放所有功能包的顶层目录。通常命名为ros2_ws。一个标准工作空间包含 4 个子目录:

  • src:存放功能包源码。
  • build:编译过程中生成的中间文件。
  • install:编译完成后的安装文件和环境变量。
  • log:编译和运行日志。

创建标准工作空间:

mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build

编译完成后,工作空间中会自动生成buildinstalllog目录。记住一个原则:不要把源码直接放在buildinstall中,所有功能包都放在src下,源码和编译产物分离,便于管理和清理。

3.2 创建第一个功能包

功能包是 ROS2 代码复用和发布的基本单元。一个功能包可以包含节点源码、配置文件、launch 文件、消息接口等。创建功能包时,需要先进入工作空间的src目录:

cd ~/ros2_ws/src ros2 pkg create my_pkg --build-type ament_python --dependencies rclpy std_msgs

解释一下参数:

  • my_pkg:功能包名称。
  • --build-type ament_python:指定 Python 构建类型。
  • --dependencies rclpy std_msgs:声明依赖的库。rclpy是 ROS2 的 Python 客户端库,std_msgs包含常见的标准消息类型。

创建完成后,my_pkg目录结构大致如下:

my_pkg ├── package.xml ├── resource ├── setup.cfg ├── setup.py ├── test └── my_pkg └── __init__.py

其中my_pkg/my_pkg是实际的 Python 包目录,后续编写的节点代码都放在这里。setup.py负责声明可执行程序的入口,package.xml负责声明依赖信息。

3.3 编译与 source

功能包代码写完后,需要回到工作空间根目录编译:

cd ~/ros2_ws colcon build

编译成功后,需要 source 当前工作空间的 setup 文件,这样才能让终端找到新功能包里的可执行程序:

source install/setup.bash

建议把这条命令也写入~/.bashrc,这样每次打开新终端都能自动加载:

echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc

这里需要提醒新手:每次修改功能包代码后,都要重新执行colcon build才能让改动生效,否则ros2 run运行的还是旧版本代码。

4. 通信机制:节点、话题、服务、动作

4.1 核心概念速览

在 ROS2 中,一个独立运行的程序被称为“节点”。节点之间通过不同的通信机制交换数据。最常用的有三种:

  • 话题通信:节点持续发布或订阅数据,适合传感器数据、状态信息等高频单向数据流。
  • 服务通信:客户端发起请求,服务端返回响应,适合一次性调用,比如请求某个位置的坐标。
  • 动作通信:客户端发起目标,服务端持续返回反馈,最终返回结果,适合执行时间长、需要取消的任务。

简单理解:话题是“广播”,服务是“问答”,动作是“交办任务并持续汇报进度”。

4.2 通信机制怎么选

实际项目里,三种通信方式经常会混用。

话题用于高频、单向、无需应答的数据。例如激光雷达扫描数据、摄像头图像、机器人当前速度,都会用话题发布。

服务用于一次性的请求-响应。例如打开机械臂的夹爪、校准陀螺仪、查询当前地图名称,这类操作不需要持续反馈,服务就够用了。

动作用于需要多次反馈的长时间任务。例如让机器人导航到某个目标点、让机械臂执行一段复杂轨迹,这类任务可能执行几十秒甚至几分钟,需要实时反馈进度,并且允许中途取消,这时候就必须使用动作通信。

4.3 rqt_graph 可视化理解

可以用一个可视化工具 rqt_graph 查看节点和话题的连接关系。安装:

sudo apt install ros-humble-rqt-graph -y

启动 turtlesim 并让小海龟运动后,打开新终端运行:

rqt_graph

你会看到两个节点和一个话题连接线。话题的名字和连接方向都一目了然,这对排查通信问题非常有帮助。以后如果发现节点之间收不到数据,第一步就应该打开 rqt_graph 看连接是否存在。

5. 话题通信实战

5.1 发布者节点实现

话题通信中,发布者负责把数据发送到指定话题,订阅者负责从同一话题接收数据。

现在我们在my_pkg中创建一个发布者节点。文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/publisher_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/publisher_node.py import rclpy from rclpy.node import Node from std_msgs.msg import String class SimplePublisher(Node): def __init__(self): super().__init__('simple_publisher') self.publisher_ = self.create_publisher(String, 'chatter', 10) self.timer = self.create_timer(0.5, 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 = SimplePublisher() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

这段代码的核心逻辑是:

  • 继承Node,并将节点命名为simple_publisher
  • 通过create_publisher创建发布者,消息类型为String,话题名为chatter,队列长度为 10。
  • 通过create_timer创建一个 0.5 秒触发一次的定时器。
  • 每次定时器触发,就发布一条字符串消息,并打印日志。

最后那个if __name__ == '__main__':是为了让这个文件也能被命令行直接执行。不过 ROS2 的推荐方式是使用setup.py配置入口,然后通过ros2 run启动。

5.2 订阅者节点实现

发布者写完,再写一个订阅者节点,文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/subscriber_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/subscriber_node.py import rclpy from rclpy.node import Node from std_msgs.msg import String class SimpleSubscriber(Node): def __init__(self): super().__init__('simple_subscriber') self.subscription = self.create_subscription( String, 'chatter', self.listener_callback, 10 ) def listener_callback(self, msg): self.get_logger().info(f'I heard: {msg.data}') def main(args=None): rclpy.init(args=args) node = SimpleSubscriber() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

订阅者的关键点是回调函数listener_callback,只要话题上来了新消息,这个回调函数就会被自动执行。注意create_subscription需要指定消息类型、话题名、回调函数和 QoS 队列长度。

这里有一个新手容易搞混的地方:不要在主线程里反复手动读取话题数据,ROS2 是事件驱动模型,收到消息后回调函数会先执行,所以只需要等待rclpy.spin(node)把程序阻塞住,不断处理回调即可。

5.3 配置入口并编译运行

为了让ros2 run能找到这两个节点,需要在setup.py中配置入口。编辑文件:

~/ros2_ws/src/my_pkg/setup.py

entry_points中添加:

entry_points={ 'console_scripts': [ 'simple_publisher = my_pkg.publisher_node:main', 'simple_subscriber = my_pkg.subscriber_node:main', ], },

回到工作空间根目录编译:

cd ~/ros2_ws colcon build source install/setup.bash

打开两个终端。终端 1 运行订阅者,终端 2 运行发布者:

ros2 run my_pkg simple_subscriber
ros2 run my_pkg simple_publisher

终端 1 中应该能看到发布者发来的每一条消息,例如:

[INFO] I heard: Hello ROS2: 0 [INFO] I heard: Hello ROS2: 1

话题通信已经跑通了。

5.4 使用 CLI 工具验证话题

除了写代码,ROS2 提供了一系列命令行工具,排查问题非常方便。

查看当前话题列表:

ros2 topic list

查看某个话题的消息类型:

ros2 topic info /chatter

实时监听话题中的数据:

ros2 topic echo /chatter

手动向话题发布数据:

ros2 topic pub /chatter std_msgs/msg/String "{data: 'Hello CLI'}" --once

这些命令是话题调试的利器。尤其当你写了一个复杂系统却收不到数据时,先用 CLI 工具确认话题是否存在、消息是否真正发布,可以快速缩小问题范围。

6. 服务通信实战

6.1 服务通信接口介绍

服务通信使用客户端-服务端模式。客户端发送请求,服务端处理请求并返回响应。ROS2 中服务的接口文件后缀为.srv,格式分成上下两部分,中间用---分隔,上半部分是请求,下半部分是响应。

本文使用example_interfaces/srv/AddTwoInts.srv,这是一个内置的示例服务接口,内容等价于:

int64 a int64 b --- int64 sum

也就是说,客户端传入两个整数ab,服务端返回它们的和sum

6.2 服务端节点实现

my_pkg中创建服务端节点,文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/service_server_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/service_server_node.py import rclpy from rclpy.node import Node from example_interfaces.srv import AddTwoInts class AddTwoIntsService(Node): def __init__(self): super().__init__('add_two_ints_service') 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'Received: {request.a} + {request.b} = {response.sum}' ) return response def main(args=None): rclpy.init(args=args) node = AddTwoIntsService() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

这里最需要注意的是回调函数的写法:requestresponse都由框架自动创建,回调返回的是填充后的response。这个返回值会被自动发送给客户端,不需要手动处理网络传输。

6.3 客户端节点实现

客户端节点比服务端稍复杂一点,因为它需要等待服务上线,并通过异步方式发送请求。文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/service_client_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/service_client_node.py 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.cli = self.create_client(AddTwoInts, 'add_two_ints') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('Service not available, waiting...') self.req = AddTwoInts.Request() def send_request(self, a, b): self.req.a = a self.req.b = b future = self.cli.call_async(self.req) rclpy.spin_until_future_complete(self, future) return future.result() def main(args=None): rclpy.init(args=args) node = AddTwoIntsClient() response = node.send_request(5, 3) node.get_logger().info(f'Service result: {response.sum}') node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

服务端启动后,客户端会先循环等待服务可用,避免出现“服务没起来,请求直接失败”的情况。call_async是异步调用,它会立即返回一个 future,后续通过rclpy.spin_until_future_complete阻塞直到拿到结果。

6.4 运行验证

setup.pyentry_points中加入服务端和客户端入口:

'add_two_ints_server = my_pkg.service_server_node:main', 'add_two_ints_client = my_pkg.service_client_node:main',

重新编译:

cd ~/ros2_ws colcon build source install/setup.bash

先启动服务端:

ros2 run my_pkg add_two_ints_server

然后启动客户端:

ros2 run my_pkg add_two_ints_client

客户端会打印结果:

[INFO] Service result: 8

服务端终端中也会收到一条日志。这说明服务通信成功。

也可以用命令行直接请求服务,不需要写客户端代码。新开终端执行:

ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 10, b: 20}"

这个命令非常适合测试服务端是否正常。

7. 动作通信实战

7.1 动作通信接口与适用场景

动作通信是最接近真实机器人任务的一种交互方式。它包含目标、反馈、结果三部分,客户端下发目标,服务端在执行过程中不断发布反馈,最后返回结果。整个过程还支持取消任务。

本文使用example_interfaces/action/Fibonacci.action。这个接口内容等价于:

int32 order --- int32[] sequence --- int32[] partial_sequence

目标是计算斐波那契数列的前order项,服务端在计算过程中不断反馈已经算出的部分序列,最后返回完整结果。

在实际项目中,动作通信常用于导航、机械臂规划这类长时间任务。

7.2 动作服务端节点实现

my_pkg中创建动作服务端节点,文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/action_server_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/action_server_node.py import rclpy import rclpy.action from rclpy.node import Node from example_interfaces.action import Fibonacci class FibonacciActionServer(Node): def __init__(self): super().__init__('fibonacci_action_server') self._action_server = rclpy.action.create_server( self, Fibonacci, 'fibonacci', self.execute_callback ) def execute_callback(self, goal_handle): self.get_logger().info('Executing goal...') feedback_msg = Fibonacci.Feedback() feedback_msg.partial_sequence = [0, 1] for i in range(1, goal_handle.request.order): feedback_msg.partial_sequence.append( feedback_msg.partial_sequence[i] + feedback_msg.partial_sequence[i - 1] ) goal_handle.publish_feedback(feedback_msg) goal_handle.succeed() result = Fibonacci.Result() result.sequence = feedback_msg.partial_sequence return result def main(args=None): rclpy.init(args=args) node = FibonacciActionServer() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

动作服务端的核心是execute_callback。这个回调函数接收一个goal_handle,通过它可以发布反馈、标记目标成功或失败。在长时间任务中,建议在循环里周期性调用publish_feedback,这样客户端才能实时看到进度。

7.3 动作客户端节点实现

动作客户端在发送目标之后,需要等待服务端接受目标,同时接收反馈,最后等待最终结果。文件路径为:

~/ros2_ws/src/my_pkg/my_pkg/action_client_node.py

代码如下:

# 文件路径:~/ros2_ws/src/my_pkg/my_pkg/action_client_node.py import rclpy import rclpy.action from rclpy.node import Node from example_interfaces.action import Fibonacci class FibonacciActionClient(Node): def __init__(self): super().__init__('fibonacci_action_client') self._action_client = rclpy.action.create_client( self, Fibonacci, 'fibonacci' ) def send_goal(self, order): goal_msg = Fibonacci.Goal() goal_msg.order = order self._action_client.wait_for_server() future = self._action_client.send_goal_async( goal_msg, feedback_callback=self.feedback_callback ) future.add_done_callback(self.goal_response_callback) def goal_response_callback(self, future): goal_handle = future.result() if not goal_handle.accepted: self.get_logger().info('Goal rejected.') return self.get_logger().info('Goal accepted.') result_future = goal_handle.get_result_async() result_future.add_done_callback(self.get_result_callback) def feedback_callback(self, feedback_msg): self.get_logger().info( f'Received feedback: {feedback_msg.feedback.partial_sequence}' ) def get_result_callback(self, future): result = future.result().result self.get_logger().info(f'Result: {result.sequence}') rclpy.shutdown() def main(args=None): rclpy.init(args=args) node = FibonacciActionClient() node.send_goal(10) rclpy.spin(node) if __name__ == '__main__': main()

动作客户端的回调比较多,理解起来比话题复杂。可以这样记忆:发送目标后,先触发“目标响应回调”,判断服务端是否接受目标;接受后,后续每一次服务端发布反馈都会触发“反馈回调”;最终任务完成,触发“结果回调”。整个过程是一个完整的异步状态机。

7.4 运行验证

setup.pyentry_points中添加:

'fibonacci_action_server = my_pkg.action_server_node:main', 'fibonacci_action_client = my_pkg.action_client_node:main',

编译并 source:

cd ~/ros2_ws colcon build source install/setup.bash

先启动动作服务端:

ros2 run my_pkg fibonacci_action_server

再启动动作客户端:

ros2 run my_pkg fibonacci_action_client

客户端会依次打印反馈序列,最终打印结果:

[INFO] Received feedback: [0, 1, 1] [INFO] Received feedback: [0, 1, 1, 2] [INFO] Received feedback: [0, 1, 1, 2, 3] ... [INFO] Result: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

也可以用命令行发送动作目标:

ros2 action send_goal /fibonacci example_interfaces/action/Fibonacci "{order: 5}" --feedback

--feedback参数可以实时显示反馈,这对调试动作通信非常方便。

8. 常见问题与排查思路

8.1 高频报错与解决方式

新手在 ROS2 环境搭建和通信代码编写中,最常遇到下面这些问题:

问题现象常见原因解决思路
找不到命令ros2环境变量没有加载执行source /opt/ros/humble/setup.bash,并检查~/.bashrc
ros2 pkg create报 Package not found功能包名冲突或语法错误确认包名符合命名规范,不要使用已存在的相同包名
编译时报模块找不到依赖没有安装或未 source 工作空间安装对应依赖,并重新执行source install/setup.bash
话题收不到消息话题名不一致、节点没有运行、QoS 不匹配ros2 topic listrqt_graph检查连接关系
服务调用超时服务端没有启动或服务名错误先启动服务端,用ros2 service list查询服务名
动作目标一直不被接受动作服务端没有运行或 action 类型不匹配检查动作服务名称和类型,使用ros2 action list查看
在 WSL2 中看不到图形窗口WSLg 异常更新 WSL 内核,或检查 Windows 系统版本

8.2 调试工具与排查思路

遇到通信问题,不要盲目改代码,建议按顺序排查:

第一步,确认节点都在运行:

ros2 node list

如果节点不在列表中,检查是否 source 了工作空间,是否编译成功。

第二步,确认话题、服务、动作都在列表中:

ros2 topic list ros2 service list ros2 action list

第三步,查看消息类型是否匹配:

ros2 topic info /chatter ros2 service type /add_two_ints ros2 action type /fibonacci

第四步,直接使用 CLI 工具发布或请求,判断问题在发送端还是接收端。

这套流程基本可以解决 90% 的通信问题。

9. 工程实践与开发建议

9.1 命名与目录规划

功能包名称建议使用小写字母和下划线,例如my_pkgrobot_navigation,不要使用大写字母或中划线。节点名称一般和功能包名区分开,表达功能含义,比如camera_nodelidar_node

与 ROS1 相比,ROS2 的命名空间和话题重映射能力更强。同一个功能包可以启动多个节点实例,通过不同命名空间区分数据流,这在多机器人场景中非常实用。

目录规划上,一个功能包内部可以按作用拆分:

my_pkg/ ├── my_pkg/ │ ├── publisher_node.py │ ├── subscriber_node.py │ ├── service_server_node.py │ ├── service_client_node.py │ └── action_server_node.py ├── launch/ ├── config/ ├── package.xml └── setup.py

节点文件多以后,把 launch 和 config 单独放在目录中,是工程化的重要一步。

9.2 尽量使用 launch 文件

实际项目中通常有多个节点需要同时启动。如果每个节点都开一个终端,既不便于维护,也不能保证启动顺序。ROS2 的 launch 文件可以统一管理节点启动。

在功能包下新建launch目录,创建一个 launch 文件:

# 文件路径:~/ros2_ws/src/my_pkg/launch/talker_listener.launch.py from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='my_pkg', executable='simple_publisher', name='publisher', output='screen' ), Node( package='my_pkg', executable='simple_subscriber', name='subscriber', output='screen' ), ])

setup.pydata_files中加入 launch 目录:

import os from glob import glob data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')), ],

之后只需要一条命令启动多个节点:

ros2 launch my_pkg talker_listener.launch.py

launch 文件支持参数传递、条件判断、命名空间设置,是 ROS2 大型项目不可缺少的一部分。

9.3 QoS 与日志规范

ROS2 的 QoS(Quality of Service)策略决定了消息传输的可靠性。默认情况下,话题使用keep_last(10)策略,这条策略表示发送端缓冲区最多保留 10 条历史消息。在传感器数据传输或命令控制中,需要根据实际场景调整 QoS。

新手最容易遇到的问题就是:发布者和订阅者的 QoS 不匹配,导致消息传输失败或降级。如果通信不稳,优先检查双方的 QoS 设置,例如:

self.publisher_ = self.create_publisher(String, 'chatter', 10)

这里的10就是 QoS 队列深度。在关键控制场景中,建议使用reliable传输;在高频传感器场景中,建议使用best_effort,否则会引入不必要的延迟。

日志方面,ROS2 提供了统一的日志系统,节点中打印日志应该使用self.get_logger(),而不是直接print。这样日志可以集成到 ROS2 的日志框架中,便于按级别过滤和控制。

10. 总结与后续学习路线

到这里,你已经完成了 ROS2 的第一个完整闭环:安装环境、创建工作空间、创建功能包,并分别实现了话题、服务、动作三种通信方式的节点代码。这四件事是 ROS2 开发的基本功,无论以后做什么机器人项目,都离不开这些基础能力。

下一步可以继续学习三个方向:

第一个方向是硬件接入。把单片机、激光雷达、摄像头接入 ROS2,需要掌握传感器驱动包的使用方法,以及消息类型如何转换。建议从摄像头和雷达开始,因为这些传感器的 ROS2 驱动比较成熟。

第二个方向是机器人建模与仿真。学习 URDF 文件格式、Gazebo 仿真环境、rviz2 可视化,在仿真中验证算法,再迁移到真机,可以大幅降低开发风险。

第三个方向是导航和运动控制。学习 Nav2 导航框架,理解地图、定位、路径规划、代价地图等概念,这些都是机器人项目落地时最常用的模块。

如果你现在还在犹豫要不要继续学,不妨先不要看太多理论。安装好 Humble 后,跑一次小海龟,再动手把 publisher 和 subscriber 这两段代码改成你想要的数据类型。等你亲手把一个自定义话题跑通,ROS2 的大门就已经打开了。

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

Claude发现密码学算法数学缺陷:AI如何变革安全验证方法论

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

作者头像 李华
网站建设 2026/9/3 23:30:06

U17女排世锦赛五连胜晋级16强:系统作战背后的技术复盘

从3-0横扫秘鲁看U17女排世锦赛:五连胜小组第一,16强背后是一套“系统作战” 如果你只看最终比分,中国女排3-0横扫秘鲁女排,五连胜小组第一晋级16强,你可能会觉得这是一场“理所当然”的胜利。 但真正值得拆解的&#…

作者头像 李华
网站建设 2026/9/3 23:26:32

合成孔径雷达回波信号与RDA成像算法详解

简介:面向SAR成像算法学习与遥感初学者,这份资源以MATLAB实现为基础,演示了从回波信号生成到RDA成像的完整链路,重点解决距离徙动校正如何影响图像聚焦质量这一核心问题。压缩包体积仅7KB,共包含3个.m脚本,…

作者头像 李华
网站建设 2026/9/3 23:24:14

基于YOLOv8与PySide6的花卉识别系统完整开发实践

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

作者头像 李华
网站建设 2026/9/3 23:23:34

SpringBoot毕业设计全流程:选题、论文、答辩与排查指南

计算机专业毕业设计的完整链路,并不只是把 SpringBoot 项目跑通,也不只是写完论文就结束。真正影响成绩的,往往是选题是否合适、论文能否把系统讲清楚、答辩时能否回答老师的提问。SpringBoot 是目前高校毕设中使用率最高的技术栈之一&#x…

作者头像 李华