This commit is contained in:
ywb
2026-06-13 15:56:09 +08:00
parent cbc88def27
commit 62292edc70
2 changed files with 29 additions and 8 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ MAP_CONFIG = {
# ========== 摄像头 ========== # ========== 摄像头 ==========
CAMERA_CONFIG = { CAMERA_CONFIG = {
"device_index": 3, # AGV 摄像头 video3Orbbec Gemini 彩色流,V4L2后端 "device_index": 4, # AGV 摄像头 video4Orbbec Gemini 彩色流,YUYV 640x480
"backend": "v4l2", # 使用 V4L2 后端获取标准彩色格式(640x480) "backend": "v4l2", # 使用 V4L2 后端获取标准彩色格式(640x480)
"qr_detect_interval": 0.5, "qr_detect_interval": 0.5,
"capture_delay": 0.5, "capture_delay": 0.5,
+26 -5
View File
@@ -74,7 +74,7 @@ class QRScanner:
# 情况 2: 3 通道但实际帧数据显示为 YUYV(绿屏特征:G 通道全满,B/R 近空) # 情况 2: 3 通道但实际帧数据显示为 YUYV(绿屏特征:G 通道全满,B/R 近空)
if ndim == 3: if ndim == 3:
g_mean = frame[:, :, 1].mean() g_mean = frame[:, :, 1].mean()
if g_mean > 220 and frame[:, :, 0].mean() < 30 and frame[:, :, 2].mean() < 30: if g_mean > 80 and frame[:, :, 0].mean() < 30 and frame[:, :, 2].mean() < 30:
# 典型的"Lime"绿屏 — 当做 YUYV 原始数据解码 # 典型的"Lime"绿屏 — 当做 YUYV 原始数据解码
logger.debug(f"检测到绿屏 (G={g_mean:.0f}, B={frame[:,:,0].mean():.0f}, R={frame[:,:,2].mean():.0f}),尝试修复") logger.debug(f"检测到绿屏 (G={g_mean:.0f}, B={frame[:,:,0].mean():.0f}, R={frame[:,:,2].mean():.0f}),尝试修复")
try: try:
@@ -85,7 +85,7 @@ class QRScanner:
# 我们只需要取前 w*h*2 字节作为 YUYV 数据 # 我们只需要取前 w*h*2 字节作为 YUYV 数据
yuyv_len = w * h * 2 yuyv_len = w * h * 2
if len(raw_bytes) >= yuyv_len: if len(raw_bytes) >= yuyv_len:
yuyv_img = np.frombuffer(raw_bytes[:yuyv_len], dtype=np.uint8).reshape(h, w * 2, 1) yuyv_img = np.frombuffer(raw_bytes[:yuyv_len], dtype=np.uint8).reshape(h, w, 2)
frame = cv2.cvtColor(yuyv_img, cv2.COLOR_YUV2BGR_YUYV) frame = cv2.cvtColor(yuyv_img, cv2.COLOR_YUV2BGR_YUYV)
logger.debug("绿屏修复完成") logger.debug("绿屏修复完成")
return frame return frame
@@ -101,14 +101,35 @@ class QRScanner:
# 正常 BGR 帧 # 正常 BGR 帧
return frame return frame
def read_frame(self) -> Optional[np.ndarray]: def read_frame(self, timeout: float = 2.0) -> Optional[np.ndarray]:
"""读取一帧""" """读取一帧(带超时保护,避免 V4L2 select() 永久阻塞)"""
if not self._cap or not self._cap.isOpened(): if not self._cap or not self._cap.isOpened():
return None return None
ret, frame = self._cap.read()
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout
pool = ThreadPoolExecutor(max_workers=1)
try:
fut = pool.submit(self._cap.read)
ret, frame = fut.result(timeout=timeout)
if not ret or frame is None: if not ret or frame is None:
return None return None
return self._fix_frame(frame) return self._fix_frame(frame)
except FuturesTimeout:
logger.warning(f"摄像头 read_frame 超时 ({timeout}s),尝试重建 _cap")
self.close()
self.open()
# 重建后重试一次
if self._cap and self._cap.isOpened():
ret, frame = self._cap.read()
if ret and frame is not None:
return self._fix_frame(frame)
return None
except Exception as e:
logger.error(f"read_frame 异常: {e}")
return None
finally:
pool.shutdown(wait=False)
def detect_qr(self, frame: np.ndarray) -> Optional[str]: def detect_qr(self, frame: np.ndarray) -> Optional[str]:
"""从图像帧中检测二维码""" """从图像帧中检测二维码"""