1. 为什么ROS入门要先学会“玩包”
1.1 包是什么?为什么不先学代码,而是先学包
很多新手打开ROS教程,第一反应是“又让我学一套编程框架”。但实际上,ROS这套体系里真正天天打交道的单位不是程序文件,而是“包”(package)。一个ROS包就是一坨功能单元的集合,里面可以放节点源码、launch启动文件、配置文件、消息定义、参数文件,甚至模型和地图数据。机器人上的每一个功能,从驱动底盘到跑导航,在ROS世界里几乎都是一个包。
所以学ROS,本质上就是学三件事:创建包、编译包、运行包。包玩顺了,后面的机器人实操才有根基。你不需要先啃完C++或者Python语法才动手,完全可以在“照着做一个包→改里面的代码→看效果”的过程中把语言和框架一起学会。这也是我给所有入门者推荐的第一条路径:不要先埋头背API,先搭一个能跑的最小包,用起来再说。
这篇教程就是围绕“包”展开的:在Ubuntu下怎么创建自己的ROS包,以及怎么把GitHub上别人写好的包下载下来、编译通过、跑起来。两条线走完,你对ROS工作空间的理解会有一个质的提升。
1.2 这篇教程适合谁,动手前需要准备什么
如果你是下面这三种人,这篇教程就是冲着你写的:
- 刚装好Ubuntu和ROS,但打开终端不知道该敲什么的人;
- 已经会写一点Python或C++,但始终搞不清ROS的目录结构、依赖关系、编译流程的人;
- 从GitHub上克隆了别人的代码,却面对一堆编译报错不知道怎么办的人。
动手之前需要准备的东西不多,但务必确认到位:一台装了Ubuntu的电脑(实体机、虚拟机、双系统都行)、相应版本的ROS环境、一个能联网的终端。版本配合方面,下面会专门讲,这里先卖个关子:Ubuntu版本和ROS版本不是随便搭的,搭错了会让你装到怀疑人生。
2. 环境准备:Ubuntu和ROS版本搭配
2.1 选对版本组合,能少走半年弯路
ROS最折磨人的不是代码,而是版本匹配。ROS 1和ROS 2是两代差异非常大的系统,ROS 1末代版本是Noetic,对应Ubuntu 20.04;ROS 2的明星版本Humble对应Ubuntu 22.04。如果你用的是Ubuntu 18.04,那对应的是ROS 1的Melodic;Ubuntu 24.04则要搭配ROS 2的Jazzy。版本错位会导致你在安装源里根本找不到对应的包,这一步就能劝退一半新手。
我个人的建议:如果你纯粹是学习入门、照着大量老教程操作,选Ubuntu 20.04 + ROS Noetic最舒服,因为网上能找到的教材、博客、演示项目绝大多数基于ROS 1。如果你目标很明确,就是想搞ROS 2的新项目,那就用Ubuntu 22.04 + ROS 2 Humble,生态正在快速迁移。但一定要清楚,这两个分支的命令、目录结构、编译工具都不一样,千万不要混着学,否则会把自己绕晕。
顺带说一个很多新手没意识到的问题:ROS对Windows的支持很差,即便有办法在Windows下跑,也充满各种坑。所以老老实实在Ubuntu环境下学,别试图在Windows上硬刚。
2.2 安装ROS的几个实用建议
安装ROS最标准的做法是参考官方Wiki,一步步添加软件源、添加密钥、更新索引、安装ros-noetic-desktop-full。这些步骤网上到处都是,我不再完整抄一遍,但有几个容易被忽略的点值得单独提醒。
第一,安装时不必纠结装desktop-full还是desktop。新手直接装desktop-full版,它把Gazebo仿真、RViz可视化、各种常用库一股脑打包好了,省去后面缺一个装一个的痛苦。第二,装完记得初始化rosdep,这个工具后面解析依赖时要用。第三,务必设置环境变量,把source /opt/ros/noetic/setup.bash写进~/.bashrc,不然每次开新终端都找不到ros命令。
很多人也觉得手动装太麻烦,社区里有做得比较成熟的“一键安装”脚本,本质上就是帮你把上面这些步骤自动化完成。如果你不想跟那些软件源配置较劲,直接用这类脚本按提示选择对应版本,也能顺利装完。我见过不少学生用这个方式上手,效果并没问题,等基础扎实了再回头看手动安装的细节也完全来得及。
2.3 快速验证环境:roscore能跑起来就算成功
装完之后,最重要的不是急着写代码,而是先验证环境是否真的可用。打开一个新终端,输入:
roscore正常情况下你会看到一堆日志,最后停留在“started core service”之类的提示,这就说明ROS 1的核心节点已经跑起来了。注意这个终端要保持开着,它在运行时占据着当前终端,后面所有操作都要另开新终端。
如果你输入roscore提示找不到命令,多半是环境变量没生效。检查一下~/.bashrc里有没有source那行,执行source ~/.bashrc后再试。如果提示依赖库缺失,那就要回头检查安装环节,看看是不是软件源有问题导致包没装全。
3. 创建自己的ROS包:完整跑通第一套流程
3.1 初始化工作空间:catkin_ws里到底放了什么东西
ROS开发的第一步是创建工作空间。这个名字里的“工作空间”听起来玄乎,实际就是一个普通目录,里面规定了src、build、devel三个文件夹的固定结构。src放源码,build放编译中间文件,devel放编译产物和环境脚本。你完全可以把这一步理解成“建一个约定好的工程文件夹”。
建工作空间的命令非常标准:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace在src目录下执行catkin_init_workspace后,目录里会生成一个CMakeLists.txt链接文件,这是整个编译系统的入口。接着回到工作空间根目录,执行:
cd ~/catkin_ws catkin_make这时候我建议你干一件事:用ls命令逐层看看build和devel目录里生成了什么。看到编译产物被自动归类,你就明白为什么“把源码放在src下”是必须遵守的约定。编译完还会生成一个devel/setup.bash,这个文件后面天天要用。
3.2 catkin_create_pkg创建功能包:每一条依赖都不是随便写的
工作空间建好之后,就可以创建自己的包了。ROS提供了一个辅助命令catkin_create_pkg,它的典型用法是:
cd ~/catkin_ws/src catkin_create_pkg beginner_pkg std_msgs rospy roscpp这条命令的意思是:创建一个名叫beginner_pkg的包,它依赖std_msgs、rospy、roscpp。命令执行完,src下会多出一个beginner_pkg文件夹,里面自动生成了package.xml和CMakeLists.txt这两个核心文件。
很多新手不理解为什么后面要跟一堆依赖名字。打个比方:你申请成立一个部门,需要先报备这个部门要使用哪些公司的公共资源。这里的std_msgs是“标准消息类型”,rospy是Python客户端库,roscpp是C++客户端库。你后面写节点时用到什么,创建时就应该声明什么。万一创建时漏了某个依赖也没关系,临时在package.xml里补上即可,不必重新创建。
这里要说一个纯粹的实操经验:创建包时宁多勿少。把常用依赖全部加上不会影响编译速度,但漏了某个依赖,编译时就要回头改文件,反而麻烦。我通常创建新手练习包时至少带上std_msgs、rospy、roscpp,后面写消息服务还得加message_generation和message_runtime,这是后话,先记住前面三个就行。
3.3 写一个最简单的发布订阅节点,把包跑起来
包建好之后,动手写第一个节点。所谓节点,其实就是一个Python脚本或者C++程序,在ROS系统里以一个独立进程运行。我们写一个最经典的最小例程:发布者每秒发一个数字,订阅者把它打印出来。
在beginner_pkg目录下新建scripts文件夹,然后创建一个talker.py:
#!/usr/bin/env python3 import rospy from std_msgs.msg import String def talker(): pub = rospy.Publisher('my_topic', String, queue_size=10) rospy.init_node('talker_node', anonymous=True) rate = rospy.Rate(1) count = 0 while not rospy.is_shutdown(): msg = String() msg.data = "hello ros %d" % count pub.publish(msg) count += 1 rate.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass再建一个listener.py:
#!/usr/bin/env python3 import rospy from std_msgs.msg import String def callback(data): rospy.loginfo("I heard: %s", data.data) def listener(): rospy.init_node('listener_node', anonymous=True) rospy.Subscriber('my_topic', String, callback) rospy.spin() if __name__ == '__main__': listener()别急着运行,先做两件至关重要的小事:给脚本加可执行权限,并确保python3路径正确。
chmod +x scripts/talker.py scripts/listener.py然后编辑CMakeLists.txt,找到catkin_install_python那一块,把两行取消注释并改成实际路径。这一步的目的,是让安装时把脚本当成可执行程序放进去,不做的话后面运行会提示找不到包或没有权限。
3.4 catkin_make之后千万记得source:很多新手卡在这一步
代码和配置都弄好后,回到工作空间根目录执行编译:
cd ~/catkin_ws catkin_make看到100%或build finished字样后,很多人就兴冲冲地开新终端运行节点,结果提示找不到包,或者找不到节点命令。原因只有一个:没有source。编译产物在devel目录里,你不告诉ROS去哪里找,系统当然不知道你新编译了什么东西。
正确的做法是执行:
source ~/catkin_ws/devel/setup.bash这个source只对当前终端生效,因此我强烈建议把它也写进~/.bashrc,这样以后每次开终端自动生效。注意不要只source一次就以为万事大吉:改过包结构或新增了包,要重新catkin_make之后再source一次。
验证跑通的方式很简单:开三个终端。第一个跑roscore,第二个跑:
rosrun beginner_pkg talker.py第三个跑:
rosrun beginner_pkg listener.py如果listener端不停打印“I heard: hello ros ...”,恭喜,你已经在Ubuntu下创建并运行了自己的第一个ROS包。
4. 使用GitHub下载的ROS包:从clone到编译运行
4.1 怎么挑选一个靠谱的GitHub仓库
创建自己的包解决了“从无到有”的问题,但真实项目里你更多时候是要“站在别人肩膀上”。GitHub上能搜到大量现成的ROS包,问题在于不是每个仓库都值得你去折腾:有些包三五年不维护,依赖老旧;有些包的说明文档写得稀碎,根本看不出能干什么。
挑选仓库时,我一般按下面几条经验判断:优先看stars数量,别人帮你筛选过的东西大概率能用;再看最近一次commit时间,半年内有更新说明作者还在维护;然后看README,如果里面清楚写了支持的ROS版本、Ubuntu版本、安装步骤和示例用法,这个包基本靠谱;最后看是否有issue区,有人提问且得到回复,说明作者还在回应问题。
新手最容易犯的错是把仓库整个克隆下来就闷头编译,不看版本要求。一个在ROS 2 Humble下正常工作的包,原封不动放进ROS 1 Noetic里几乎必挂,因为两代系统的API差异巨大。
4.2 clone到src之后,先解决依赖再编译
从GitHub拿包的标准姿势很简单:
cd ~/catkin_ws/src git clone https://github.com/用户名/仓库名.git克隆完成后的动作顺序很重要:千万别直接catkin_make。第一步先检查仓库里有没有说明文档,看它声明了哪些依赖。第二步用rosdep安装依赖:
cd ~/catkin_ws rosdep install --from-paths src --ignore-src -r -yrosdep会根据src下所有包的package.xml,自动找出系统里缺的依赖库并安装。这一步能解决大量“编译报错找不到头文件”的问题。不过要留意,rosdep只处理Ubuntu软件源里能直接装的那些依赖,部分第三方库需要你手动安装,这种情况说明文档里一般会写。
第三步才是编译。如果一次编译报错,先看错误信息是在哪个包、哪个文件、什么环节,不要整屏滚动就发懵,下面专门讲怎么读。
4.3 编译报错时,第一反应应该是看这几类信息
GitHub的包编译报错太常见了,本质上分三类,判断依据很直接:
第一类,找不到头文件或找不到Python模块,典型报错是fatal error: xxx.h: No such file or directory。这说明依赖没装全,要么rosdep没跑,要么依赖在文档里没写清楚。解决办法是先确认系统里是否真的没有这个库,用apt-cache search或locate查一下,能装就装。
第二类,API版本不匹配,典型报错是某个函数调用的参数数量不对,或者某个类没有这个成员函数。这说明包是用不同版本的ROS/OpenCV/PCL写的,跟你的环境对不上。这种问题最费时间,建议先去仓库的issue里搜一下报错关键词,看有没有人遇到过并给出方案。
第三类,编译路径或CMake配置问题,典型报错是找不到某个CMake package,或者版本太低。这种情况尝试单独编译那个包,用catkin_make --pkg 包名指定目标,能更精确定位问题。
读报错信息有个笨办法但非常有效:用滚动缓冲区把最后30行截出来,逐行看,从第一个error关键词开始找,后面的error往往只是连锁反应。先解决第一个真正的error,后面的很多时候自动消失。
4.4 把别人的包变成自己的:小改动上手练
下载别人的包不只是为了“跑起来”,更要学会改。跑通之后,我建议立刻做三件小练习:第一,用rosnode list和rostopic list看这个包到底起了哪些节点、发了哪些话题;第二,用rostopic echo的话题名直接查看它发布的消息内容;第三,把某个节点的发布频率或话题名称改动一下,重新编译运行,观察变化是否如预期。
这套动作练完,你才算真正“拥有”了这个包。很多人只停留在能运行,一旦出错完全不敢动代码,这样后面做机器人项目会很被动。改别人的包、读别人的代码,是ROS学习曲线里提升最快的一段路。
5. 常见问题与排查技巧实录
5.1 找不到包、命令不存在:八成是source问题
新手使用频率最高的报错大概就是:
roscd: No such package 'beginner_pkg'或者rosrun的时候提示找不到命令。我在实验室带新人时,遇到这种问题第一反应永远是问一句“你source了吗”。因为几乎所有这类报错的根源,都是当前终端不知道新包已经编译到了哪里。
排查顺序记一下:先确认包有没有编译成功,再看devel目录下有没有对应的setup.bash,然后在出问题的终端手动执行source,最后检查~/.bashrc里的source行。特别注意,如果你用zsh或其他shell,需要source对应的setup.zsh,不要照抄bash命令。
5.2 缺依赖、编译中断:典型报错怎么一步步查
如果编译到一半报错,最稳的排查路径是:先看是哪个包报错,再看是C++编译还是CMake配置阶段报错,然后从第一个error开始看。C++报错先查头文件缺失,再查函数API问题;CMake阶段报错先看缺哪个find_package库,再用apt搜索对应开发包。
我遇到过最典型的一次:一个包依赖OpenCV,但README里只字未提版本要求。结果系统装的是OpenCV 4,包的代码还是按OpenCV 3写的,编译时一堆函数签名错误。后来去issue区确认,作者已经提示需要另装OpenCV 3的开发环境。这类问题无法完全避免,但能通过“先看文档、再看issue、最后动手”的顺序减少大量无效尝试。
5.3 从GitHub拉代码遇到网络慢怎么处理
很多同学反映从GitHub克隆仓库时速度很慢,甚至断断续续。这个情况确实存在,而且跟本地网络环境有直接关系,不是代码问题。
我的处理经验是:第一,避开网络高峰时段,比如工作日晚上的克隆速度往往不如清晨;第二,如果仓库不大,可以直接在GitHub网页端下载Zip压缩包,解压后放进src目录,效果一样;第三,国内的一些开源代码托管平台上经常能找到热度较高的ROS仓库的搬运版本,搜索仓库的原始名称就能找到;第四,如果只是查代码片段,没必要完整克隆,用GitHub网页端的文件浏览功能直接看单个文件内容就够了。
这些都不是什么“魔法方案”,本质就是换个更稳妥的方式把代码拿下来。真正要避免的是病急乱投医,网上流传的一些“一键加速”工具来路不明,容易带来安全风险,不建议碰。
5.4 评估一个ROS包是否值得用的快速清单
最后分享一个我每次决定要不要折腾一个包之前都会快速过一遍的清单,共五条:
- 仓库有没有README,README里有没有写版本要求和安装步骤;
- 最近一次提交是什么时候,超过一年没动过的包要谨慎;
- issue区有没有未解决的高频问题,特别是编译类问题;
- 依赖数量多不多,依赖越杂,在你自己环境里的不确定性越大;
- 有没有提供示例launch文件或demo脚本,直接决定你跑起来需要花多少时间。
这五条看着简单,但能过滤掉大部分“下载一小时、编译两小时、最后放弃”的无效尝试。
6. 给新手的几条额外建议
写到最后,说几句不一定写在任何官方文档里的经验。
第一,别追求一次性搞懂所有原理。ROS涉及的概念太多了,节点、话题、服务、参数服务器、TF、URDF、launch,每一个都能单独写一本书。入门阶段先把“包能创建、能编译、能运行”这条链路走通,其他东西后面有的是时间去补。
第二,一定要养成看编译日志的习惯。我见过太多人一看到大段报错就直接把窗口关掉重新来,这很可惜。编译日志里的信息精度非常高,一行一行读,配合搜索引擎,绝大多数问题都能自己解决。
第三,在你的Ubuntu里给自己写一份“操作笔记”。装了什么包、改了什么配置、踩过什么坑,随手记下来,三个月后你会感谢自己。这不是老生常谈,是无数人走了弯路之后用时间换来的教训。
第四,跑通别人的包之后,尽量去读它的代码。不要怕看不懂,第一次读不懂很正常。你只要能看懂“它发布什么话题”“订阅什么话题”,就已经具备了改造它的基本能力。这种能力在后面的机器人开发里会反复用到。
ROS的学习路径很长,但“创建自己的包”和“使用GitHub下载的包”这两个入口一旦打通,你就拥有了自主探索的能力。后面的路,不会被环境配置和编译问题绊住太久,可以真正把精力放在机器人本身的控制逻辑上了。