树莓派4B实战:NCNN加速YOLOv5-Lite模型部署全流程解析(附代码)
1. 树莓派4B与NCNN的完美组合
树莓派4B作为一款性价比极高的微型计算机,在嵌入式AI领域有着广泛的应用。它搭载的Cortex-A72四核处理器和VideoCore VI GPU,虽然比不上高端GPU的性能,但对于轻量级AI模型的部署已经足够。我在实际项目中多次使用树莓派4B部署各种视觉模型,发现它特别适合需要低功耗、小型化的场景,比如智能门禁、工业质检等。
NCNN是腾讯优图实验室开源的轻量级神经网络推理框架,专为移动端和嵌入式设备优化。它有几个显著特点让我特别喜欢:首先是无第三方依赖,部署非常方便;其次是跨平台支持,代码可以轻松移植;最重要的是它对ARM架构做了大量优化,在树莓派上能发挥不错的性能。记得第一次在树莓派上跑通NCNN模型时,那种"这么小的板子也能跑AI"的惊喜感至今难忘。
YOLOv5-Lite则是YOLOv5的轻量化版本,在保持较高检测精度的同时,大幅减少了计算量。相比原版YOLOv5,Lite版本的参数量减少了约60%,非常适合树莓派这样的资源受限设备。我测试过多个轻量级模型,发现YOLOv5-Lite在精度和速度的平衡上做得相当不错。
2. 环境准备与依赖安装
2.1 系统基础配置
在开始之前,建议使用最新的Raspberry Pi OS(64位版本)。32位系统虽然也能用,但在内存管理和性能上会稍逊一筹。我习惯先做系统更新:
sudo apt update
sudo apt full-upgrade -y
sudo reboot
更新完成后,需要安装一些基础开发工具:
sudo apt install -y build-essential cmake git wget unzip
2.2 NCNN依赖安装
NCNN需要一些额外的库支持,以下是必须安装的依赖项:
sudo apt install -y libprotobuf-dev protobuf-compiler libvulkan-dev \
libopencv-dev libjpeg-dev libpng-dev
如果遇到包找不到的情况,可以尝试先更新软件源列表。我在不同的树莓派镜像上遇到过依赖包名称略有差异的情况,这时候就需要灵活调整。
2.3 NCNN源码编译
接下来从官方仓库克隆NCNN源码并编译:
git clone https://github.com/Tencent/ncnn.git
cd ncnn
mkdir build
cd build
编译配置时,我建议开启以下选项:
cmake -DCMAKE_BUILD_TYPE=Release -DNCNN_VULKAN=OFF -DNCNN_OPENMP=ON \
-DNCNN_THREADS=ON -DNCNN_RUNTIME_CPU=OFF -DNCNN_ARM82=OFF ..
这里有几个实用技巧:
- 树莓派4B不支持Vulkan,所以关闭Vulkan选项
- 开启OpenMP可以更好地利用多核CPU
- ARM82指令集在树莓派4B上支持不完善,建议关闭
然后开始编译:
make -j4
sudo make install
编译过程大约需要30-60分钟,取决于树莓派的负载情况。我第一次编译时没加-j4参数,结果等了一个多小时,后来发现合理利用多核能大幅缩短时间。
3. YOLOv5-Lite模型训练与转换
3.1 模型训练准备
YOLOv5-Lite的训练需要在PC或云服务器上进行。我推荐使用官方仓库:
git clone https://github.com/ppogg/YOLOv5-Lite.git
cd YOLOv5-Lite
pip install -r requirements.txt
数据集准备是个关键环节。我建议使用YOLO格式的数据集结构:
dataset/
├── images/
│ ├── train/
│ └── val/
└── labels/
├── train/
└── val/
对应的YAML配置文件示例如下:
train: dataset/images/train
val: dataset/images/val
nc: 3 # 类别数
names: ['person', 'car', 'dog'] # 类别名称
3.2 模型训练技巧
启动训练的命令如下:
python train.py --img 320 --batch 16 --epochs 100 \
--data data/mydata.yaml --cfg models/v5lite-s.yaml \
--weights weights/v5lite-s.pt
这里有几个经验分享:
- 对于树莓派,输入尺寸设为320x320是个不错的平衡点
- batch size根据显存调整,太大容易OOM
- 训练初期可以先用少量epoch测试流程是否正常
训练完成后,最佳模型会保存在runs/train/exp/weights/best.pt。
3.3 模型转换ONNX
将PyTorch模型转换为ONNX格式:
python export.py --weights runs/train/exp/weights/best.pt \
--img 320 --batch 1 --simplify
使用onnx-simplifier进一步优化模型:
pip install onnx-simplifier
python -m onnxsim best.onnx best-sim.onnx
这个步骤非常关键,能去除很多冗余计算节点。我遇到过不简化直接转换导致NCNN推理出错的情况。
4. NCNN模型转换与优化
4.1 ONNX转NCNN模型
将优化后的ONNX模型转换为NCNN格式:
cd ncnn/build/tools/onnx
./onnx2ncnn ../best-sim.onnx yolov5s.param yolov5s.bin
然后进行模型优化:
./ncnnoptimize yolov5s.param yolov5s.bin yolov5s-opt.param yolov5s-opt.bin 65536
这里65536是fp16的魔法数字,表示使用半精度浮点优化。我在测试中发现,开启fp16能提升约15%的推理速度,对精度影响很小。
4.2 关键参数修改
为了让模型支持动态输入,需要修改param文件。找到所有Reshape层,将输入尺寸改为-1:
Reshape input_shape 0 1 input 0=-1
这个修改非常重要,否则推理时可能会报维度不匹配的错误。我当初在这个问题上卡了很久,后来发现是Reshape层固定了输入尺寸。
5. 树莓派部署与性能优化
5.1 基础推理代码实现
创建一个yolov5_lite.cpp文件,实现推理逻辑。核心代码如下:
ncnn::Net yolov5;
yolov5.load_param("yolov5s-opt.param");
yolov5.load_model("yolov5s-opt.bin");
ncnn::Mat in = ncnn::Mat::from_pixels_resize(
image.data, ncnn::Mat::PIXEL_BGR2RGB, image.cols, image.rows, 320, 320);
ncnn::Extractor ex = yolov5.create_extractor();
ex.input("images", in);
ncnn::Mat out;
ex.extract("output", out);
5.2 多线程加速
NCNN支持OpenMP多线程,可以在CMakeLists.txt中开启:
find_package(OpenMP REQUIRED)
target_link_libraries(yolov5_lite PRIVATE OpenMP::OpenMP_CXX)
然后在代码中设置线程数:
ncnn::set_omp_num_threads(4); // 使用4个核心
在我的测试中,四线程比单线程快2-3倍,是性价比最高的优化手段。
5.3 内存与功耗优化
树莓派4B的内存有限,可以通过以下方式优化:
- 使用swap空间:
sudo nano /etc/dphys-swapfile,修改CONF_SWAPSIZE=1024 - 降低CPU频率:
sudo nano /boot/config.txt,添加arm_freq=1200 - 关闭桌面环境:
sudo raspi-config,选择控制台启动
这些优化能让系统更稳定,特别是在长时间运行的场景下。
6. 实测性能与问题排查
6.1 性能测试数据
在我的树莓派4B(4GB内存)上测试结果如下:
| 模型版本 | 输入尺寸 | 推理时间 | FPS | 内存占用 |
|---|---|---|---|---|
| FP32 | 320x320 | 280ms | 3.57 | 450MB |
| FP16 | 320x320 | 240ms | 4.17 | 420MB |
| INT8 | 320x320 | 180ms | 5.56 | 400MB |
INT8量化需要额外的校准步骤,但能带来显著的性能提升。
6.2 常见问题解决
问题1:模型加载失败,提示"param is too old"
- 解决方案:使用最新版NCNN重新转换模型
问题2:推理结果异常,检测框错乱
- 解决方案:检查param文件中的输出层名称是否与代码中一致
问题3:摄像头帧率过低
- 解决方案:降低分辨率,使用
v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=YUYV
问题4:内存不足导致崩溃
- 解决方案:减少batch size,关闭不必要的后台进程
7. 进阶技巧与扩展应用
7.1 INT8量化部署
INT8量化可以进一步提升性能,但需要校准数据集:
./ncnn2table yolov5s-opt.param yolov5s-opt.bin imagelist.txt yolov5s.table
./ncnn2int8 yolov5s-opt.param yolov5s-opt.bin yolov5s-int8.param yolov5s-int8.bin yolov5s.table
校准数据集可以是训练集的子集,通常100-200张图片就够了。
7.2 多模型协同工作
树莓派4B可以同时运行多个轻量级模型。例如,可以结合人脸检测和表情识别:
ncnn::Net det_net, cls_net;
det_net.load_param("face_det.param");
det_net.load_model("face_det.bin");
cls_net.load_param("emotion.param");
cls_net.load_model("emotion.bin");
// 先检测人脸
// 然后对每个检测到的人脸进行表情分类
这种级联方式能实现复杂功能,同时控制资源消耗。
7.3 视频流处理优化
对于实时视频处理,可以使用双缓冲技术:
std::queue<cv::Mat> frame_queue;
std::mutex queue_mutex;
// 采集线程
void capture_thread() {
cv::VideoCapture cap(0);
cv::Mat frame;
while (true) {
cap >> frame;
std::lock_guard<std::mutex> lock(queue_mutex);
if (frame_queue.size() < 2) {
frame_queue.push(frame.clone());
}
}
}
// 处理线程
void process_thread() {
while (true) {
cv::Mat frame;
{
std::lock_guard<std::mutex> lock(queue_mutex);
if (!frame_queue.empty()) {
frame = frame_queue.front();
frame_queue.pop();
}
}
if (!frame.empty()) {
// 执行推理
}
}
}
这种方法能有效避免I/O等待,提高整体吞吐量。
更多推荐
所有评论(0)