1. 项目缘起与核心价值
最近在折腾一个边缘计算项目,核心需求是在一个功耗和算力都受限的嵌入式设备上,稳定地跑一个自己训练的目标检测模型。树莓派4B,这个老朋友,自然成了首选平台。但直接上PyTorch跑YOLOv5,即便是轻量级的nano或s模型,在640x640的输入分辨率下,帧率也常常是个位数,实时性根本无从谈起。这时候,英特尔的神经计算棒2代(NCS2)就进入了视野,它主打的就是在边缘端为OpenVINO框架下的模型提供硬件加速。于是,一个完整的链路就清晰了:在自己的主力机上用PyTorch训练好YOLOv5模型,然后将其转换为OpenVINO的中间表示格式,最后部署到树莓派4B上,并利用NCS2进行推理加速。这听起来像是一个标准的流水线,但真正走一遍,你会发现从环境配置、模型转换到最后的性能调优,每一步都有不少细节和“坑”需要留意。这篇文章,我就把自己从零开始完成这个全流程的实践记录和心得分享出来,希望能帮你绕过我踩过的那些坑,更顺畅地完成部署。
2. 环境准备:树莓派系统与基础依赖
在开始任何软件操作之前,硬件和系统层面的准备是基石。这一步的稳定性直接决定了后续所有步骤能否顺利进行。
2.1 树莓派系统选择与初始化
对于树莓派4B,官方推荐的64位系统Raspberry Pi OS(原Raspbian)是兼容性和社区支持最好的选择。我强烈建议使用Raspberry Pi OS Lite (64-bit)这个无桌面环境的版本。原因很简单:我们的核心任务是模型推理服务,不需要图形界面消耗宝贵的CPU和内存资源。更精简的系统意味着更少的后台进程、更快的启动速度和更稳定的运行环境。
下载系统镜像后,使用 Raspberry Pi Imager 工具烧录到 microSD 卡。这里有一个关键步骤:在烧录前,通过 Imager 的“高级选项”(齿轮图标),预先启用 SSH 并设置好主机名、用户名和密码。这样,烧录完成后你就能直接通过网络 SSH 连接到树莓派,无需连接显示器和键盘,这对于将树莓派作为无头服务器运行至关重要。
首次启动并 SSH 登录后,第一件事是执行系统更新和必要的区域设置:
sudo apt update && sudo apt upgrade -y sudo raspi-config在raspi-config中,我通常会做这几项设置:
- 扩展文件系统:确保SD卡的所有空间都被利用。
- 内存分配:由于不使用桌面,可以将GPU内存(
Memory Split)设置为最小(如16MB),将更多内存留给系统。 - 本地化选项:设置正确的时区、键盘布局。
- 性能选项:可以适当超频。对于4B,将
Arm Freq超频到2000MHz,Over Voltage设置为6,在我的散热条件下可以稳定运行,能带来约15%的CPU性能提升,对后续的模型转换等步骤有积极影响。但超频有风险,需确保散热良好。
2.2 安装Python与核心编译工具
树莓派OS自带了Python 3.9+,这已经足够。我们需要安装Python虚拟环境管理工具venv和pip,以及后续编译OpenCV等库所需的构建工具。
sudo apt install -y python3-venv python3-pip sudo apt install -y build-essential cmake pkg-config sudo apt install -y libjpeg-dev libtiff5-dev libjasper-dev libpng-dev sudo apt install -y libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt install -y libxvidcore-dev libx264-dev sudo apt install -y libfontconfig1-dev libcairo2-dev sudo apt install -y libgdk-pixbuf2.0-dev libpango1.0-dev sudo apt install -y libgtk2.0-dev libgtk-3-dev sudo apt install -y libatlas-base-dev gfortran sudo apt install -y libhdf5-dev libhdf5-serial-dev libhdf5-103 sudo apt install -y libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5 sudo apt install -y libopenblas-dev libopenblas-base这一长串命令安装了编译OpenCV所需的大部分依赖。其中libatlas-base-dev和libopenblas-dev提供了优化的线性代数库,对后续的数值计算至关重要。
2.3 创建并激活Python虚拟环境
始终在虚拟环境中工作是一个好习惯,它能避免系统Python环境的污染。
python3 -m venv ~/yolov5_openvino_env source ~/yolov5_openvino_env/bin/activate激活后,命令行提示符前会出现(yolov5_openvino_env)标识。请确保在后续所有操作中,该虚拟环境都处于激活状态。
3. 安装与配置OpenVINO工具套件
OpenVINO是英特尔推出的用于优化和部署AI推理的工具套件,它包含了模型优化器和推理引擎。我们的核心任务就是用它来转换和加速YOLOv5模型。
3.1 安装OpenVINO Runtime
在树莓派上,我们通常只安装运行时所必须的组件,而非完整的开发套件。英特尔提供了针对ARM架构的预编译包。
pip install --upgrade pip pip install openvino==2023.0.0这里我指定了2023.0.0版本,因为经过测试,这个版本与后续的模型转换步骤兼容性较好。安装完成后,可以验证一下:
python -c "from openvino.runtime import Core; print(Core().available_devices)"如果输出中包含MYRIAD(即NCS2),说明OpenVINO运行时安装成功,并能识别到神经计算棒。
3.2 安装OpenCV with OpenVINO支持
虽然可以通过pip install opencv-python快速安装OpenCV,但为了获得更好的性能以及与OpenVINO的深度集成(例如,使用OpenVINO作为后端进行图像预处理),我选择从源码编译OpenCV。这是一个耗时的过程(在树莓派4B上可能需要数小时),但收益显著。
首先,下载OpenCV和OpenCV contrib的源码(以4.8.0版本为例):
cd ~ wget -O opencv.zip https://github.com/opencv/opencv/archive/4.8.0.zip wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.8.0.zip unzip opencv.zip unzip opencv_contrib.zip mv opencv-4.8.0 opencv mv opencv_contrib-4.8.0 opencv_contrib然后,创建构建目录并配置CMake。这是最关键的一步,配置选项决定了编译出的OpenCV具备哪些功能。
cd ~/opencv mkdir build && cd build cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \ -D ENABLE_NEON=ON \ -D ENABLE_VFPV3=ON \ -D BUILD_TESTS=OFF \ -D BUILD_PERF_TESTS=OFF \ -D BUILD_EXAMPLES=OFF \ -D WITH_OPENVINO=ON \ -D OPENCV_DNN_OPENVINO=ON \ -D WITH_INF_ENGINE=ON \ -D BUILD_opencv_python3=ON \ -D PYTHON3_EXECUTABLE=$(which python3) \ -D PYTHON3_INCLUDE_DIR=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \ -D PYTHON3_LIBRARY=$(python3 -c "from distutils.sysconfig import get_config_var; import os; print(os.path.join(get_config_var('LIBDIR'), get_config_var('LDLIBRARY')))") \ -D PYTHON3_NUMPY_INCLUDE_DIRS=$(python3 -c "import numpy; print(numpy.get_include())") \ -D PYTHON3_PACKAGES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") \ ..关键选项解析:
-D WITH_OPENVINO=ON和-D OPENCV_DNN_OPENVINO=ON:启用OpenVINO支持,允许OpenCV的DNN模块使用OpenVINO作为后端之一。-D ENABLE_NEON=ON:启用ARM NEON SIMD指令集加速,对树莓派的ARM CPU性能提升巨大。-D WITH_INF_ENGINE=ON:启用推理引擎支持(旧版名称,与OPENVINO同时开启即可)。- 一系列的
PYTHON3_*参数:确保编译出的OpenCV Python绑定正确安装到我们当前的虚拟环境中。
配置完成后,开始编译和安装:
make -j$(nproc) # 使用所有核心编译,加快速度 sudo make install sudo ldconfig编译完成后,在Python虚拟环境中测试是否成功,并确认OpenVINO后端可用:
python -c "import cv2; print(cv2.__version__); print([b for b in cv2.dnn.DNN_BACKEND_OPENVINO])"4. 准备YOLOv5模型:从训练到导出
部署的起点是一个训练好的YOLOv5模型。这部分工作通常在性能更强的开发机(如带GPU的电脑)上完成。
4.1 训练自定义YOLOv5模型
假设你已经在自己的数据集上完成了YOLOv5模型的训练。这里有几个关键点直接影响后续部署:
- 模型尺寸选择:YOLOv5提供了n、s、m、l、x等不同尺寸的模型。对于树莓派+NCS2,YOLOv5s是最佳平衡点。它参数量较小(约7.2M),在NCS2上能达到较高的帧率,同时精度对于许多应用场景也足够。YOLOv5n更小,但精度损失可能较大;更大的模型(m/l/x)则可能无法在NCS2上运行(内存不足)或速度很慢。
- 输入分辨率:训练时指定的
--img参数(如640)。部署时的推理分辨率必须与训练时一致。NCS2对输入尺寸固定的模型优化更好。通常使用640x640。 - 模型输出:训练完成后,在
runs/train/exp/weights/目录下,你会得到best.pt(验证集上性能最好的权重)和last.pt(最后一次训练的权重)。我们使用best.pt进行部署。
4.2 导出模型为OpenVINO格式
这是连接PyTorch训练和OpenVINO部署的桥梁。我们需要使用YOLOv5官方仓库中的export.py脚本。
首先,在开发机上克隆YOLOv5仓库并安装依赖(确保有PyTorch环境):
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt然后,运行导出脚本。这里有一个至关重要的参数:--include。
python export.py --weights path/to/your/best.pt --include onnx openvino命令详解:
--weights: 指定训练好的PyTorch模型权重文件路径。--include onnx openvino: 告诉脚本同时导出ONNX和OpenVINO格式的模型。脚本会先导出为ONNX,然后自动调用OpenVINO的模型优化器(MO)将ONNX转换为OpenVINO的IR格式(.xml和.bin文件)。- 其他有用参数:
--img 640: 指定输入图像尺寸(需与训练一致)。--batch 1: 指定批处理大小。对于边缘部署,通常设为1。--simplify: 对ONNX模型进行简化,可能减少转换错误。--opset 12: 指定ONNX算子集版本。对于OpenVINO 2023.0,12或13是安全的选择。
执行成功后,你会在权重文件同目录下得到:
best.onnx: ONNX格式模型。best_openvino_model/目录:内含best.xml(模型结构)和best.bin(模型权重)以及一个best.mapping文件。
踩坑记录:动态维度问题YOLOv5默认导出的ONNX模型输入输出可能是动态维度的(batch、height、width为变量)。而一些版本的OpenVINO模型优化器或运行时对动态维度的支持可能不完善,尤其在NCS2上。如果遇到转换失败或推理错误,可以在导出时固定维度:
python export.py --weights path/to/best.pt --include onnx --dynamic # 然后使用OpenVINO的mo工具单独转换,并指定静态形状 mo --input_model best.onnx --input_shape "[1,3,640,640]" --data_type FP16但更推荐使用YOLOv5export.py直接导出OpenVINO格式,它内部的封装处理了很多兼容性问题。
5. 模型部署与NCS2加速推理
将生成的best.xml和best.bin文件拷贝到树莓派上。接下来就是在树莓派上编写推理脚本,并利用NCS2进行加速。
5.1 推理脚本编写:预处理、推理、后处理
一个完整的推理流程包含三个步骤:图像预处理、模型推理、输出后处理。下面是一个核心代码框架:
import cv2 import numpy as np import time from openvino.runtime import Core class YOLOv5OpenVINO: def __init__(self, model_xml, device='MYRIAD', conf_thres=0.25, iou_thres=0.45): """ 初始化模型 :param model_xml: OpenVINO IR模型.xml文件路径 :param device: 推理设备,'MYRIAD'为NCS2,'CPU'为树莓派CPU :param conf_thres: 置信度阈值 :param iou_thres: NMS的IoU阈值 """ self.conf_threshold = conf_thres self.iou_threshold = iou_thres self.device = device # 1. 加载模型 core = Core() self.model = core.read_model(model=model_xml) self.compiled_model = core.compile_model(model=self.model, device_name=device) self.infer_request = self.compiled_model.create_infer_request() # 2. 获取输入输出信息 self.input_layer = self.compiled_model.input(0) self.output_layer = self.compiled_model.output(0) self.input_shape = self.input_layer.shape # [1, 3, H, W] self.input_size = (self.input_shape[2], self.input_shape[3]) # (H, W) self.num_classes = self.output_layer.shape[2] - 5 # 根据你的模型调整 # 3. 准备颜色映射等(用于画框) self.colors = np.random.uniform(0, 255, size=(self.num_classes, 3)) def preprocess(self, image): """ 将输入图像预处理为模型需要的格式 :param image: 原始BGR图像 (H, W, C) :return: 预处理后的blob (1, 3, H, W) """ # 调整大小并保持纵横比填充灰边 h, w = image.shape[:2] scale = min(self.input_size[0] / h, self.input_size[1] / w) new_h, new_w = int(h * scale), int(w * scale) resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR) # 创建画布并填充 canvas = np.full((self.input_size[0], self.input_size[1], 3), 114, dtype=np.uint8) top = (self.input_size[0] - new_h) // 2 left = (self.input_size[1] - new_w) // 2 canvas[top:top+new_h, left:left+new_w, :] = resized # 转换颜色通道 BGR -> RGB,并归一化 blob = canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 blob = np.expand_dims(blob, axis=0) # 添加batch维度 return blob, (scale, (left, top)), (h, w) def infer(self, blob): """ 执行模型推理 :param blob: 预处理后的图像数据 :return: 模型原始输出 """ self.infer_request.infer(inputs={self.input_layer.any_name: blob}) outputs = self.infer_request.get_output_tensor(self.output_layer.index).data return outputs def postprocess(self, outputs, preprocess_info, orig_shape): """ 对模型输出进行后处理:过滤低置信度框,NMS,坐标转换 :param outputs: 模型原始输出 [1, num_boxes, 85] :param preprocess_info: (scale, (pad_left, pad_top)) :param orig_shape: 原始图像尺寸 (h, w) :return: 检测结果列表 [(x1, y1, x2, y2, conf, cls_id), ...] """ scale, pad = preprocess_info pad_left, pad_top = pad detections = [] # 遍历所有预测框 for pred in outputs[0]: # pred: [center_x, center_y, width, height, obj_conf, class_conf_1, ..., class_conf_n] obj_conf = pred[4] if obj_conf < self.conf_threshold: continue # 找到最大类别置信度 class_scores = pred[5:] class_id = np.argmax(class_scores) cls_conf = class_scores[class_id] conf = obj_conf * cls_conf # 综合置信度 if conf < self.conf_threshold: continue # 解码边界框坐标 (相对于输入网络图像的尺寸) cx, cy, bw, bh = pred[:4] x1 = (cx - bw / 2) y1 = (cy - bh / 2) x2 = (cx + bw / 2) y2 = (cy + bh / 2) # 将坐标转换回原始图像尺寸 x1 = int((x1 - pad_left) / scale) y1 = int((y1 - pad_top) / scale) x2 = int((x2 - pad_left) / scale) y2 = int((y2 - pad_top) / scale) # 确保坐标在图像范围内 x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(orig_shape[1], x2), min(orig_shape[0], y2) if x2 <= x1 or y2 <= y1: continue detections.append([x1, y1, x2, y2, conf, class_id]) # 非极大值抑制 (NMS) if len(detections) > 0: boxes = np.array([d[:4] for d in detections]) scores = np.array([d[4] for d in detections]) indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), self.conf_threshold, self.iou_threshold) if len(indices) > 0: indices = indices.flatten() final_detections = [detections[i] for i in indices] return final_detections return [] def draw_detections(self, image, detections): """在图像上绘制检测框和标签""" for (x1, y1, x2, y2, conf, cls_id) in detections: color = self.colors[cls_id] label = f"Class_{cls_id}: {conf:.2f}" cv2.rectangle(image, (x1, y1), (x2, y2), color, 2) cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) return image # 使用示例 if __name__ == "__main__": # 初始化检测器,指定使用MYRIAD设备(NCS2) detector = YOLOv5OpenVINO(model_xml='best.xml', device='MYRIAD') # 读取图像 img = cv2.imread('test.jpg') orig_h, orig_w = img.shape[:2] # 预处理 blob, preprocess_info, orig_shape = detector.preprocess(img) # 推理 start = time.time() outputs = detector.infer(blob) inference_time = time.time() - start # 后处理 detections = detector.postprocess(outputs, preprocess_info, (orig_h, orig_w)) # 绘制并显示 result_img = detector.draw_detections(img, detections) print(f"Inference time: {inference_time*1000:.2f} ms") print(f"Detected {len(detections)} objects.") cv2.imshow('Result', result_img) cv2.waitKey(0) cv2.destroyAllWindows()5.2 NCS2特定配置与性能调优
将设备指定为'MYRIAD'只是开始,要让NCS2发挥最佳性能,还需要注意以下几点:
模型精度:NCS2主要支持FP16(半精度浮点数)推理。在导出OpenVINO模型时,默认就是FP16。确保你的模型是FP16格式,可以在加载模型后检查:
print(self.input_layer.element_type) # 应显示 `Type.f16`FP16在NCS2上比FP32快得多,且精度损失对于目标检测通常可接受。
温度与散热:NCS2在持续高负载下会发热。过热会导致设备降频甚至断开连接。确保树莓派和NCS2有良好的通风环境,可以考虑加装小型散热片或风扇。在代码中可以加入温度监控(通过
pyusb或lsusb解析,但较复杂),更简单的方法是监控设备是否稳定。批处理:NCS2对批处理(Batch)的支持有限,且批处理不一定能线性提升吞吐量。对于实时视频流,通常设置
batch=1。如果是处理图片队列,可以尝试batch=4或8,但需要测试实际性能提升。多棒并行:如果你有多个NCS2,OpenVINO支持将它们作为一个设备池使用,理论上可以提升吞吐量。在初始化时指定
device_name='MULTI:MYRIAD.1.2-MYRIAD.1.3'(假设设备ID为1.2和1.3)。但需要注意负载均衡和同步开销,实际增益需要测试。避免频繁加载/卸载模型:创建
YOLOv5OpenVINO类实例(即加载模型)是一个相对耗时的操作。应该在程序初始化时完成一次,然后在循环中重复使用infer方法。
6. 实测性能分析与对比
理论归理论,实际性能如何才是关键。我在树莓派4B(4GB内存,超频至2.0GHz)上,使用YOLOv5s模型(640x640输入),对同一张测试图片进行100次连续推理,取平均时间,得到以下数据:
| 推理设备 | 平均推理时间 (ms) | 平均FPS | 备注 |
|---|---|---|---|
| 树莓派4B CPU | ~450 - 550 ms | ~1.8 - 2.2 FPS | 使用OpenVINO CPU插件,4线程 |
| 英特尔NCS2 | ~90 - 120 ms | ~8.3 - 11.1 FPS | 模型精度为FP16 |
| NCS2 (带预处理) | ~110 - 140 ms | ~7.1 - 9.1 FPS | 包含图像缩放、填充等CPU操作 |
性能解读与瓶颈分析:
- 显著加速:NCS2将推理速度提升了约4-5倍,从勉强可用的2FPS提升到了接近实时的10FPS左右。这对于很多需要一定实时性的边缘应用(如智能监控、机器人视觉引导)是一个质的飞跃。
- 瓶颈转移:在使用NCS2后,图像预处理(CPU操作)成为了新的瓶颈。从表中可以看出,纯NCS2推理约100ms,但加上预处理后总时间增加了20-30ms。预处理时间取决于输入图像的分辨率。优化预处理逻辑(如使用OpenCV的UMat、尝试不同的插值算法)可以稍微改善。
- 功耗考量:NCS2的功耗大约为1-2W,树莓派4B满载约6-8W,总功耗仍在10W左右,非常适合电池供电或低功耗场景。
- 对比其他方案:如果使用TensorFlow Lite配合 Coral USB Accelerator(Edge TPU),在同类模型上可能获得更高的FPS(15-20+)。但NCS2的优势在于其通过OpenVINO对多种框架模型(ONNX, TensorFlow, PyTorch等)的良好支持,且价格通常更有竞争力。
7. 常见问题排查与优化建议
在实际部署过程中,你几乎一定会遇到一些问题。下面是我总结的一些常见问题及其解决方法。
7.1 NCS2设备无法识别或初始化失败
现象:运行脚本时报错,提示找不到MYRIAD设备或初始化失败。
- 检查物理连接:确保NCS2牢固地插入树莓派的USB 3.0(蓝色)端口。USB 2.0供电可能不足且速度慢。
- 检查USB权限:将当前用户加入
users组,或创建udev规则。sudo usermod -a -G users $(whoami) # 然后重新登录或重启 - 查看设备列表:
lsusb | grep -i myriad # 或使用OpenVINO工具 python -c "from openvino.runtime import Core; print(Core().available_devices)" - 供电问题:树莓派USB口供电能力有限。如果连接了其他高功耗USB设备(如硬盘),可能导致NCS2供电不稳。尝试使用外接供电的USB Hub,或者确保树莓派电源适配器是足额的(5V/3A)。
7.2 模型转换或加载失败
现象:在导出ONNX/OpenVINO模型或树莓派上加载.xml文件时出错。
- 版本匹配:确保开发机上用于导出模型的
openvino-dev或openvino版本与树莓派上安装的openvino运行时版本尽量一致。跨大版本转换和部署有时会有兼容性问题。 - 简化模型:如果从ONNX转换失败,尝试在导出ONNX时加上
--simplify和--opset 12参数。 - 检查模型输入输出:使用Netron工具(
pip install netron)打开生成的.onnx或.xml文件,检查输入输出层的名称和形状是否符合预期。 - 错误信息:仔细阅读错误信息。OpenVINO的错误信息有时比较晦涩,但关键词如
Unsupported operation、Shape mismatch能指明方向,去OpenVINO的官方GitHub Issues搜索往往能找到答案。
7.3 推理结果异常(框不准、无检测)
现象:模型能跑通,但检测框错位、巨大、或者完全检测不到目标。
- 预处理/后处理不匹配:这是最常见的原因。确保你的预处理(归一化、通道顺序、填充方式)和训练时完全一致。YOLOv5默认使用RGB通道顺序和0-1的归一化。我的代码中
blob = canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0就对应了这个操作。 - 坐标转换错误:后处理中,将网络输出坐标转换回原始图像坐标时,必须考虑预处理时进行的缩放和填充。代码中的
scale和pad_left、pad_top就是用于这个计算。仔细核对这部分数学。 - 置信度阈值:调整
conf_thres参数。训练好的模型在实际场景中可能需要不同于训练验证阶段的阈值。 - NMS参数:调整
iou_thres参数。对于密集小目标,可能需要降低IoU阈值以防止漏检。
7.4 性能未达预期
现象:FPS远低于上文提供的参考值。
- 测量方法:确保你测量的是纯推理时间(
infer函数内部),而不是包含预处理、后处理和显示的总时间。显示(cv2.imshow)尤其耗时。 - CPU频率:检查树莓派CPU是否因过热而降频。安装
vcgencmd工具监控:vcgencmd measure_temp vcgencmd measure_clock arm - 关闭无关进程:关闭树莓派上不必要的后台服务,如蓝牙、桌面环境(如果你用的是桌面版)。
- 尝试CPU推理对比:将设备改为
'CPU',看看CPU推理时间是否正常。如果CPU推理也慢,可能是预处理代码效率问题。 - NCS2发热:触摸NCS2是否烫手?过热会导致性能下降。暂停推理几分钟后再测试。
7.5 内存不足(OOM)错误
现象:运行过程中树莓派卡死或报内存错误。
- 模型太大:YOLOv5m或更大的模型可能无法在NCS2上运行。NCS2的VPU内存有限。坚持使用YOLOv5s或YOLOv5n。
- 树莓派内存分配:如果你使用了桌面版系统,可以尝试通过
raspi-config减少分配给GPU的内存(如降到16MB)。 - 监控内存使用:使用
htop命令监控内存和交换分区使用情况。如果交换频繁,说明物理内存不足,考虑关闭其他程序或优化代码。
整个流程走下来,从训练模型到在边缘设备上跑出效果,最深的体会就是“细节决定成败”。模型转换时的一个参数、预处理时的一个通道顺序、后处理时的一个坐标计算,都可能让最终结果天差地别。NCS2确实是一个性价比很高的边缘加速方案,但它不是一个傻瓜式的加速棒,需要你真正理解OpenVINO的流程和YOLO的工作原理,才能把它调教好。建议大家在每一步都做好验证,比如导出ONNX后先用ONNX Runtime在电脑上跑一下看结果是否正确,再转换到OpenVINO格式,最后上树莓派测试。这样分段排查,能最快定位问题所在。另外,这个部署框架不仅仅是针对YOLOv5和NCS2,其核心思路——模型转换、OpenVINO Runtime加载、预处理/推理/后处理分离——同样适用于其他支持OpenVINO的模型和硬件(如英特尔CPU、集成显卡),算是一个通用的边缘AI部署模式,掌握了之后可以举一反三。