Jetson Nano边缘AI实战:YOLOv5与TensorRT部署优化
2026/9/17 23:00:42 网站建设 项目流程

1. 先搞清楚这块板子能干什么:Nano 的定位与选型逻辑

第一次把 Jetson Nano 插上电的时候,很多人会有点失望——它长得就像一块稍微大点的树莓派,没有任何"AI 开发板"的排面。但当你把第一个模型跑起来,看着它在一张 640 分辨率的画面上圈出人和车,功耗表却只爬升到十瓦出头的时候,那种感觉是不一样的。Jetson Nano 的核心价值就藏在这句话里:它是一块能用 5W 到 10W 的功耗,在本地完成推理的完整计算平台,不是玩具,也不是服务器,而是介于两者之间的一块"边缘算力"。

很多视频教程是录屏加解说的形式,字幕默认是关掉的,语速一快就完全跟不上,来回拖进度条又很折磨人。这篇就当那份教程的文字版来看——把里面一闪而过的命令、参数、报错都写清楚,遇到哪一步卡住了直接翻对应章节,比反复暂停截图省事得多。我会从硬件准备讲到 TensorRT 加速,中间穿插大量实际踩过的坑,适合刚拿到板子的新手,也适合已经在用但一直被内存和帧率折磨的老用户。

1.1 472 GFLOPS 放在今天是什么水平

先把参数摆出来:Jetson Nano 4GB 版本用的是四核 ARM Cortex-A57 处理器,主频 1.43GHz,GPU 是 128 核的 Maxwell 架构,频率 921MHz,官方标称 FP16 算力 472 GFLOPS。内存是 4GB 的 64 位 LPDDR4,带宽 25.6GB/s,CPU 和 GPU 共享这一整块内存,没有独立显存。

这个规格放到今天,单看 GFLOPS 数字确实不好看。但你要理解它的对手是谁:同价位的树莓派 4B 用的是 VideoCore VI,那玩意儿的浮点算力和 Nano 差着两个数量级;一台同价位的迷你主机没有 GPU,跑深度学习推理只能靠 CPU,帧率会低到没法用。Nano 的意义在于,它把 CUDA 生态完整地搬到了一个十几瓦的设备上,你能在它上面用 PyTorch、TensorRT、DeepStream,这些工具链跟你在服务器上用的是一模一样的。

生活化的类比:如果台式机加显卡是一辆越野车,云端 GPU 是一整支车队,那 Jetson Nano 就是一辆电动自行车。它拉不了重货,但它能自己骑到任何地方,不需要拖一根长长的电源线。边缘计算的场景里,"能到现场"这件事比"算得快"重要得多。

注意:Nano 的 4GB 内存是 CPU 和 GPU 共用的。这意味着你开一个 4GB 的 swap 文件,实际上是拿存储卡的空间换内存,速度差距是数量级的,不要指望它当内存用。

1.2 Nano、Orin Nano 该怎么选

如果你现在正准备买板子,这个问题必须先回答清楚,因为两者的差距不是一代,是两代半。

对比项Jetson Nano 4GBJetson Orin Nano 8GB(Super 模式)
GPU 架构Maxwell,128 核Ampere,1024 核,含 Tensor Core
FP16 算力约 0.47 TFLOPS约 67 TOPS(INT8 稀疏)
内存4GB LPDDR4,25.6GB/s8GB LPDDR5,102GB/s
CPU四核 A57 @1.43GHz六核 A78AE @1.7GHz
系统Ubuntu 18.04,JetPack 4.6.x 止步Ubuntu 22.04,JetPack 6.x 持续更新
功耗档位5W / 10W7W / 15W / 25W
典型价格段入门级中端

结论很直接:如果你只是想花最少的钱体验边缘 AI 的完整流程,Nano 依然够用,YOLOv5s 这种小模型跑十几帧完全没问题,学习成本也低,教程最多。但如果你要做产品原型、要跑多路视频、要用最新的 JetPack 6 和新版 PyTorch,那 Orin Nano 是唯一合理的答案,Nano 的 4GB 内存和 Ubuntu 18.04 会在半年内把你逼疯。

我个人的判断标准是:买板子只做学习和验证,Nano;任何带有"以后要交付"性质的项目,直接上 Orin Nano。因为从 Nano 迁到 Orin 的时候,你会发现代码基本不用改,但环境全部要重搭一遍,这个时间成本比板子差价高得多。

