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等待,提高整体吞吐量。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