news 2026/9/28 17:29:51

Ubuntu 24.04 源码编译 ROS1 Noetic 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu 24.04 源码编译 ROS1 Noetic 实战指南

ROS1 Noetic 是 ROS1 系列的最后一个长期支持版本,官方对它的系统支持原本锁定在 Ubuntu 20.04 Focal Fossa。但现实情况是,Ubuntu 24.04 LTS 已经铺开,新装的机器、新配的开发环境、公司统一升级的镜像,清一色都是 24.04。你手上可能有一块 RK3588 开发板、一台刚装好的虚拟机、或者一台换了系统的工控机,跑的都是 24.04,而项目代码却依赖 ROS1 Noetic。这时候摆在面前的路只有两条:要么降级系统,要么从源码编译。降级系统意味着放弃新内核、新工具链和一堆已经适配好的软件,代价太大;从源码编译虽然麻烦,但一劳永逸,编译一次之后整个工作空间都能正常用。

这篇内容就是把我自己在 Ubuntu 24.04 上从零编译 ROS1 Noetic 的完整过程拆开讲清楚。涉及的核心关键词包括 Ubuntu、ROS1 Noetic、源码编译、catkin、rosdep。适合已经对 Linux 命令行有基本操作能力、知道apt和cmake是什么、但没真正从源码构建过 ROS 的开发者。如果你只是想跑个 demo,官方 Docker 镜像可能更省事;但如果你需要长期维护、需要改 ROS 底层、需要在没有官方二进制包的架构上跑,那源码编译是绕不开的。下面我会从依赖准备、工作空间初始化、编译顺序、报错排查到环境固化,一步步说清楚,并且把每一步"为什么这么做"讲透。

1. 为什么 Ubuntu 24.04 上装 Noetic 必须走源码这条路

1.1 官方二进制包的发行边界在哪里

ROS1 Noetic 的官方deb包只针对 Ubuntu 20.04 构建,这是 ROS 发行版和 Ubuntu 版本一一绑定的结果。ROS 的发布策略是:每个 ROS 发行版对应一个 Ubuntu LTS 版本,Noetic 对应 Focal(20.04),Melodic 对应 Bionic(18.04),再往后的 ROS2 才逐步放宽。这个绑定不是随便定的,而是因为 ROS 的二进制包在构建时链接了特定版本的libboost、libpython3、libtinyxml2等系统库,这些库的 ABI 在不同 Ubuntu 版本之间会变。你在 24.04 上直接apt install ros-noetic-desktop-full,会提示找不到包,因为 ROS 的 apt 源里根本没有为 Noble(24.04)构建的 Noetic 包。

有人会想,那我把 20.04 的源加到 24.04 上不就行了?这个思路很危险。20.04 的包依赖libpython3.8,而 24.04 默认是libpython3.12,强行安装会导致依赖链断裂,轻则装不上,重则把系统的 Python 环境搞乱,影响其他工具。所以源码编译不是"更折腾的选择",而是在 24.04 上唯一干净、可控的路径。

1.2 源码编译到底编译了什么

很多人对"从源码编译 ROS"有误解,以为是把整个 ROS 生态几千个包全部编译一遍。实际上 Noetic 的核心源码仓库(ros_comm、roscpp、rospy、roslaunch等)加起来也就几十个包,真正需要编译的核心组件数量是可控的。ros-noetic-desktop-full之所以大,是因为它把 RViz、Gazebo、导航栈、感知库这些上层包都打包进去了,而这些上层包大部分是纯 Python 或者依赖系统库的 C++ 包,源码编译时可以选择性地只编译你需要的部分。

源码编译的本质是:用catkin_make或catkin build这套构建系统,把 ROS 的 C++ 源码编译成.so库和可执行文件,把 Python 源码安装到指定路径,然后通过环境变量让系统找到它们。编译产物放在你自己的devel或install目录里,不污染系统路径,这也是源码编译的一个隐性好处——想删就删,不留残留。

1.3 24.04 带来的新变量:Python 3.12 和 GCC 13

Ubuntu 24.04 默认的 Python 是 3.12,GCC 是 13。这两个版本对 ROS1 Noetic 来说都偏新。Noetic 的代码库最后活跃维护是在 2020 到 2022 年之间,那时候主流是 Python 3.8 和 GCC 9。Python 3.12 移除了一些废弃的 API(比如distutils被标记弃用),GCC 13 对 C++ 标准的默认行为也更严格,一些老代码会报 warning 甚至 error。

