ROS入门实战:从零搭建环境到掌握Topic与Service通信

发布时间:2026/8/25 4:41:04
ROS入门实战:从零搭建环境到掌握Topic与Service通信 如果你是一名机器人开发者或者对具身智能、自动驾驶、无人机、机械臂等前沿领域感兴趣那么你一定绕不开一个名字ROS。但当你真正开始学习时可能会立刻陷入迷茫官方文档浩如烟海概念抽象难懂环境搭建频频报错好不容易装好了却不知道如何让两个程序“对话”。这导致很多人的ROS学习之旅在“Hello World”之前就宣告结束。这篇文章要解决的正是这个核心痛点。我们不会从枯燥的“什么是ROS”开始而是直接切入你最关心的问题如何用最短的时间搭建一个可用的ROS环境并亲手实现两个最核心的通信机制——Topic和Service从而真正迈出机器人软件开发的第一步。本文的判断是ROS的学习曲线陡峭但核心逻辑清晰。一旦掌握了“通信”这一基石后续的导航、感知、控制等复杂功能都是在此基础上的模块化堆叠。本文将为你提供一个保姆级的实战教程。你将学到的不只是“怎么做”更是“为什么这么做”。我们会从零开始在Ubuntu系统上搭建ROS环境然后通过两个生动的Python示例分别演示话题通信持续、单向的数据流如传感器数据和服务通信请求-响应式的远程调用如开关控制。每个步骤都包含可复制的代码和命令并解释其背后的设计哲学。读完本文你将能够独立创建ROS节点理解消息传递机制并为后续开发真实的机器人应用打下坚实基础。1. ROS究竟是什么重新理解“机器人操作系统”很多教程会告诉你ROS是Robot Operating System的缩写是一个用于编写机器人软件的框架。这个定义没错但过于抽象无法指导实践。我们需要一个更本质的理解。你可以把ROS想象成一个机器人软件界的“邮政系统”“电话系统”“服务注册中心”。它本身不直接控制电机或读取传感器而是为那些真正干活的程序我们称之为“节点”提供一个高效、可靠、解耦的沟通方式。邮政系统Topic一个节点比如激光雷达驱动可以把扫描到的数据“打包”成标准格式的“信件”消息然后“投递”到某个“邮箱”话题。任何对此感兴趣的节点比如建图程序、避障程序都可以去“订阅”这个邮箱定期收取信件。这个过程是异步、单向、一对多的。传感器数据流、速度指令流都适合用这种方式。电话系统Service一个节点比如机械臂控制器提供一项“服务”比如“移动到某位置”。另一个节点比如任务规划器可以像打电话一样发出一个具体的“请求”目标坐标然后等待对方的“响应”是否成功。这个过程是同步、双向、一对一的。执行某个具体命令、查询某个状态适合用这种方式。服务注册中心MasterROS Master是这个系统的“总机”或“通讯录”。所有节点启动时都要向Master注册自己提供了哪些话题或服务以及需要哪些话题或服务。Master负责帮它们“牵线搭桥”。没有Master节点之间就无法发现彼此。所以ROS的核心价值在于通信和工具。它通过标准化消息格式、提供丰富的通信中间件和可视化调试工具如Rviz, rqt让开发者可以专注于编写单个功能模块节点而无需操心模块之间如何连接、数据如何序列化、网络如何传输等底层繁琐问题。这正是“操作系统”一词的隐喻——它管理的是机器人软件进程之间的资源和通信。2. 环境搭建避开新手第一个大坑理论之后立刻动手。环境搭建是新手的第一道坎网上教程众多但良莠不齐容易因版本、源、依赖等问题失败。我们选择最主流、最稳定的组合Ubuntu 20.04 LTS ROS Noetic Ninjemys。这是ROS1的最后一个长期支持版本社区支持完善资料最多。2.1 系统准备与源配置首先确保你有一个Ubuntu 20.04的环境物理机、虚拟机或WSL2均可。打开终端开始操作。配置软件源为了从ROS官方服务器快速下载我们需要设置Ubuntu的软件源。执行以下命令它会打开源列表文件。sudo sh -c echo deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main /etc/apt/sources.list.d/ros-latest.list设置密钥添加ROS仓库的密钥确保下载的软件包是官方的、未被篡改的。sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654如果上述密钥服务器访问慢或失败可以尝试备用命令curl -sSL http://keyserver.ubuntu.com/pks/lookup?opgetsearch0xC1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 | sudo apt-key add -更新软件包列表让系统识别新添加的ROS源。sudo apt update2.2 安装ROS Noetic现在开始安装ROS。ROS提供了多个版本ros-noetic-desktop-full完整版包含GUI工具、仿真器、ros-noetic-desktop桌面版、ros-noetic-ros-base基础版。对于学习和大多数开发推荐安装完整版。sudo apt install ros-noetic-desktop-full这个过程会下载数百个软件包耗时较长取决于你的网络速度请耐心等待。2.3 环境配置与依赖安装安装完成后ROS的核心命令如roscore,rosrun还无法直接在终端中使用因为它们的路径没有被添加到系统的环境变量中。初始化rosdeprosdep是ROS的一个工具用于安装系统依赖。这是很多功能如编译自己的包能正常运行的前提。sudo rosdep init rosdep update注意sudo rosdep init只需执行一次。如果提示文件已存在可忽略或先备份删除/etc/ros/rosdep/sources.list.d/20-default.list文件后再试。设置环境变量为了让终端识别ROS命令需要将ROS的环境设置脚本添加到你的shell配置文件中如~/.bashrc。这样每次打开新终端环境都会自动配置好。echo source /opt/ros/noetic/setup.bash ~/.bashrc source ~/.bashrc第一行命令将配置语句追加到.bashrc文件末尾。第二行命令是立即生效无需重启终端。安装构建工具为了后续创建和编译我们自己的ROS软件包需要安装ROS的构建系统依赖。sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool build-essential2.4 验证安装完成以上所有步骤后让我们验证ROS是否安装成功。启动ROS Master在一个终端中运行ROS的核心——Master节点。roscore如果看到类似下面的输出并且程序没有退出说明Master启动成功。... logging to /home/yourname/.ros/log/... started core service [/rosout]测试小乌龟仿真器保持roscore运行新开一个终端。在新终端中运行一个经典的ROS演示程序——小乌龟仿真器。rosrun turtlesim turtlesim_node此时一个带有随机小乌龟的蓝色窗口应该会弹出。控制小乌龟再新开一个终端运行键盘控制节点。rosrun turtlesim turtle_teleop_key在这个终端窗口激活的状态下用鼠标点一下它按键盘上的方向键你应该能看到蓝色窗口里的小乌龟开始移动。恭喜如果你完成了以上所有步骤并看到了移动的小乌龟那么你的ROS基础环境已经成功搭建。按CtrlC可以停止上面运行的各个程序。3. 理解ROS的核心工作空间与包在开始写代码之前必须理解ROS的代码组织方式。ROS代码不是随便放在一个文件夹里就能用的它必须存在于一个工作空间中并且以包的形式组织。工作空间Workspace一个包含所有ROS包源代码、编译中间文件和安装文件的目录树。你可以有多个工作空间来隔离不同项目。包PackageROS软件的基本单元是组织代码的最小单位。一个包可以包含节点可执行程序、库、配置文件、启动文件等。每个包都必须有一个package.xml文件定义包信息和依赖和一个CMakeLists.txt文件用于编译。接下来我们创建一个名为catkin_ws的工作空间并在其中创建我们的第一个包my_first_ros_pkg。# 1. 创建并进入工作空间目录 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src # 2. 初始化工作空间。这会在src目录下生成CMakeLists.txt的符号链接。 catkin_init_workspace # 3. 回到工作空间根目录并编译。即使现在src里没有包也要编译一次来生成必要的环境设置文件。 cd ~/catkin_ws catkin_make # 4. 编译后会生成devel和build文件夹。同样需要将devel文件夹下的环境设置脚本加入bashrc。 echo source ~/catkin_ws/devel/setup.bash ~/.bashrc source ~/.bashrc # 5. 现在在src目录下创建我们的第一个包。包名是my_first_ros_pkg依赖roscpp和rospyC和Python的ROS客户端库。 cd ~/catkin_ws/src catkin_create_pkg my_first_ros_pkg rospy std_msgs命令执行后你会看到my_first_ros_pkg目录下自动生成了package.xml和CMakeLists.txt文件以及src、include等文件夹。我们的Python代码将放在scripts文件夹里需要手动创建。4. 实战一Topic通信——模拟传感器数据发布Topic是ROS中最常用、最典型的通信方式。我们来实现一个经典场景一个节点talker.py模拟发布温度传感器数据另一个节点listener.py订阅并打印这些数据。4.1 创建消息发布者Publisher首先在包内创建scripts文件夹来存放Python脚本并创建发布者节点。cd ~/catkin_ws/src/my_first_ros_pkg mkdir scripts cd scripts touch talker.py chmod x talker.py # 赋予可执行权限用文本编辑器如nano,vim或VSCode打开talker.py写入以下代码#!/usr/bin/env python3 # 文件路径~/catkin_ws/src/my_first_ros_pkg/scripts/talker.py import rospy from std_msgs.msg import Float32 import random def talker(): # 初始化一个ROS节点命名为temperature_publisher # anonymousTrue 确保节点名称唯一避免重复启动冲突 rospy.init_node(temperature_publisher, anonymousTrue) # 创建一个Publisher发布到名为sensor_temperature的topic # 消息类型是Float32浮点数队列大小queue_size10 pub rospy.Publisher(sensor_temperature, Float32, queue_size10) # 设置发布频率每秒1次 (1 Hz) rate rospy.Rate(1) rospy.loginfo(温度传感器节点已启动正在发布数据...) # 在ROS没有被关闭的情况下循环 while not rospy.is_shutdown(): # 模拟生成一个20.0到25.0度之间的随机温度值 temperature random.uniform(20.0, 25.0) # 发布消息 pub.publish(temperature) # 在终端打印发布的信息便于调试 rospy.loginfo(发布温度: %.2f °C, temperature) # 按照设定的频率休眠 rate.sleep() if __name__ __main__: try: talker() except rospy.ROSInterruptException: # 当用户按下CtrlC时会触发此异常安静退出 pass代码关键点解析rospy.init_node(): 每个ROS节点都必须初始化并有一个唯一名称。rospy.Publisher(): 创建发布者。三个参数分别是话题名、消息类型、队列大小。队列用于缓存来不及处理的消息防止数据丢失。Float32: 这是ROS标准消息库std_msgs中的一种基本类型。我们也可以自定义复杂的消息类型。rospy.Rate(): 控制循环频率是ROS中常用的定时工具。rospy.loginfo(): ROS提供的日志工具比print更好可以输出不同级别INFO, WARN, ERROR的日志。4.2 创建消息订阅者Subscriber在同一个scripts目录下创建订阅者节点。cd ~/catkin_ws/src/my_first_ros_pkg/scripts touch listener.py chmod x listener.py打开listener.py写入以下代码#!/usr/bin/env python3 # 文件路径~/catkin_ws/src/my_first_ros_pkg/scripts/listener.py import rospy from std_msgs.msg import Float32 def temperature_callback(data): 这是一个回调函数。 当订阅的topic有新的消息到达时这个函数会被自动调用。 data参数就是接收到的消息内容。 # 从消息中提取数据Float32类型的数据在.data属性中 temp data.data rospy.loginfo(接收到温度数据: %.2f °C, temp) # 这里可以添加更多的处理逻辑比如判断温度是否超限控制空调等。 def listener(): # 初始化节点命名为temperature_subscriber rospy.init_node(temperature_subscriber, anonymousTrue) # 创建一个Subscriber订阅名为sensor_temperature的topic # 消息类型是Float32当有新消息时调用temperature_callback函数 rospy.Subscriber(sensor_temperature, Float32, temperature_callback) rospy.loginfo(温度监听节点已启动等待数据...) # rospy.spin() 使节点保持运行等待并处理回调函数。 # 它是一个阻塞调用会一直运行直到节点被关闭。 rospy.spin() if __name__ __main__: listener()代码关键点解析rospy.Subscriber(): 创建订阅者。参数分别是话题名、消息类型、回调函数。一旦有消息发布到该话题回调函数就会被触发。回调函数这是ROS异步编程的核心。你不需要主动去“轮询”或“读取”数据只需要定义好收到数据后要做什么回调函数ROS会在数据到达时自动调用它。这非常高效。rospy.spin(): 让节点进入等待循环持续监听消息并调用相应的回调函数。没有这行代码程序会直接结束。4.3 编译与运行测试Python脚本不需要catkin_make编译C节点需要但需要确保ROS能找到我们的包。在工作空间根目录下执行编译可以刷新包路径。cd ~/catkin_ws catkin_make source devel/setup.bash现在让我们运行这个Topic通信的demo。终端1启动ROS Masterroscore终端2运行发布者节点cd ~/catkin_ws source devel/setup.bash rosrun my_first_ros_pkg talker.py你应该看到终端开始每秒打印一次发布的温度值。终端3运行订阅者节点cd ~/catkin_ws source devel/setup.bash rosrun my_first_ros_pkg listener.py你应该看到这个终端开始每秒打印一次接收到的温度值与发布者的数据同步。现象与理解你成功创建了一个一对一的发布-订阅模型。talker节点不断向/sensor_temperature话题发布数据listener节点订阅该话题并处理数据。它们彼此不知道对方的存在只通过话题名这个“邮箱地址”进行通信实现了彻底的解耦。你可以尝试再启动一个listener.py需要修改节点名或使用anonymousTrue会发现两个订阅者都能收到数据这就是Topic的一对多特性。5. 实战二Service通信——实现一个加法器Service用于处理请求-响应式的交互。我们来实现一个简单的加法器服务客户端发送两个数字服务端计算它们的和并返回结果。5.1 创建服务端Server首先我们需要定义服务的数据类型。ROS Service的消息定义包含两部分请求Request和响应Response。对于加法器请求是两个float64数字响应是一个float64的和。幸运的是ROS已经为我们提供了一个标准的加法服务定义AddTwoInts虽然名字是Ints但实际处理浮点数也可以。为了学习我们假设使用这个标准服务。在scripts目录下创建服务端add_two_ints_server.py。cd ~/catkin_ws/src/my_first_ros_pkg/scripts touch add_two_ints_server.py chmod x add_two_ints_server.py写入以下代码#!/usr/bin/env python3 # 文件路径~/catkin_ws/src/my_first_ros_pkg/scripts/add_two_ints_server.py from rospy_tutorials.srv import AddTwoInts, AddTwoIntsResponse import rospy def handle_add_two_ints(req): 处理加法请求的回调函数。 req 是服务请求对象里面包含了客户端发送的a和b。 rospy.loginfo(收到请求: %d %d, req.a, req.b) # 执行加法计算 sum req.a req.b rospy.loginfo(返回结果: %d, sum) # 返回一个AddTwoIntsResponse对象其中包含结果sum return AddTwoIntsResponse(sum) def add_two_ints_server(): # 初始化节点 rospy.init_node(add_two_ints_server) # 创建一个Service服务名称为add_two_ints # 使用AddTwoInts服务类型指定处理回调函数为handle_add_two_ints s rospy.Service(add_two_ints, AddTwoInts, handle_add_two_ints) rospy.loginfo(加法器服务端已就绪等待请求...) # 保持节点运行等待服务调用 rospy.spin() if __name__ __main__: add_two_ints_server()5.2 创建客户端Client在同一个目录下创建客户端add_two_ints_client.py。cd ~/catkin_ws/src/my_first_ros_pkg/scripts touch add_two_ints_client.py chmod x add_two_ints_client.py写入以下代码#!/usr/bin/env python3 # 文件路径~/catkin_ws/src/my_first_ros_pkg/scripts/add_two_ints_client.py import sys import rospy from rospy_tutorials.srv import AddTwoInts def add_two_ints_client(x, y): 客户端函数用于调用远程服务。 # 等待名为add_two_ints的服务变得可用即服务端启动 rospy.loginfo(等待服务 add_two_ints 上线...) rospy.wait_for_service(add_two_ints) try: # 创建一个服务调用的代理可以理解为一个本地的函数句柄 add_two_ints rospy.ServiceProxy(add_two_ints, AddTwoInts) # 像调用本地函数一样调用远程服务并传入参数 # 这里会阻塞直到收到服务端的响应 resp add_two_ints(x, y) # 返回响应结果 return resp.sum except rospy.ServiceException as e: # 处理服务调用失败的情况 rospy.logerr(服务调用失败: %s, e) return None if __name__ __main__: # 从命令行参数获取要相加的两个数字 if len(sys.argv) 3: a int(sys.argv[1]) b int(sys.argv[2]) else: # 如果没有提供参数使用默认值 print(用法: python add_two_ints_client.py X Y) print(未提供参数使用默认值 5 和 3) a 5 b 3 # 初始化客户端节点。注意节点名要唯一这里加了个_client后缀。 rospy.init_node(add_two_ints_client_node) rospy.loginfo(请求计算: %d %d, a, b) # 调用客户端函数 result add_two_ints_client(a, b) if result is not None: rospy.loginfo(计算结果: %d %d %d, a, b, result) else: rospy.loginfo(计算失败。)5.3 运行Service通信测试确保roscore仍在运行如果已关闭请先启动。终端2启动服务端cd ~/catkin_ws source devel/setup.bash rosrun my_first_ros_pkg add_two_ints_server.py终端显示“加法器服务端已就绪等待请求...”。终端3启动客户端cd ~/catkin_ws source devel/setup.bash rosrun my_first_ros_pkg add_two_ints_client.py 10 20你会看到客户端输出“等待服务...”然后很快显示“请求计算: 10 20”和“计算结果: 10 20 30”。同时服务端终端会显示“收到请求: 10 20”和“返回结果: 30”。现象与理解你成功实现了一个同步的请求-响应模型。客户端发出一个包含具体参数的请求并阻塞等待服务端的响应。服务端处理请求后返回一个结果。这个过程就像一次远程函数调用RPC。与Topic的持续数据流不同Service是一次性的任务执行。6. Topic vs Service如何选择通过两个实战你已经直观感受了ROS两大通信机制。在实际项目中如何选择特性Topic (话题)Service (服务)通信模型发布/订阅 (异步单向)请求/响应 (同步双向)数据流持续的数据流一次性的任务执行关系一对多 (一个发布者多个订阅者)一对一 (一个客户端对一个服务端)适用场景传感器数据激光雷达、摄像头、连续状态速度、位置、日志流执行命令开启/关闭、移动到某点、查询状态电池电量、是否到达、触发计算类比电台广播 (听众随时可以收听)电话呼叫 (拨通说完得到回复挂断)简单决策原则如果你的数据需要持续、频繁、单向地传递给多个模块用Topic。如果你需要触发一个动作并立即得到结果用Service。7. 核心工具与调试技巧仅仅能运行代码还不够高效的ROS开发者必须掌握核心调试工具。rostopic用于查看和操作Topic的命令行工具。# 列出所有活跃的topic rostopic list # 查看某个topic上发布的消息内容实时 rostopic echo /sensor_temperature # 查看某个topic的消息类型 rostopic type /sensor_temperature # 手动向某个topic发布一条消息用于测试 rostopic pub /some_topic std_msgs/String data: hello -oncerosservice用于查看和操作Service的命令行工具。# 列出所有活跃的service rosservice list # 查看某个service的类型 rosservice type /add_two_ints # 调用某个service rosservice call /add_two_ints a: 10 b: 20rqt_graph可视化工具显示当前系统中所有节点、话题、服务之间的连接关系图。这是理解系统架构的神器。rqt_graph运行后你会看到一个图形界面清晰地展示出talker、listener、add_two_ints_server等节点以及它们之间通过什么话题或服务相连。rosnode管理节点的工具。# 列出所有运行中的节点 rosnode list # 查看某个节点的详细信息 rosnode info /add_two_ints_server8. 常见问题与排查思路在学习和开发中你一定会遇到各种问题。下表列出了典型问题及解决方法。问题现象可能原因排查方式解决方案roscore启动失败或rosrun找不到包1. ROS环境变量未设置。2. 工作空间未source。3. 包未编译或编译失败。1. 执行echo $ROS_PACKAGE_PATH看路径是否包含你的工作空间。2. 检查终端是否执行了source devel/setup.bash。3. 检查catkin_make是否有错误输出。1. 确保~/.bashrc中已添加source /opt/ros/noetic/setup.bash和source ~/catkin_ws/devel/setup.bash并source ~/.bashrc。2. 回到工作空间根目录重新执行catkin_make。节点启动后立刻退出1. Python脚本没有执行权限。2. 脚本中缺少rospy.spin()或主循环。3. 代码存在语法错误或导入错误。1. 用ls -l script_name.py检查文件权限应有x。2. 查看节点启动时的输出信息是否有Python报错。1. 使用chmod x script_name.py添加权限。2. 确保订阅者或服务端节点中有rospy.spin()发布者节点中有循环。3. 单独运行python3 script_name.py检查Python语法。订阅者收不到消息1. 话题名称拼写不一致大小写敏感。2. 消息类型不匹配。3. 发布者节点未运行或发布频率极低。4. 网络问题多机通信时。1. 使用rostopic list确认话题名。2. 使用rostopic type /topic_name和rosmsg show MessageType确认消息类型。3. 使用rostopic hz /topic_name查看消息发布频率。4. 检查ROS_MASTER_URI环境变量。1. 统一话题名称建议在代码中定义为常量。2. 确保发布和订阅使用相同的.msg类型。3. 检查发布者代码逻辑。4. 确保所有机器能互相ping通且ROS_MASTER_URI指向正确的Master。服务调用失败1. 服务端节点未启动。2. 服务名称或类型错误。3. 请求/响应数据结构不匹配。1. 使用rosservice list确认服务是否存在。2. 使用rosservice type /service_name确认类型。3. 使用rossrv show ServiceType查看服务数据结构。1. 先启动服务端节点。2. 检查客户端代码中的服务名和类型。3. 确保客户端构造的请求对象与服务定义一致。catkin_make编译错误1.package.xml或CMakeLists.txt配置错误。2. 缺少依赖包。3. C代码语法错误。1. 仔细阅读错误信息通常指向具体文件和行号。2. 检查package.xml中声明的依赖是否已通过sudo apt install ros-noetic-包名安装。1. 根据ROS官方文档修正CMakeLists.txt和package.xml。2. 使用rosdep install --from-paths src --ignore-src -r -y自动安装缺失依赖。3. 检查C代码。9. 最佳实践与工程化建议当你开始真正的机器人项目时以下建议能帮你少走弯路。命名规范包名全小写用下划线分隔如my_robot_driver。话题/服务名小写用下划线分隔具有描述性如/camera/rgb/image_raw。通常以/开头表示全局命名空间。节点名应能清晰反映其功能如laser_filter_node。使用Launch文件手动在多个终端启动节点非常低效。ROS Launch文件.launch可以一次性启动多个节点并设置参数。这是项目部署的标配。!-- 示例my_launch_file.launch -- launch node pkgmy_first_ros_pkg typetalker.py nametemperature_publisher outputscreen/ node pkgmy_first_ros_pkg typelistener.py nametemperature_subscriber outputscreen/ node pkgmy_first_ros_pkg typeadd_two_ints_server.py nameadder_server outputscreen/ /launch运行roslaunch my_first_ros_pkg my_launch_file.launch自定义消息与服务当标准消息库std_msgs不够用时你需要定义自己的消息.msg和服务.srv文件。这需要在包的msg/和srv/目录下创建文件并修改package.xml和CMakeLists.txt。这是构建复杂机器人系统的必经之路。善用参数服务器ROS提供了一个全局的字典式参数服务器rosparam用于存储静态配置如机器人尺寸、控制器增益、算法阈值等。节点可以在运行时读取这些参数使得配置与代码分离。日志与调试坚决使用rospy.loginfo(),rospy.logwarn(),rospy.logerr()代替print。它们可以按级别过滤输出并带有时间戳和节点名便于在复杂的多节点系统中定位问题。版本控制你的ROS工作空间尤其是src下的源代码必须使用Git等版本控制系统管理。忽略build,devel,install等编译生成目录。至此你已经完成了从ROS环境搭建到核心通信机制实战的完整旅程。我们从一个具体的开发痛点出发通过搭建环境、理解核心概念、亲手编写和运行Topic与Service的示例代码最终掌握了ROS最精髓的通信模型和调试方法。记住ROS的本质是一个通信中间件框架Topic和Service是它的两大支柱。理解并熟练运用它们你就拿到了开发任何ROS机器人软件的钥匙。下一步你可以尝试修改示例将温度传感器换成其他数据将加法服务换成其他计算。探索工具链深入学习rviz3D可视化、rqtGUI工具集、Gazebo仿真环境。学习导航栈了解move_base、AMCL等包这是实现机器人自主移动的核心。尝试ROS 2了解下一代ROS在实时性、分布式和安全方面的改进。机器人软件开发的世界大门已经为你打开接下来的探索将更加精彩。建议收藏本文在后续实践中随时查阅。