1. 环境准备与系统调优

想在树莓派5上跑通YOLOv8,并且达到一个能用的帧率,第一步不是急着去装软件,而是先把你的树莓派“身体”调理好。我刚开始玩的时候,直接照着教程一顿猛装,结果跑起来卡得不行,一度怀疑是不是买到了假货。后来才发现,很多性能瓶颈其实在第一步就埋下了。树莓派5虽然性能比前代强了不少,但毕竟还是个资源受限的嵌入式设备,不做好基础优化,再好的模型也跑不顺。

1.1 系统选择与基础配置

我强烈推荐使用树莓派官方提供的 64位 Raspberry Pi OS (Bookworm) 作为起点。别用32位系统,也别用其他第三方发行版,官方的64位系统对树莓派5的硬件支持最完善,能最大程度发挥其四核Cortex-A76的性能。拿到新系统后,第一件事就是更新软件源和升级所有包。别小看这几条命令,它能确保你安装的依赖都是最新的,避免很多奇怪的兼容性问题。

sudo apt update
sudo apt full-upgrade -y
sudo reboot

更新完记得重启一下。接下来,安装一些最基础的开发工具,这些是后续编译的基石。

sudo apt install -y build-essential cmake git wget pkg-config

1.2 内存与交换空间优化

这是新手最容易忽略,但对性能影响巨大的一个环节。树莓派5有4GB和8GB内存版本。如果你用的是4GB版本,那么编译OpenCV这种大型库时,很可能会因为内存不足而失败,或者系统开始疯狂使用交换空间(Swap),导致编译速度慢如蜗牛。我实测过,编译OpenCV 4.9.0时,峰值内存使用会超过5GB。

所以,我们必须手动调整交换空间的大小。默认的交换空间可能只有100MB,这远远不够。我们可以把它扩大到2GB甚至更多。具体操作是修改 /etc/dphys-swapfile 这个配置文件。

sudo nano /etc/dphys-swapfile

找到 CONF_SWAPSIZE 这一行,把后面的值改成 2048(单位是MB,代表2GB)。如果你硬盘空间充足,改成4096(4GB)也可以。改完之后保存退出,然后重启交换文件服务。

sudo dphys-swapfile setup
sudo dphys-swapfile swapon

你可以用 free -h 命令检查一下,看看Swap那一行是不是变成了2G左右。这个操作相当于给你的树莓派增加了虚拟内存,虽然速度比物理内存慢,但能保证编译过程不会因为内存不足而崩溃。编译完成后,如果你觉得交换空间占用硬盘,可以再把它改回来。但为了后续运行模型的稳定性,我个人建议保留一个1GB左右的交换空间。

2. 编译与部署NCNN推理框架

环境准备好了,接下来就是重头戏:部署NCNN。NCNN是腾讯开源的一个为移动端和嵌入式平台优化的神经网络推理框架,它的优势就是极致轻量和高效。在树莓派上,我们必须自己从源码编译,才能针对ARM架构进行最好的优化。

2.1 安装NCNN的编译依赖

NCNN的编译需要一些额外的库支持。我们需要提前安装好。

sudo apt install -y libprotobuf-dev protobuf-compiler libvulkan-dev

这里注意,libvulkan-dev 是可选的,它是Vulkan GPU加速的支持库。树莓派5的VideoCore VII GPU理论上支持Vulkan,但截至我写这篇文章时,驱动和生态支持还不是很完善。为了稳定和通用性,我们这次先使用CPU推理,所以这个包装了备用,但编译NCNN时我们先不开启Vulkan支持。

2.2 源码编译NCNN

现在,我们从GitHub上拉取NCNN的源码。这里有个小技巧,使用 --depth=1 参数可以只克隆最新的一次提交,节省下载时间和磁盘空间。

git clone --depth=1 https://github.com/Tencent/ncnn.git
cd ncnn

接下来是关键的编译配置环节。我们创建一个 build 目录,在里面进行编译,这是个好习惯,保持源码目录的干净。

mkdir build
cd build

执行cmake命令进行配置。这里有几个关键参数:

  • -D NCNN_DISABLE_RTTI=OFF:保持运行时类型信息,某些情况下需要。
  • -D NCNN_BUILD_TOOLS=ON:编译模型转换等工具,后面可能会用到。
  • -D CMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake这是最重要的一步!指定交叉编译工具链文件,告诉编译器我们是为树莓派5的ARM64架构编译。如果不指定,编译器可能会按本地x86架构编译,那就全错了。
  • 我们暂时不开启Vulkan (-D NCNN_VULKAN=OFF),也不开启OpenMP多线程 (-D NCNN_OPENMP=OFF),因为树莓派5的CPU核心不多,OpenMP有时反而会增加开销。我们先追求最稳定的基础版本。
cmake -D NCNN_DISABLE_RTTI=OFF \
      -D NCNN_BUILD_TOOLS=ON \
      -D NCNN_VULKAN=OFF \
      -D NCNN_OPENMP=OFF \
      -D CMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake ..

配置成功后,就可以开始编译了。make -j4 中的 -j4 表示使用4个线程并行编译(树莓派5是4核CPU),这能大幅加快编译速度。这个过程大概需要10-20分钟。