这意味着源码编译不是"照着官方 wiki 敲命令"就能过的,中间会遇到若干因为工具链版本过新导致的编译错误。这些错误不是 ROS 本身的问题,而是代码年龄和编译器年龄的错位。后面我会把我在 24.04 上实际遇到的几个典型报错和修法都列出来,这部分是官方文档里没有的,也是这篇内容最有价值的地方。

2. 编译前的依赖准备:哪些能 apt 装,哪些必须手动补

2.1 基础构建工具链的安装

第一步是把编译需要的基础工具装齐。这些工具在 24.04 的官方源里都有,直接apt装即可:

sudo apt update sudo apt install -y build-essential cmake git wget curl sudo apt install -y python3-pip python3-dev python3-setuptools python3-yaml sudo apt install -y libboost-all-dev libconsole-bridge-dev libtinyxml2-dev sudo apt install -y liblz4-dev libbz2-dev libeigen3-dev

这里有几个点值得说明。build-essential会带上 GCC 13 和make,这是编译的底座。python3-dev提供 Python 头文件,ROS 的 Python 扩展模块编译时需要。libboost-all-dev是 ROS 重度依赖的库,roscpp里的很多数据结构都基于 boost。libconsole-bridge-dev和libtinyxml2-dev是 ROS 日志和 XML 解析的依赖,缺了会在编译roscpp时报找不到头文件。

注意:不要装python3-distutils就以为万事大吉。24.04 的 Python 3.12 里distutils已经被移除,取而代之的是setuptools内置的实现。如果你在编译过程中看到ModuleNotFoundError: No module named 'distutils',正确的修法是pip install setuptools而不是去装一个不存在的python3-distutils。

2.2 rosdep 的安装与初始化

rosdep是 ROS 的依赖管理工具,它会读取每个包package.xml里声明的依赖,然后调用apt去装对应的系统包。源码编译前必须先把rosdep装好并初始化:

sudo apt install -y python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool sudo rosdep init rosdep update

rosdep init会往/etc/ros/rosdep/写一个源列表文件,rosdep update会把这些源拉下来缓存到本地。这两步在 24.04 上可能会因为网络原因失败,如果rosdep update卡住或者超时,可以多试几次,或者检查一下 DNS 解析是否正常。

这里有个经验:rosdep update拉取的源文件里,有一部分是针对 20.04 的依赖映射。在 24.04 上,某些包名可能对不上,比如 20.04 叫libpython3.8-dev,24.04 叫libpython3.12-dev。rosdep在解析时会尝试匹配,匹配不到就会报Cannot locate rosdep definition for [xxx]。遇到这种情况,不要慌,先看它要装的是什么,手动apt装一个功能等价的包,然后用rosdep install --skip-keys跳过这个键即可。

2.3 那些 apt 装不到、需要手动处理的依赖

有几个依赖在 24.04 的源里要么没有,要么版本不对,需要手动处理。第一个是python3-empy,ROS 的构建系统用它来生成消息代码,24.04 源里有,但版本可能偏新,如果编译时报 empy 相关的语法错误,用pip install empy==3.3.4锁到老版本。第二个是python3-catkin-pkg,这个必须装,否则catkin_make跑不起来。

还有一个容易被忽略的是liborocos-kdl-dev,这是 KDL 运动学库,robot_state_publisher和很多导航包依赖它。24.04 源里有这个包,直接装即可。但如果你后续要编译moveit这类上层包,还需要liburdfdom-dev、libassimp-dev等,这些可以等真正需要时再补,不必一开始就全装,避免引入不必要的版本冲突。

3. 工作空间初始化:rosinstall_generator 的正确用法

3.1 用 rosinstall_generator 生成包列表

ROS 源码编译的官方推荐方式是用rosinstall_generator生成一个.rosinstall文件,里面列出了需要下载的仓库和分支。以desktop_full为例:

mkdir -p ~/ros_noetic_ws/src cd ~/ros_noetic_ws rosinstall_generator desktop_full --rosdistro noetic --deps --tar > noetic-desktop_full.rosinstall

这里--deps表示把依赖也包含进来,--tar表示用 tar 包而不是 git 仓库,这样下载更快、更稳定。如果你需要改某个包的源码,就把--tar去掉,改成 git 拉取。生成的.rosinstall文件里每一行是一个仓库地址和版本,wstool会按这个列表把源码拉到src目录下。

提示:desktop_full包含的包非常多,编译时间会很长。如果你只是需要核心的 ROS 通信功能,用ros_comm就够了,编译量小很多。我自己的做法是先编ros_comm把核心跑通,确认环境没问题,再逐步加desktop_full里的其他包。

