很多朋友问我:“ROS2到底怎么入门?网上教程东一篇西一篇,看着看着就乱了。”这个感受我太理解了。ROS2入门教程最怕的不是难,而是信息太碎——装环境一个教程,写节点一个教程,跑仿真又是一个教程,每个单独看都挺清楚,串起来就断片。这篇我打算换个讲法,从“为什么需要ROS2”讲起,把安装、核心概念、第一个话题程序、可视化工具一直到常见坑一次走完,让你先搭起一个完整骨架。后面再碰到具体问题,你至少知道该往哪个方向查。适合完全零基础的同学,也适合被各种报错劝退、想系统重来一遍的人。
我接触ROS2也有几年了,从ROS1时代的老项目一路迁移过来,踩过的坑不算少。刚开始学的时候,我也犯过上来就敲命令、结果连“节点”“话题”都分不清的毛病。后来带过几届学生和同事入门,慢慢总结出一条相对顺的学习路线:先理解设计思想,再装环境,再动手写程序,最后用工具验证。这条路线我用了很多次,效果比随机刷教程好得多。
接下来就按这条路线,把整个ROS2入门过程给你拆开揉碎讲清楚。
1. 为什么是ROS2:先搞清楚你学的是什么
1.1 ROS2到底解决了什么问题
简单说,ROS2是一套帮助开发者搭建机器人软件的“积木框架”。机器人不是一个程序,而是很多程序同时跑的集合体——有处理激光雷达的、有做人脸识别的、有控制电机转动的、有做路径规划的。这些程序之间需要互相说话、传数据、同步状态,如果每个程序都用自己的一套通信方式,系统很快就变成一团乱麻。
ROS2把这件事标准化了。它规定了每个程序(节点)怎么注册、怎么发布数据(话题)、怎么请求某个功能(服务)、怎么协调一个耗时任务(动作),还提供了一套命令行工具和可视化工具,让你随时能“看到”系统里数据是怎么流的。换句话说,ROS2给机器人系统立了一套“通用语言和交通规则”。
这也是为什么现在很多机器人公司、高校实验室都在用ROS2。它最大的价值是让团队协作变的容易——你写传感器驱动,我写导航算法,他写上层应用,大家互不干扰,但通过ROS2定义的接口就能无缝对接。对于个人学习来说,它则是进入机器人开发领域最实用的一个入口,学会ROS2,你之后看开源机器人项目、跑仿真、做毕设、进实验室都会顺畅很多。
1.2 ROS1和ROS2的核心差异
学ROS2之前,不少人会先听别人聊起ROS1。ROS1确实曾经很流行,但它是2010年左右设计的老架构,最大问题是通信靠一个中心节点(roscore)管理,中心节点一旦挂了,整个系统就瘫痪。这对实验室里的机器人问题不大,但放到真实产品里就是灾难。
ROS2从底层重新设计,把通信换成了DDS(Data Distribution Service,数据分发服务)。DDS是一种去中心化的通信协议,每个节点都能直接找到对方,没有单点故障。打个比方,ROS1像开派对,所有人都要到一个总台那里查通讯录;ROS2像用手机通讯录直接打电话,总台挂了也不影响你和朋友联系。
除此之外,ROS2还支持多机通信、加密通信、实时控制,生命周期管理也更完善。对初学者来说,最直观的变化是命令变了——原来用rostopic、rosnode,现在变成ros2 topic、ros2 node;原来用catkin_make编译,现在用colcon。你要是之前学过ROS1,转型过程中最大的门槛其实就是这层“肌肉记忆”,新学的话反而没有这个负担。
2. 环境准备:20分钟把ROS2跑起来
2.1 版本选择与系统要求
选ROS2版本,先看你的Ubuntu系统。ROS2的每个发行版和Ubuntu版本有严格对应关系,用不对版本,装起来全是依赖冲突。目前最主流、教程最丰富的搭配是:
| Ubuntu系统 | 对应ROS2发行版 | 支持状态 |
|---|---|---|
| Ubuntu 22.04 | Humble Hawksbill | 长期支持,推荐 |
| Ubuntu 20.04 | Foxy Fitzroy | 长期支持,偏老旧 |
| Ubuntu 24.04 | Jazzy Jalisco | 较新,资料在积累中 |
我现在的建议非常明确:新入门首选Ubuntu 22.04配ROS2 Humble。原因很简单,Humble是LTS长期支持版,维护到2027年,社区教程、第三方包适配都是最全的。Foxy虽说也是LTS,但发布较早,很多新库已经不优先支持它了。Jazzy虽然新,但你遇到问题的时候能搜到的资料会少很多。
如果你电脑是Windows或macOS,我不是特别建议直接在原系统上装ROS2。虽然官方有Windows安装包,macOS也有实验性支持,但机器人生态里的第三方工具很多只考虑Linux,你后面做仿真、接硬件大概率还得回到Linux。最省事的方案是装虚拟机——用VMware或VirtualBox装一个Ubuntu 22.04,4核CPU、8G内存起步,日常学习和跑小仿真完全够用。双系统也可以,性能更好,但切换麻烦一点。
2.2 安装方式对比与实操
ROS2安装主要有两条路:用官方apt源手动装,或者用社区的一键安装脚本。两条路我都走过,给你分析一下利弊。
官方apt安装的好处是最干净,你能清楚知道自己装了什么东西。核心执行三步:
- 设置软件源:把ROS2的apt源地址加入系统,并添加ROS2官方的GPG密钥。
- 安装核心包:建议直接装桌面版完整包,包含ROS、RViz、演示程序和教程,省得后面缺东少西。
- 配置环境变量:把ROS2的环境变量写入.bashrc,之后每个新终端都能直接用ros2命令。
如果你不熟悉Linux命令,或者想省时间,可以用“鱼香ROS一键安装”脚本。这个脚本在国内机器人社区流传很广,启动后会出现一个交互菜单,让你选安装ROS2哪个发行版,然后自动配置源、安装依赖、设置环境变量,整个过程基本不需要动脑。它的原理其实是对官方安装步骤的封装,底层做的还是上面那三件事。
但我要提醒一点,如果用了把环境弄得很乱的一键脚本,后期排查问题会比正常步骤难不少。我的建议是:第一次装用官方步骤,遇到网络问题再考虑脚本辅助。因为官方步骤一旦卡住,你很容易定位是网络问题还是依赖问题;脚本装好之后,出了问题你不知道它到底改了哪些文件。
装好以后,一定记得在终端里验证一下:
source /opt/ros/humble/setup.bash ros2 --help如果能看到ros2的命令帮助列表,说明环境变量OK。然后执行:
ros2 run demo_nodes_cpp talker另一个终端执行:
ros2 run demo_nodes_cpp listener看到talker在发消息、listener在收消息,恭喜你,ROS2已经能正常跑了。
2.3 配置环境:小乌龟里藏着的门道
官方验证完基础通信之后,我建议你再跑一个小乌龟示例,它比talker/listener更直观:
ros2 run turtlesim turtlesim_node再开一个终端:
ros2 run turtlesim turtle_teleop_key这时会弹出一个窗口,里面有一只小乌龟,用键盘方向键就能控制它移动。这个示例看起来简单,但背后包含的信息量其实很大:turtlesim_node是海龟仿真器,turtle_teleop_key是键盘控制节点,它们之间通过一个叫/velocity的话题通信,控制指令就是话题上发的一段带线速度和角速度的数据。
你可以在第三个终端敲:
ros2 topic list ros2 topic echo /turtle1/cmd_vel然后回第二个终端按方向键,第三个终端就会实时刷出速度指令数据。这一刻你对“话题通信”就有了最直观的理解。很多同学说学ROS2抽象,其实不是抽象,是缺一个“让我看见数据在流动”的瞬间。小乌龟就是制造这个瞬间最好的工具。
3. 核心概念一次打通:节点、话题、服务、动作
3.1 节点与话题:机器人世界的“聊天群”
节点是ROS2里最小的计算单元。你可以把每个节点理解成一个独立的小程序,它只干一件事,比如读摄像头图像、算里程计、控制电机。一个机器人系统通常由几十个节点组成,它们各司其职,通过通信协同工作。
节点之间最常用的通信方式是话题,机制是“发布-订阅”,相当于一个聊天群。发布者往群里发消息,不关心谁在听;订阅者进群接收消息,不关心谁在发。一个话题可以有很多发布者和订阅者,这种解耦设计特别适合机器人——因为传感器数据就是要给很多模块同时用的,比如摄像头图像,导航要用、避障要用、物体识别也要用。
话题通信的特点是单向、持续、异步,适合传输高频数据流。你在终端敲ros2 topic list看系统里现有话题,敲ros2 topic info /话题名看发布者和订阅者有几个,敲ros2 topic hz /话题名看发布频率,这三个命令是我日常排查问题的利器。
3.2 服务与动作:请求-应答和长期任务
话题解决的是“持续发数据”的问题,但有些场景需要“问一句、答一句”。比如你让机器人打开机械爪,它打开之后要给你回一个“已经打开了”。这种请求-应答模式在ROS2里叫服务。服务由客户端发起请求,服务端处理后返回响应。适合一次性、短耗时、有明确结果的操作,比如开关设备、查询状态。
那如果一个任务耗时很长呢?比如让机器人从A点导航到B点,这需要几秒钟甚至几分钟,中途还可能被人叫停。用请求-应答不合适,因为客户端会一直傻等;用话题也不合适,因为任务有明确的开始、结束和过程反馈。ROS2专门为这种场景设计了动作机制:动作客户端发送目标,动作服务端持续回报进度,完成后返回最终结果,整个过程还能随时取消。
新手最容易混淆的就是该用话题还是服务还是动作。我有一个记忆口诀:持续流数据用话题,一问一答用服务,长任务加进度反馈用动作。理解这三者的区别,后面写节点的时候接口选型就不会纠结。
3.3 DDS与rmw:通信这层到底怎么工作
前面提到ROS2底层用的是DDS。DDS不是单个软件,而是一个标准,市面上有多个实现版本,比如Fast DDS、Cyclone DDS、RTI Connext DDS。ROS2通过一个抽象层把具体某个DDS实现接进来,这个抽象层叫rmw(ROS Middleware Interface)。
你平时不需要关心DDS细节,但有两个场景会接触到它。一是做多机通信时,两台机器人要在同一个网络里互发数据,需要配置DDS的发现机制;二是数据延迟调优时,不同DDS实现的性能特性不一样。至少你要知道,输入ros2 doctor可以查看当前正在用哪个DDS实现。
有的教程会让你通过环境变量切换DDS后端,但入门阶段不要折腾这个。保持默认的Fast DDS,稳定够用。等你能熟练处理话题和服务,再研究底层协议,那时候你已经有足够经验去判断性能差异了。
4. 第一个真正的项目:写一个发布订阅程序
4.1 创建功能包
小乌龟能跑起来、概念理解了,接下来要自己动手写代码。我建议第一个程序就写一个发布订阅程序,这是ROS2开发的“Hello World”。
先创建工作空间。ROS2里所有代码都放在一个名叫工作空间(workspace)的目录里,常见叫法是ros2_ws,里面必须有src子目录:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src然后创建功能包。功能包就是ROS2代码的组织单位,相当于一个项目模块。Package可以用C++或Python来写,入门我更推荐Python,语法直观,不用处理编译细节,能专注于理解通信机制。创建命令是:
ros2 pkg create py_pubsub --build-type ament_python --dependencies rclpy std_msgs很多新手会漏掉--dependencies参数。它的作用是声明这个功能包依赖哪些库,rclpy是ROS2的Python客户端库,std_msgs是标准消息类型。声明之后,系统会自动帮你配置依赖,省得后面手动改文件。
4.2 编写发布者和订阅者
在功能包目录下找到src文件夹,创建一个publisher.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, 'topic_hello', 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 = SimplePublisher() rclpy.spin(node) node.destroy_node() rclpy.shutdown()这段代码看着短,但包含了三个关键要素:初始化节点、创建发布者、用定时器周期性发布消息。create_publisher的第三个参数10是队列长度,意思是如果订阅者处理不过来,最多缓存10条,超过就丢弃最老的。队列长度在真实项目里需要根据数据量调,入门阶段记住这个参数存在即可。
订阅者代码类似,核心是创建订阅者、绑定回调函数:
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, 'topic_hello', 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()需要注意,rclpy.spin(node)这个函数会让节点一直运行并处理回调,程序不会退出。新手最容易踩的坑是忘了调用rclpy.init,或者忘了把回调函数名字写对,导致节点迟迟收不到数据。调试方法很简单,用ros2 node list看看节点注册了没,用ros2 topic list看看话题存在没,逐层排查很快能找到问题。
4.3 编译运行与rqt_graph可视化
写完之后回到工作空间根目录编译:
cd ~/ros2_ws colcon build --packages-select py_pubsub这里有一个新手常踩的坑:编译完一定要先source,否则运行不了自己的包:
source install/setup.bash然后就可以用ros2 run启动了:
ros2 run py_pubsub simple_publisher ros2 run py_pubsub simple_subscriber看到发布者打印Publishing,订阅者打印I heard,你的第一个ROS2程序就跑通了。
这时候我强烈建议你打开rqt_graph,用图形化方式看看节点之间是怎么连的:
rqt_graph窗口里会显示两个椭圆形的节点图标(简单发布者、简单订阅者),中间连着一条有向线,线旁边标注话题名topic_hello。这是我见过最能让初学者“顿悟”的一个工具——原来系统结构就长这样,节点是点,话题是线,数据沿线流动。学会看rqt_graph,排查通信问题会快很多。
5. 常用工具与进阶方向:rviz2、gazebo、回调组
5.1 rviz2可视化
ROS2配套的工具里,rviz2是机器人开发中最常用的可视化软件。它可以显示传感器数据、机器人模型、路径规划结果,是调试机器人系统的“眼睛”。
启动rviz2的命令很简单:
ros2 run rviz2 rviz2但刚打开时界面是空荡荡的,需要自己添加显示项。比如你在跑turtlesim小乌龟,可以点击左下角Add,选择By topic,找到/turtle1/pose这个话题添加,界面上就会出现海龟的位置信息。更常见的场景是显示激光雷达数据:话题类型是sensor_msgs/msg/LaserScan,添加LaserScan显示项后选对话题,点云就会扫出来。
rviz2对新手最大的价值,是让你真正看到“抽象的数据”变成了“可见的世界”。很多同学写程序一开始报数据对了,但不知道数据到底代表什么;一旦放进rviz2里看到点云轮廓、看到路径轨迹,理解立刻深了一层。这个工具值得花时间熟悉,导航、机械臂项目基本天天都要跟它打交道。
5.2 gazebo仿真
gazebo是ROS2生态里最常用的物理仿真环境。它的作用是在没有真实机器人的时候,先在电脑上测试算法。你可以加载一个机器人模型,加上传感器仿真插件,然后在虚拟世界里跑自己的节点。
入门阶段跑gazebo有一个最简方案:
ros2 launch gazebo_ros gazebo.launch.py这个命令会启动一个空空的仿真世界,然后你可以往里面放模型。需要先在系统里安装gazebo_ros包和模型资源:
sudo apt install ros-humble-gazebo-ros sudo apt install ros-humble-turtlebot3-gazebo安装之后可以用环境变量选择TurtleBot3机器人模型,然后把机器人加载进仿真世界。这个过程中你会接触到launch文件——ROS2里用来一次性启动多个节点的配置文件。我之前见过很多同学在终端里开一大堆窗口分别启动节点,其实用launch文件一条命令全搞定,还有日志管理,后面一定要学会用。
5.3 回调组与多线程:性能问题提前打个底
如果你只是入门,可能暂时用不到回调组。但只要你想在真实的机器人上跑多个传感器,或者有多个定时任务同时执行,很快会遇到“为什么我的节点卡住了”“为什么一个回调执行慢,其他回调都不响应”的问题。
这里要讲一个ROS2比较特殊的设计。默认情况下,同一个节点里的所有回调(订阅回调、定时器回调、服务回调)都在同一个单线程执行器里跑,也就是说它们是“排队”执行的——一个回调没跑完,后面的都得等着。如果你的定时器回调里有个耗时2秒的重计算,订阅回调这2秒内肯定不会被触发。这就导致节点看起来像“卡死”了。
解决办法是使用回调组。ROS2里回调组分为互斥回调组和可重入回调组:互斥组里的回调还是排队执行,不同组的回调可以并行;可重入组里的回调允许多线程同时进入。你需要把不同线程分配给不同回调组,还要配合MultithreadedExecutor(多线程执行器)来调度。
入门阶段不需要深究,但建议你把“回调组”这个词记下来。等你发现自己的节点总是莫名其妙地卡顿、数据迟迟不更新,八成就是回调互相等待的问题,回头再看这个概念会非常有感觉。
6. 常见问题与排查技巧
我把自己带新人的过程中最常遇到的几个问题整理成了一张速查表,基本覆盖了入门阶段90%的报错场景:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| ros2命令找不到 | 没有source环境变量 | 执行source /opt/ros/humble/setup.bash,或加入.bashrc |
| 编译报ModuleNotFoundError | Python包路径没配好 | 检查setup.py里entry_points和install_requires |
| 订阅者收不到消息 | 话题名不匹配或队列长度为0 | ros2 topic list对比名字,检查topic名拼写 |
| 节点启动后立刻退出 | 代码中rclpy.init或脚本名没写对 | ros2 node list看节点是否注册,检查控制台输出 |
| rqt_graph里看不到节点 | 节点不在同一DDS网络 | 确认所有节点在同一台机器或用同一DDS配置 |
| colcon build找不到包 | 没在ros2_ws根目录执行 | cd ~/ros2_ws再执行colcon build |
| 运行自己的包报找不到package | 没有source install/setup.bash | 编译后先source再运行 |
| 虚拟机里rviz2/仿真特别卡 | 3D加速没开启 | 虚拟机设置中开启3D图形加速,安装增强工具 |
补充几个容易踩的隐蔽坑。
第一,多个终端窗口之间环境变量不互通。你在终端A里source了install/setup.bash,终端B里如果不source,是找不到你自定义包的。我经常见到有人问“为什么终端A能跑,终端B说找不到包”,其实就是这个原因。建议把source命令直接写进~/.bashrc,这样每个新终端都会自动加载:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc第二,Python代码修改后一定重新编译。很多新手以为Python是解释型语言改完就能直接跑,但ROS2的Python包其实也需要colcon build把文件安装到install目录。如果你改了代码但没重新编译,运行时用的还是旧版本,会白白消耗很多时间。我自己也犯过这个错,后来养成了改完代码先colcon build再运行的习惯。
第三,apt更新和ROS2源冲突的问题。如果你在安装ROS2之后又执行sudo apt upgrade,有可能触发第三方源和ROS2源之间的依赖冲突。建议不要随便第三方添加PPA源,需要装什么库之前先想想会不会影响已有环境。
第四,网络问题导致安装失败。国内网络环境下,官方源下载可能极慢。可以换用国内镜像源(网上很多教程都有具体步骤),也可以直接用鱼香ROS一键脚本,它会自动配置国内镜像。归根结底,ROS2安装卡住先报错信息截图,再去社区搜索,比盲目重装有效率得多。
我在实际带人的过程中发现,入门阶段的大多数卡点其实不在代码逻辑,而在环境问题和对概念的理解上。代码写错了编译器会直接告诉你,环境配置错了反而报出一堆看不懂的莫名错误,最容易让人崩溃。所以遇到报错的时候,先深呼吸,按顺序check一下:环境变量source了没有,依赖装全了没有,话题名字对不对,版本对不对。这一套流程走下来,问题基本就暴露了。
最后再分享一个我自己的体会:学ROS2最好的方式不是把所有概念都背一遍,而是先跑通一个极简例子,然后不断往里面加东西,遇到问题解决问题。小乌龟就是一个极好的起点——它简单到不会让你被代码细节劝退,又完整展示了节点、话题、控制指令这些核心概念。把它的运行机制彻底弄明白,比刷十篇理论文章都有用。接下来你可以试着给小乌龟写一个自己的控制节点,或者修改话题的数据格式,从消费者变成生产者,这一步跨过去,你就算是真正踏上ROS2开发这条路了。