1. 从模型跑通到摄像头接入中间差了什么很多人跟着教程把 YOLOv5 在香橙派 RK3588 上跑通之后会卡在同一个地方模型能对着一张固定的测试图输出检测框但一旦想换成实时抓一帧摄像头画面再推理就发现事情没那么简单。我自己第一次做这一步的时候以为无非是把图片路径换成摄像头设备号结果折腾了大半天才把链路打通。问题出在几个容易被忽略的环节摄像头在 Linux 下的设备节点到底叫什么、OpenCV 读到的帧格式和模型输入格式之间的差异、RK3588 上 NPU 推理和 CPU 图像采集之间的数据流转顺序以及摄像头权限和 V4L2 后端的选择。这篇内容就是把这个从静态图片到摄像头实时抓帧推理的完整过程拆开讲清楚。核心关键词是香橙派、RK3588、YOLOv5、OpenCV、摄像头适合已经完成了前面模型部署和单图推理、现在想接入摄像头做实时检测的开发者。我会从摄像头识别与设备节点确认讲起到 OpenCV 抓帧、格式转换、送入 NPU 推理、结果绘制每一步都说明为什么这么做以及我实际踩过的坑。读完之后你应该能独立完成摄像头抓一帧并推理这个最小闭环并且知道后续扩展成连续视频流该往哪个方向走。需要提前说明的是本文聚焦的是抓一帧并推理这个最小可用单元不涉及多路摄像头并发、推流编码、RTSP 网络摄像头接入这些进阶话题那些留到后面单独展开。先把单帧链路走通后面加循环、加多线程、加编码都是在这个基础上叠加。2. 摄像头在 RK3588 上到底是怎么被系统认出来的2.1 USB 摄像头和 MIPI 摄像头的识别路径完全不同香橙派 RK3588 上接摄像头有两条路USB 摄像头和 MIPI CSI 摄像头。这两者在系统里的表现差别很大很多人卡住就是因为没搞清楚自己手上的是哪种。USB 摄像头走的是 UVC 协议插上去之后内核会自动加载uvcvideo驱动在/dev/下生成video0、video1这样的设备节点。你可以用一条命令确认ls -l /dev/video* v4l2-ctl --list-devicesv4l2-ctl --list-devices这条命令特别有用它会把每个视频设备对应的物理设备名列出来。比如输出里会显示USB Camera: USB Camera (usb-fc800000.usb-1)对应/dev/video0这样你就不会把设备号搞混。我遇到过板子上同时插了 USB 摄像头和 HDMI 采集卡的情况/dev/video0和/dev/video1到底哪个是哪个全靠这条命令确认。MIPI CSI 摄像头就不一样了。它不走 USB 总线而是通过 MIPI CSI 接口直接连到 RK3588 的 ISP 或 VICAP 模块。这类摄像头需要设备树里正确配置了对应的 sensor 节点内核加载了对应的 sensor 驱动比如 ov5647、imx219 这些才会在/dev/下出现视频节点。如果你用的是树莓派那款 OV5647 摄像头模块接到香橙派上大概率需要自己改设备树因为默认的 DTS 里不一定使能了这个 sensor。这一点和树莓派上插上就能用的体验差别很大也是很多人从树莓派转到香橙派后第一个不适应的地方。提示如果你执行ls /dev/video*什么都没有先别怀疑代码先确认硬件连接和驱动。USB 摄像头换一个 USB 口试试MIPI 摄像头检查排线方向和设备树配置。2.2 用 v4l2-ctl 确认摄像头支持的格式和分辨率确认了设备节点之后下一步是搞清楚这个摄像头支持哪些像素格式和分辨率。这一步非常关键因为 OpenCV 抓帧时的格式协商如果和摄像头实际能力不匹配要么抓不到帧要么抓到的是花屏。v4l2-ctl -d /dev/video0 --list-formats-ext这条命令会列出摄像头支持的所有格式常见的包括YUYV、MJPG、NV12等。USB 摄像头通常同时支持 YUYV 和 MJPGMJPG 是压缩格式同样分辨率下占用的 USB 带宽更小所以高分辨率下往往只能用 MJPG。MIPI 摄像头则常见 NV12 或 RAW 格式。这里有个实际经验在 RK3588 上通过 OpenCV 的 V4L2 后端抓帧时如果设置的分辨率是 1920x1080 但摄像头在 YUYV 格式下只支持到 640x480OpenCV 不会报错而是默默给你一个 640x480 的帧或者直接返回空帧。所以先查清楚能力再写代码能省掉大量调试时间。格式类型典型来源特点OpenCV 转换注意YUYVUSB 摄像头未压缩带宽占用大需转 BGRMJPGUSB 摄像头压缩高分辨率首选OpenCV 自动解码NV12MIPI 摄像头YUV420 半平面需手动转 BGRRAWMIPI 摄像头原始 Bayer需 ISP 处理2.3 权限问题为什么普通用户读不了 video 设备还有一个特别容易被忽略的点/dev/video*默认属于video组普通用户不在这个组里就没有读写权限。你可能会遇到Permission denied或者 OpenCV 返回空帧但没有任何报错的情况。解决办法有两个一是把当前用户加入 video 组sudo usermod -aG video $USER改完之后需要重新登录才生效。二是临时用sudo跑但这样会带来环境变量和 Python 包路径的问题不推荐长期这么干。我自己的习惯是直接把用户加进 video 组一劳永逸。3. OpenCV 抓帧这一步参数设置比代码本身更重要3.1 VideoCapture 的构造方式和后端选择OpenCV 在 Linux 上抓摄像头帧核心就是cv2.VideoCapture。构造的时候有两种写法import cv2 # 写法一直接传设备号 cap cv2.VideoCapture(0) # 写法二指定 V4L2 后端 cap cv2.VideoCapture(0, cv2.CAP_V4L2)我强烈建议用第二种显式指定cv2.CAP_V4L2。原因是 OpenCV 在 Linux 上可能编译了多个后端V4L2、GStreamer、FFmpeg不指定的话它可能选到 GStreamer 后端而 GStreamer 后端在某些香橙派的系统镜像上行为不稳定表现为打开设备很慢或者抓帧超时。显式指定 V4L2 后端能避开这个问题。设备号这里也要注意cv2.VideoCapture(0)里的 0 对应的是/dev/video0如果你确认摄像头在/dev/video2那就要传 2。别想当然认为插上去就是 video0。3.2 分辨率、格式、缓冲区这三个参数怎么设构造完 VideoCapture 之后在正式抓帧之前要设置几个关键参数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)) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)分辨率设成 640x480 是有讲究的。YOLOv5s 的默认输入是 640x640你抓 640x480 的帧之后做 letterbox 填充到 640x640缩放比例接近 1图像信息损失最小。如果你抓 1920x1080 再缩到 640虽然也能跑但缩放本身消耗 CPU而且小目标在缩放后可能变得更难检测。所以在摄像头端就抓接近模型输入尺寸的帧是性价比最高的做法。CAP_PROP_FOURCC设成 MJPG 是为了让 USB 摄像头用压缩格式传输这样在 640x480 下帧率能跑满 30fps。如果不设默认走 YUYV带宽占用大帧率可能掉到 15fps 以下。当然如果你的摄像头不支持 MJPG这行设置会被忽略不影响后续流程。CAP_PROP_BUFFERSIZE设成 1 是很多人不知道的一个技巧。V4L2 默认会缓冲多帧你调用cap.read()拿到的可能是几帧之前的旧画面。对于抓一帧就推理的场景缓冲多帧没有意义反而增加延迟。设成 1 让缓冲区只保留最新一帧抓到的就是当前画面。注意CAP_PROP_BUFFERSIZE不是所有后端都支持V4L2 后端支持但设置后要用cap.get确认一下是否真的生效。有些摄像头驱动会忽略这个设置。3.3 抓帧失败时的排查顺序cap.read()返回的第二个值是布尔量表示是否成功抓到帧。如果一直是 False按这个顺序排查设备节点是否存在且权限正确回到第 2 节确认分辨率设置是否超出摄像头能力用 v4l2-ctl 查格式设置是否被支持MJPG 不支持就换 YUYV是否有其他进程占用了摄像头fuser /dev/video0查一下换一个 USB 口或者换一根线我遇到过一次抓帧一直失败最后发现是另一个终端里有个测试脚本没退干净一直占着摄像头。这种问题用fuser一查就出来了。4. 从 OpenCV 的 BGR 帧到 NPU 能吃的输入中间要过几道手4.1 RK3588 上 YOLOv5 推理的输入格式要求RK3588 的 NPU 通过 RKNN 运行时来调用。YOLOv5s 转成 RKNN 模型之后输入通常是一个1x3x640x640的浮点或量化张量。这里有几个关键点第一通道顺序。OpenCV 抓到的是 BGR而模型训练时用的是 RGB所以要做cvtColor转换。第二布局。OpenCV 是 HWC高、宽、通道模型要的是 CHW通道、高、宽需要 transpose。第三归一化。YOLOv5 的输入是 0 到 1 之间的浮点所以要除以 255。第四尺寸。640x480 要 letterbox 到 640x640保持宽高比空白处填灰色114,114,114。这四步任何一步做错模型都不会报错但检测结果会完全不对——要么什么都检测不到要么框的位置全偏。我自己就犯过忘记除以 255 的错误模型输出一堆乱七八糟的框排查了半天才发现是归一化漏了。4.2 letterbox 的实现和为什么不能直接 resize直接cv2.resize到 640x640 会改变宽高比把 640x480 的帧压扁成正方形物体形状变形检测精度会下降。letterbox 的做法是保持宽高比缩放然后上下或左右填充灰边。import cv2 import numpy as np def letterbox(img, new_shape(640, 640), color(114, 114, 114)): shape img.shape[:2] # 当前 h, w 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 new_shape[1] - new_unpad[0] dh new_shape[0] - new_unpad[1] dw / 2 dh / 2 img_resized 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_padded cv2.copyMakeBorder(img_resized, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img_padded, r, (left, top)这个函数返回三个值处理后的图像、缩放比例、填充偏移。缩放比例和偏移在后面把检测框映射回原图坐标时要用到不能丢。4.3 数据从 NumPy 到 RKNN 输入张量的转换RKNN 的 Python API 接受的是 NumPy 数组。完整的预处理链路是这样的img_letterboxed, ratio, (dw, dh) letterbox(frame) img_rgb cv2.cvtColor(img_letterboxed, cv2.COLOR_BGR2RGB) img_input img_rgb.astype(np.float32) / 255.0 img_input np.transpose(img_input, (2, 0, 1)) # HWC - CHW img_input np.expand_dims(img_input, axis0) # 加 batch 维度这里每一步都有讲究。astype(np.float32)要在除法之前做否则整数除法会丢精度。transpose的顺序是(2,0,1)把通道从最后一位挪到第一位。expand_dims加 batch 维度是因为模型输入是 4 维的。如果你的模型是量化模型int8那预处理还要多一步量化把浮点转成 int8。这一步通常在 RKNN 工具链转换模型时就配置好了推理时用rknn.inference传入浮点数组运行时会自动处理量化。具体用哪种方式取决于你转换模型时的配置建议先用浮点输入跑通再考虑量化优化。5. 推理结果怎么画回原图坐标映射是重头戏5.1 从模型输出到检测框RKNN 推理出来的原始输出是三个尺度的特征图需要经过解码才能变成检测框。YOLOv5 的解码包括把预测的偏移量转换成中心点坐标和宽高、应用 anchor、做 sigmoid 激活、然后做非极大值抑制NMS去掉重叠框。这部分代码比较长核心逻辑是# 伪代码示意实际解码依赖你的模型输出格式 boxes, scores, class_ids decode_yolov5_output(outputs, conf_threshold0.25, iou_threshold0.45)置信度阈值 0.25 和 NMS 的 IoU 阈值 0.45 是 YOLOv5 的默认值实测在大多数场景下够用。如果发现漏检多把置信度阈值降到 0.1 试试如果发现同一个物体出了好几个框把 IoU 阈值降到 0.3。5.2 把 640x640 坐标系下的框映射回原始帧模型输出的框坐标是在 640x640 的 letterbox 图像上的。要画回原始的 640x480 帧上需要做逆变换def scale_boxes(boxes, ratio, dw, dh, orig_shape): boxes[:, [0, 2]] - dw boxes[:, [1, 3]] - dh boxes[:, :4] / ratio boxes[:, [0, 2]] boxes[:, [0, 2]].clip(0, orig_shape[1]) boxes[:, [1, 3]] boxes[:, [1, 3]].clip(0, orig_shape[0]) return boxes这里的ratio和(dw, dh)就是 letterbox 那一步返回的值。先减去填充偏移再除以缩放比例就回到了原图坐标。最后 clip 一下防止框超出图像边界。这一步特别容易出错的地方是dw和dh的顺序。letterbox 里返回的是(left, top)对应的是 x 方向的偏移和 y 方向的偏移。映射的时候 x 坐标减 lefty 坐标减 top别搞反了。我见过有人把这两个搞反结果框整体偏移排查了很久。5.3 绘制结果和保存映射回原图坐标之后用 OpenCV 的rectangle和putText把框和类别标签画上去for box, score, cls_id in zip(boxes, scores, class_ids): x1, y1, x2, y2 box.astype(int) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) label f{class_names[cls_id]} {score:.2f} cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imwrite(result.jpg, frame)对于抓一帧并推理的场景把结果存成图片就够了。如果你想在屏幕上直接看可以用cv2.imshow但香橙派如果跑的是不带桌面的系统镜像imshow会报错这时候存图片是唯一选择。提示cv2.imwrite保存中文路径可能出问题建议用英文路径。另外保存的图片格式用 jpg 就行png 文件更大但无损对于调试结果来说 jpg 足够。6. 实测中遇到的几个典型问题和解决思路6.1 抓到的帧是绿色的或者花屏这个问题几乎每个人都遇到过。原因通常是格式协商失败。OpenCV 请求 MJPG 格式但摄像头实际输出的是 YUYVOpenCV 按 MJPG 解码就花了。解决办法是先用v4l2-ctl确认摄像头默认输出格式然后在代码里设置成匹配的格式。如果摄像头默认就是 YUYV那就不要设 MJPG让 OpenCV 用默认的转换逻辑处理。另一个可能的原因是 MIPI 摄像头的 NV12 格式没有被正确处理。OpenCV 的 V4L2 后端对 NV12 的支持取决于编译时的配置有些版本不能自动转换。这种情况需要手动把 NV12 转成 BGR或者用 GStreamer 管道来抓帧。6.2 推理速度比预期慢很多在 RK3588 上YOLOv5s 用 NPU 推理一帧 640x640 的图正常应该在 30 到 50 毫秒左右。如果你测出来要几百毫秒检查这几个点第一模型是不是真的跑在 NPU 上。RKNN 初始化的时候会打印日志确认用的是 NPU 而不是 CPU 回退。第二预处理是不是在 CPU 上做的。letterbox、cvtColor、transpose 这些操作都在 CPU 上如果图像分辨率很大这部分耗时会超过推理本身。第三有没有开启 RKNN 的性能模式。rknn.init_runtime的时候可以指定 core maskRK3588 有多个 NPU 核心合理分配能提升吞吐。rknn.init_runtime(targetrk3588, core_maskrknn.NPU_CORE_0_1_2)6.3 检测框位置整体偏移如果框的位置系统性地偏了大概率是 letterbox 的偏移量没算对。检查 letterbox 函数返回的(dw, dh)和映射时用的值是否一致。另一个可能是模型训练时的预处理和推理时的预处理不一致比如训练时用了 RGB 但推理时忘了转或者训练时的归一化方式不同。这种问题只能通过对比训练配置和推理配置来排查。6.4 摄像头打开很慢或者第一次抓帧超时USB 摄像头在 Linux 上打开时有一个协商过程通常几百毫秒。如果超过几秒可能是驱动或者 USB 带宽的问题。试试换一个 USB 3.0 口或者降低分辨率。另外OpenCV 的VideoCapture构造之后不要立刻read先set参数再read给驱动一点时间完成协商。如果还是慢可以在构造之后加一个time.sleep(0.5)等一下。7. 从单帧到连续推理下一步该往哪走单帧链路跑通之后把它扩展成连续视频推理其实就是在外面套一个while循环cap cv2.VideoCapture(0, cv2.CAP_V4L2) # ... 设置参数 ... while True: ret, frame cap.read() if not ret: continue # ... 预处理、推理、后处理、绘制 ... cv2.imshow(result, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()但这里有几个性能上的坑。第一cap.read()是阻塞的如果推理耗时 50 毫秒那帧率就被限制在 20fps 以下。解决办法是把抓帧和推理放到不同线程抓帧线程只管往队列里放帧推理线程从队列取帧处理。第二cv2.imshow在无桌面环境下不可用需要换成保存视频或者推流。第三连续推理时 NPU 的发热和功耗会上升香橙派需要做好散热否则会降频。我自己的做法是先用单帧把整条链路调通确认检测结果正确然后再加循环和多线程。这样出问题的时候容易定位是链路问题还是并发问题。如果一上来就写多线程版本调试难度会大很多。另外如果你后面想接入 RTSP 网络摄像头OpenCV 的VideoCapture也支持传 RTSP 地址但延迟会比 USB 摄像头大而且需要处理断流重连。这些内容等单帧和本地连续推理都稳定了再考虑。
企业数字化 ERP 产品动态
相关推荐
Laravel Lang 本地化代码(Locale)对照指南:128 个语言目录与翻译规则的完整映射 后端 【免费下载链接】lang List of 128 languages for Laravel Framework, Laravel Jetstream, Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova and Laravel UI. 项目地址: https://gitcode.com/gh_mirrors/la/lang 点击查看 免费下载 本指南以… · 2026/9/27 10:46:18
STM32 SBUS解析:DMA+IDLE中断+状态机工业级实现方案 1. 项目概述:为什么 SBUS 解析必须用 DMA IDLE 状态机,而不是普通中断?SBUS 是 Futaba、FrSky 等主流航模遥控器广泛采用的串行通信协议,它不是简单的 UART 数据流,而是一套有严格时序、固定帧结构、高实时性要求的工… · 2026/9/27 10:46:18
JSON:API 规范仓库实战指南:媒体类型、文档结构与本地构建 后端API设计 【免费下载链接】json-api A specification for building JSON APIs 项目地址: https://gitcode.com/gh_mirrors/js/json-api 点击查看 免费下载 本文以 JSON:API 官方规范仓库(README.md)为入口,系统梳理 applicati… · 2026/9/27 10:46:18
软件开发成本归零倒计时:用 TaoToken 统一 Key 打通 AI 工具链的配置实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 11:40:20
车上seo是什么意思从零搭建避坑指南 车上seo是什么意思从零搭建避坑指南 模板网站太丑且功能僵化,根本撑不起业务增长,这是很多老板找建站公司时的第一反应。想从零搭建一个既美观又利于搜索引擎收录的站点,先得搞清楚那些玄乎的名词,比如最近群里老问的【车上seo是什么意思】。别笑,… · 2026/9/27 11:40:20
OpenStation + VSCode:本地大模型接入 Continue 的配置实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 11:40:20
3招搞定成都建设网站高级工程师查询难题 3招搞定成都建设网站高级工程师查询难题 域名解析指向错误,服务器防火墙策略没配好,这是很多站长深夜崩溃的根源。你明明买好了服务器,域名也备案成功了,但一访问全是乱码或者直接 502 Bad… · 2026/9/27 11:40:14
AcEdJig 随笔:用 TaoToken 统一 Key 打通 AI 辅助绘图配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 11:40:14
2026最新电子商城平台选型避坑指南:搞定备案与架构 2026最新电子商城平台选型避坑指南:搞定备案与架构 备案材料填了五遍被打回?别急,这真是大多数老板做电子商城平台时最头疼的坎。很多同行以为商城上线就是写代码,其实2026年最新的技术栈里,合规性才是第一道门槛,尤其是涉及资金交易的系统,I… · 2026/9/27 11:40:02
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01