3.2 wstool 拉取源码与常见网络问题

生成.rosinstall之后,用wstool初始化并拉取:

wstool init src noetic-desktop_full.rosinstall

如果中途断了,可以wstool update -t src继续。这一步最常见的坑是网络问题,某些仓库地址在国内访问不稳定,会卡在某个包上。我的处理方式是:先看卡在哪个仓库,如果是 github 的地址,可以手动git clone下来放到src对应目录,然后wstool update会跳过已存在的目录。

拉取完成后,src目录下会有几十个文件夹,每个是一个 ROS 包。这时候不要急着编译,先检查一下有没有拉取不完整的,用wstool status -t src看一下状态,确认没有报错的仓库。

3.3 用 rosdep 安装剩余依赖

源码拉下来之后,用rosdep把package.xml里声明的依赖装齐:

rosdep install --from-paths src --ignore-src --rosdistro noetic -y

这个命令会遍历src下所有包的package.xml,把缺失的依赖用apt装上。在 24.04 上,这一步大概率会报几个Cannot locate rosdep definition的错误。我的处理流程是:把报错的键记下来,逐个判断。如果是 Python 包,用pip装;如果是系统库,用apt装一个功能等价的;如果确实找不到,就在命令后面加--skip-keys "键名1 键名2"跳过。跳过的键对应的依赖,需要你自己确认已经在系统里装好了,否则编译时会报缺头文件或缺库。

4. 编译过程:catkin_make 的坑与 GCC 13 的适配

4.1 第一次 catkin_make 会发生什么

依赖装齐后,进入工作空间根目录执行:

cd ~/ros_noetic_ws catkin_make

catkin_make会先编译catkin本身,然后用catkin去编译src下的所有包。第一次编译时间很长,取决于机器性能,半小时到两小时都正常。编译过程中会输出大量日志,重点看有没有error,warning可以暂时忽略。

这里有个细节:catkin_make默认用Release模式编译,如果你需要调试符号,可以加-DCMAKE_BUILD_TYPE=Debug。但 Debug 模式编译出来的库体积大、运行慢,日常开发用 Release 就够了。

4.2 GCC 13 报错:那些老代码过不了新编译器

在 24.04 上编译 Noetic,GCC 13 会挑出一批老代码的问题。我实际遇到的有这么几类:

第一类是-Werror导致的编译中断。ROS 的一些包在CMakeLists.txt里开了-Werror,把 warning 当 error 处理。GCC 13 新增了一些 warning,老代码触发后直接编译失败。修法是找到对应的CMakeLists.txt,把-Werror去掉,或者在catkin_make时加-DCMAKE_CXX_FLAGS="-Wno-error"全局关掉。

第二类是 C++ 标准问题。GCC 13 默认用 C++17,而一些老代码是按 C++11 写的,某些语法在 C++17 下行为变了。比如register关键字在 C++17 里被移除,老代码里如果有register int x;就会报错。修法是在CMakeLists.txt里显式指定set(CMAKE_CXX_STANDARD 14),把标准降下来。

第三类是std::string和char*的隐式转换。GCC 13 对类型转换更严格,一些以前能过的隐式转换现在会报错。这类问题需要改源码,把隐式转换改成显式c_str()调用。好在核心包里这类问题不多,主要集中在少数几个老包上。

4.3 Python 3.12 相关的编译错误

Python 3.12 移除distutils之后,一些用distutils的构建脚本会失败。ROS 的catkin在生成 Python 包时,部分逻辑依赖distutils。如果你看到ModuleNotFoundError: No module named 'distutils',执行:

pip install setuptools

setuptools在较新版本里内置了distutils的替代实现,装完之后 Python 能找到distutils的兼容层。如果还不行,可以pip install setuptools==59.6.0锁一个老版本,这个版本对distutils的兼容性最好。

另一个 Python 3.12 的坑是imp模块被移除。ROS 的一些老脚本用imp来动态加载模块,Python 3.12 里imp没了,会报ModuleNotFoundError: No module named 'imp'。修法是把imp的调用改成importlib,或者装一个imp的兼容包。这类问题通常出现在roslaunch和rospy的某些边缘功能上,核心功能不受影响。

4.4 编译顺序与并行度控制

catkin_make默认会并行编译,用满所有 CPU 核心。这在内存充足的机器上没问题,但如果你的机器内存偏小(比如 8GB),并行编译多个大包时可能触发 OOM,编译进程被系统杀掉,报c++: fatal error: Killed signal terminated program cc1plus。遇到这种情况,用-j参数限制并行度:

