ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3588部署yolov5s:USB摄像头抓帧避坑指南

RK3588部署yolov5s:USB摄像头抓帧避坑指南 做完模型转换、试过几次推理脚本之后你会发现 RK3588 上跑 yolov5s 这件事真正卡人的往往不是模型本身而是最不起眼的摄像头。香橙派接 USB 摄像头并抓一帧验证听起来就是一条命令的事真到板子上操作的时候格式、设备节点、权限、供电哪一个都能让你折腾一晚上。这篇教程就是把“接摄像头 抓一帧”这个环节从头到尾走一遍所有命令、参数、坑点都给出来让你在香橙派 RK3588 yolov5s 的部署链条上先把图像入口打通。适合正在跟着教程做部署、或者打算在 RK3588 上做视觉应用的兄弟参考新手也一样能跟下来。1. 为什么先要把摄像头接起来而不是直接跑模型1.1 一条完整的 yolov5s 部署链路摄像头是第一个环节在 RK3588 上部署 yolov5s完整链路大概是图像采集 → 图像预处理 → NPU 推理 → 后处理 → 结果输出。很多人把精力全放在模型转换、rknn-toolkit2 配置这些环节上模型好不容易转出来了推理脚本一跑结果报“cant open camera by index”这时候才意识到摄像头这一环还没通。摄像头是整个链条的数据入口这个入口没打通后面所有环节都是在空转。哪怕你只是先用一张图片做测试最终落地到实际项目里也绕不开实时画面输入。抓一帧验证的意义就是用最小的成本确认板子能识别摄像头、驱动加载正常、设备节点存在、图像能完整地取出来。这一环确认了后面模型推理的调试才能专心处理模型本身的问题。我见过不少兄弟喜欢跳过这一步直接把摄像头接到板子上然后打开推理脚本结果问题一堆有的是 /dev/video0 不存在有的是权限不足打不开有的是抓出来的帧全是黑的。这些问题如果在“抓一帧”阶段就暴露出来定位起来非常快。等模型也转好了、推理代码也写好了再回头查摄像头问题排查范围会大很多心态也容易崩。1.2 为什么起步阶段选 USB 摄像头而不是 MIPIRK3588 本身支持 MIPI CSI 接口香橙派5 上也预留了 MIPI 摄像头/屏幕接口。但我在这个系列教程里起步阶段强烈建议先用 USB 摄像头原因很简单USB 摄像头走的是 UVC 协议Linux 内核自带 uvcvideo 驱动插上就能枚举出 /dev/video0几乎不需要额外适配。MIPI 摄像头则是另一回事。它需要设备树dts里正确配置 sensor 型号、I2C 地址、数据通道、时序参数而且香橙派5 上能直接兼容的 MIPI 摄像头模组并不多官方支持列表之外的 sensor 往往要自己调驱动。社区里关于 MIPI 摄像头的案例也没有 USB 摄像头那么多踩了坑想找个参考都费劲。如果你后面有低延迟、多路 camera 的需求MIPI 确实有优势。但那是第二个阶段的事。先把 yolov5s 整个流程跑通用 USB 摄像头是成本最低、成功率最高的路径。等模型、推理、应用层都稳定了再回头研究 MIPI心态完全不一样。起步阶段不要给自己加难度这是我在多个板子上折腾出来的经验。1.3 摄像头选型与格式参数背后的门道USB 摄像头选型上不用太纠结品牌几十块钱的普通 1080p UVC 摄像头就能满足 yolov5s 开发验证。我这边用的就是一个常见的免驱摄像头支持 YUYV 和 MJPEG 两种格式输出。这里有个关键点很多摄像头标称 1080p实际在 YUYV 格式下只能跑 640x480要到 1920x1080 就必须切到 MJPEG 模式。为什么因为 YUYV 是裸流数据一帧 1080p 的数据量大约 1920×1080×2 ≈ 4MBUSB 2.0 的带宽上限 480Mbps 算下来也就 60MB/s扛不住 30fps 的 1080p 裸流。MJPEG 是压缩后的数据同样分辨率单帧可能只有几百 KB带宽压力小得多。这个格式问题在后面接 yolov5s 推理时也要注意OpenCV 的 VideoCapture 默认会尝试用摄像头能力最强的格式读流有时候它自动选了 MJPEG有时候又退回 YUYV解码逻辑不一样首帧速度和 CPU 占用都有差别。虽然 RK3588 的 CPU 解个 1080p MJPEG 很轻松但提前搞清楚摄像头的格式支持列表能省掉很多莫名的故障。至于分辨率yolov5s 默认输入是 640x640所以摄像头输出分辨率不用追求最高。我通常把摄像头设为 1280x720 或 640x480后面预处理再 resize 到 640x640信息量完全够用。分辨率越高单帧数据处理量越大对后续推流或者实时性要求高的场景反而是一种负担。2. 硬件准备与系统状态检查别急着插电2.1 硬件清单与供电注意事项先清点一下硬件避免后面排错时变量太多香橙派5 开发板一块本教程以 Ubuntu 20.04 系统为例官方推荐的电源适配器功率一定要给够USB 摄像头一个UVC 免驱的即可USB 延长线尽量短线材质量要好TF 卡或 NVMe 硬盘提前烧写好 Ubuntu 20.04 系统镜像供电是我在 RK3588 板子上踩过最多的坑。香橙派5 对供电要求不低如果用的是功率不足的手机充电器表面上看系统能起来一旦接上 USB 摄像头这类外设很容易出现设备随机断开、枚举失败甚至整板重启的问题。我这边用的是官方推荐的电源档位USB 摄像头直接插板子的 USB 口没有额外供电也一直很稳。如果你手头只有 5V/2A 的充电器建议先给板子换电源再折腾摄像头否则你会陷入“刚才还好的怎么又没了”的无限循环。另外USB 摄像头的线材也会影响稳定性。有些劣质延长线内部屏蔽很差插上之后系统能识别到设备但抓帧时图像会随机花屏或者直接请求超时。调试阶段尽量把摄像头插在板子原生 USB 口上不要用延长线尽量减少变量。2.2 确认系统已经认出摄像头lsusb 与 dmesg开机进入系统之后把摄像头插到板子的 USB 口。先别急着一帧一帧抓图先用系统自带的工具确认硬件被正确枚举出来了。终端执行lsusb正常情况你会看到一行类似下面的记录Bus 002 Device 002: ID 0c45:6366 Microdia USB Camera Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub0c45:6366这个就是摄像头的 VID:PID每家厂商不一样但只要是 UVC 摄像头lsusb都会显示出来。如果你插了摄像头之后lsusb 里根本没多出设备来直接怀疑物理链路换一个 USB 口、换一根线或者把摄像头插到电脑上确认它本身是好的。接着看一下内核日志确认 uvcvideo 驱动有没有被正确加载dmesg | tail -20如果看到类似“uvcvideo: Found UVC 1.00 device USB Camera”的日志说明内核已经识别到设备并完成了驱动绑定。如果完全没有 uvcvideo 相关的输出说明摄像头大概率不兼容或者硬件链路有问题。验证到这里硬件这一层就算确认完毕。2.3 设备节点与权限检查/dev/video0 能打开才算数硬件枚举正常之后看看系统有没有创建视频设备节点ls -l /dev/video*正常输出会有 video0有时候还会多一个 video1。这里提醒一句video1 通常是 metadata 节点或者部分驱动的第二个通道不是我们要用的主视频流别拿它做抓帧测试。真正能出图像的是 video0。然后看一下节点权限crw-rw---- 1 root video 81, 0 Jan 1 00:00 /dev/video0默认属主是 root组是 video。如果你当前登录用户不在 video 组里直接打开 /dev/video0 会被权限拒绝。解决办法是把用户加进 video 组然后重新登录sudo usermod -aG video $USER嫌麻烦的话临时测试也可以直接加 sudo但后面跑推理脚本时如果不想每次都用 sudo还是老老实实把用户加到组里。这一步做完再执行一次ls -l /dev/video*确认节点还在就进入下一步。3. 三种抓帧方法实测总有一种适合你3.1 最快的方式fswebcam 一条命令出图如果你想验证摄像头能不能出图不想折腾任何代码fswebcam 是首选。它是一个命令行小工具装完就能用sudo apt update sudo apt install fswebcam抓一帧的命令很简单fswebcam -d /dev/video0 -r 1280x720 --no-banner frame.jpg参数说明一下-d指定设备节点-r指定分辨率--no-banner是去掉它在图片底部加的时间戳字幕。如果你不指定--no-banner抓出来的图右下角会带上一行字文档和演示时不太好看。执行成功后会输出设备信息、格式、分辨率等一列信息。然后检查文件ls -lh frame.jpg file frame.jpg只要文件大小不为 0而且file认出来是 JPEG 图像数据这一帧基本就算抓成了。把这张图拷回电脑或者用板子上的看图程序打开确认画面内容正常摄像头这件事就算完成了八成。fswebcam 的缺点也很明显它对格式的控制能力很弱你没法指定用 MJPEG 还是 YUYV 取流它自己会挑。如果它默认选了一个很低的 YUYV 分辨率抓出来的图可能就 640x480。作为第一条路径验证足够但要做更细的诊断得用 v4l2-ctl。3.2 最硬核的方式v4l2-ctl 手动控制格式抓帧v4l2-ctl 是 v4l-utils 包里的工具专门用来和 V4L2 设备交互能查看设备支持的格式也能手动指定格式抓帧。安装sudo apt install v4l-utils先查看摄像头到底支持哪些格式和分辨率v4l2-ctl --device/dev/video0 --list-formats-ext输出会列出每个像素格式以及对应分辨率。通常你会看到 YUYV 和 MJPEG 两行MJPEG 下可能才支持 1920x1080。这时候就能验证我之前说的格式与分辨率关系。手动抓一帧 MJPEG 格式的图v4l2-ctl --device/dev/video0 --set-fmt-videowidth1920,height1080,pixelformatMJPEG --stream-mmap --stream-count1 --stream-toframe.jpg这条命令的写法在 v4l2-ctl 里很标准--set-fmt-video设置采集格式--stream-mmap用 mmap 模式采集--stream-count1只采一帧--stream-to指定输出文件名。有一点一定要记住如果摄像头当前格式是 YUYV你直接把它存成 .jpg 文件文件是打不开的因为 YUYV 是裸的未压缩数据不是 JPEG 编码。只有 MJPEG 格式抓下来的数据才是真正的 JPEG 文件。这也是很多人抓帧失败的原因之一文件名写着 jpg里子却是 YUYV打开必然报错。如果你确实想要 YUYV 的裸数据那抓下来之后需要用工具转成图片或者直接用 Python 读取后转换成 OpenCV 的 BGR 帧。这就是为什么要推荐第三种方法代码里做这件事最方便。3.3 与 yolov5s 衔接最顺的方式Python OpenCV后面跑 yolov5s 推理几乎必然要用 Python OpenCV 来读摄像头。所以这一节可以当“半个教程正文”来看因为这段代码框架后面还会被复用。先装依赖pip3 install opencv-python如果 pip 环境有冲突也可以用系统包sudo apt install python3-opencv抓一帧并保存的脚本import cv2 cap cv2.VideoCapture(0, cv2.CAP_V4L2) if not cap.isOpened(): print(Failed to open camera) exit(1) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*MJPG)) # 摄像头刚打开时首帧可能未稳定先读几帧丢弃 for _ in range(10): ret, frame cap.read() ret, frame cap.read() if ret: cv2.imwrite(frame.jpg, frame) print(saved frame.jpg, shape:, frame.shape) else: print(failed to read frame) cap.release()这里几个点值得展开。指定cv2.CAP_V4L2后端是为了避免 OpenCV 自动选择后端时出现一些奇怪的兼容问题在 RK3588 上显式指定 V4L2 是更稳的选择。cvtColor和格式转换这里不用写因为 OpenCV 的 VideoCapture 读到的是 BGR 帧imwrite会直接保存成正常的 JPG。先读几帧丢弃再保存是我自己实验出来最有效的“黑帧免疫法”。很多 USB 摄像头上电瞬间还没完成曝光和增益收敛第一帧往往偏暗甚至全黑直接保存就会得到一张没有意义的黑图。连续读十几帧再取用基本上能拿到正常画面。写完之后运行python3 capture_frame.py同样用ls -lh frame.jpg确认文件大小正常。到这里三种方式你随便选一种摄像头抓帧这件事就算落地了。4. 抓帧遇到的那些坑我一个一个说4.1 问题速查表现象、原因、解法对照我在不同板子上接 USB 摄像头归纳出下面几个高频问题。建议直接收藏碰上了对号入座。现象可能原因解决办法/dev/video0 不存在设备未枚举成功或驱动没加载换 USB 口/线材执行 lsusb 和 dmesg 确认打开设备时报 Permission denied用户不在 video 组sudo usermod -aG video $USER重新登录设置分辨率失败摄像头在该格式下不支持该分辨率用 v4l2-ctl --list-formats-ext 查看真实支持列表抓出的 jpg 打不开实际是 YUYV 裸流数据存成了 jpg改成 MJPEG 格式抓取或用 OpenCV 转换图像全黑或全绿首帧未稳定或格式不匹配先读多帧再保存检查 FOURCC 设置运行时设备节点变了热插拔后枚举顺序变化使用 /dev/v4l/by-id 下的符号链接或重启板子摄像头间歇性消失供电不足换足功率电源或使用带供电的 USB HUB这个表里最容易被忽视的是最后一项。RK3588 板子的功耗波动比一般开发板大推理时 CPU/NPU 负载一上来瞬时电流需求会拉高如果电源余量不足最先出问题的往往是 USB 摄像头。我遇到过摄像头在空闲时一切正常一跑模型推理就掉线换了电源之后再没出现过。排查这种问题别在软件里浪费太多时间先看供电。4.2 排查问题的基本套路从硬件到应用逐层剥遇到摄像头问题我建议按下面的顺序一层层排查不要一上来就改代码。先看硬件枚举再看驱动加载然后确认设备节点最后才回到应用层调试。具体来说执行顺序是lsusb # 第一层硬件是否被 USB 总线识别 dmesg | grep uvcvideo # 第二层驱动是否成功绑定设备 ls -l /dev/video0 # 第三层系统是否创建了字符设备 v4l2-ctl --list-formats-ext # 第四层设备是否响应 V4L2 控制命令 python3 capture_frame.py # 第五层应用层抓帧是否正常每一步通过之后才进入下一步哪一步卡住了就去解决哪一步的问题。比如 lsusb 里就没有设备那 dmesg 和 /dev/video0 根本没意义直接去查物理链路和供电。这套思路帮我定位过至少十来个摄像头问题几乎都是前面几层的问题应用层其实很少出故障。4.3 三个容易忽略的细节第一热插拔之后设备节点会变。板子上接多个 USB 设备时拔插一次摄像头它可能从 video0 变成 video0/video1 之外的编号脚本里写死 /dev/video0 就会踩坑。稳定做法是用 /dev/v4l/by-id/ 下的符号链接。先执行ls /dev/v4l/by-id/找到对应设备再用这个链接路径。不过这个符号链接也存在拔插后变化的可能最保险的方案是脚本里做一个扫描遍历所有 video 节点找第一个能打开的。第二USB 摄像头和存储设备抢带宽。在 RK3588 上用 USB 启动盘或者外接 USB 硬盘跑 yolov5s 时摄像头取流和大容量存储的读写会争抢 USB 控制器带宽。你可能会看到抓帧突然变慢或者掉几帧。建议系统跑在 NVMe 或者 TF 卡上把 USB 带宽尽量留给摄像头。如果必须用 USB 硬盘至少让它单独走 USB 3.0 口摄像头走 USB 2.0 口分开。第三摄像头的“花屏”不一定是摄像头坏了。花屏或者图像有大片色块很多时候是电磁干扰或者 USB 线质量太差。我之前用一根很长的延长线抓 FIF 图的时候屏幕上全是彩虹纹换了根短线上来就好了。插拔时要注意 USB 口有没有接触不良这种问题在开发板上比想象中更常见。5. 这一帧能做什么从图像到 yolov5s 推理的衔接5.1 抓帧验证的真正作用有人会觉得抓一帧图出来有什么可说的不就是一个 read 函数的事吗。但结合实际部署流程这一步真正的作用是把你后续所有调试的“标准输入”固定下来。当你把这一帧存成 frame.jpg 之后模型转换、精度验证、推理脚本调试都可以先基于这张静态图进行不需要每次调试都插着摄像头、开着实时流。这样做的好处是排查问题更快模型这边出了问题你不会分心去怀疑摄像头。相当于把“摄像头接入”和“模型推理”两个问题彻底解耦一次只解决一个问题。等到模型在静态图上跑通了再把输入源从图片文件切换回 VideoCapture 实时帧只需要改几行代码的事。这个环节我在实际工作中验证过很多次静态图调稳定实时流基本不会出大问题反过来如果跳过静态图直接上实时流问题会混在一起非常难定位。5.2 一帧图像进入模型之前的标准化处理摄像头抓到的一帧在进入 yolov5s 模型之前还需要一套标准化的处理流程。yolov5s 预训练模型的输入尺寸是 640x640通道顺序是 RGB像素值范围需要归一化到 0~1 左右。实际做推理时读帧得到 BGR 图像 → 缩放/letterbox 到 640x640 → BGR 转 RGB → 像素值归一化 → 排布成模型需要的维度1x3x640x640 → 送入 RK3588 NPU这里最容易忽略的是 letterbox 操作。如果有人直接把图像拉伸成正方形再送进模型检测框的坐标映射会出问题因为原图的长宽比被破坏了。正确做法是保持长宽比居中填充推理出来的框坐标再按比例映射回原图。这些细节在跑通 yolov5s 本体时都会碰到但你现在应该能理解摄像头输出的每一帧都只是这个预处理流程的“初始原材料”。5.3 给后续教程留的口子代码怎么组织我建议在项目里单独建一个摄像头模块而不是把 VideoCapture 逻辑散在推理脚本里。目录结构大概是这样rk3588_yolov5s/ ├── capture_frame.py # 抓帧测试脚本 ├── camera.py # 摄像头封装类 ├── models/ # 转换后的 RKNN 模型 ├── inference.py # yolov5s 推理脚本 └── frame.jpg # 验证用静态图camera.py 里面封装一个类最简版本可以只提供get_frame()和release()两个方法。后面推理脚本只需要从摄像头类拿帧不需要关心设备节点、格式设置这些底层细节。这个设计在后面接 RkNN 推理、做简单目标检测 demo 时会非常顺手。class USBCamera: def __init__(self, index0, width1280, height720): self.cap cv2.VideoCapture(index, cv2.CAP_V4L2) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) def get_frame(self): ret, frame self.cap.read() return frame if ret else None def release(self): self.cap.release()这套代码你今天写好后面用 yolov5s 做实时检测时只需要import camera然后调用get_frame()省掉所有重复劳动。按我的习惯任何需要长期维护的视觉项目摄像头读取一定会独立成一个模块不为别的就为后面少踩几次“摄像头打不开”的坑。我在实际板上操作时还有一个体会抓帧验证前先用 v4l2-ctl 把格式和分辨率确认一遍再写脚本这个习惯能帮你省掉至少一半的排查时间。很多人一上来就写 Python 脚本结果格式、设备节点全是未知状态报错了才回头查。先花两分钟把硬件状态摸清再动代码整个流程会顺畅很多。这篇内容到这里摄像头那边的坑你基本可以绕开了接下来就可以放心去磨 yolov5s 模型转换和 NPU 推理了。
返回列表