ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ROS2 Jazzy工作空间搭建与colcon编译实战指南

2026/10/3 10:13:44 拓冰建站 浏览量
ROS2 Jazzy工作空间搭建与colcon编译实战指南 1. Jazzy版本背景与工作空间到底是个啥1.1 Jazzy是什么版本为什么我现在推它先交代一下背景。ROS2 Jazzy Jalisco是ROS2在2024年发布的长期支持版本官方支持周期一直延续到2029年对应的操作系统是Ubuntu 24.04Noble。这意味着你如果打算做一个跨几年的机器人项目、毕设或者产品原型选Jazzy会比选短期支持版本稳很多起码不会做到一半系统停止维护。和上一代LTS版本Humble相比Jazzy在工具链上有几个明显变化默认编译器是GCC 13.2、Python版本是3.12、CMake最低版本要求也提升了ament_cmake整套构建工具也做了不少更新。最直接的感受就是你在网上搜到的大量Humble时代教程很多命令在Jazzy下跑不通或者编译报错信息完全不一样。这也是我写这个系列的原因把自己在Jazzy上从零搭建环境、编译、跑通第一个节点过程中踩过的坑整理出来希望后来的人少折腾几个晚上。这篇是第一步只聊两件事工作空间怎么建、colcon怎么把包编译起来。先把这两个问题搞透后续的节点通信、话题、服务、自定义消息接口才有基础。适合刚装好ROS2 Jazzy、正准备开始写第一行代码的读者如果你已经能熟练用Humble这篇文章也可以帮你快速锁定Jazzy和旧版本之间的差异点。1.2 工作空间的结构和它的设计逻辑ROS2的工作空间说白了就是你把机器人代码堆在一起的一个根目录。标准模板一般长这样ros2_ws/ ├── src/ # 放你自己写的功能包源码也可以放git clone下来的第三方包 ├── build/ # 编译过程中的中间文件相当于C项目的build目录 ├── install/ # 编译完的产物脚本、库、可执行文件都会安装到这里 └── log/ # 每次编译的日志记录很多新手第一次看到这么多目录会懵其实这布局的逻辑非常直白src是你唯一需要手动编辑的地方build和log基本可以无视install才是真正被ROS2运行时加载的地方。为什么要分成build和install两层我用自己的理解解释一下。build里存的是CMake生成的中间文件和一堆临时对象文件如果哪天编译出问题你可以直接删掉build目录重新来不会伤到源码。install是“安装之后的状态”它把每个包的可执行文件、库文件、launch脚本、配置文件统一放到一个干净的地方这样运行时候只需要把install目录告诉ROS2它就能通过一套机制找到所有包。这种设计的好处是你不需要把一堆动态库拷贝到系统目录里也不用担心不同工作空间之间的文件互相污染想切换项目就切换source路径非常干净。另外提一个容易踩坑的细节如果你有一个包暂时不想被编译可以在它的源码根目录放一个名为COLCON_IGNORE的空文件colcon会自动跳过这个目录。这个技巧在工程后期特别有用比如你git clone了一堆参考包但只想编译其中几个不想把整个项目拖进来用这个文件做标记比每次都删目录方便得多。1.3 从catkin到colcon编译体系到底变了啥用惯了ROS1的朋友应该知道早期ROS1用catkin_make把整个工作空间“一把梭”编译后来出现了catkin tools支持增量编译。到了ROS2官方直接换了新工具叫colcon它的设计思路更通用不仅支持ROS2的ament_cmake、ament_python还支持纯CMake项目、纯Python项目甚至可以混合编译。colcon相对于catkin_make的优势我用一个最直观的对比来说明对比项catkin_makecolcon构建系统仅限ROS1的catkin通用的ament_cmake / CMake / Python增量编译支持但多包调度一般支持且按依赖顺序智能调度并行编译需要手动指定默认自动用满CPU核心日志管理混在一起每个包一个日志文件安装部署生成devel目录生成install目录更接近最终部署形态我在实际使用中最喜欢的是colcon的日志分隔机制。以前catkin_make编译失败时一大段报错信息夹杂在几百行输出里你得往上翻很久。而colcon会把每个包的日志单独放到log/日期/目录下终端只显示Summary一行提示哪个包成功、哪个包失败、失败日志在哪里。排查问题时直接打开对应包的那份日志文件效率完全不一样。搞清楚这些背景之后下面这节就开始真正动工了。2. 环境准备与第一次创建工作空间2.1 检查ROS2环境是否装对装完别急着敲命令我见过太多人装完ROS2 Jazzy就急着创建目录、编译包结果第一步就报错然后开始怀疑人生。其实在创建工作空间之前花两分钟确认基础环境没问题能帮你省掉后面大量排查时间。先打开一个新终端执行source /opt/ros/jazzy/setup.bash printenv | grep ROS正常的话会输出类似这样几行ROS_DISTROjazzy ROS_VERSION2 ROS_PYTHON_VERSION3这里每个变量都有意义。ROS_DISTRO表示当前环境是哪个发行版ROS_VERSION2说明是ROS2而不是ROS1ROS_PYTHON_VERSION3表示ROS2内部使用的是Python3。如果你执行完printenv | grep ROS后什么都没输出说明source没生效或者ROS2根本没正确安装。接着跑两条命令进一步验证ros2 --help ros2 pkg listros2 pkg list会列出当前环境中所有可以加载的包。如果这个命令能正常刷出一大串包名说明ROS2核心环境基本没问题如果报错或者卡住那大概率是安装步骤出了问题这时候不要强行往下走先回到安装流程把环境弄好再继续。另外特别提醒一点Jazzy只官方支持Ubuntu 24.04你在22.04上硬装的Jazzy多半会缺依赖后续编译各种奇奇怪怪的报错与其折腾不如直接换系统。2.2 创建属于自己的工作空间目录环境验证没问题接下来正式创建工作空间。我这里直接用一个最常见、也最规范的命名mkdir -p ~/ros2_ws/src cd ~/ros2_ws先解释一下mkdir -p这个参数的用途加上-p后如果~/ros2_ws已经存在不会报错还会顺便把下一级src目录一起创建。有些教程会让你分两条命令mkdir ~/ros2_ws然后mkdir ~/ros2_ws/src效果一样但用-p一步到位更省事。关于工作空间的名字个人建议就固定用ros2_ws别起中文名别带空格也别放在桌面之类带空格的路径下。这个问题在编译阶段不一定会暴露但如果你后面要用到Docker、CI流水线、或者把项目分享给队友路径里有空格会引发一堆莫名其妙的问题。我还见过有人直接把代码放在/home/用户名/桌面/项目这种路径下结果编译时CMake各种路径解析错误最后全部挪了一次目录才消停。目录建好之后你可以在~/ros2_ws/src里放一个空的测试包后面编译时不需要在src目录里执行。我刚入门时就犯过这个错误在src目录下运行colcon build结果colcon找不到任何包提示一个空的工作空间。正确的执行位置永远是工作空间的根目录也就是~/ros2_ws这一层。2.3 Underlay与Overlaysource这步背后的玄机“为什么每次打开新终端都要source一下”这是新手必问的问题。理解这个机制对排查“编译成功但运行找不到包”这类问题特别关键。ROS2的环境管理靠的是环境变量叠加机制你可以把它想成两张透明胶片叠在一起看地图。最底层的那张胶片是系统级的ROS2环境路径在/opt/ros/jazzy/setup.bash这叫underlay。你自己的工作空间编译后生成的~/ros2_ws/install/setup.bash是叠在上面的一层叫overlay。每次source一个setup.bash实际上是把对应目录里的各种路径比如AMENT_PREFIX_PATH、PYTHONPATH、LD_LIBRARY_PATH追加到当前终端的环境变量里。这里的关键点是环境变量是进程级别的每个终端都是一个独立的进程你在一个终端里source过另一个终端不会自动继承。所以每次打开新终端如果要用到ROS2命令和自己的工作空间包就必须重新source。这也是几乎所有ROS2教程都要求你把source命令写进~/.bashrc的原因这样每次开终端都会自动加载。echo source /opt/ros/jazzy/setup.bash ~/.bashrc echo source ~/ros2_ws/install/setup.bash ~/.bashrc source ~/.bashrc但这里一定要小心如果你在~/.bashrc里同时source了多个工作空间后source的会覆盖先source的同名包这相当于“上面那张胶片盖住了下面那张”你实际运行的可能不是你最新改的那个版本。所以我个人的习惯是/opt/ros/jazzy/setup.bash写进bashrc全局自动加载自己的工作空间需要用时手动source或者写一个别名快速source。至于两个LTS版本比如Humble和Jazzy同时装在机器上的情况我强烈建议不要把它们都写进bashrc否则你连ros2 --help都可能跑错版本。3. 我的第一个功能包从创建到编译通过3.1 功能包的最小结构工作空间是“容器”真正干活的是功能包。ROS2功能包分为两大类型ament_cmake类型主要放C代码和ament_python类型放Python代码。我建议第一步先用C包来跑通流程因为C包的编译链路更完整能让你对构建过程的理解更深入Python包虽然不用编译但后续涉及自定义消息接口时你还得回到CMake这套体系里。创建一个C功能包的命令如下cd ~/ros2_ws/src ros2 pkg create hello_ws --build-type ament_cmake --dependencies rclcpp解释一下这几个参数。hello_ws是包名建议全小写、不能有连字符下划线可以--build-type ament_cmake指定包的构建类型--dependencies rclcpp告诉ROS2这个包依赖rclcpp库后面生成的文件里会自动帮你填好依赖关系。生成完的功能包大概长这样hello_ws/ ├── CMakeLists.txt ├── package.xml ├── include/ │ └── hello_ws/ # 头文件目录暂时空的 └── src/ # 源文件目录暂时空的你可能注意到这里没有main函数是的ros2 pkg create只生成一个包的“骨架”主要代码还是要自己写。但这个骨架已经能编译通过了你可以在src里放一个简单的节点文件让这个包真正有点用。3.2 package.xml和CMakeLists.txt里有哪些坑创建完包之后先不要急着写代码花几分钟把两个核心文件看懂因为它们是最容易踩坑的地方。package.xml是包的“身份证”里面记录包名、版本、作者、许可证、依赖项等信息。最低要求是以下几项不能缺name必须和包名一致就是hello_wsversion版本号默认0.0.0也可以不改description描述信息随便填点什么maintainer维护者名字和邮箱不能为空license许可证建议写Apache-2.0或者MIT我的经验是很多新手创建完包后直接编译结果报错说description或者license为空其实就是这里没填。另外dependrclcpp/depend这行表示编译时需要rclcpp、运行时也需要rclcpp在ROS2中我们一般直接用depend这个标签它会自动同时处理build和exec依赖比ROS1时代分别写build_depend和exec_depend简洁很多。再看CMakeLists.txt这是C包编译的核心。最低限度的关键内容cmake_minimum_required(VERSION 3.8) project(hello_ws) find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) add_executable(hello_node src/hello_node.cpp) target_link_libraries(hello_node ${rclcpp_LIBRARIES}) ament_target_dependencies(hello_node rclcpp) install(TARGETS hello_node DESTINATION lib/${PROJECT_NAME}) ament_package()我在这里特别提醒两点。第一target_link_libraries并不是唯一需要写的在ROS2的ament体系里ament_target_dependencies(hello_node rclcpp)更关键它负责把rclcpp的头文件路径、库路径、依赖项全都传递进来。第二install(TARGETS ...)这行千万不要漏它决定了编译好的可执行文件会被安装到哪个目录。少了这行你会发现colcon build提示成功但ros2 run hello_ws hello_node就是找不到可执行文件。为什么这里有这么多规则因为ROS2的ament_cmake在CMake之上封装了一层“包的安装和发现机制”。它不仅要编译出可执行文件还要把可执行文件放到ROS2能搜到的标准位置lib/包名/同时把包的信息注册到AMENT的索引里。所以只编译不安装等于你做好了菜但没端上桌客人自然找不到。3.3 写一个最小节点验证整条链路现在在~/ros2_ws/src/hello_ws/src/目录下新建一个hello_node.cpp文件写入最简单的一段代码#include rclcpp/rclcpp.hpp int main(int argc, char** argv) { rclcpp::init(argc, argv); auto node rclcpp::Node::make_shared(hello_node); RCLCPP_INFO(node-get_logger(), Hello ROS2 Jazzy!); rclcpp::shutdown(); return 0; }这段代码干什么前三行是ROS2 C程序的标准初始化流程auto node rclcpp::Node::make_shared(hello_node)创建了一个名为hello_node的节点RCLCPP_INFO是打印日志的宏作用类似printf但会带上节点名和时间戳最后rclcpp::shutdown()正常退出。然后修改CMakeLists.txt把刚才那几行关键内容加进去。注意add_executable里的src/hello_node.cpp路径要和你实际文件名一致。这里我不推荐直接复制网上完整模板最好在ros2 pkg create生成的文件基础上改因为不同版本生成的模板会有细微差异直接覆盖容易漏掉Jazzy特有的配置。3.4 真正执行colcon build从编译到运行所有文件准备好之后回到工作空间根目录执行cd ~/ros2_ws colcon build --packages-select hello_ws--packages-select参数表示这次只编译hello_ws这一个包后续如果你工作空间里有很多包用这个参数可以只编译你修改过的部分大幅节省时间。第一次编译建议不加任何额外参数让colcon按默认配置跑一遍确保基础流程没问题。顺利的话终端最后会显示Summary: 1 package finished [x.xxs]这表示编译成功。然后执行source install/setup.bash ros2 run hello_ws hello_node如果看到终端打印出Hello ROS2 Jazzy!恭喜你的第一个ROS2程序已经跑通了。这里要展开说一下--symlink-install这个参数我建议从第二次编译开始就习惯性加上colcon build --packages-select hello_ws --symlink-install它的作用是install目录里的文件不是复制品而是指向源码的软链接。对于launch文件、Python脚本、配置文件这类内容你修改源码后不需要重新编译文件会自动同步。但要注意C源码改了以后依然要重新编译因为可执行文件是编译出来的二进制不是脚本。这个参数在开发阶段能救你很多次特别是调试launch文件时改一行存盘就能直接运行不用再等colcon跑一遍。4. Jazzy下最常见的四个编译问题排查复盘4.1 colcon命令找不到或者版本不对这是所有问题里最容易解决的但也最常见。执行colcon build时报colcon: command not found说明你的系统里没有安装colcon这跟ROS2主包是分开的。在Ubuntu 24.04下用apt安装即可sudo apt install python3-colcon-common-extensions这里我特别建议不要用pip install colcon-common-extensions来装。原因在于pip安装的包是放在用户Python环境里的如果之后创建了虚拟环境或者系统里存在多个Python版本ROS2可能引用错环境导致colcon找不到或者加载一堆不相干的依赖。Ubuntu桌面版默认是禁止pip操作系统Python环境的硬装会提示externally-managed-environment错误没必要为了装一个工具破坏系统包管理机制。还有一个常见情况是colcon --version能输出版本号但编译时提示某个内部模块缺失。这个多半是你之前用pip装过旧版colcon和apt版本冲突了。处理办法是先把pip版的卸载干净再用apt安装或者直接用apt的版本覆盖。4.2 编译时找不到ament_cmake或rclcpp初始化CMake阶段报错最常见的一类信息是CMake Error at /usr/share/cmake-3.28/Modules/FindPackageHandleStandardArgs.cmake:230 (message): Could NOT find ament_cmake (missing: ament_cmake_DIR)这句话翻译成人话就是CMake在搜索依赖库的路径里没找到ament_cmake。99%的原因是你没有source ROS2的环境。在你执行colcon build的同一个终端里先运行source /opt/ros/jazzy/setup.bash然后检查环境变量是否生效printenv | grep AMENT_PREFIX_PATH如果这个变量非空说明ROS2环境已经加载了再去执行colcon build就不会报这个错了。这里我想多啰嗦一句很多人喜欢用sudo colcon build来解决权限问题这绝对是灾难的开始。sudo会把当前用户的环境变量全部丢掉ROS2在root环境下根本找不到ament_cmake而且编译产物会变成root所有之后你自己都没权限操作install目录里的东西。记住colcon build永远不要在root权限下执行。还有一种情况是环境变量正常但依然找不到某个特定包。这时候要先确认这个包有没有装。Jazzy的完整桌面版装的包比较全但如果你装的是ros-base最小版很多工具包都没有需要自己补装sudo apt install ros-jazzy-包名4.3 编译成功但ros2 run找不到功能包这类问题最让人抓狂编译明明显示1 package finished但执行ros2 run hello_ws hello_node却提示Package hello_ws not found。原因几乎可以锁定为你没有source当前工作空间的install目录或者终端跑到了别的shell、别的目录导致ROS2不知道去哪找这个包。解决流程如下# 确认当前ROS发行版 echo $ROS_DISTRO # source工作空间环境 source ~/ros2_ws/install/setup.bash # 检查当前环境是否包含该工作空间 echo $COLCON_PREFIX_PATH # 搜索系统里是否有这个包 ros2 pkg list | grep hello_wsCOLCON_PREFIX_PATH是colcon往环境里写入的一个重要变量它记录了你source过的所有工作空间路径。如果这个变量是空的说明install/setup.bash根本没生效。另外提醒一下ros2 run从当前终端的环境变量里查找包和你当前在哪个目录执行无关只和source状态有关。所以就算你站在~/ros2_ws目录下不source照样找不到包。个人建议把下面这两行加到~/.bashrc末尾source /opt/ros/jazzy/setup.bash source ~/ros2_ws/install/setup.bash这样每个新终端都会自动加载环境。但这种方式有一个副作用如果你同时开发多个工作空间后source的会把先source的同名包覆盖掉。我的处理习惯是针对单个项目单独写一个env.sh脚本里面只source当前项目的依赖链开终端时手动source这个脚本避免全局环境被污染。4.4 Python虚拟环境和conda带来的诡异问题这个坑在JazzyUbuntu 24.04上特别突出因为Jazzy默认的Python版本是3.12很多Python依赖都要求比较新的环境。如果你机器上装了Anaconda、Miniconda或者某个Python虚拟环境很可能出现以下现象ros2 pkg list执行后卡住半天不输出ros2 run一个Python节点报ModuleNotFoundError: No module named rclpy编译Python包时跑到某个环节莫名其妙失败原因很简单ROS2的很多命令本质上是Python程序它们需要依赖系统的Python3.12和放在/opt/ros/jazzy/lib/python3.12/site-packages下的rclpy等模块。如果你当前shell激活了conda那么which python3指向的是conda环境里的PythonROS2命令在Python路径里找不到自己的库自然就崩了。排查方法which python3 # 如果输出里有conda或venv路径说明你处在虚拟环境中解决办法是在编译和运行ROS2程序之前先退出虚拟环境conda deactivate # 或者 deactivate然后重新打开一个干净终端。我的个人习惯是把ROS2开发的终端和用来跑机器学习、数据分析的conda环境分开各用各的标签页互不干扰。在同一个终端里反复切换环境早晚会搞混。4.5 编译报错速查表报错关键词大概率原因处理方式command not found: colconcolcon未安装sudo apt install python3-colcon-common-extensionsCould NOT find ament_cmake未source ROS2环境在当前终端执行source /opt/ros/jazzy/setup.bashPackage hello_ws not found未source install目录source ~/ros2_ws/install/setup.bashModuleNotFoundError: rclpyPython环境被虚拟环境污染conda deactivate后重新开终端Permission denied之前用sudo编译过sudo chown -R 用户名:用户名 ~/ros2_wsCMake Error: package xxx not found某个依赖没装sudo apt install ros-jazzy-xxx把xxx替换为实际包名5. 效率提升colcon编译的几个实用习惯5.1 常用编译参数组合起来更舒服单条colcon build命令在实际开发中很少直接用因为它会把整个工作空间从头到尾扫一遍哪怕你只改了一个文件的注释。我日常最常用的组合是colcon build --symlink-install --packages-select hello_ws这条命令只编译hello_ws这一个包同时把Python脚本和launch文件以软链接方式安装。开发调试阶段我几乎都用它。如果某个包需要编译Release版本比如要做性能测试可以用colcon build --packages-select hello_ws --cmake-args -DCMAKE_BUILD_TYPERelease这里有个细节要注意如果你先用Debug模式编译过再用Release模式重新编译同一个包build目录里会残留旧配置可能编译报错。稳妥的做法是先删掉这个包对应的build和install残留再重新编译rm -rf build/hello_ws install/hello_ws colcon build --packages-select hello_ws --cmake-args -DCMAKE_BUILD_TYPERelease另外一个参数--event-handlers console_direct可以让你在终端实时看到完整编译输出不用打开日志文件就能定位错误。缺点是输出会非常长一般只有编译报错想快速定位时才加上。5.2 别名配置把常用命令变成快捷键重复敲这么长的命令很耽误时间我建议在~/.bashrc里配几个别名alias cbscolcon build --symlink-install --packages-select alias cbcolcon build --symlink-install alias sbsource install/setup.bash配置之后日常操作就变成了cbs hello_ws # 编译hello_ws sb # source当前工作空间 ros2 run hello_ws hello_node别小看这几个别名省下来的时间是次要的关键是减少了敲错参数的概率。我有一次因为没有加--symlink-install改了launch文件后忘了重新编译结果运行的一直是旧版本排查了半天才反应过来。5.3 一个容易忽略的坑修改launch文件到底要不要重新编译这个问题在热词里也出现了“launch文件修改了需要编译吗”。直接给答案如果你用--symlink-install编译过不需要重新编译直接重新运行launch文件即可。原因是install目录里的launch文件是软链接指向你源码里的原始文件你改的就是install里“看到”的那个文件。但如果你用的是默认编译方式不带--symlink-installinstall目录里的launch文件是编译时复制出来的普通文件跟源码已经没有任何关联。这时候修改源码里的launch文件install目录里的还是旧版本必须重新编译才能生效。所以我强烈建议从第一天起就养成加--symlink-install的习惯这个参数带来的便利远大于它的存在感。等哪一天你需要给别人分发编译好的包、或者部署到机器人上的时候再用不带该参数的正式编译即可。5.4 每次编译完成后养成检查Summary的习惯编译结束后不要急着运行程序先看一眼终端末尾的SummarySummary: 1 package finished [4.23s]如果某个包编译失败Summary里会显示1 package failed并给出日志路径。这时候打开日志文件cat log/latest_build/hello_ws/stdout_stderr.log这个日志记录了完整的编译过程错误信息基本都在最后几十行。我见过不少新手看到屏幕上有一堆红色警告就开始慌其实大部分warning不影响编译通过真正致命的是带error:前缀的日志行。先把心态稳住定位到error再对照“常见问题速查表”里的场景去排查大多数问题都能在几分钟内解决。我在实际操作中最深刻的体会是工作空间和编译这一层百分之八十的问题本质上都是环境变量的问题而不是代码的问题。你写的代码再烂顶多报个语法错误但环境变量配错了编译能通过、运行却找不到包这类问题最消磨人。所以每次遇到诡异现象第一反应应该是检查当前终端里ROS2的环境状态第二步再怀疑代码。这个思路能帮你少走很多弯路。下一步我打算写“第二步”内容会聚焦在节点之间如何通过话题通信、怎么写发布订阅程序、以及怎么用launch文件一键启动多个节点。这些内容都会继续基于Jazzy版本实测争取把每一步都踩成路。如果你在搭建工作空间时遇到我文章里没提到的问题多看看log目录里的编译日志再回过头检查package.xml和CMakeLists.txt大部分答案都在那里。