catkin_make -j2

-j2表示同时只编译两个文件,内存占用会小很多,代价是编译时间变长。我自己的经验是,16GB 内存的机器用-j4比较稳,8GB 的机器用-j2,4GB 的机器建议-j1并且关掉其他占内存的程序。

5. 编译后的环境固化与验证

5.1 source 环境变量与永久生效

编译成功后,工作空间里会生成devel目录,里面有setup.bash。每次开新终端都要 source 一下:

source ~/ros_noetic_ws/devel/setup.bash

如果不想每次都手动 source,可以把它加到~/.bashrc末尾:

echo "source ~/ros_noetic_ws/devel/setup.bash" >> ~/.bashrc

但这里有个坑:如果你同时装了 ROS2 或者多个 ROS 工作空间,~/.bashrc里的 source 顺序会影响环境变量。ROS1 和 ROS2 的环境变量有冲突,同时 source 会导致ros2命令找不到或者roscore起不来。我的做法是写一个切换脚本,需要哪个 source 哪个,而不是全塞进~/.bashrc。

5.2 验证核心功能是否正常

source 之后,跑几个命令验证:

roscore

如果roscore能正常启动,说明核心通信层没问题。再开一个终端:

rosnode list

应该能看到/rosout节点。然后测试消息发布订阅:

rostopic pub /test std_msgs/String "hello" -r 1

再开一个终端rostopic echo /test,能看到消息输出,说明消息系统正常。这几步过了,ROS 的核心就算跑通了。

5.3 编译产物清理与重编

源码编译的工作空间会占不少磁盘,build和devel目录加起来可能几个 GB。如果编译过程中改了源码或者依赖,需要重新编译,最干净的做法是删掉build和devel重新来:

cd ~/ros_noetic_ws rm -rf build devel catkin_make

如果只是改了某个包的源码,可以只重编那个包:

catkin_make --pkg 包名

这样只编译指定的包和它依赖的包,速度快很多。但要注意,如果改的是被很多包依赖的基础包(比如roscpp),重编时依赖它的包也会被重编,时间不一定省。

6. 那些官方文档不会告诉你的实操细节

6.1 关于 rosdep 的 skip-keys 策略

rosdep install报Cannot locate rosdep definition时,很多人第一反应是去改 rosdep 的源文件,手动加映射。这个做法不推荐,因为 rosdep 的源文件更新后你的修改会被覆盖。正确的做法是用--skip-keys跳过,然后手动确认依赖已装。我一般会把跳过的键记在一个文本文件里,下次重装环境时直接带上这个 skip 列表,省得再排查一遍。

6.2 编译日志的定位技巧

catkin_make的输出很长,报错信息容易被淹没。我的做法是把输出重定向到文件,然后用grep找 error:

catkin_make 2>&1 | tee build.log grep -n "error:" build.log

这样能快速定位到第一个报错的位置。注意,编译报错往往有连锁反应,第一个 error 才是根因,后面的 error 可能是第一个导致的。所以修的时候从第一个 error 开始修,修完再编,不要一次改一堆。

6.3 关于 Python 包的安装路径

源码编译的 ROS Python 包会装到devel/lib/python3/dist-packages下。如果你用pip装了一些 Python 依赖,注意pip装的位置和 ROS 的 Python 路径可能不一致。用python3 -c "import sys; print(sys.path)"看一下路径顺序,确保 ROS 的路径在前面。如果顺序不对,可以在setup.bash里手动调整PYTHONPATH。

6.4 长期维护的建议

源码编译的 ROS 工作空间,建议用 git 管理你的src目录里的修改。因为编译过程中你可能会改一些包的源码来适配 GCC 13 和 Python 3.12,这些修改如果不记录,下次重装环境时又要重新踩一遍。我的做法是在src目录下建一个 git 仓库,把对第三方包的修改单独提交,这样升级或重装时能快速应用。

另外,编译好的devel目录不建议提交到 git,体积太大且和机器相关。真正需要版本化的是你的源码修改和编译脚本。可以写一个setup.sh把整个编译流程脚本化,包括依赖安装、rosdep、catkin_make,这样换机器时一条命令就能重建环境。

7. 常见报错速查与处理思路

7.1 依赖类报错

报错信息根因处理方式
Cannot locate rosdep definition for [xxx]rosdep 源里没有 24.04 的映射手动 apt/pip 装等价包,用--skip-keys跳过
Could not find a package configuration file provided by "xxx"某个依赖包没装或没编译确认依赖包是否在 src 里,或 apt 装对应 dev 包
fatal error: xxx.h: No such file or directory头文件缺失找到提供该头文件的 dev 包 apt 安装