1.3 为什么第一课总是拿 YOLOv5 来验证

几乎所有 Jetson 教程都从目标检测起步,而且大概率是 YOLOv5,这不是偶然。原因有三个层面。

第一,它是一个端到端的完整验证链路。从摄像头取流、预处理、模型推理、后处理到画框输出,每一个环节都涉及硬件能力,跑通了说明你的整条链路都是健康的。你要是只跑一个 MNIST 分类,GPU 用了没用到都看不出来。

第二,它的模型尺寸可调。YOLOv5 有 n、s、m、l、x 五个规格,Nano 上从 yolov5n 开始试,跑得动再往上加,这个渐进的过程刚好能让你直观感受到算力边界在哪里。yolov5s 是 Nano 上的甜点,也是绝大多数教程的默认选择。

第三,它的部署路径非常标准化。PyTorch 权重导出 ONNX,ONNX 转 TensorRT engine,engine 直接推理,这条路走通了,你换成 YOLOv8、换成 MobileNet、换成任何其他检测模型,方法论是一样的。

不过要提前说清楚一个容易被忽略的点:YOLOv5 官方仓库对 Jetson 的适配是"能跑但没专门优化"的状态,很多默认参数是给服务器写的。照着 README 直接跑,在 Nano 上大概率会遇到内存爆掉或者速度感人。后面第五章我会把该改的参数一个个拆开讲。

2. 从烧录到开机:硬件准备工作别省

这一步看起来最没技术含量,但实测下来,新手大部分"板子是不是坏了"的疑问都出在这里。我见过太多人拿着板子折腾一整天,最后发现是电源适配器的问题,或者 SD 卡是杂牌的。

2.1 镜像与 SD 卡的选择逻辑

Jetson Nano 的系统镜像是通过 JetPack SDK 提供的,最终下载到的是一个.img.img.zip文件,大小在 6GB 到 8GB 之间。你需要准备一张至少 32GB 的 microSD 卡,我个人建议直接上 64GB。

为什么强调卡的品质?因为很多时候你的系统盘就是这张卡,Ubuntu 系统本身有大量的随机小文件读写,杂牌卡在持续写入时会出现明显的延迟抖动,表现出来就是界面卡顿、apt 安装超时、有时候甚至系统莫名其妙崩掉。选 UHS-I Speed Class 3(卡面上标 U3)以上的产品,别省这几十块钱。

关于 JetPack 版本,Nano 的最后一版官方支持停在 JetPack 4.6.x 系列,对应的 L4T 版本是 32.7.x。如果你在网上看到有人用 JetPack 5.x 装 Nano,那大概率是套壳的第三方镜像或者是从别的模块移植的,不建议新手碰。记住一个铁律:Nano 用 JetPack 4.6.x,Orin Nano 用 JetPack 6.x,两者不通用。

2.2 烧录与首次开机

烧录工具有两个主流选择:balenaEtcher 和官方的 SDK Manager。SDK Manager 是给整机刷 eMMC 用的,Nano 这种 SD 卡启动的方案,直接用 balenaEtcher 把解压出来的.img写到卡上就行,选择目标设备、选镜像、点 Flash,等进度条走完。

开机前有几件事要确认:HDMI 显示器接好、USB 键盘鼠标接好、网线接好。第一次开机必定会走一段 oem-config 配置向导,设置语言、时区、用户名密码、主机名。这个过程必须用显示器,因为它没有默认账号。如果你手头没有 HDMI 显示器,较新版本的 BSP 里提供了预设默认用户的脚本,可以在烧录后挂载 SD 卡提前写好配置,但脚本名称和用法在各个版本之间不一样,具体以你下载的那版 BSP 里的说明为准,别照抄网上的老教程。

首次进入桌面之后,先别急着装东西,做两件事:确认网络通、跑一次系统更新。

sudo apt update sudo apt upgrade -y sudo reboot

注意:升级过程中如果弹出内核或者固件相关的配置文件冲突对话框,选择保留当前版本(keep the local version)通常更安全,避免引导文件被替换导致起不来。

2.3 电源、跳线和散热这三个硬门槛

这是最容易翻车的一节,我按重要程度排。

