树莓派4 64位系统无法稳定同时访问两个USB摄像头问题咨询
树莓派64位系统双USB摄像头多进程稳定调用方案
问题背景
- 运行环境:64位版本树莓派4操作系统
- 开发场景:实现目标检测、人脸识别两个功能模块,分别调用索引为0、2的USB摄像头,采用
multiprocessing多进程模式并行运行 - 故障表现:双摄像头无法稳定同步工作,频繁随机出现单摄像头正常运行、另一摄像头返回
can't open camera by index报错的问题。编写简化测试代码验证时故障可稳定复现,双摄像头启动成功概率随机,经常仅能识别到其中一个。
测试代码如下:
import time cam1 = cv2.VideoCapture(0) cam2 = cv2.VideoCapture(2) time.sleep(2.0) while True: ret1, frame1 = cam1.read() ret2, frame2 = cam2.read() if ret1: frame1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) cv2.imshow('cam1', frame1) if ret2: frame2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) cv2.imshow('cam2', frame2) if cv2.waitKey(1) & 0xFF == ord('s'): break cam1.release() cam2.release() cv2.destroyAllWindows()
已完成排查
- 尝试初始化摄像头时显式指定V4L后端,问题未解决,对应代码:
cap = cv2.VideoCapture(index, cv2.CAP_V4L)
- 硬件兼容性校验通过,
lsusb可正常识别两个摄像头设备,返回结果:
pi@raspberrypi:~ $ lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 005: ID 10c4:8108 Silicon Labs USB OPTICAL MOUSE Bus 001 Device 004: ID 2a7a:9a18 CASUE CASUE USB Keyboard Bus 001 Device 007: ID 0ac8:0345 Z-Star Microelectronics Corp. USB 2.0 PC Camera Bus 001 Device 008: ID 0c45:6366 Microdia Webcam Vitade AF Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
/dev目录下两个摄像头的设备节点均正常生成。
可落地稳定解决方案
- 放弃数字索引初始化方式,绑定固定设备路径。数字索引随系统设备枚举顺序动态变化,极易出现索引抢占问题。先执行
v4l2-ctl --list-devices查询两个摄像头对应的设备路径,再配置udev规则将两个摄像头绑定为固定别名(如/dev/cam_detect对应目标检测摄像头、/dev/cam_face对应人脸识别摄像头),初始化时直接传入固定路径而非数字索引:
cam_detect = cv2.VideoCapture("/dev/cam_detect", cv2.CAP_V4L) cam_face = cv2.VideoCapture("/dev/cam_face", cv2.CAP_V4L)
- 主动降低摄像头带宽占用。树莓派4的USB接口为共享带宽设计,双摄像头默认以最高分辨率、最高帧率启动时会出现带宽抢占,导致随机枚举失败。摄像头初始化完成后立刻设置合理的分辨率与帧率,双路同时运行时建议单路参数不超过640*480@15fps,设置完成后预留2秒硬件初始化时间再开始读帧:
cam_detect.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cam_detect.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) cam_detect.set(cv2.CAP_PROP_FPS, 15) time.sleep(2)
如果调整参数后仍存在带宽不足问题,将两个摄像头分别插在树莓派不同的USB控制器接口上,不要共用同一个USB Hub扩展接口。
- 多进程场景下禁止跨进程传递VideoCapture对象。
multiprocessing默认采用fork模式创建子进程,主进程中初始化的摄像头句柄无法被子进程正常继承,会出现资源抢占。必须在每个子进程的启动逻辑内部独立初始化对应摄像头,不要在主进程提前创建摄像头对象再传入子进程。 - 增加初始化重试逻辑。USB摄像头硬件初始化存在不确定的时间差,单次打开失败属于正常现象,实现带重试的初始化逻辑,单次失败后释放资源间隔1秒重试,最多重试5次:
import cv2 import time def init_camera(dev_path, width=640, height=480, fps=15, retry=5): cam = None for attempt in range(retry): cam = cv2.VideoCapture(dev_path, cv2.CAP_V4L) if cam.isOpened(): cam.set(cv2.CAP_PROP_FRAME_WIDTH, width) cam.set(cv2.CAP_PROP_FRAME_HEIGHT, height) cam.set(cv2.CAP_PROP_FPS, fps) ret, _ = cam.read() if ret: return cam if cam: cam.release() time.sleep(1) raise RuntimeError(f"摄像头 {dev_path} 初始化失败")
- 强制V4L2采用用户指针IO模式。OpenCV默认使用MMAP方式读取V4L2设备帧,在部分廉价USB摄像头上会出现资源冲突,在导入cv2前设置环境变量关闭MMAP模式即可:
import os os.environ["OPENCV_VIDEOIO_PRIORITY_V4L2"] = "1" os.environ["OPENCV_VIDEOIO_V4L2_USE_MMAP"] = "0" # 环境变量设置完成后再导入cv2 import cv2
内容的提问来源于stack exchange,提问作者Ahmed saad
相关产品推荐
相关产品推荐