make -j4
sudo make install

编译安装完成后,NCNN的库文件和头文件会被安装到系统的 /usr/local/ 目录下。你可以通过 ls /usr/local/lib/libncnn*ls /usr/local/include/ncnn 来确认。

3. 编译优化版OpenCV

OpenCV是计算机视觉的“标准库”,我们用它来读取摄像头、处理图像、显示结果。系统自带的APT仓库里的OpenCV版本可能比较老,或者没有包含我们需要的优化选项。因此,从源码编译一个“量身定做”的OpenCV是提升性能的另一个关键。

3.1 安装OpenCV的庞杂依赖

OpenCV的依赖非常多,缺少任何一个都可能导致编译失败或某些功能不可用。别怕麻烦,一条命令搞定它们。

sudo apt install -y libjpeg-dev libtiff5-dev libjasper-dev libpng-dev \
libavcodec-dev libavformat-dev libswscale-dev libv4l-dev \
libxvidcore-dev libx264-dev libgtk-3-dev libatlas-base-dev \
gfortran libhdf5-dev libhdf5-serial-dev python3-dev python3-pip

3.2 使用自动化编译脚本

手动配置OpenCV的cmake选项非常复杂。幸运的是,社区有爱好者为我们准备了针对树莓派的自动化编译脚本。这能省去大量研究和试错的时间。我们可以从网上找到一个可靠的脚本,比如针对OpenCV 4.9.0的。

我通常会把脚本下载到用户主目录,并赋予执行权限。

cd ~
wget -O install_opencv.sh [一个可靠的脚本URL]
# 请注意:在实际操作中,请从Github Gist或可信源获取最新的编译脚本
chmod +x install_opencv.sh

在运行脚本前,强烈建议你打开脚本看一眼。主要关注两点:一是它定义的cmake参数,二是安装路径。一个好的脚本应该会关闭一些在树莓派上用不到的功能(如CUDA、非英文字体、一些额外的模块)来加快编译速度,同时开启NEON优化(ARM的SIMD指令集)来提升性能。

查看脚本内容可以用 nano install_opencv.sh。确认无误后,就可以运行了。这个过程非常漫长,在我的树莓派5 8GB版本上,用了将近两个小时。期间CPU会持续满载,风扇呼呼转,这是正常的。你可以去喝杯咖啡,或者干点别的。

./install_opencv.sh

脚本运行结束后,它应该会自动完成编译和安装。你可以通过以下命令验证是否安装成功,以及版本是否正确:

pkg-config --modversion opencv4

如果显示 4.9.0 或类似的版本号,恭喜你,最耗时的部分已经过去了。

4. 获取与运行YOLOv8-NCNN项目

框架和库都齐了,现在把“演员”——YOLOv8模型项目请上场。我们直接使用一个已经适配好的开源项目,这比自己从头写C++推理代码要快得多。

4.1 下载项目与模型

我们可以从GitHub上克隆一个成熟的项目,比如之前提到的Qengineering/YoloV8-ncnn-Raspberry-Pi-4。这个项目已经把模型转换、C++推理代码都准备好了。

cd ~
git clone https://github.com/Qengineering/YoloV8-ncnn-Raspberry-Pi-4.git
cd YoloV8-ncnn-Raspberry-Pi-4

进入项目目录,你会发现里面已经包含了YOLOv8-nano模型的NCNN格式文件(.param.bin 文件),以及完整的C++源代码。这就是开源的魅力,我们站在了巨人的肩膀上。

4.2 编译与测试静态图片

项目通常提供了简单的编译方法,比如一个Makefile或者CMakeLists.txt。我们以CMake为例(如果没有,你可能需要自己写一个简单的)。

mkdir build
cd build
cmake ..
make -j4

编译完成后,你会得到一个可执行文件,比如叫 yolov8。先用它测试一下图片检测,确保基础功能是正常的。

./yolov8 test.jpg

如果终端输出了检测到的物体类别和坐标,并且生成了一个带标注框的图片(比如叫 test_result.jpg),那么最基础的流程就走通了。这一步的成功,验证了NCNN库、OpenCV、模型文件三者之间没有兼容性问题。

5. 核心调优:平衡帧率与精度的实战

好了,基础流程跑通了,但帧率只有个位数,完全没法用。接下来才是真正的“调优”实战。我们的目标是在可接受的精度损失下,尽可能提升帧率。这里没有银弹,只有一系列的权衡和测试。

5.1 模型输入尺寸(target_size)的魔力

在YOLOv8的代码里,target_size 这个参数至关重要。它决定了在送入神经网络之前,输入图像被缩放到多大。这个值越小,计算量呈平方级减少,帧率提升越明显,但检测小物体的能力会下降。

原始项目里可能默认是640。我们可以尝试把它调小。YOLOv8要求输入尺寸必须是32的倍数,所以常见的可选值有:608, 576, 512, 480, 448, 416, 384, 352, 320等。

你需要修改源代码里的 target_size 变量,通常是 yolov8main.cpp 或类似的主文件里。找到这行:

int target_size = 640; // 把它改成你想测试的值,比如320