电源。Nano 开发板上有两种供电方式:一个是 micro-USB 口,一个是圆头 DC 接口(丝印 J25)。micro-USB 口只能提供 5V/2A,也就是 10W 的上限,圆头 DC 口支持 5V/4A,也就是 20W 的输入能力。如果你打算使用 10W 性能模式,必须走 DC 口。用 micro-USB 供电还硬开 10W 模式,典型表现是跑着跑着突然掉电重启,你以为是软件崩溃,其实是电源撑不住。

还有个细节坑:板子上有个跳线帽 J48,用来选择供电来源。默认位置是接 DC 口那一侧,如果你用 micro-USB 供电,需要把跳线帽挪到另外两针上。这一步说明书上写得很小,很多人直接忽略。

散热。Nano 的官方开发板只有一个巴掌大的铝制散热片,默认没有风扇。短时间跑推理还好,一旦你开始编译 TensorRT engine 或者跑连续的视频推理,温度十分钟内就能冲到 80 度以上,然后触发降频,帧率断崖式下跌。建议直接配一个 5V 的 PWM 风扇插到 4 针风扇座(J15)上,成本很低,效果立竿见影。

风扇转速可以直接用命令调:

# 查看当前转速(0-255) cat /sys/devices/pwm-fan/target_pwm # 拉满 sudo sh -c 'echo 255 > /sys/devices/pwm-fan/target_pwm' # 安静一点,六成 sudo sh -c 'echo 150 > /sys/devices/pwm-fan/target_pwm'

存储。如果你后面想把 engine 文件、数据集、录制的视频都放在板子上,64GB 卡很快就不够用了。可以考虑插一个 USB 3.0 的固态硬盘,把工作目录挂过去,读写速度比 SD 卡快好几倍,尤其是读图的时候差距非常明显。

3. 系统调优:把 4GB 内存榨出应有的性能

环境搭好之后,别急着上深度学习框架。先在系统层面做几项调优,这些操作看起来不起眼,但对后续跑模型的稳定性影响极大。我的经验是,没有做过调优的 Nano,跑 YOLOv5 时的崩溃概率至少高一倍

3.1 换源与基础依赖

Ubuntu 18.04 在 ARM64 上的默认软件源是国外的,国内网络环境下 apt 下载速度可能只有几十 KB/s。换一个国内镜像源能把这个数字提升一到两个数量级。操作就是编辑/etc/apt/sources.list,把ports.ubuntu.com相关的地址替换成镜像站对应的 ARM64 路径。

换完之后装一批基础依赖,这一步不能跳:

sudo apt install -y python3-pip python3-dev python3-setuptools \ build-essential cmake git libopenblas-dev liblapack-dev \ libjpeg-dev zlib1g-dev libpython3-dev libavcodec-dev \ libavformat-dev libswscale-dev

这些包看着杂,其实分三类:编译工具链(后面的 Python 包有些没有现成 wheel,需要现场编译)、数学库(BLAS/LAPACK,PyTorch 的矩阵运算依赖它们)、多媒体库(OpenCV 处理视频流要用)。现在花三分钟装好,能省掉后面一堆"为什么 import 失败"的排查时间。

pip 也顺手换源,编辑~/.pip/pip.conf,写入镜像地址。之后所有 pip 安装都会快很多。

3.2 swap 配置与内存实测

4GB 内存是什么概念?Ubuntu 桌面环境本身要吃掉 800MB 到 1GB,Python 解释器加 PyTorch 的库加载大概 700MB,剩给模型和数据的不到 2GB。YOLOv5 在 640 分辨率下做推理,中间张量加上前处理缓存,峰值很容易顶到 2.5GB 以上。所以 swap 不是可选项,是必需项。

幸运的是 JetPack 4.6 默认已经启用了 zram(内存压缩当 swap 用),大概能提供 2GB 左右的虚拟空间。但 zram 的代价是压缩解压要消耗 CPU,而且压缩比有限。我建议再额外挂一个文件 swap:

# 创建 8GB 的 swap 文件 sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 验证 free -h swapon --show

要让它在重启后自动生效,把这一行写进/etc/fstab

/swapfile none swap sw 0 0

然后是 swappiness 参数。这个参数决定了内核有多"愿意"把内存页换出去。默认值是 60,对 Nano 这种共享内存的架构来说偏高,会导致频繁的换页抖动。我的建议是调到 20 左右:

