简介:面向RDK X5开发新手,提供在RDK X5上部署YOLOv5的完整源码与教程,覆盖从WSL2环境搭建、Ubuntu配置、数据集训练到ONNX转换、Docker镜像挂载及模型量化的全流程。压缩包共3个文件,以HTML说明页、INSCode工程配置和Git忽略规则文件为主,体积仅6KB,轻量实用。已有587人学习浏览。资源结合地瓜创客孵化营项目经验,包含智能消防机器人硬件介绍与实战进展,并附有大量避坑指南和实用链接,帮助初学者避开常见问题,快速跑通部署流程。代码包结构清晰,适合研究源码组织与配置细节,可作为算法移植与项目集成的高效参考。 把YOLO跑在RDK X5上,到底有没有网上说的那么轻松?我花了一个周末把整个流程从零走了一遍,源码也在GitHub上大致翻了一圈,今天直接把整个部署过程拆开聊。RDK X5是地平线旭日系列里定位相当实在的一块开发板,板载BPU算力足够跑主流目标检测模型,而YOLO在嵌入式场景里又是绕不开的算法,两者凑到一起,正好是边缘端视觉项目最常见的组合。这篇文章覆盖环境准备、源码结构、模型转换、编译部署、实测调优和踩坑记录,适合刚拿到板子想快速跑通YOLO的开发者,也适合已经跑通但想进一步优化性能的朋友。
1. 项目背景与部署思路
1.1 为什么选RDK X5跑YOLO
RDK X5这块板子在边缘AI设备里属于比较特殊的存在。它没有走GPU路线,而是用地平线自研的BPU(Brain Processing Unit)做神经网络加速,核心优势是低功耗下的高能效比。我实测板子满载跑YOLO时的功耗表现比同价位带GPU的开发板明显克制,这对做户外供电、电池供电的视觉项目来说非常关键。板卡接口也比较全,MIPI CSI摄像头接口、千兆以太网、USB 3.0都有,接到实际项目里做视频流接入和结果回传都不需要额外转接。
从算法适配角度看,YOLO系列模型在BPU上有着成熟的部署工具链支持。地平线提供了OE(OpenExplorer)工具链,可以把ONNX格式的模型转换为BPU能直接运行的二进制指令,同时板端运行时通过hobot_dnn或onnxruntime调用BPU推理。这意味着部署路径并不是只有“硬啃C++”一条路,Python和C++都有对应的推理接口。
1.2 部署方案选型:板端编译还是交叉编译
拿到源码后第一个需要拍板的问题就是编译环境。我在初期调研时发现,RDK X5的部署方案主要分两条路:直接在板子上编译,或者在上位机交叉编译后拷贝到板子运行。
板端编译的好处是省事,SSH连上板子、clone代码、直接make,依赖缺了什么现场装就行,不太会遇到架构不匹配的问题。缺点是RDK X5毕竟是一块嵌入式板卡,CPU性能比不上桌面级处理器,全量编译YOLO推理工程大概需要几分钟到十几分钟,反复改代码反复编译时会觉得有点折磨。
交叉编译的优势是编译速度快、不占用板子资源,适合项目进入稳定迭代期后用脚本批量构建。但要对工具链比较熟,依赖库的路径、sysroot的配置都要提前处理好。我自己实际走下来,首次折腾推荐板端编译,把流程跑通之后再考虑要不要切交叉编译,这样排障成本最低。
1.3 源码整体结构一览
我参考的这份源码是一个比较完整的YOLO部署工程,目录组织如下:
rdk_x5_yolo/ ├── model/ # 存放模型文件:yolov5s.onnx、yolov8s.onnx,以及转换后的.bin ├── src/ │ ├── main.cpp # C++推理入口 │ ├── yolo_detector.cpp # YOLO检测器封装 │ ├── image_utils.cpp # 图像读取、缩放、letterbox处理 │ └── postprocess.cpp # 解码、NMS后处理 ├── include/ # 头文件 ├── scripts/ │ ├── convert_onnx.sh # ONNX转BPU脚本 │ └── run_demo.sh # 一键运行脚本 ├── CMakeLists.txt └── README.md源码把模型转换、推理、前后处理做了一个比较清晰的分层,引用到自己的项目里时,可以直接替换模型和输入输出层,不需要大改代码逻辑。
2. 环境准备与依赖搭建
2.1 板卡基础配置与联网准备
拿到RDK X5后的第一步是烧录镜像。官方提供的是基于Ubuntu的RDK OS镜像,用烧录工具写入microSD卡即可。烧录完成后开机,默认账户登录,第一件事就是确认BPU驱动是否正常。
# 确认BPU设备节点 ls /dev/hobot* cat /proc/version如果能正常看到设备节点,说明BPU驱动已经加载。接下来配置网络,我这边是直接插网线用的DHCP,比较省心。如果要用Wi-Fi,RDK X5本身不带无线模块,需要自己接USB无线网卡或者通过路由器中转,这点在布线时要提前考虑。
2.2 基础依赖安装与工具链准备
源码编译需要依赖一系列基础库,包括OpenCV、CMake、onnxruntime等。RDK OS自带部分依赖,但保险起见还是手动装一遍。
pip3 install --upgrade pip sudo apt update sudo apt install -y cmake libopencv-dev wget unzip pip3 install onnxruntime horizon-oe这里的horizon-oe是地平线的模型转换工具链,安装后用来做ONNX到BPU模型的转换。实测下来,工具链的版本和板端推理库的版本需要匹配,否则转换出来的模型在板端加载时会报版本兼容错误。安装完成后,可以用hb_perf命令确认工具链是否可用。
2.3 源码获取与依赖库检查
git clone https://github.com/your_repo/rdk_x5_yolo.git cd rdk_x5_yolo chmod +x scripts/*.sh ./scripts/run_demo.sh --check拿到源码后先跑检查脚本,脚本会自动检测OpenCV、onnxruntime、CMake等关键依赖是否齐全,缺失的会提示安装命令。这一步非常推荐,避免你一路手工检查容易漏掉某个库。
3. 模型转换与核心代码解析
3.1 YOLO模型转成BPU可执行格式
模型部署中的关键一步是把训练好的YOLO模型从PyTorch权重导出为ONNX,再转换成BPU可执行格式。源码仓库的model目录里已经替你准备了yolov5s和yolov8s的ONNX文件,不过为了让读者理解全流程,我还是把导出命令写出来。
import torch model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) dummy_input = torch.randn(1, 3, 640, 640) torch.onnx.export(model, dummy_input, "yolov5s.onnx", opset_version=11, input_names=["images"], output_names=["output"])导出ONNX后,使用OE工具链转换为BPU模型:
hb_mapper makertbin --model-type onnx \ --model yolov5s.onnx \ --output_dir model_output \ --input-shape "images:1x3x640x640"配置转换时需要注意几点:输入尺寸建议和推理时的统一尺寸一致,我用的640x640;输出节点的解析要清楚,YOLOv5和YOLOv8的输出结构不同,后处理代码也要跟着变。转换成功后会生成一个.bin文件和对应的.yaml配置,这个.bin就是后续板端推理要加载的模型。
3.2 关键源码模块拆解
核心源码里最值得关注的是模型加载、预处理和后处理三个模块。
模型加载与推理
// main.cpp 核心片段 Detector detector; detector.load_model("model/yolov5s_640x640.bin"); cv::Mat frame = cv::imread("test.jpg"); std::vector<Detection> results = detector.detect(frame);Detector类封装了BPU推理的完整流程:加载模型、创建任务、输入数据填充、执行推理、取回输出。这个封装让主流程非常清晰,不用被底层的配置细节干扰。
预处理Letterbox
YOLO推理前需要把原始图像缩放到固定尺寸,并且不能直接resize,因为直接resize会破坏宽高比,导致目标形变、检测精度下降。源码里用的是letterbox方式:计算缩放比例、填充灰边到目标尺寸。
// image_utils.cpp 片段 cv::Mat letterbox(const cv::Mat& img, int target_size) { float scale = std::min(target_size * 1.0 / img.cols, target_size * 1.0 / img.rows); int new_w = round(img.cols * scale); int new_h = round(img.rows * scale); cv::Mat resized; cv::resize(img, resized, cv::Size(new_w, new_h)); cv::Mat canvas = cv::Mat::zeros(target_size, target_size, img.type()); canvas = cv::Scalar(114, 114, 114); resized.copyTo(canvas(cv::Rect((target_size - new_w) / 2, (target_size - new_h) / 2, new_w, new_h))); return canvas; }顺带解释一下为什么填充颜色是114,这是YOLO训练时对输入图像做的标准化值,推理时保持一致的预处理能减少精度损失。
后处理与NMS
解码和NMS是整个后处理的关键。YOLOv5的输出shape是[1, 25200, 85],25200是不同尺度特征图上的anchor数量之和,85是4个坐标+1个目标置信度+80个类别概率。源码中解码时按这个结构解析,再做置信度过滤和NMS去重。这一块也是YOLOv5和YOLOv8后处理最大的差异点。
// postprocess.cpp 片段 std::vector<Detection> decode_output(float* data, int num_anchors, int num_classes, float conf_thresh) { std::vector<Detection> dets; for (int i = 0; i < num_anchors; i++) { float* ptr = data + i * (5 + num_classes); float obj_conf = ptr[4]; if (obj_conf < conf_thresh) continue; // 找最高类别置信度 int cls_id = 0; float max_cls_conf = 0; for (int j = 0; j < num_classes; j++) { if (ptr[5 + j] > max_cls_conf) { max_cls_conf = ptr[5 + j]; cls_id = j; } } float final_conf = obj_conf * max_cls_conf; if (final_conf < conf_thresh) continue; // 坐标解码 Detection det; det.x = ptr[0]; det.y = ptr[1]; det.w = ptr[2]; det.h = ptr[3]; det.conf = final_conf; det.class_id = cls_id; dets.push_back(det); } return dets; }NMS部分用了经典的按置信度排序+IoU去除逻辑,没有引入复杂优化,但胜在逻辑清晰。
4. 编译部署与实测调优
4.1 板端编译流程
环境就绪后,直接在板子上编译。源码用CMake管理,流程比较标准。
mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc)编译过程如果顺利,会在build目录下生成yolo_demo可执行文件。第一次编译时如果提示缺库,根据CMake报错逐一把依赖装上就行。
4.2 运行测试与结果分析
./yolo_demo --model ../model/yolov5s_640x640.bin --image ../test.jpg --conf 0.25 --nms 0.45我用一张包含行人、车辆、猫狗混合场景的测试图跑了一次,检测输出会在终端打印每个目标的类别、置信度和坐标,并生成一张画框后的输出图。
实测下来RDK X5在640x640输入下的推理耗时大约在60ms到90ms之间,换算成帧率大致是11到16 FPS。这个表现对实时性要求不高的巡检、安防、农业监测项目已经完全够用。如果按模型和分辨率不同,帧率会有浮动,后面会给出调优建议。
4.3 视频流推理扩展
源码虽然默认是单张图片推理,但main.cpp里留了视频流的扩展接口。我把摄像头输入替换进去后,只改了几行代码就实现了本地视频文件的检测。如果要接MIPI摄像头,RDK X5有对应的摄像头驱动,调用方式略有不同,但推理核心逻辑完全复用。对于需要连续处理视频流的项目,建议在循环里做帧率控制和丢帧处理,避免内存上涨。
5. 常见问题与排查技巧实录
5.1 模型转换失败或算子不支持
转换ONNX时最容易遇到的问题就是某些算子(如部分版本的Focus、SiLU)BPU不支持。我实际遇到的主要是Focus算子的兼容问题,YOLOv5早期版本会用到Focus结构,转换时报错。解决方案是在导出ONNX时将Focus替换为普通的Conv+Slice组合,或者直接使用较新的YOLOv5版本,新版本已经把Focus去掉。
此外,转换时如果报维度动态范围的错误,检查一下ONNX导出的输入shape是否是固定值。BPU要求静态shape,所有动态维度都要固定。
5.2 推理结果不准或全为空
推理结果和预期差距大,第一优先级检查预处理和后处理。常见原因包括:
- 输入图像的归一化方式不对,YOLO推理时一般要求像素值除以255并归一化到0到1,但有些版本的模型训练时用的是0到255直接输入,需要先确认模型训练时的输入格式。
- letterbox的参数不对,填充颜色、缩放比例、坐标偏移任何一个搞错了,都会导致框的位置偏移严重。
- 后处理中的anchor尺寸和模型训练时不一致,这个在YOLOv5中尤其常见,需要和训练配置对齐。
5.3 性能优化方向
性能优化是实际部署中最有挑战的部分。我实测后整理了几条有效方向:
- 降低输入分辨率:从640x640降到416x416,推理耗时能降低约40%,精度损失对大部分场景可以接受。
- 模型量化:OE工具链支持INT8量化,量化后模型体积和推理耗时都有明显下降。量化时如果精度掉得厉害,准备一些校准数据重新跑一遍量化流程。
- 多线程流水线:在视频流场景中把采集、预处理、推理、后处理拆到不同线程,能显著提升整体吞吐。我简单测试后帧率提升了30%以上。
5.4 板子发热和稳定性
RDK X5跑YOLO时如果长时间满载运行,机身温度会明显上升,但实测没有出现降频或死机。给板子加装一个小散热片或者风扇,对稳定性会有明显帮助。长期在户外做无人值守项目的话,建议在代码里加上看门狗机制,定时检查推理进程存活状态,异常时自动重启。
6. 个人实操心得
第一次把YOLO跑在RDK X5上时最让我意外的恰恰是踩坑最多的地方:模型转换。原本以为板端推理代码是最大工作量和难点,实际搞下来,模型转换、算子兼容、输出解析这些环节对整体开发节奏的影响远超预期。把转换流程理顺之后,后面换模型、换分辨率、换应用场景都变得非常快。
开源的好处在于可以灵活复用和修改。我后来把detector类单独抽了出来,换了YOLOv8s的模型,只改了后处理解析部分的代码就完成了切换。如果你的项目也计划在不同YOLO版本之间切换,建议从一开始就把后处理逻辑做成可插拔的模块,别把版本相关的解析逻辑写死在主流程里。
如果你刚拿到RDK X5,建议先不要急着换模型,用源码自带的yolov5s把整个流程跑通,理解每个环节做了什么,再逐步替换模型和调优。这样遇到问题时能更精准地定位到底是模型转换、代码逻辑还是硬件限制。
最后分享一个很实用的小技巧:调试阶段在板子上编译时,尽量加-j1参数,虽然慢,但报错信息可读性远高于多线程编译时的一堆乱码,排障效率反而更高。等代码稳定了再改成-j$(nproc)全速编译,几乎没有遇到过问题。
本文还有配套的精品资源,点击获取