1. 核心谜题:为什么 Jetson Orin 默认跑在 22.04,却总有人想退回 20.04?
1.1 被 Noetic 支持矩阵卡住的项目
我先从结论说起:如果你在 Jetson Orin(Arm64 平台)上做 ROS1 开发,并且项目依赖还停留在 noetic 时代,那么 Ubuntu 22.04 基本等于一堵墙。ROS Noetic 是 ROS1 的最后一个发行版,官方预编译包只面向 Ubuntu 20.04(Focal)和 Debian Buster 提供,并且公开了 arm64 架构的二进制包。Noetic 对应 Python 3.8 的生态,打包路径固定在/opt/ros/noetic。当你把操作系统换成 22.04(Jammy)之后,packages.ros.org 的源里根本没有ros-noetic-desktop-full的 jammy 安装包;强行把 focal 源挂上去,apt 大概率会在依赖解析阶段把整个系统搞成不可用的状态。
为什么大家还要坚守 Noetic?我接触了不少实际项目:搬运机器人、AGV、机械臂、多传感器融合的小车。这些项目里的自研代码很多是 2019 到 2022 年之间写的,深度依赖了move_base、gmapping、amcl、gazebo_ros这些老牌包。把整套技术栈迁到 ROS2 不是不能,但要重写 launch 文件、调整 tf 树处理逻辑、改回调函数和生命周期节点,业务上往往等不起。因此在 Orin 算力明显过剩的今天,把操作系统“降回去”反而是最务实的一条路,这也是行业内称它为“降级党”的原因。这篇文章就是给这批人准备的:怎么把 Orin 从 Ubuntu 22.04 安全退回 20.04,然后干净利落地装好 ROS Noetic。
1.2 降级不是 apt 降级,而是 JetPack 整体回退
很多从 x86 转过来的朋友会问:“我能不能在 22.04 上直接用 noetic 的软件源,或者用do-release-upgrade退回 20.04?”这里必须澄清一个关键认知:Jetson 上的 Ubuntu 并不是普通 Ubuntu。它除了 rootfs 之外,还有 L4T 内核、TegraBoot/CBoot 引导、以及/opt/nvidia下面一组闭源用户态库。NVIDIA 把这整套环境打包成 JetPack:JetPack 5.x 对应 Ubuntu 20.04,JetPack 6.x 对应 Ubuntu 22.04。内核、固件、驱动和 rootfs 是绑在同一个 L4T 版本里的,单纯改一下/etc/os-release或者 apt 源没有任何意义,底层完全对不上。
所以这里说的“回退”,本质上并不是操作系统层面的降级,而是把整个设备重新刷成 JetPack 5.1.3。这个 JetPack 版本基于 Ubuntu 20.04 Focal,官方支持 AGX Orin、Orin NX、Orin Nano 全系模块。刷写过程中,NVIDIA 的工具会重写目标板的 bootloader、内核分区、设备树和 rootfs,相当于一次干净的底层重装。正因为如此,所有讲这个操作的教程都会把“备份数据”放在最前面——刷机过程会清空 rootfs,分区表也会被重新初始化,没有备份等于裸奔。
顺便提一句,也有人用 Docker 在 22.04 上装一个 20.04 的容器,在容器里跑 ROS Noetic,省掉刷机的麻烦。但这个方案对 CSI 摄像头、串口、GPIO 这类硬件直通非常不友好,每接一个外设都要跟权限和挂载选项搏斗。如果你的机器人节点需要长时间稳定跑,原生系统永远比容器方案省心。更何况刷回 JetPack 5.1.3 之后,CUDA 11.4、cuDNN、TensorRT 全都在 20.04 上原生可用,后续装什么依赖都顺畅得多。
1.3 两条正经路:SDK Manager 与命令行 flash.sh
回退的实操路线有两条。一条是用 NVIDIA 官方图形界面工具 SDK Manager,它会自动下载镜像、自动把设备拉进 recovery、再自动执行底层刷写脚本,适合绝大多数开发者,尤其适合第一次刷机的新手。另一条是我个人更偏爱的命令行方式,也就是直接用Linux_for_Tegra目录里的flash.sh手动刷写,它在批量部署、离线环境、定制 rootfs 时非常有用。两条路底层用的其实是同一个刷写引擎,区别只在于 SDK Manager 帮你把下载、解包、设备握手这些步骤都封装掉了。我建议两条路都了解一下:平时用 GUI 图省事,一旦 GUI 翻车或者被网络问题卡住,至少你还有 Plan B。
2. 动手前的关键准备:硬件、主机与镜像选择
2.1 确认你的硬件模块属于哪个家族
开始前,先在设备上执行下面命令,确认你手里的到底是哪一块模块:
cat /proc/device-tree/model输出可能是NVIDIA Jetson AGX Orin Developer Kit,也可能是NVIDIA Jetson Orin Nano Developer Kit。不同模块和载板搭配,进入刷写模式的入口完全不同,USB 数据口的位置也不一样。我把常见的组合整理成了表格,方便你对照:
| 模块 | 常见载板 | Recovery 入口 | 刷写接口 |
|---|---|---|---|
| AGX Orin 32/64GB | 官方 DevKit | 按住 Recovery,再按 Reset | USB-C 数据口 |
| Orin NX 8/16GB | 官方 DevKit 载板 | 按住 Recovery,再按 Reset | USB-C 数据口 |
| Orin Nano 4/8GB | 官方 DevKit 载板 | 按住 Recovery,再按 Reset | USB-C 数据口 |
| Orin NX/Nano(第三方载板) | 各厂家载板 | 短接 Recovery 插针或拨码 | 通常 USB-C,视载板而定 |
JetPack 5.1.3 对上面这些 Orin 模块都提供官方支持,包括后来新出的 Orin NX 16GB 版本。你不用担心自己的硬件“太新刷不进去”,因为 5.1.3 的 L4T R35.5.0 已经包含了 Orin 全系所需的引导固件和设备树。
2.2 准备一台 x86_64 主机,别拿目标板自己干这事
SDK Manager 官方只支持运行在 x86_64 架构的 Ubuntu 20.04/22.04 主机上。你不可能在一台 Jetson 上给它自己降级刷机,因为刷写过程需要操作 USB recovery 设备。你可以在 Windows 或 macOS 上通过虚拟机跑 Ubuntu,但 USB 直通在虚拟机里经常出现识别不到的问题,折腾成本很高。我建议直接准备一台实体 Ubuntu 主机,哪怕是老旧的 i5 笔记本,只要能装 Ubuntu、有能用的 USB 口就够了。
主机配置有两个硬性要求:系统盘剩余空间至少 20GB,内存建议 8GB 以上。SDK Manager 会下载完整的 JetPack 镜像,还要解包 rootfs,空间太小会直接刷写失败。另外,目标板供电问题也容易被忽略。AGX Orin DevKit 自带的电源适配器没问题,但 Orin NX/Nano 插在第三方载板上时,如果电源功率不足,设备在刷写中途可能掉电重连,导致 flash 失败。动手前先确认载板电源适配器能满足官方功耗要求。
2.3 进入 Force Recovery 模式并确认 USB 识别
这一步十有八九是新手翻车点。拿 AGX Orin DevKit 举例:先接好 DC 电源,用一根能传数据的 USB-C 线把开发板和 Ubuntu 主机连起来。注意板子上可能有多个 Type-C 口,要插在标注了 USB 功能的那一个,不是所有 Type-C 口都能传数据。然后按住板子上写着 “Force Recovery” 的小按钮不松手,再按一下 Reset 按钮,等两秒后松开 Recovery 按钮。
接着在主机上执行:
lsusb看到类似NVIDIA Corp. APX(设备 ID 通常是0955:7023)的设备,就说明开发板已经进入 recovery 模式了。如果没看到,优先换一根确定能传数据的 USB 线,再换一个主机 USB 口,最后再重复一次 Recovery+Reset 组合。有些第三方载板把 recovery 做成了插针,你需要短接对应引脚才能进入,具体以载板手册为准。这一步不要跳过,不进 recovery 就刷机,基本等于白费时间。
2.4 选择正确的目标版本:JetPack 5.1.3 是降级的终点
回退到 20.04,目标版本锁定 JetPack 5.1.3。它是 JetPack 5.x 系列的最终维护版本,基于 L4T R35.5.0,系统是 Ubuntu 20.04 Focal。它给 Orin 全系提供 CUDA 11.4、配套 cuDNN 和 TensorRT 8.5 等组件。这些版本和 ROS Noetic 常用生态的匹配度很好,很多基于 CUDA 的点云库、目标检测库在 CUDA 11.4 上踩坑最少。
不要在这里追求“越新越好”。JetPack 6.x 对应 Ubuntu 22.04,ROS Noetic 官方源没有 Jammy 预编译包,装了也会陷入依赖地狱。你这次的目标就是回到 20.04,那么 5.1.3 就是终点版本。如果买到的开发板默认预装 JetPack 6.x,不用慌张,SDK Manager 和命令行刷写都能把它完整降回 5.1.3。
2.5 备份已经存在的数据
刷写会清空整个 rootfs,如果你的项目数据在 eMMC 或 NVMe 上,请务必先备份。我通常分两层操作:第一层,重要的工程文件直接 rsync 到外部磁盘或者另一台电脑,这一步能保住你的代码和文档;第二层,如果确实需要完整还原当前系统,可以用dd做整盘镜像。但dd镜像文件大小几乎等于整个分区大小,动不动几十上百 GB,不适合大容量 NVMe。如果不是必须完整保留系统状态,我更推荐只备份/home、/etc下的关键配置,刷完机后重新部署环境,反而更干净。
提示:跨 JetPack 大版本降级时,SDK Manager 里那个“保留用户数据”的选项我不建议勾选。虽然理论上可以保留
/home,但在跨版本固件差异较大的情况下,残留的系统库和配置反而会带来各种奇怪问题。老老实实完整擦除,是最稳妥的做法。
3. 主流做法:用 SDK Manager 从 22.04 刷回 20.04
3.1 SDK Manager 安装与首启设置
先去 NVIDIA 官网下载sdkmanager_2.x.x_amd64.deb,然后在一台 x86_64 Ubuntu 主机上安装:
sudo apt update sudo apt install -y ./sdkmanager_2.x.x_amd64.deb sdkmanager首次启动会要求登录 NVIDIA 开发者账号。这一步需要确保主机网络能正常访问 NVIDIA 官网,否则登录和后续下载镜像都会失败。登录成功后,SDK Manager 会自动查询可用的 JetPack 版本列表,你就进入了图形化刷机的核心界面。
3.2 选择 Target Hardware 与 JetPack 5.1.3 镜像
打开 SDK Manager 后,左侧 “Product Category” 选 Jetson,右侧会列出默认推荐的版本。如果默认版本是 JetPack 6.x,你需要在版本下拉框里手动切换到 5.1.3。如果下拉列表里没有,点右下角的 “Available Versions” 或者修改 SDK Manager 设置里的版本偏好,一般能找到历史版本。
选定版本后,勾选你的目标设备。以 AGX Orin DevKit 为例,目标硬件选择 “Jetson AGX Orin Developer Kit”。接下来 SDK Manager 会列出组件清单,包含 Jetson Linux 35.5.0、CUDA、cuDNN、TensorRT,以及可选的 Deep Learning Frameworks。这里我建议保留 NVIDIA 核心组件的默认勾选。如果后续不打算在板上跑 PyTorch 等深度学习训练,Deep Learning Frameworks 可以不勾,能省下不少下载时间。最后一步要注意选择 “Install to Target” 而不是 “Download only”,否则只会下载镜像却不会刷到设备上。
3.3 刷写过程中的真实日志长什么样
确认所有选项后,SDK Manager 开始下载组件,默认缓存路径是主机上的~/Downloads/nvidia/sdkm_downloads。下载完成后进入刷写阶段,界面上会逐条列出执行步骤。这个过程本质上是在后台解压Tegra_Linux_Driver_Package和Sample Root Filesystem,然后通过 USB 让设备进入 recovery,执行类似这样的命令:
sudo ./flash.sh jetson-agx-orin-devkit mmcblk0p1屏幕上你会看到tegraflash.py的输出、分区写入日志、设备自动重启等信息。这个阶段最忌讳手动切断 USB 或者拔电源。如果卡住超过十分钟,多半是网络下载额外组件失败,SDK Manager 会报红并在界面上给出 Retry 按钮。
3.4 刷写完成后的第一轮验证
设备重启后进入系统,先做一轮基础检查。打开终端执行:
cat /etc/nv_tegra_release cat /etc/os-release uname -m/etc/nv_tegra_release第一行如果以# R35 (release)...开头,说明 L4T 版本正确;/etc/os-release里VERSION_ID="20.04"说明系统已经是 Focal;uname -m输出aarch64则确认是 Arm64 架构。再跑一次:
nvcc --version看到 Cuda compilation tools release 11.4 之类的字样,说明 JetPack 5.1.3 的 CUDA 环境也已经就位。到这里,从 22.04 回退到 20.04 的刷机部分就算真正完成了。
4. 进阶做法:在终端里通过命令行完成整体刷写
4.1 为什么还要学命令行刷写方式
SDK Manager 确实方便,但它也有几个不太舒服的场合。例如主机不是标准 Ubuntu,或者你想在离线环境里刷多台设备;又或者你需要定制 rootfs,比如把 ROS 环境、自研 deb 包直接预置到系统镜像里,流水线式批量部署。这时候命令行方式就成了唯一选择。另外,SDK Manager 偶尔会因为版本列表更新、登录状态异常而卡住,掌握命令行刷写能让你在关键时刻绕开这些外部依赖。
命令行刷写的底层原理和 SDK Manager 完全一致:都是把 NVIDIA 发布的 BSP 包和 Rootfs 组合成一个完整的系统镜像,再通过 USB recovery 写入设备。SDL 只是把整个过程封装成了图形界面,手动方式无非是把命令一条条执行出来。
4.2 下载 Driver Package 与 Sample Root Filesystem
到 NVIDIA 的 JetPack 下载页面找到 Jetson Linux 35.5.0,你需要两个文件:
Jetson_LINUX_BSP_R35.5.0_aarch64.tbz2:BSP 包,包含内核、u-boot、设备树、刷写工具链。Tegra_Linux_Sample-Root-Filesystem_R35.5.0_aarch64.tbz2:Ubuntu 20.04 的 rootfs 压缩包。
下载时注意文件名里的aarch64,不要下成其他架构。下载完成后把两个文件放到同一个目录,例如~/l4t。
4.3 组装 rootfs 并执行 flash.sh
在 Ubuntu 主机上解压:
mkdir ~/l4t && cd ~/l4t tar xf Jetson_LINUX_BSP_R35.5.0_aarch64.tbz2 cd Linux_for_Tegra sudo tar xpf ../Tegra_Linux_Sample-Root-Filesystem_R35.5.0_aarch64.tbz2注意第二句解压 rootfs 一定要用sudo,并且保持xpf参数。因为 rootfs 里有很多设备节点、软链接和权限位,普通用户解压会丢失权限信息,刷出来的系统会有各种诡异问题,比如 sudo 不可用、服务起不来。
然后执行:
sudo ./apply_binaries.shapply_binaries.sh的作用是把 NVIDIA 闭源驱动、固件和库正确安置到 rootfs 里。这一步漏掉的话,刷出来的系统虽然能开机,但/opt/nvidia会缺大量驱动,后面装 CUDA 相关库必挂。
接着让设备进入 recovery 模式,并在主机上确认lsusb能看到 APX 设备,最后执行:
sudo ./flash.sh jetson-agx-orin-devkit mmcblk0p1如果你是 Orin NX 或者 Orin Nano 的官方 DevKit,把第一个参数换成jetson-orin-nx-devkit或jetson-orin-nano-devkit。第三个参数mmcblk0p1代表把系统根分区写到目标板的内置存储设备上。第三方载板用户一定要向载板厂家确认对应 flash 配置名称,不要想当然用默认配置,否则设备树不匹配,开机后可能网口、显示或者 PCIe 设备全部失效。
4.4 命令行刷写的常见踩坑点
命令行刷写有两个高频坑。第一个就是我刚说的解压 rootfs 权限问题,很多人图省事直接tar xf,刷完发现系统起不来,再来回折腾。第二个是网络相关,虽然 BSP 包理论上支持离线刷写,但如果你下载的版本不完整,flash 过程可能中途尝试在线获取组件,这时候目标板必须能访问外网。我的项目经验是:先手动把镜像下载完整,再断网刷写,这样最可控。
还有一个习惯我非常推荐:保留Linux_for_Tegra这个目录不要删。后续你想往 rootfs 里加 ROS 环境,只需要在Linux_for_Tegra/rootfs里操作,改完重新执行apply_binaries.sh和flash.sh即可。每次从头解包再下载,时间成本高,也容易出权限问题。
5. 拿到 20.04 之后:ROS Noetic 适配全流程
5.1 配置 ROS 软件源:现在用 keyring 方式更稳妥
刷完系统后,先确认 Python 版本。Ubuntu 20.04 自带 Python 3.8,ROS Noetic 官方就是基于 3.8 构建的,不需要再折腾 Python 环境。接下来配置 ROS 软件源。老教程里大量使用apt-key add,但新版 apt 会提示公钥不信任的问题,这里我推荐用 keyring 方式:
sudo apt update sudo apt install -y curl ca-certificates curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros/ubuntu focal main" | sudo tee /etc/apt/sources.list.d/ros-latest.list sudo apt update注意我把发行版写死成了focal,而不是用$(lsb_release -sc)动态获取。虽然当前系统就是 focal,但写死的好处是,以后如果有人误操作把系统源升级到 jammy,ROS 源不会跟着变乱。
5.2 安装 Desktop Full 与常用构建工具
直接安装完整桌面版:
sudo apt install -y ros-noetic-desktop-full这个包包含 ROS core、机器人常用库、导航栈、感知栈、Gazebo 11、rviz 等,对绝大多数机器人项目来说一步到位。在 Orin 的 Arm64 架构下,安装时间会比 x86 稍长,几分钟到十几分钟都正常。如果中途报依赖错误,先执行sudo apt --fix-broken install,再重新执行安装。
装完后再装一组构建工具,后面编译自己工作空间时少不了:
sudo apt install -y python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential sudo rosdep init rosdep update5.3 用 turtlesim 做一次最小冒烟测试
新环境是否真的能跑 ROS,最直接的方法就是启动一个 turtlesim。开两个终端,第一个终端启动 master:
source /opt/ros/noetic/setup.bash roscore第二个终端启动海龟窗口:
source /opt/ros/noetic/setup.bash rosrun turtlesim turtlesim_node如果一切正常,会弹出一个窗口,里面有一只固定位置的海龟。再开第三个终端,用键盘控制海龟移动:
source /opt/ros/noetic/setup.bash rosrun turtlesim turtle_teleop_key这一步通过,说明 ROS 本体、Python 绑定和 GUI 工具链都没问题。如果你用的是无桌面版的 Ubuntu,可以跳过 GUI 测试,只观察roscore输出和rosnode list的结果,同样能达到验证目的。
5.4 让 Jetson 的 CUDA/OpenCV 与 Noetic 顺畅协作
JetPack 5.1.3 装好后,CUDA 11.4 位于/usr/local/cuda。先验证驱动和编译工具链:
ls /usr/local/cuda/bin/nvcc && nvcc --version在 Jetson 上做视觉相关开发,OpenCV 也是一个绕不开的环节。系统自带的是 Ubuntu 20.04 源里的 OpenCV 4.2,不带 CUDA 加速。对绝大多数 ROS Noetic 节点来说,直接用ros-noetic-cv-bridge就足够,它默认链接的是系统 OpenCV 4.2,兼容性没问题。如果后续确实需要 CUDA 加速的 OpenCV,可以自己编译一份 4.x 再重新编译 cv_bridge,但这属于性能调优阶段的工作,不建议一上来就做。先把业务逻辑跑通,再考虑加速,效率会更高。
如果你还需要验证 GPU 通用计算,可以看一眼nvidia-smi是否正常输出。Jetson 上nvidia-smi不像 x86 桌面上显示那么丰富,但能看到驱动和 GPU 状态就说明算力环境没问题。
6. 实操联排:故障排查与避坑心得
6.1 设备无法进入 Recovery 模式的排查顺序
不管用 SDK Manager 还是命令行刷写,第一步永远是进入 recovery 并让主机识别到设备。我通常按这个顺序排查:
- 执行
lsusb,看有没有NVIDIA Corp. APX设备; - 没有就换一根 USB 线,很多线只能充电不能传数据;
- 再换一个 USB 口,优先插主机背部接口;
- 确认电源稳定,尤其是第三方载板,供电不稳会导致设备反复重启;
- 重试“按住 Recovery、按 Reset、等两秒松开”的组合。
如果还是不行,检查载板上的跳线帽和拨码开关,部分工业载板需要拨到 USB Recovery 模式才会开放刷写口。
6.2 SDK Manager 刷到一半失败时的急救
SDK Manager 最常见的失败场景是网络下载中断,或者刷写阶段 USB 连接异常。遇到这种情况先不要慌,按顺序做三件事:
sudo pkill -f sdkmanager sudo rm -rf ~/Downloads/nvidia sudo apt --fix-broken install删掉~/Downloads/nvidia缓存之后,重新启动 SDK Manager 下载一遍。如果每次都在同一个百分比失败,优先考虑换 USB 线、换 USB 口,或者干脆切换到第四章的命令行方式,把下载和解包流程单独控制,反而更容易定位问题。
6.3 apt 安装 ROS Noetic 时的依赖地狱
在 Jetson 上安装ros-noetic-desktop-full,最常见的坑是软件源里混入了 ROS2 源,或者 apt 源变成了 jammy。如果你之前手滑配过 Humble 的源,apt 会把ros-*包解析到错误版本,导致各种 “Unable to locate package”。我的建议是逐项检查/etc/apt/sources.list.d/里有没有 ROS2 源,有就先注释掉。然后确认ros-latest.list里的发行版是 focal 而不是 jammy。如果报“缺少公钥”,重新执行一次 keyring 配置命令。最后的兜底手段是sudo apt --fix-broken install,把破损依赖修复后再继续。
6.4 我建议所有降级党提前做好的三件事
这是我被折腾过几次之后总结出的教训。第一,精确保留你的Linux_for_Tegra解包目录,不要放在/tmp,放一个长驻磁盘路径。后续需要定制 rootfs、增删软件包时,有这套 base 环境,一次编译就能同步到多台设备。第二,刷写前把目标板和主机的 IP、硬件型号、当前 JetPack 版本拍照存下来。排查问题时这个习惯非常管用,很多“刷完网口不通”的案例,其实只是板子和主机在同一网段发生了地址冲突,有了记录就能快速定位。第三,如果目标是长期跑机器人项目,我强烈建议把时间花在 rootfs 定制上:先在 Ubuntu 20.04 的干净环境里把 ROS Noetic、依赖库、自研包全部装好,再用这个 rootfs 做整机镜像,之后每台设备都是即刷即用。
我个人在实际操作中还有一个体会:回退到 20.04 并不是终点,而是项目稳定性的起点。Jetson Orin 在 Arm64 + Ubuntu 20.04 + ROS Noetic 这个组合下,社区资料最丰富,踩坑成本最低。做完这次降级之后,你会发现很多以前在 22.04 上要绕路解决的问题,现在直接 apt 就能装好。最后再分享一个小经验:刷完机后第一时间修改/etc/apt/sources.list,把 Ubuntu 源替换成你访问速度快的镜像站,装 ROS Noetic 时能明显感觉到下载速度的差异。这个细节看着不起眼,却能省下大把等待时间。