echo 'vm.swappiness=20' | sudo tee -a /etc/sysctl.conf sudo sysctl -p

提示:swap 只是防止进程被 OOM killer 直接杀掉,不是性能优化手段。一旦你发现推理时 swap 使用量在持续增长,说明真实内存真的不够了,这时候要么降分辨率,要么换更小的模型,别硬撑。

3.3 nvpmodel、jetson_clocks 与温度墙

Nano 有两个功耗模式,通过nvpmodel切换:

# 查看当前模式和可选模式 sudo nvpmodel -q --verbose # 切到 10W 模式(四核全开,GPU 921MHz) sudo nvpmodel -m 0 # 切到 5W 模式(两核,GPU 640MHz) sudo nvpmodel -m 1

跑推理一定要切到 10W 模式,默认是 5W,GPU 频率被锁在 640MHz,性能差距接近 40%。切完之后要重启才生效。

jetson_clocks是另一个神器,它会把 CPU、GPU、内存控制器全部锁在最高频率,取消动态调频。默认的动态调频在负载波动时会来回切换频率,导致帧率不稳定。

sudo jetson_clocks # 查看当前频率状态 sudo jetson_clocks --show

代价是功耗和发热都会上升,所以风扇一定要配。如果你想开机自动执行,可以写一个 systemd service,或者直接加进~/.bashrc(不建议,因为每次开终端都会执行一次)。

温度方面,Nano 的 GPU 在 85 度左右会开始降频。用下面的命令可以实时看温度:

cat /sys/devices/virtual/thermal/thermal_zone*/temp

每个数字除以 1000 就是摄氏度。我实测下来,加了风扇和散热片之后,连续跑 YOLOv5 的温度稳定在 55 到 65 度之间,不加风扇直接冲到 80 度以上并降频,帧率从 15 掉到 8 左右。这个差距就是二十块钱风扇带来的。

3.4 jtop:把板子的状态可视化

命令行看温度、频率、内存太累,装一个jetson-stats会舒服很多:

sudo pip3 install -U jetson-stats sudo reboot

重启后直接运行jtop,会出来一个全屏的监控界面,分几个标签页:CPU/GPU 各核心的实时占用和频率、内存和 swap 的使用量、温度和功耗、以及当前 JetPack 各组件的版本号。最后这个功能特别有用,因为你在装 PyTorch 之前,必须知道自己的 CUDA 和 TensorRT 具体是哪个版本,装错了就白折腾。

q退出,别按 Ctrl+C,有时候会留下僵尸进程。

4. 深度学习环境:版本链条一环都不能错

这一章是整篇的核心,也是最容易劝退人的部分。ARM64 架构加上 NVIDIA 定制的系统,导致你在 x86 上习惯的"pip install 一把梭"完全不适用。你必须理解版本链条:JetPack 版本决定了 L4T 版本,L4T 版本决定了 CUDA 和 TensorRT 版本,CUDA 版本决定了你能用哪个 PyTorch wheel。任何一环错位,你都会得到一个语焉不详的报错。

4.1 JetPack 决定了所有版本号

以 JetPack 4.6.x 为例,它包含的组件大概是这样的:

组件版本
L4T32.7.x
Ubuntu18.04.6 LTS
CUDA10.2
cuDNN8.2.x
TensorRT8.2.x
Python3.6(系统默认)

这些版本不是你选的,是 NVIDIA 打包好的。所以你在网上找 PyTorch 安装教程的时候,第一件事是确认那篇教程针对的是不是 JetPack 4.6 + Python 3.6。如果对方是 JetPack 5.x,那 CUDA 版本是 11.4,PyTorch 也是 1.12 以上,安装包完全不通用。

系统里已经预装了 CUDA 和 cuDNN,不需要额外安装,nvcc --version能查到 CUDA 版本。TensorRT 也是预装的,dpkg -l | grep nvinfer可以查。

4.2 PyTorch 与 torchvision 的安装

这是第一个大坑。你绝对不能pip3 install torch,因为 PyPI 上的 torch wheel 是给 x86_64 编译的,装到 ARM64 上要么直接报错,要么装上了但 CPU 版本,根本用不到 GPU。