我的实测数据如下(摄像头分辨率设为640x480时):

target_size平均帧率 (FPS)检测精度主观感受
6408-10最好,远处的小物体也能看清
48014-16良好,稍远的物体可能漏检
32022-25一般,只适合检测近处较大物体
25628-32较差,漏检较多,仅适用于特定场景

建议:对于通用场景,从480开始尝试。如果对实时性要求极高,且场景中物体都比较大,可以降到320。切忌无脑追求高帧率而牺牲过多精度,一个检测不到物体的模型,帧率再高也没用。

5.2 摄像头分辨率与ROI裁剪

另一个巨大的性能瓶颈是摄像头采集分辨率。很多人喜欢一上来就设置成1920x1080,这对树莓派来说是巨大的负担。因为每一帧图像,OpenCV都需要从摄像头驱动读取、在内存中处理(色彩空间转换、缩放等),这个开销本身就不小。

原则:摄像头采集分辨率不要高于你需要的分辨率。 如果你最终 target_size 只设为320,那么摄像头分辨率设为640x480就绰绰有余了。设置更高的分辨率,只会白白增加CPU在图像预处理上的负担,对最终检测精度毫无帮助。

在代码中,通常在打开摄像头后这样设置:

cv::VideoCapture cap(0);
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);  // 根据你的target_size调整
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);

更进一步的高级技巧是 ROI(Region of Interest)裁剪。如果你的摄像头视野很大,但物体只出现在画面中央区域,你完全可以在检测前,只裁剪出那一部分区域进行处理。这相当于直接降低了处理的分辨率。例如,从1280x720的画面中,裁剪出中心的640x360区域进行检测,计算量直接减少为原来的四分之一。

5.3 代码层面的微优化

除了调整参数,代码写法也有讲究。一些细微的改动能积少成多。

  1. 避免不必要的拷贝:确保你的图像数据(cv::Mat)在函数间传递时,使用的是引用(&)而不是值拷贝。一帧640x480的RGB图像,值拷贝一次就需要近1MB的内存搬运。
  2. 预热与循环外加载:模型加载 (yolov8.load()) 一定要放在循环外面,只执行一次。在正式检测前,可以先跑一两次“预热”推理,让NCNN内部的内存分配和调度稳定下来。
  3. 简化后处理与绘图:检测结果的绘制 (yolov8.draw()) 也是开销。如果不需要实时显示所有框和标签,可以每N帧绘制一次,或者只绘制置信度最高的几个框。计算FPS的代码也要尽量轻量。

6. 系统级性能排查与进阶思路

当你按照上面的步骤调整后,如果帧率还是达不到预期,就需要进行系统级的排查了。

6.1 性能监控工具

别瞎猜,用数据说话。在运行你的检测程序时,新开一个终端窗口,使用以下工具监控系统状态:

  • CPU使用率htoptop。看看是四个核心都跑满了,还是只有一两个在忙?这能判断程序是否充分利用了多核。
  • 内存与交换空间free -h。观察Swap是否被频繁使用(si/so 数值高)。如果Swap使用频繁,说明物理内存不足,这是性能杀手,你需要回头检查交换空间设置,或者优化程序内存占用。
  • CPU频率与温度vcgencmd measure_tempvcgencmd measure_clock arm。树莓派在温度过高时会自动降频保护,导致性能下降。确保散热良好,必要时加装散热风扇或散热片。

6.2 进阶优化思路

如果上述常规手段都试遍了,还可以尝试一些更深入的优化:

  1. 量化模型:YOLOv8的原始浮点模型精度高但计算量大。可以尝试将模型转换为 INT8量化 模型。NCNN支持INT8推理,在精度损失很小的情况下,能获得显著的推理速度提升。但这需要额外的模型转换步骤和校准数据集。
  2. 尝试NCNN的OpenMP多线程:重新编译NCNN,开启 -D NCNN_OPENMP=ON 选项。然后在你的代码中,在 load 模型之前,调用 ncnn::set_cpu_powersave(0)ncnn::set_omp_num_threads(4) 来尝试启用多线程推理。注意,这不一定总是有效,有时会因为线程同步开销导致性能不变甚至下降,需要实测。
  3. 探索Vulkan后端(未来可期):如前所述,树莓派5的GPU潜力巨大。持续关注Raspberry Pi OS的更新和Vulkan驱动的发展。未来一旦Vulkan支持成熟,重新编译开启Vulkan的NCNN,将计算任务卸载到GPU,帧率可能会有质的飞跃。这可能是从15帧跳到30帧甚至更高的关键。

调优是一个螺旋上升的过程:修改参数 -> 测试帧率和精度 -> 分析瓶颈 -> 再修改。没有一套参数能通吃所有场景。我的经验是,在树莓派5上,使用YOLOv8-nano模型,在 target_size=480,摄像头分辨率 640x480 的条件下,实现 15-20 FPS 的稳定实时检测,是一个比较现实且实用的目标。这个帧率已经足以支持很多智能监控、机器人视觉等项目的需求了。记住,在边缘计算设备上,懂得权衡和取舍,比单纯追求某一项指标更重要。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