7.2 编译类报错

报错信息根因处理方式
error: 'register' storage class specifier is deprecatedC++17 移除了 registerCMakeLists 里指定 C++14 标准
error: invalid conversion from 'const char*' to 'char*'GCC 13 类型检查更严改源码用显式转换
c++: fatal error: Killed signal terminated program cc1plus内存不足降低并行度-j2或-j1
ModuleNotFoundError: No module named 'distutils'Python 3.12 移除 distutilspip install setuptools
ModuleNotFoundError: No module named 'imp'Python 3.12 移除 imp改用 importlib 或装兼容包

7.3 运行类报错

报错信息根因处理方式
roscore启动后立即退出环境变量冲突或端口占用检查ROS_MASTER_URI,确认 11311 端口没被占
rosnode list报连接失败没 source 环境或 master 没起sourcedevel/setup.bash,确认 roscore 在跑
Python 脚本 import rospy 失败PYTHONPATH 不对检查sys.path,调整 PYTHONPATH 顺序

这张表里的报错是我在 24.04 上实际遇到过的,覆盖了从依赖到编译到运行的主要问题。遇到报错时,先对照这张表看有没有匹配的,没有的话再按"从第一个 error 开始修"的原则逐步排查。

8. 编译完成之后:把环境变成可复现的资产

8.1 写一个可复用的编译脚本

整个流程走通之后,最值得做的一件事是把流程脚本化。我写了一个build_ros_noetic.sh,内容大致是:检查系统版本、安装 apt 依赖、初始化 rosdep、生成 rosinstall、wstool 拉取、rosdep install、catkin_make。脚本里把--skip-keys的列表也写进去,这样换机器时直接跑脚本,不用再手动排查依赖。

脚本里我加了几个判断:如果src目录已存在就跳过 wstool init,如果devel已存在就提示是否清理重编。这些小判断能避免重复劳动,也让脚本更健壮。

8.2 记录你的源码修改

编译过程中对第三方包的修改,一定要单独记录。我的做法是在src目录下建一个patches文件夹,把每个修改做成 patch 文件,脚本里在拉取源码后自动应用这些 patch。这样即使重新拉取源码,修改也能自动恢复。patch 文件用git diff生成,应用时用git apply,简单可靠。

8.3 关于后续升级的考虑

ROS1 Noetic 已经停止官方维护,源码编译的环境不会有官方更新。如果你的项目需要长期维护,建议把编译好的环境做成容器镜像,这样部署到其他机器时不用重新编译。容器里把devel目录和系统依赖都固化进去,运行时直接 source 即可。这个做法在团队协作里特别有用,能保证每个人的环境完全一致,避免"在我机器上能跑"的问题。

我自己在 24.04 上编译 Noetic 前后折腾了大概两天,大部分时间花在排查 GCC 13 和 Python 3.12 的兼容性报错上。走通之后回头看,其实核心步骤就那么几步,难点全在版本错位带来的细节上。如果你也在做同样的事,建议先把ros_comm编通,确认核心没问题,再逐步加包,不要一上来就desktop_full全量编译,那样一旦报错很难定位是哪个包的问题。另外,编译日志一定要留好,修过的报错记下来,下次遇到能省很多时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 17:28:48

CLI-Anything:将零散脚本变成标准命令行命令的轻量框架

如果你跟我一样,日常要在终端里敲一堆维护脚本,那你大概率经历过这种场景:项目目录里有scripts/、tools/、ops/至少三四个放脚本的文件夹,里面躺着各种.sh和.py,每个脚本的参数规则又都不一样。有的用--date 2025-01-0…

作者头像 李华
网站建设 2026/9/28 17:28:16

Claude插件与托管Agent:金融场景落地实践

1. 从"financial-services"这个标题说起:一个被低估的插件化落地场景第一次看到financial-services这个项目标题时,我脑子里冒出来的第一个念头不是"又一个金融类 Demo",而是——这大概率是一个围绕Claude 生态的插件&am…

作者头像 李华
网站建设 2026/9/28 17:26:32

金融智能体插件化落地:基于托管Agent与Cowork的工程实践

1. 从"financial-services"这个标题说起:一个被低估的插件化落地场景第一次看到financial-services这个项目名,很多人会下意识觉得它是个业务系统——账户、交易、风控、报表那一套。但结合关键词里的Claude、Cowork、Managed Agents API、plu…

作者头像 李华