正确做法是用 NVIDIA 官方论坛提供的 ARM64 wheel。这些 wheel 有明确的命名规则,文件名里包含 Python 版本和 CUDA 版本,比如类似torch-1.8.0-cp36-cp36m-linux_aarch64.whl这样的形式。你需要根据自己系统的 Python 版本(3.6 对应 cp36)挑对应的文件下载。

安装过程:

# 先装依赖,numpy 版本要控制 sudo apt install -y libopenblas-base libopenmpi-dev # 安装 torch(文件名以你实际下载的为准) pip3 install numpy==1.19.4 pip3 install torch-1.8.0-cp36-cp36m-linux_aarch64.whl

torchvision 官方没有提供 wheel,需要从源码编译,而且必须选对版本。PyTorch 1.8 对应 torchvision 0.9.0,1.9 对应 0.10.0,1.10 对应 0.11.0。版本错配会导致 import 时报符号找不到。

编译 torchvision 之前先装好 Pillow:

sudo apt install -y libjpeg-dev zlib1g-dev libpython3-dev libavcodec-dev libavformat-dev libswscale-dev pip3 install pillow

然后从 GitHub 拉对应 tag 的源码编译:

git clone --branch v0.9.0 https://github.com/pytorch/vision torchvision cd torchvision export BUILD_VERSION=0.9.0 python3 setup.py install --user

这一步在 Nano 上大概要跑二十到四十分钟,取决于 SD 卡速度。跑到一半内存爆掉的话,先把 swap 加大,或者加MAX_JOBS=1限制并行编译数量。编译完之后python3 -c "import torch; print(torch.cuda.is_available())",返回 True 就说明 GPU 可用了。

注意:编译期间风扇一定要开,CPU 四核全负载,温度会飙得很快。

4.3 OpenCV 千万别用 pip 装

JetPack 里其实已经预装了 OpenCV,而且是带 GStreamer 支持的定制版本。这个支持非常关键,因为 Jetson 的 CSI 摄像头和硬件编解码器都是通过 GStreamer 管线访问的,标准版 OpenCV 读不了。

如果你手贱执行了pip3 install opencv-python,它会把 pip 版本装到用户目录,优先级高于系统版本,结果就是cv2.VideoCapture()打不开 CSI 摄像头,报一堆 GStreamer 相关的错误。而且 pip 版本的 OpenCV 在 ARM64 上经常缺少必要的依赖,import 都未必成功。

解决方案:永远不要 pip 装 opencv。如果需要版本信息,直接:

python3 -c "import cv2; print(cv2.__version__)"

应该输出类似 4.1.1 这样的版本号。如果输出了别的东西,说明被覆盖了,卸载掉 pip 版本:

pip3 uninstall opencv-python opencv-contrib-python

4.4 那个让人抓狂的 Illegal instruction

装完 PyTorch,你兴冲冲地 import,结果终端吐出一句:

Illegal instruction (core dumped)

这个报错在 Nano 上出现的频率极高,尤其是在 import numpy 或者 import torch 的时候。原因是 ARM 上的 OpenBLAS 会检测 CPU 支持的最高指令集,有时候检测逻辑出问题,用了硬件不支持的指令。

解决方案是强制指定指令集:

export OPENBLAS_CORETYPE=ARMV8

写进~/.bashrc让它永久生效:

echo 'export OPENBLAS_CORETYPE=ARMV8' >> ~/.bashrc source ~/.bashrc

这个坑我前后遇到过三次,第一次排查了整整一个下午,因为报错信息完全没提 OpenBLAS。记住这个方法,能省你几个小时。

5. YOLOv5 实战:从权重到 TensorRT 引擎

环境备齐,可以上模型了。这一章的思路是:先跑通纯 PyTorch 推理确认链路健康,再导出 TensorRT engine 拿到性能,最后接摄像头做端到端验证。

5.1 拉代码和最小验证

git clone https://github.com/ultralytics/yolov5 cd yolov5 pip3 install -r requirements.txt

这里有个细节:requirements.txt里有 torch 和 torchvision 的版本要求,但我们已经用官方 wheel 装好了。直接让它装会把你的 ARM 版 torch 覆盖成 x86 版。所以要么先注释掉那两行,要么装完之后立刻重新装一遍 ARM wheel。我一般选前者,干净利落。

