尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

香橙派RK3588实战:YOLOv5摄像头抓帧与推理全链路

发布时间:2026/9/29 7:26:40

资讯中心
01
ARTICLE

香橙派RK3588实战:YOLOv5摄像头抓帧与推理全链路

香橙派RK3588实战:YOLOv5摄像头抓帧与推理全链路
1. 从零打通摄像头到推理的完整链路香橙派RK3588上跑YOLOv5模型转换和推理脚本都调通了但一直用测试图片跑总觉得差点意思。真正要把这套东西用起来第一步就是让板子自己“看见”画面——接上摄像头抓一帧送进模型拿到检测结果。这个环节看着简单实际上从设备识别、图像格式转换到推理输入适配每一步都有坑。我前后折腾了三四块不同型号的摄像头才把这条链路彻底跑顺。这篇内容适合已经在香橙派RK3588上完成YOLOv5基础部署、模型能正常推理静态图片的开发者。如果你还没走到这一步建议先把前面的模型转换和基础推理跑通再来看。整条链路的核心关键词就几个香橙派、RK3588、YOLOv5、OpenCV、摄像头。我会从硬件选型开始一步步讲到怎么用OpenCV抓帧、怎么把抓到的帧喂给YOLOv5、以及中间那些让人抓狂的报错怎么解决。先说清楚最终要实现的效果在香橙派RK3588上运行一个Python脚本调用USB摄像头或者MIPI摄像头实时抓取一帧画面经过预处理后送入YOLOv5s模型进行推理输出检测框和类别信息。整个过程不依赖网络全部在板端完成。这个方案可以直接作为后续视频流实时检测的基础框架也可以用来做定时抓拍分析。2. 硬件选型与摄像头接入方案2.1 USB摄像头 vs MIPI摄像头怎么选香橙派RK3588支持两种摄像头接入方式USB接口和MIPI CSI接口。这两种方案各有优劣选哪个取决于你的具体场景。USB摄像头最大的好处是即插即用系统自带UVC驱动基本上插上去就能在/dev/video*下面看到设备节点。我手头有一个罗技C270和一个杂牌1080P摄像头两个都是免驱的插上就能用。缺点是USB带宽有限高分辨率下帧率会受限而且USB摄像头的图像质量参差不齐有些便宜货色彩偏差很大。MIPI摄像头需要通过FPC排线接到板子的CSI接口上香橙派官方有配套的摄像头模组比如OV5647就是常见的选择。MIPI摄像头的好处是带宽大、延迟低、图像质量好适合对实时性要求高的场景。但问题是驱动配置麻烦设备树要改不同模组的寄存器配置还不一样。我第一次接OV5647的时候设备树没配对/dev/video*下面根本看不到节点折腾了大半天才搞定。提示如果你是第一次在香橙派RK3588上接摄像头强烈建议先用USB摄像头把整个链路跑通确认软件层面没问题之后再换MIPI摄像头做优化。这样可以避免同时排查硬件和软件两个方向的问题。2.2 设备节点识别与权限配置摄像头接上之后第一件事是确认系统有没有识别到设备。打开终端执行ls /dev/video*正常情况下会看到/dev/video0、/dev/video1这样的设备节点。如果有多个摄像头编号会依次递增。我遇到过一种情况板子自带的HDMI输入也会占用一个video节点所以插上USB摄像头之后可能看到的是/dev/video1而不是/dev/video0。这时候需要用v4l2-ctl工具来确认哪个节点才是真正的摄像头v4l2-ctl --list-devices这个命令会列出所有视频设备及其对应的节点输出类似这样USB Camera: USB Camera (usb-fc800000.usb-1): /dev/video1 /dev/video2看到这个信息就知道/dev/video1是摄像头的采集节点。有些摄像头会同时注册两个节点一个是采集用的一个是元数据用的通常采集节点是第一个。权限问题也很常见。普通用户默认没有访问/dev/video*的权限直接跑OpenCV会报“Permission denied”。解决办法有两个一是把当前用户加入video组执行sudo usermod -aG video $USER然后重新登录二是临时用sudo跑脚本。我推荐第一种一劳永逸。2.3 OpenCV的安装与摄像头支持验证香橙派RK3588的Ubuntu 20.04系统上安装OpenCV有好几种方式。最简单的是用aptsudo apt update sudo apt install python3-opencv但apt装的OpenCV版本可能比较老而且不一定带FFmpeg支持。如果你需要处理视频流或者用一些新特性建议用pip装pip3 install opencv-python不过要注意pip装的opencv-python是预编译的wheel包在ARM平台上可能没有针对RK3588的NPU做优化但用来抓帧和预处理是足够的。我实测下来pip装的OpenCV 4.5.x在香橙派上跑摄像头抓帧没问题CPU占用也在可接受范围内。装完之后验证一下import cv2 print(cv2.__version__) cap cv2.VideoCapture(1) # 注意这里的编号 if cap.isOpened(): print(摄像头打开成功) ret, frame cap.read() if ret: print(抓帧成功图像尺寸:, frame.shape) cap.release() else: print(摄像头打开失败)这段代码能跑通说明OpenCV和摄像头的基本链路没问题。如果cap.isOpened()返回False先检查设备节点编号对不对再检查权限。3. 抓帧与预处理的完整实现3.1 用OpenCV抓取一帧图像抓帧本身很简单cap.read()就搞定了。但这里有几个细节值得注意。首先是摄像头的初始化时间。有些USB摄像头插上之后需要一两秒才能稳定输出图像如果VideoCapture之后立刻read()可能会拿到空帧或者花屏。我的做法是在打开摄像头之后先空转几帧cap cv2.VideoCapture(1) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(M, J, P, G)) # 预热丢弃前10帧 for _ in range(10): cap.read() ret, frame cap.read()设置CAP_PROP_FOURCC为MJPG很重要。很多USB摄像头默认输出YUYV格式在640x480分辨率下YUYV的带宽占用比MJPG大得多导致帧率上不去。改成MJPG之后同样分辨率下帧率能翻倍。这个坑我踩过一开始没设FOURCC摄像头只能跑5帧改成MJPG之后直接跑到30帧。分辨率设置也有讲究。YOLOv5s的输入尺寸是640x640如果你抓到的帧是1920x1080后面需要缩放会浪费CPU资源。直接在摄像头层面设置成640x480后续处理更高效。但要注意有些摄像头不支持任意分辨率设置之后实际输出可能还是默认值需要用cap.get()确认一下。3.2 图像格式转换与YOLOv5输入适配OpenCV抓到的帧是BGR格式的numpy数组而YOLOv5模型通常期望RGB格式的输入。所以第一步是颜色空间转换img_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)接下来是尺寸调整。YOLOv5s的标准输入是640x640但直接resize会改变宽高比导致检测框位置偏移。正确的做法是保持宽高比缩放然后填充黑边def letterbox(img, new_shape(640, 640), color(114, 114, 114)): shape img.shape[:2] r min(new_shape[0] / shape[0], new_shape[1] / shape[1]) new_unpad int(round(shape[1] * r)), int(round(shape[0] * r)) dw, dh new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] dw / 2 dh / 2 img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) top, bottom int(round(dh - 0.1)), int(round(dh 0.1)) left, right int(round(dw - 0.1)), int(round(dw 0.1)) img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img这个letterbox函数是YOLOv5官方仓库里的标准做法。它的逻辑是先按比例缩放让长边对齐到640短边按比例缩放然后在短边两侧填充灰色。这样既保持了宽高比又满足了模型输入尺寸要求。归一化也是必须的。YOLOv5期望输入像素值在0到1之间img img.astype(np.float32) / 255.0最后是维度变换。模型期望的输入形状是(1, 3, 640, 640)也就是batch、channel、height、width的顺序img img.transpose(2, 0, 1) # HWC - CHW img np.expand_dims(img, axis0) # 增加batch维度3.3 推理输入的数据类型与内存布局这一步是很多人容易忽略的。香橙派RK3588的NPU推理通常通过RKNN API或者ONNX Runtime来调用。如果你用的是RKNN模型输入数据的类型和内存布局有特定要求。RKNN模型通常要求输入是uint8类型而不是float32。这意味着你不需要做归一化而是直接把0到255的像素值传进去。但具体要看模型转换时的配置。我在转换YOLOv5s模型时设置了mean_values[[0, 0, 0]]和std_values[[255, 255, 255]]这样模型内部会自动做归一化外部输入就保持uint8即可。如果你用的是ONNX模型加ONNX Runtime那输入通常是float32需要手动归一化。两种方式的预处理代码不一样一定要根据实际使用的推理后端来调整。内存布局方面RKNN模型通常要求NHWC格式而ONNX模型要求NCHW格式。这个也要根据后端来定。我建议在预处理阶段就把数据准备好直接喂给推理接口不要在推理接口内部再做转换那样效率低。4. 推理执行与结果解析4.1 调用RKNN模型执行推理假设你已经把YOLOv5s转换成了RKNN模型文件名为yolov5s.rknn。推理的基本流程是加载模型、初始化运行时、设置输入、执行推理、获取输出。from rknnlite.api import RKNNLite rknn RKNNLite() ret rknn.load_rknn(yolov5s.rknn) ret rknn.init_runtime() # 假设img是预处理好的NHWC格式uint8数据 outputs rknn.inference(inputs[img])rknn.inference返回的是一个列表里面包含模型的所有输出。YOLOv5s通常有三个输出层分别对应不同尺度的特征图。每个输出的形状是(1, 25200, 85)或者类似的形式其中25200是候选框数量85是(x, y, w, h, obj_conf, class_conf_1, ..., class_conf_80)。4.2 后处理从输出张量到检测框模型原始输出是一堆数字需要经过后处理才能变成人类可读的检测框。后处理主要包括三步解码边界框、置信度过滤、非极大值抑制。解码边界框的公式是x_center (sigmoid(tx) * 2 - 0.5 grid_x) * stride y_center (sigmoid(ty) * 2 - 0.5 grid_y) * stride width (sigmoid(tw) * 2) ** 2 * anchor_w height (sigmoid(th) * 2) ** 2 * anchor_h其中tx, ty, tw, th是模型输出的原始值grid_x, grid_y是网格坐标stride是特征图的下采样倍数anchor_w, anchor_h是预设的锚框尺寸。置信度过滤就是设定一个阈值比如0.25把obj_conf低于这个值的候选框全部丢掉。非极大值抑制则是把重叠度高的框合并只保留置信度最高的那个。def non_max_suppression(prediction, conf_thres0.25, iou_thres0.45): # 简化版NMS实现 output [] for pred in prediction: # 过滤低置信度 mask pred[:, 4] conf_thres pred pred[mask] if len(pred) 0: continue # 按置信度排序 pred pred[pred[:, 4].argsort(descendingTrue)] # NMS keep [] while len(pred) 0: keep.append(pred[0]) if len(pred) 1: break iou compute_iou(pred[0], pred[1:]) pred pred[1:][iou iou_thres] output.append(np.array(keep)) return output实际项目中我建议直接用YOLOv5官方仓库里的utils/general.py中的non_max_suppression函数那个实现经过充分测试支持批量处理和多种参数配置。4.3 检测结果可视化与保存拿到检测框之后用OpenCV画出来for det in detections: x1, y1, x2, y2, conf, cls_id det label f{class_names[int(cls_id)]} {conf:.2f} cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imwrite(result.jpg, frame)注意坐标要映射回原始图像尺寸。因为预处理时做了letterbox检测框坐标是在640x640空间下的需要反向变换回原始分辨率。反向变换的公式是x1 (x1 - dw) / r y1 (y1 - dh) / r x2 (x2 - dw) / r y2 (y2 - dh) / r其中dw, dh是letterbox时填充的宽度和高度r是缩放比例。这个映射关系一定要搞清楚否则画出来的框位置会偏。5. 常见问题与排查技巧实录5.1 摄像头打不开的几种原因摄像头打不开是最常见的问题表现是cap.isOpened()返回False。排查思路如下现象可能原因解决方法/dev/video*不存在驱动未加载或硬件未识别检查USB连接dmesg权限拒绝用户不在video组sudo usermod -aG video $USER后重新登录设备节点编号错误多个video节点用v4l2-ctl --list-devices确认打开后读帧失败摄像头被其他进程占用fuser /dev/video1查看占用进程图像花屏或绿屏格式不匹配设置FOURCC为MJPG我遇到过一次特别诡异的情况摄像头在/dev/video1上能打开但读出来的帧全是绿色的。后来发现是FOURCC设置的问题默认的YUYV格式在某个分辨率下驱动有bug改成MJPG就正常了。5.2 推理结果异常排查推理结果异常通常表现为检测框位置偏移、类别错误、置信度异常低。检测框位置偏移最常见的原因是letterbox的反向映射没做对。检查一下dw, dh和r的计算是否正确特别是当原始图像宽高比和640x640不一致时填充量计算容易出错。类别错误可能是类别标签映射错了。YOLOv5的COCO数据集有80个类别索引从0到79。如果你自己训练的模型类别数不一样要确保class_names列表和模型输出对应。置信度异常低可能是预处理的问题。检查一下输入数据的归一化是否正确uint8和float32有没有搞混。我有一次把float32的归一化数据喂给了期望uint8的RKNN模型结果所有检测框的置信度都低于0.1排查了半天才发现是数据类型的问题。5.3 性能优化的一点经验香橙派RK3588的NPU算力足够跑YOLOv5s实时推理但前提是预处理和后处理不能成为瓶颈。预处理的瓶颈通常在cv2.resize和cvtColor上。如果分辨率是1920x1080这两个操作在CPU上跑会占用不少时间。我的做法是在摄像头层面直接设置成640x480省掉resize的开销。如果摄像头不支持那就用cv2.INTER_NEAREST插值比默认的INTER_LINEAR快不少。后处理的瓶颈在NMS上。如果检测框数量很多NMS的循环会很耗时。可以用numpy向量化操作来加速或者用Cython写一个扩展。不过对于YOLOv5s在640x640输入下的25200个候选框numpy版本的NMS通常在几毫秒内完成问题不大。还有一个容易被忽略的点是内存拷贝。从摄像头读到帧之后如果经过多次copy()操作会累积不少开销。尽量用原地操作比如img img.transpose(2, 0, 1)之后直接传给推理接口不要额外np.ascontiguousarray()除非推理接口明确要求连续内存。注意RKNN模型在首次推理时会有一个预热过程耗时明显比后续推理长。如果你在测帧率记得把前几帧排除掉否则数据不准。5.4 摄像头热插拔与异常恢复在实际部署中摄像头可能会因为各种原因断开连接比如USB接触不良、供电不足等。如果脚本没有异常处理一旦摄像头断开整个程序就会崩溃。我的做法是在主循环里加一个重连机制def get_camera(cap, device_id): if cap is None or not cap.isOpened(): cap cv2.VideoCapture(device_id) if cap.isOpened(): cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(M, J, P, G)) return cap cap None while True: cap get_camera(cap, 1) if cap is None or not cap.isOpened(): time.sleep(1) continue ret, frame cap.read() if not ret: cap.release() cap None time.sleep(1) continue # 推理处理...这段代码的逻辑是如果摄像头没打开或者读帧失败就释放资源等一秒后重试。这样即使摄像头临时断开程序也能自动恢复不需要人工干预。6. 从单帧抓取到视频流推理的扩展思路单帧抓取跑通之后扩展到视频流推理其实很简单就是把抓帧和推理放到一个循环里。但这里有几个实际问题需要考虑。首先是帧率匹配。如果摄像头输出30帧但推理只能跑15帧那就会累积延迟。我的做法是丢帧处理每两帧取一帧做推理保证实时性。或者用多线程一个线程专门抓帧另一个线程专门推理中间用一个队列做缓冲。其次是显示问题。如果板子接了HDMI显示器可以用cv2.imshow直接显示。但如果是无头模式就需要把结果推流出去或者保存成视频文件。香橙派RK3588支持硬件编码可以用FFmpeg或者GStreamer把处理后的视频流推出去CPU占用比软件编码低很多。最后是模型切换。YOLOv5s跑通之后你可能会想换更大的模型比如YOLOv5m或者YOLOv5l。这时候要注意NPU的内存限制RK3588的NPU内存是共享的模型太大可能会初始化失败。我实测YOLOv5s在RK3588上跑得很流畅YOLOv5m也能跑但帧率会下降不少。我个人在实际操作中的体会是摄像头抓帧这个环节看似简单但细节特别多。不同型号的摄像头行为不一样同一个摄像头在不同分辨率下的表现也不一样。最稳妥的做法是先用v4l2-ctl把摄像头的所有支持格式列出来然后从中选一个最合适的而不是盲目设置参数。另外预处理和后处理的代码一定要和模型转换时的配置严格对应否则推理结果会莫名其妙地出错。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。