
最近在后台接到好几条几乎一模一样的私信ROS装好之后看到一个GitHub上的ROS功能包下载下来不知道怎么往自己的系统里加自己用命令创建一个新包又看不懂package.xml该填什么。这两个问题其实是一件事——你的脑子里的“包管理”概念还没有转过来。这篇文章就把“创建自己的ROS包”和“使用GitHub下载的包”这两条线放在一起讲因为它们在本质上是同一套流程建工作空间、放源码、写描述文件、编译、配置环境。文章面向的是刚装好ROS、还没太搞明白catkin工作空间的新手Ubuntu版本以20.04为基准ROS版本用Noetic这也是目前ROS1资料最多、坑最少的一套组合。如果你正在用Ubuntu 22.04道理完全一样只是命令里的发行版名换成humble编译工具换成colcon思路可以照搬。1. 动手之前先把Ubuntu版本和ROS版本这对“搭档”校对好1.1 新手第一对组合Ubuntu 20.04加ROS Noetic很多初学者打开教程就开始敲命令敲到一半发现源加不上、依赖装不上、编译报错满天飞最后才意识到是版本不匹配。ROS对不同Ubuntu版本有严格的绑定关系不是随便装哪个都行的。我见过太多新手在Ubuntu 22.04上装了ROS2 Humble然后跑去GitHub下载一个2018年做的ROS1老包编译时满屏报错以为是自己不会用其实是包和系统根本不搭。所以第一步先把这张对应表刻在脑子里Ubuntu版本对应ROS发行版适合谁来用Ubuntu 20.04 (Focal)ROS Noetic (ROS1终极版)新手学习、论文复现、大量老代码调试的最佳选择Ubuntu 22.04 (Jammy)ROS2 Humble (LTS)想直接迈入新架构、做工业级项目的人Ubuntu 18.04 (Bionic)ROS Melodic已停止维护老设备、老驱动不推荐新手这里我给新手的明确建议是如果你没有任何历史包袱直接用Ubuntu 20.04加ROS Noetic。为什么因为网上95%以上的中文教程、GitHub示例、机器人竞赛代码都是围绕ROS1写的Noetic是ROS1最后一个长期支持版本官方支持虽然停了但社区积累海量你要踩的坑别人基本都踩过了。ROS2是趋势等你把ROS1最基本的“节点-话题-服务”这套概念玩明白再迁移会顺很多因为核心思想是一样的只是工具链换了。1.2 装ROS的三种路径以及我推荐的优先级安装ROS的方式五花八门我按省心程度排个序第一种官方源安装。添加ROS软件源然后直接sudo apt install ros-noetic-desktop-full。缺点是从国外软件源拉包速度不稳定这时候可以把软件源换成国内高校的镜像源清华、中科大都行把packages.ros.org替换成镜像站域名即可。这是最正统、最容易排查问题的方式。第二种社区一键安装脚本。国内有个叫“鱼香ROS”的开源项目维护了一个自动安装脚本访问fishros.com按文档操作它会自动识别你的系统版本、换源、装依赖你按菜单选择要装ROS1还是ROS2就行。对完全不想折腾的人来说这是最快通的一条路。我自己的态度是脚本可以用但你要知道它替你做了什么至少要知道最后加进.bashrc的那行环境变量是什么否则出了问题只能干瞪眼。第三种源码编译安装。新手绝对不要碰一条条排错的成本太高完全没必要。不管用哪种方式装完以后先做一次验证source /opt/ros/noetic/setup.bash roscore如果能看到started core service这类日志就说明ROS核心环境通了。顺便检查一下echo $ROS_DISTRO应该打印出noetic。1.3 装完ROS之后把这些工具顺手补齐ROS本身不附带Git也别指望它帮你装好所有的编译工具。我每次给新手写环境准备清单固定都是这四样sudo apt update sudo apt install git vim python3-pip sudo apt install ros-noetic-desktop-full sudo rosdep init rosdep update这里重点说下rosdep很多人忽略它但它其实是“从GitHub下载包”这一步能不能顺利编译的关键。你可以把它理解成ROS版的“pip”——它负责读取某个ROS包里的依赖声明然后自动把系统级的依赖库用apt装好。sudo rosdep init只需要执行一次rosdep update则是在每次下载新包之前跑一下更新依赖索引。另外有一个非常隐蔽的坑Ubuntu安装时如果你创建了中文用户名或者把工作目录放在带中文的路径下后面编译、运行都会遇到莫名其妙的环境变量问题。我建议安装系统时就使用英文用户名工作目录也全程用英文路径这是最省心的选择。2. 创建自己的ROS包从空工作空间到跑通一个hello节点2.1 catkin工作空间里src、build、devel到底各管什么ROS1里的包管理工具叫catkin它要求所有包都放在一个“工作空间”的结构里。一个标准的工作空间长这样~/catkin_ws/ ├── src/ # 你的源码、从GitHub下载的包全部扔这里 ├── build/ # 编译中间文件catkin_make生成不要手动改 └── devel/ # 编译产物编译后会自动生成可执行程序和setup.bash新手最容易犯的错是从GitHub下载一个包直接解压到任意目录然后在那个目录里跑catkin_make结果报错说找不到包。记住catkin只认工作空间里的src目录其他地方的包它一概不认。你需要做的第一件事永远是cd ~/catkin_ws/src然后把包放进来。如果你还没有工作空间先建一个mkdir -p ~/catkin_ws/src cd ~/catkin_ws catkin_make这一步会生成build和devel目录。第一次编译没有实际内容只是让工作空间“成形”。2.2 一条命令创建包骨架依赖参数别乱省进入src目录用catkin_create_pkg命令生成新包cd ~/catkin_ws/src catkin_create_pkg hello_ros roscpp rospy std_msgs命令最后的三个参数是依赖项roscpp是C的ROS客户端库rospy是Python的ROS客户端库std_msgs是标准消息类型。新手会问如果我只写Python还要不要写roscpp从编译角度可以不写但实际开发中你几乎总会用到别的包提供的节点、服务或消息所以一开始就养成“按需声明依赖”的习惯。执行完src/hello_ros下自动生成了package.xml、CMakeLists.txt、src/目录。这就是一个“包”的骨架。任何ROS包本质上就是一堆源码加这两个描述文件描述文件告诉系统“这个包叫什么、需要什么、编译规则是什么”。2.3 package.xml和CMakeLists.txt里最容易填错的字段打开package.xml默认内容是一堆注释模板。新手容易犯的错是直接不改就编译结果catkin_make虽然不报错后面用rospack或rqt_graph查看时会提示一堆WARNING。我需要重点标出这三个必填项description一个最简单的ROS发布订阅示例/description maintainer emailyournameexample.comyourname/maintainer licenseMIT/licensedescription、maintainer、license这三项必须填否则ROS工具链会认为这个包不规范。license不能随便写个“1.0”之类写MIT或Apache-2.0都可以目的是告诉别人这个包能不能商用、能不能改。再往下看你会看到buildtool_depend、build_depend、exec_depend这些标签。它们是分场景声明依赖的标签作用典型例子buildtool_depend构建工具依赖catkinbuild_depend编译时需要roscpp、std_msgsexec_depend运行时需要rospy、std_msgsROS Noetic新版用depend标签可以同时代替build_depend和exec_depend所以上面用catkin_create_pkg生成的模板里相关的依赖行通常是干净的dependroscpp/depend这种形式不需要你手动增加多余配置。2.4 写一个能跑的发布订阅节点并解决rosrun找不到脚本的问题在hello_ros包下建一个scripts目录写一个发布者talker.py#!/usr/bin/env python3 import rospy from std_msgs.msg import String def talker(): rospy.init_node(talker) pub rospy.Publisher(chatter, String, queue_size10) rate rospy.Rate(1) while not rospy.is_shutdown(): pub.publish(String(hello ros)) 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(msg): rospy.loginfo(I heard: %s, msg.data) def listener(): rospy.init_node(listener) rospy.Subscriber(chatter, String, callback) rospy.spin() if __name__ __main__: listener()给脚本加执行权限chmod x ~/catkin_ws/src/hello_ros/scripts/talker.py chmod x ~/catkin_ws/src/hello_ros/scripts/listener.py然后回到工作空间根目录编译并运行cd ~/catkin_ws catkin_make source devel/setup.bash roscore新开一个终端source ~/catkin_ws/devel/setup.bash rosrun hello_ros talker.py再开一个终端source ~/catkin_ws/devel/setup.bash rosrun hello_ros listener.py如果一切正常listener窗口会不停打印I heard: hello ros。这里有个经验如果你写了Python脚本但忘记chmod xrosrun会提示找不到可执行文件这个“找不到”不是文件不存在而是权限不对排查时报错信息要仔细看。还有一个进阶细节如果你用了catkin_make install或者把包发布给其他人使用就一定要在CMakeLists.txt里配置Python脚本的安装规则否则安装后的包里不会有你的脚本。在CMakeLists.txt末尾加上catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})很多新手说“明明在源码目录里能跑为什么install之后找不到”就是这个原因。3. 从GitHub下载的ROS包该怎么判断、放哪、怎么编译3.1 先扫一眼仓库结构判断它是ROS1还是ROS2的包GitHub上的ROS仓库五花八门下载之前先花30秒判断它属于哪个ROS版本能省掉一晚上排查时间。我一般看两个标志第一看package.xml。如果buildtool_depend里是catkin那是ROS1包如果是ament_cmake或ament_python那是ROS2包。第二看根目录有没有CMakeLists.txt还是setup.py、colcon配置。ROS2的包很多是纯Python结构用flit或ament_python打包没有CMakeLists。如果你在ROS1环境里强行编译一个ROS2包报错会是五花八门的“Could not find a package configuration file provided by ament_cmake”之类。遇到这种报错先去看仓库的README和分支很多项目提供了noetic分支或ros1分支git clone -b noetic https://github.com/example/some_ros_pkg.git3.2 下载到src只是开始后面的三步才是重头戏以一个非常经典的开源项目turtlebot3_simulations为例。这个包是TurtleBot3机器人的Gazebo仿真环境几乎每个ROS新手都接触过。按官方文档它其实需要三个仓库配合cd ~/catkin_ws/src git clone https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git git clone https://github.com/ROBOTIS-GIT/turtlebot3.git git clone https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git这里有一个初学者经常忽略的点“从GitHub下载的包”很少是孤立的一个仓库它往往依赖另一些尚未安装的ROS包。你只clone了turtlebot3_simulations编译时会报“找不到turtlebot3_msgs”这样的错误因为仿真包要引用机器人的消息定义。所以下载完第一件事去读README里的Dependencies部分把依赖仓库一次性clone齐。clone齐了以后编译前先做依赖解析cd ~/catkin_ws rosdep install --from-paths src --ignore-src -r -y这条命令会扫描src下所有包把package.xml声明的依赖逐一用apt装上。然后正式编译catkin_make source devel/setup.bash对于turtlebot3仿真还需要设置一个环境变量再启动export TURTLEBOT3_MODELburger roslaunch turtlebot3_gazebo turtlebot3_world.launch看到Gazebo窗口里出现一辆机器人小车就说明整个“从GitHub下载包到跑起来”的链路通了。3.3 网络偶尔不给力几个替代拿到源码的思路很多读者会问GitHub访问不稳定、clone到一半断流怎么办。这是个很实际的网络环境问题。我通常会给出几种替代方案第一种直接从GitHub仓库页面的Code按钮里选Download ZIP把压缩包下载下来再解压。注意解压后要把文件夹名字改成和package.xml里的name一致否则catkin会报“包名与文件夹名不一致”的警告多包工作空间里很容易出现互相错乱。第二种国内一些代码托管平台会有热门仓库的镜像或者通过搜索引擎找到别人转存过的仓库副本clone之后对比一下package.xml里的版本号确认是同一份代码即可。第三种等一个网络状态相对好的时间段再clone。很多仓库的体积本身不大几十MB的东西换个时间往往就成功了。实在不行就分多次克隆或者直接下载release区的tar包。这些都只是日常网络波动下的常规办法不涉及任何额外工具。核心目标是拿到源码用什么方式拿到是次要的。3.4 从README和launch文件里读出“它还需要谁”读完本章第2小节的代码你应该已经知道要读README的Dependencies。但还有一种情况有些仓库README写得很简略但仓库里有一个launch目录里面的.launch文件会暴露它的真实依赖。.launch文件开头一般是launch node pkgturtlebot3_teleop typeturtlebot3_teleop_key nameteleop/ /launch看到pkgturtlebot3_teleop就知道启动这个功能还需要turtlebot3_teleop包。你可以在系统里用rospack find turtlebot3_teleop检查这个包是否已安装如果提示找不到就去GitHub搜索并clone它。这个习惯比什么都重要把“报错驱动的学习”变成“预读驱动的学习”能少走很多弯路。4. 第三方包编译报错的排查链路按“依赖、环境、源码”三层拆4.1 “Could not find a package configuration file”这类报错八成是缺依赖第三方包编译失败新手最常见的反应是去修改CMakeLists.txt这是最忌讳的操作。我建议你先把报错分类再去动手。如果是下面这种CMake Error at /usr/share/cmake-3.16/Modules/FindPkgConfig.cmake:... Could not find a package configuration file provided by turtlebot3_msgs它的核心信息在最后一句Could not find a package configuration file provided by xxx。翻译成人话就是编译需要xxx这个包但你的系统里没有。这不是代码的问题也不需要改代码你要做的只有一件事——把xxx装上。怎么判断用apt装还是用git clone装先试aptsudo apt install ros-noetic-turtlebot3-msgs如果能装上就直接编译。如果apt搜不到说明这个包可能不在ROS官方软件源里那就得去GitHub找源码clone到src。4.2 rosdep自动装依赖以及update卡住时的处理前面提到rosdep install是装依赖的利器。但很多新手在rosdep update这一步就卡住了因为更新索引需要访问外部的raw.githubusercontent域名在国内网络环境下经常失败。如果你的rosdep update一直失败我的建议是先确认网络状态反复多试几次同时去搜索引擎查一下当前可用的rosdep更新源的替代地址社区一直有维护换源方案按文档操作即可。有时候rosdep update部分成功但rosdep install报某个依赖找不到这种情况多半是索引文件没更新完整。重跑一次rosdep update再跑rosdep install --from-paths src --ignore-src -r -y大概率能解决。4.3 Python包报ModuleNotFoundError分清“ROS包”和“普通Python包”第三方包里的Python脚本经常会报这种错ModuleNotFoundError: No module named numpy这属于普通Python依赖不归ROS管。装法很直接python3 -m pip install numpy这里有个细节ROS用的是系统自带的Python3所以一定要用python3 -m pip install来装而不是直接用pip防止pip版本和Python版本错位。还有一个安全习惯是加--user参数装到用户目录避免污染系统级Python环境尤其是当第三方包依赖特定版本的numpy时强制装到系统目录可能会破坏其他包。4.4 OpenCV、Eigen这类固定版本依赖的冲突机器人视觉相关的包最容易踩的坑是OpenCV版本冲突。很多GitHub上的老包是2018、2019年写的当时系统默认是OpenCV 3.x而现在Ubuntu 20.04上自带OpenCV 4.x。编译时你会看到满屏的undefined reference to cv::xxx看起来像是代码错误其实是版本不匹配。我的经验是先看报错的具体符号再去搜这个包是否有适配新版OpenCV的分支或补丁。另外ROS系统自带的cv_bridge在Noetic里已经绑定了OpenCV 4很多老包只要把源码里#include opencv2/...的路径修正一下或者升级依赖库到兼容版本就能解决问题。记住一个原则不要在系统层面同时改装两套OpenCV优先在包的层面适配。Eigen版本冲突也是类似不过相对温和大多数情况是警告而非错误可以直接忽略除非它真的导致编译中断。4.5 环境变量source错乱导致的“虚假找不到”有一种报错最迷惑人你明明确认包已经编译成功目录里也有生成的库文件但运行roslaunch就是提示“找不到包”。十有八九是环境变量没有source对。一个工作空间里devel/setup.bash是必须source的。同时ROS本身的/opt/ros/noetic/setup.bash也要source。一般建议在~/.bashrc里加上source /opt/ros/noetic/setup.bash source ~/catkin_ws/devel/setup.bash注意顺序先系统后工作空间。如果两个工作空间里存在同名包后source的那个会“遮蔽”先source的。你如果同时有~/catkin_ws和~/other_ws并且都source了编译A工作空间里的包时ROS可能实际加载的是B工作空间里的旧版本这个问题排查起来极其隐蔽。我的习惯是一个电脑的.bashrc里只source一个主力工作空间其他工作空间用时再临时source。4.6 一张表总结常见报错与排查动作报错特征优先排查动作大概率原因Could not find a package configuration file provided by xxxsudo apt install ros-noetic-xxx或 clone xxx源码缺ROS依赖包fatal error: xxx.h: No such file or directory用apt search搜这个头文件对应的库缺系统级开发库ModuleNotFoundErrorpython3 -m pip install xxx缺Python包undefined reference to cv::xxx检查OpenCV版本、cv_bridge分支OpenCV版本冲突roslaunch找不到包echo $ROS_PACKAGE_PATH、重新source环境变量未配置或source顺序错rospack find 找不到包检查包是否在src下、是否编译过包没有加入编译或路径不对排查报错时请记住这条原则先看信息不要急着改代码。绝大多数第三方包编译失败问题出在依赖和环境不在源码本身。随意改动源码后续别人更新仓库时你还要自己维护补丁得不偿失。4.7 用rospack快速确认ROS“看见”了哪些包当你怀疑环境变量有问题时最直接的办法rospack find turtlebot3_msgs如果打印出完整路径说明ROS已经能发现这个包如果提示[rospack] Error: package turtlebot3_msgs not found就说明ROS没“看见”它。这时候先检查.bashrc里的source再检查包是否真的编译过。用rospack list可以一次性列出所有已发现的包当你改了环境变量后可以快速验证效果。5. 建包和用包都通了一遍往后可以这样练5.1 三个适合新手的练手方向方向一把自建的发布订阅节点接到第三方包的仿真环境里。比如启动turtlebot3_gazebo仿真后自己写一个节点订阅机器人的里程计话题odom打印位置信息。这一下就把“自己写的包”和“GitHub下载的包”结合起来了。方向二修改第三方包的launch文件添加remap或param参数把默认话题名改成自己包里的消息。比如把/scan话题重映射到你自己的订阅节点模拟激光雷达数据处理。这能让你快速理解ROS的参数传递机制。方向三把自己的URDF机器人模型放进Gazebo。很多初学者有个误区以为仿真一定要从零写其实从GitHub拉现成的机器人模型拆解它的URDF和meshes目录再改成自己的模型是学习效率最高的方式。5.2 日后想把自己的包发布到GitHub提前养成三个习惯我会建议每个新手从第二个包开始就把README、LICENSE、.gitignore配齐。README里至少写清楚这个包需要的ROS版本、依赖包、编译命令和运行方式LICENSE建议用MIT.gitignore里要加上build/和devel/目录否则把编译产物推到仓库里会让别人摸不着头脑。还有一个小细节package.xml里维护者邮箱必须真实有效。因为很多开源工具会自动给维护者发报错通知如果你填了别人的邮箱既收不到通知还会给对方造成困扰。5.3 这个系列下一步以及ROS1到ROS2的迁移思路篇幅关系ROS2的部分不在这一篇展开。但你要知道今天学的这套“包”的思维在ROS2里照样适用只是工具链换了catkin_make换成colcon buildpackage.xml里的catkin换成ament_cmakesource devel/setup.bash变成source install/setup.bash。概念没有变变的只是表达方式所以把ROS1的基础打扎实后面迁移会很自然。下一期我准备写怎么让一个从GitHub下载的驱动包真正带上你的实际硬件跑起来包括串口权限、话题重映射、以及launch文件里那些容易踩的坑。等你把上面这套流程完整跑过一遍再看到任何一个名字带“ros”的GitHub项目你大概就能自己判断出它能不能直接编译、缺什么依赖、该怎么放进自己的机器人里。我经常和刚入行的朋友说学ROS最忌讳背命令而是要把“包”这个概念想透。包就是一堆程序加上一份自我描述你的机器人就是由这么多包组装起来的。想通这一层后面学TF坐标变换、学URDF建模、学导航都会快很多。