下载一个 yolov5s 的权重,跑一张测试图:

python3 detect.py --weights yolov5s.pt --source data/images --img-size 640

第一次运行会做一些初始化,速度慢是正常的。如果能在runs/detect/exp/目录下看到画了框的图片,说明整个链路通了。这一步的帧率不重要,跑纯 PyTorch 在 Nano 上大概只有 2 到 3 FPS,慢到让人怀疑人生,但它是后面的基准。

5.2 导出 engine 的关键参数

TensorRT 加速的原理是把训练好的网络在特定硬件上做一层深度优化:融合算子、选择最快的卷积实现、把权重精度降低。优化后的引擎能带来数倍甚至十几倍的加速,代价是这个 engine 文件只对当前这台设备、当前这个 TensorRT 版本有效,换机器或者升级 JetPack 之后必须重新导出。

导出命令:

python3 export.py --weights yolov5s.pt \ --include engine \ --device 0 \ --half \ --img-size 640 \ --batch-size 1 \ --workspace 1 \ --opset 12

参数逐个解释一下,这几个都值得花时间理解:

--half表示用 FP16 精度导出。Nano 的 Maxwell GPU 对 FP16 有硬件支持,速度比 FP32 快接近一倍,精度损失在检测任务上基本可以忽略。这一个参数是性价比最高的优化

--batch-size 1是关键。默认导出可能是 batch 16 之类的,Nano 只有 4GB 内存,batch 一大直接爆。而且边缘场景本来就没有批处理的必要,一帧一帧处理就行。

--workspace 1限制构建时能用的显存工作空间是 1GB。默认值可能更大,在 Nano 上会直接分配失败。这个值是构建期的临时占用,不影响推理性能。

--opset 12是指定 ONNX 的算子集版本,TensorRT 8.2 对 opset 12 的支持最稳。

构建过程非常慢,在 Nano 上跑十几分钟到二十分钟很正常,风扇会全速转,温度会上去。别以为是卡死了,耐心等。构建完会在权重旁边生成一个.engine文件,大小几十 MB。

注意:导出过程中如果出现内存不足被杀掉,先把--img-size降到 512 试一次,或者把--workspace再降到 0.5,构建完成后再用 640 重来。

5.3 用 detect.py 跑通端到端

有趣的地方来了。YOLOv5 的代码里内置了一个自动机制:如果yolov5s.pt旁边存在同名的yolov5s.engine,detect.py 会自动优先加载 engine。所以你不需要改任何参数,还是原来的命令:

python3 detect.py --weights yolov5s.pt --source data/images --img-size 640 --half

这一次的耗时应该比之前短一个数量级。想验证到底用的是哪个,看日志里的输出,会明确写明加载的是什么格式。

接摄像头的话,分两种情况。USB 摄像头直接用序号:

python3 detect.py --weights yolov5s.pt --source 0 --img-size 640 --half

CSI 摄像头需要走 GStreamer 管线。在 Nano 上没有显示器的情况下(headless),可以把管线写成一个字符串传给--source,或者在代码里自定义 GStreamer 参数。常见的管线形式是nvarguscamerasrc取流,经过nvvidconv做色彩空间转换,最后给appsink。具体的管线字符串根据你的摄像头分辨率和格式会略有差异,用gst-launch-1.0先单独测一遍管线能不能出画面,再接进代码,能省很多排查时间。

5.4 实测数据与解读

下面是我在 Nano 4GB、10W 模式、jetson_clocks 锁频、加装风扇的条件下测到的数据,仅供参考,你的数字会因为环境温度、SD 卡速度、是否落盘录像而有波动。

配置纯推理帧率端到端帧率(含取流和绘制)
PyTorch FP32,640约 2-3 FPS约 2 FPS
TensorRT FP16,640约 20-25 FPS约 10-15 FPS
TensorRT FP16,416约 35-40 FPS约 18-22 FPS
TensorRT INT8,640约 30-35 FPS约 15-20 FPS

几个观察值得说一下。第一,PyTorch 到 TensorRT 的提升是最夸张的,接近十倍,这也是为什么在 Jetson 上做部署,TensorRT 几乎是必修课。第二,纯推理帧率和端到端帧率差距很大,因为视频解码、色彩转换、画框、编码回写这些都在消耗资源,尤其视频编解码会占用独立的硬件模块,跟 GPU 抢带宽。第三,降低分辨率带来的收益比提升精度明显得多,因为计算量大致跟像素数成正比。

