Orin NX平台OpenCV无法同时运行4台1080p 30fps相机求助
相机多开与设备节点问题排查
问题背景
在Jetson Orin NX(配备4个USB3.0接口)上运行测试代码时,遇到两个核心问题:
- 4台相机均可通过
openCamera()正常开启(未返回-1),但最多仅2台能输出图像;且所有相机在1080p分辨率下帧率极低,手动设置的30fps完全无效。 - 仅连接4台相机,执行
ls /dev/video*却显示8个设备节点,因此代码中用for i in range(0,8,2)遍历相机,需明确原因。
已尝试操作:
- 注释分辨率设置代码后,4台相机可输出640*480 30fps图像,但不符合需求;
- 单独开启每台相机均可实现1080p 30fps输出;
- 已排除供电与USB带宽问题。
最终需求:在多线程C++程序中实现4台相机同时以1080p 30fps运行。
测试代码
#!/usr/bin/env python # coding=utf8 import cv2 import time def openCamera(cam_index = 0): cap = cv2.VideoCapture(cam_index) if cap.isOpened(): cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) #1 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) #2 cap.set(cv2.CAP_PROP_FPS, 30) #3 cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G')) return cap else: print(str(cam_index) + "launch failed") return -1 cam_set = [] for i in range(0,8,2): #4 temp = openCamera(i) if temp != -1: cam_set.append(temp) print("have ", len(cam_set),"cams running") while(cam_set): window_cnt = 0 for k in cam_set: cap = k ret, frame = cap.read() if ret: cv2.imshow(str(window_cnt+1), frame) window_cnt += 1 cv2.waitKey(1) else: print("camera launched failed") for j in cam_set: j.release() cv2.destroyAllWindows()
问题解答
一、为什么4台相机对应8个/dev/video节点?
Linux下的USB相机通常会生成多个设备节点:
- 主节点(偶数编号,如
video0、video2):负责视频流捕获; - 辅助节点(奇数编号,如
video1、video3):用于设备控制、静态图像捕获或音频同步等辅助功能。
每台相机对应2个节点是正常现象,你可以通过v4l2-ctl --list-devices命令查看每个节点对应的具体功能,确认哪些是用于视频捕获的主节点。
二、实现4台相机同时1080p 30fps运行的方案
1. 先验证相机的实际支持参数
用v4l2-ctl -d /dev/videoX --list-formats-ext命令(X为相机节点号)查看相机明确支持的分辨率、帧率和编码格式,确认1080p 30fps的MJPG格式是相机原生支持的(部分相机仅在特定格式下提供高帧率)。
2. 调整OpenCV参数设置顺序
OpenCV中参数设置顺序会影响生效结果,必须先设置编码格式,再设置分辨率和帧率,否则可能导致设置无效。修改openCamera函数:
def openCamera(cam_index = 0): cap = cv2.VideoCapture(cam_index) if cap.isOpened(): # 先设置编码格式 cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G')) # 再设置分辨率和帧率 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) cap.set(cv2.CAP_PROP_FPS, 30) # 验证实际生效参数 print(f"Camera {cam_index}:") print(f" 实际分辨率: {cap.get(cv2.CAP_PROP_FRAME_WIDTH)}x{cap.get(cv2.CAP_PROP_FRAME_HEIGHT)}") print(f" 实际帧率: {cap.get(cv2.CAP_PROP_FPS)}") return cap else: print(f"{cam_index} launch failed") return -1
3. 改用多线程捕获帧
当前单线程循环读取所有相机帧的方式会导致相机间互相阻塞,高分辨率下无法及时处理所有帧,进而引发帧率下降或部分相机无输出。为每个相机创建独立线程负责捕获:
import cv2 import threading import time class CameraThread(threading.Thread): def __init__(self, cam_index): super().__init__() self.cam_index = cam_index self.cap = None self.running = False def run(self): self.cap = cv2.VideoCapture(self.cam_index) if not self.cap.isOpened(): print(f"Camera {self.cam_index} 启动失败") return # 按正确顺序设置参数 self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G')) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) self.cap.set(cv2.CAP_PROP_FPS, 30) self.running = True while self.running: ret, frame = self.cap.read() if ret: cv2.imshow(f"Camera {self.cam_index}", frame) cv2.waitKey(1) else: time.sleep(0.01) def stop(self): self.running = False if self.cap: self.cap.release() # 启动所有相机线程 threads = [] for i in range(0,8,2): thread = CameraThread(i) thread.start() threads.append(thread) # 等待退出 try: while True: time.sleep(1) except KeyboardInterrupt: for thread in threads: thread.stop() cv2.destroyAllWindows()
C++版本可参考相同逻辑,用std::thread为每个相机创建独立捕获线程。
4. 利用Jetson硬件加速捕获
Jetson平台上,使用GStreamer管道替代原生OpenCV捕获,可利用硬件解码能力提升性能,减少CPU占用:
# 替换cv2.VideoCapture的初始化方式 cap = cv2.VideoCapture( "v4l2src device=/dev/videoX ! image/jpeg,width=1920,height=1080,framerate=30/1 ! jpegdec ! videoconvert ! appsink", cv2.CAP_GSTREAMER )
5. 确认USB总线状态
用lsusb -t命令查看USB总线带宽使用情况,确认4台相机同时运行时无隐性带宽瓶颈(1080p 30fps MJPG单相机带宽约22Mbps,4台总带宽远低于USB3.0上限,此步骤仅作验证)。
内容的提问来源于stack exchange,提问作者Daniel Chen
相关产品推荐
相关产品推荐