Orin Nano 上的数字完全是另一个量级,同样是 yolov5s FP16 640,纯推理可以跑到上百 FPS,端到端也能稳定在六七十帧,这就是算力和内存带宽的代差。

6. 性能再压一层:量化、分辨率与视频管线

跑通只是开始,实际项目里你总会觉得还不够快。这一章讲三个可以直接落地的优化方向。

6.1 FP32、FP16、INT8 的取舍

精度选择本质上是拿准确率换速度。FP32 是训练时的标准精度,推理时基本没有理由用它,除了极少数对数值敏感的场景。FP16 是 Jetson 上的默认甜点,大多数视觉模型在这个精度下 mAP 掉不到一个百分点,速度接近翻倍。

INT8 就微妙一些。它的加速效果在 Turing 及以后的架构上非常明显,但 Maxwell 架构(也就是 Nano)对 INT8 的支持没有 Tensor Core 加持,实测提升幅度远不如在 Orin 上那么惊艳,大概是 FP16 基础上再快 20% 到 40%。而且 INT8 需要校准——你得准备一批有代表性的图片,跑一遍校准流程,生成一个校准表,让 TensorRT 统计每一层的激活值分布来定量化参数。

OSTU 式的建议是:Nano 上直接用 FP16,别折腾 INT8。省下的那点时间不值得你花半天做校准,还要担心精度下滑。Orin Nano 上则相反,INT8 的收益非常大,值得认真做校准。

# INT8 校准的时候需要一个 calib 目录放代表性图片 python3 export.py --weights yolov5s.pt --include engine --device 0 \ --int8 --data data/coco128.yaml --batch-size 1 --img-size 640

6.2 输入分辨率对帧率的影响

这一条我单独拎出来讲,因为它是最容易被忽略、收益又最直观的一环。卷积网络的计算量在输入尺寸上是平方关系,640 降到 416,像素数减少接近 58%,帧率自然大幅提升。

代价是小目标检测能力下降。416 分辨率下,原图上小于 30 像素的物体基本就丢了。所以怎么选,取决于你的场景:

  • 画面里主要是近处的人、车,物体在画面中占比大:416 甚至 320 都够用
  • 需要检测远处的行人或小物体:咬牙用 640,接受低帧率
  • 折中方案:用 640 训练和导出,但推理时把输入 resize 到 512,精度掉得不多,速度快一截

还有一个不太为人知的技巧:如果你的摄像头本身就是 720p,把--img-size设成 640 之后,前处理还需要做一次缩放,这次缩放消耗在 CPU 上。可以考虑把摄像头输出直接配成 640x640 或者接近的尺寸,减少一次转换。

6.3 CSI 摄像头与 USB 摄像头的管线差异

Jetson 板子上有两个 MIPI CSI-2 接口,可以接官方的摄像头模组。CSI 摄像头走的是 ISP 硬件通路,延迟低、不占 USB 带宽,但必须用 GStreamer 管线访问。USB 摄像头简单通用,但数据要经过 USB 总线和 CPU 内存,延迟稍高。

在需要多路摄像头的场景里,CSI 接口只有两个,更多的就得靠 USB 或者网络摄像头。这时候 USB 总线的带宽会成为瓶颈,四路 1080p30 的 USB 摄像头同时在 Nano 上跑,光是把数据搬进内存就够 CPU 喝一壶的,更别说推理了。

如果要在多路之间腾挪,我的一般做法是:主路用 CSI 拿到最低延迟,其余路用降低分辨率(比如 640x480)的 USB 摄像头,并且把推理频率降下来,比如每三帧处理一次,中间帧复用上一帧的结果。这个技巧在监控类场景里效果很好,人眼对检测框的刷新率其实没那么敏感。

7. 常见问题速查表与踩坑心得

这一章把散落在前面的坑集中整理,方便对照排查。我按"开机前"、"跑起来之后"、"文档里不会写"三个维度分。

7.1 开不了机的那几类问题

现象可能原因排查动作
完全不通电,指示灯不亮供电不足或跳线帽位置不对换 DC 口 5V4A 电源,检查 J48 跳线
绿灯亮但无画面输出镜像烧录失败或 SD 卡不兼容重新烧录,换一张已知好的卡
卡在开机 logo 不动文件系统损坏或升级中断重新烧录,尽量别在中途断电
进系统后频繁重启电源功率不够,或温度过高换 DC 供电,检查风扇是否转
显示器分辨率异常EDID 识别问题换一根 HDMI 线,或换个显示器试试

这里我想强调一下"频繁重启"这个现象,因为它最容易被误判成软件问题。很多人的第一反应是重装系统,折腾一圈发现没用。实际上 90% 的情况是电源功率不够——用 micro-USB 供电跑 10W 模式,负载一上来电压就被拉低,板子直接复位。

7.2 跑起来之后的各种报错

Illegal instruction:前面讲过,export OPENBLAS_CORETYPE=ARMV8

ImportError: libcudart.so.10.2: cannot open shared object file:CUDA 环境变量没配。检查~/.bashrc里有没有把/usr/local/cuda-10.2/lib64加进LD_LIBRARY_PATH。JetPack 一般已经配好了,如果你手动改过环境变量可能会覆盖掉。

RuntimeError: CUDA out of memory:内存不够。先确认同时没有别的进程占着 GPU,然后降 batch size、降分辨率,或者把 swap 加大。

ModuleNotFoundError: No module named 'cv2':OpenCV 路径问题,通常是 pip 装了 opencv-python 覆盖了系统版本,卸载掉。

AssertionError出现在 TensorRT 导出时:多半是 opset 版本或者 onnx 版本不兼容,试着固定 onnx 版本,或者换个 opset。

engine 文件存在但没用上:检查文件名是否与.pt完全同名且在同一目录,detect.py 的自动加载逻辑对文件名很敏感。

7.3 文档里一般不写的几条经验

第一条,先跑通再优化。我见过太多人在环境还没搭稳的时候就开始研究 INT8 量化和多线程流水线,最后卡在第一个环节就放弃了。正确的顺序是:能跑起来 → 跑得对 → 跑得快。跳过第一步,后面两步都是空中楼阁。

第二条,给每个成功的状态留一份备份。在 Nano 上折腾,把系统搞崩的概率不低。我一般在"系统装好"、"PyTorch 装好"、"engine 跑通"三个节点各备份一次 SD 卡镜像。听起来很土,但每次出问题能省下两三个小时重装。做法很简单,把卡插到电脑上用 dd 或者烧录工具读出来存成 img 文件就行。

第三条,日志一定要落到文件里。跑长时间推理的时候,终端输出一多就滚没了,出了问题什么都看不到。养成习惯用2>&1 | tee run.log把输出同时写进文件,回溯问题的时候会感谢自己。

第四条,不要在 Nano 上做训练。哪怕只是微调一个小模型,Nano 的速度也会让你等到怀疑人生。正确的分工是在有独立显卡的机器上训练和导出 ONNX,Nano 只负责加载 engine 做推理。ONNX 是跨平台的中间格式,这个分工方式能让你的工作流清晰很多。

第五条,记录你的版本组合。把 JetPack 版本、CUDA 版本、TensorRT 版本、PyTorch 版本、torchvision 版本、numpy 版本这六个数字写在便签上贴显示器边缘。这六个数字决定了你的环境能不能工作,也决定了你从网上抄来的命令能不能用。什么时候板子换新了,照着这张便签重新配一遍,比重新摸索快得多。

第六条,Orin Nano 的迁移其实比你想的简单。你的 Python 代码基本不用改,需要改的是环境搭建部分:JetPack 版本、PyTorch wheel 的来源、apt 源地址。所以如果你是一个团队在做,最好一开始就把代码里的设备相关配置抽出来做成配置文件,别硬编码在代码里。

说实话,我在 Nano 上折腾的时间加起来大概有几十个小时,前面一半都在各种环境问题上打转,真正调优的部分可能只占三成。后来我把整套流程整理成一个脚本,每换一台板子就跑一遍,从烧录到跑通 YOLOv5 完整地控制在一个下午之内。这个过程里最有价值的不是某个具体的命令,而是建立起"先确认版本链、再动手装、每一步留备份"的习惯。这套习惯放到 Orin 上、放到任何一块新的边缘设备上,基本都能复用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询