""" 示例 4: 多传感器并行处理(多进程模式 - 不使用 MultiSensor) 本示例演示如何使用 multiprocessing 同时运行两个传感器: - 每个传感器运行在独立的进程中 - 每个进程拥有独立的 CUDA 上下文 - 使用 multiprocessing.Process 和 Queue 进行进程间通信 架构说明: 进程 1 (传感器 0): 采集 → 光流 → HHD → 接触检测 → 队列 进程 2 (传感器 1): 采集 → 光流 → HHD → 接触检测 → 队列 ↓ 主进程: 显示 ← ───────────────┘ 适用场景:多指触觉传感器、多视角采集、需要 GPU 加速的多路处理 注意事项: - Windows 下必须在 if __name__ == '__main__' 中调用 mp.freeze_support() - 每个传感器进程会占用独立的 GPU 显存 """ import os os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0" import cv2 import orisys import numpy as np import multiprocessing as mp from multiprocessing import Queue, Process import time def sensor_worker(sensor_id, vid_src, config_name, result_queue, stop_event): """ 传感器工作进程函数 Args: sensor_id: 传感器编号 vid_src: 视频源(文件路径或摄像头索引) config_name: 配置名称 result_queue: 结果队列,用于向主进程发送数据 stop_event: 停止事件,用于通知工作进程退出 """ try: # 在每个进程中创建独立的传感器实例 sensor = orisys.Sensor( vid_src, isstitch=True, cuda=True, verbose=False, # 减少子进程输出 config_name=config_name ) print(f"传感器 {sensor_id} 已启动") while not stop_event.is_set(): try: # 步骤1: 获取图像 sensor.get_img() # 步骤2: 计算形变场 sensor.compute_deformation() # 步骤3: 计算触碰点 sensor.compute_contact() # 步骤4: 读取信息 fps, flow, vnormal, img, contour, centroid, depth_map = sensor.read_info( sensor.info.FPS, sensor.info.VRAW, sensor.info.VNORMAL, sensor.info.IMG, sensor.info.CONTOUR, sensor.info.CENTROID, sensor.info.DEPTH ) # 步骤5: 准备数据字典(确保所有数据都是可序列化的) data = { 'sensor_id': sensor_id, 'fps': fps, 'flow': flow.copy() if flow is not None else None, 'vnormal': vnormal.copy() if vnormal is not None else None, 'img': img.copy() if img is not None else None, 'contour': contour, 'centroid': centroid, 'depth_map': depth_map.copy() if depth_map is not None else None, 'timestamp': time.time() } # 步骤6: 发送数据到主进程(非阻塞) try: result_queue.put_nowait(data) except: # 如果队列已满,跳过这一帧 pass # 短暂休眠,避免过度占用CPU time.sleep(0.001) except Exception as e: print(f"传感器 {sensor_id} 处理错误: {e}") time.sleep(0.1) # 清理资源 sensor.disconnect() print(f"传感器 {sensor_id} 已停止") except Exception as e: print(f"传感器 {sensor_id} 初始化失败: {e}") result_queue.put({'sensor_id': sensor_id, 'error': str(e)}) def main(): # ========================================================================= # 1. 配置多个传感器 # ========================================================================= num_sensors = 2 sensor_configs = [ { 'vid_src': './s1.mp4', 'config_name': 'prototype' }, { 'vid_src': './s1.mp4', # 可以改为不同的视频源或摄像头索引 'config_name': 'prototype' }, ] # ========================================================================= # 2. 创建进程间通信对象 # ========================================================================= result_queues = [Queue(maxsize=num_sensors) for _ in sensor_configs] # 每个传感器一个队列 stop_events = [mp.Event() for _ in sensor_configs] # 停止事件 # ========================================================================= # 3. 启动传感器工作进程 # ========================================================================= processes = [] for i, config in enumerate(sensor_configs): p = Process( target=sensor_worker, args=(i, config['vid_src'], config['config_name'], result_queues[i], stop_events[i]) ) p.start() processes.append(p) print(f"\n已启动 {len(processes)} 个传感器进程") print("按 'q' 退出程序") # 用于存储每个传感器的最新数据 latest_data = [None] * len(sensor_configs) try: # ===================================================================== # 4. 主循环:从队列获取数据并显示 # ===================================================================== while True: # 从所有队列中获取最新数据 for i, queue in enumerate(result_queues): try: # 非阻塞获取,获取队列中最新的数据 while True: data = queue.get_nowait() latest_data[i] = data except: # 队列为空,使用之前的数据 pass # 显示所有传感器的数据 for data in latest_data: if data is None or 'error' in data: continue sid = data['sensor_id'] # 显示形变场 if data['img'] is not None and data['flow'] is not None: arrows = orisys.util.draw_arrows( data['img'], data['flow'], threshold=5, grid_spacing=10, arrow_scale=1.0 ) cv2.imshow(f"形变场 Deformation {sid}", arrows) # 显示触碰点和区域 if data['img'] is not None: image_with_foe = orisys.util.draw_contact( data['img'], data['contour'], data['centroid'] ) cv2.imshow(f"Contact Centroid {sid}", image_with_foe) # 显示深度图 if data['depth_map'] is not None and data['depth_map'].size > 0: div_abs = data['depth_map'] if div_abs.max() > div_abs.min(): div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8) else: div_normalized = np.zeros_like(div_abs, dtype=np.uint8) cv2.imshow(f"Depth Map {sid}", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET)) # 打印FPS print(f"传感器 {sid}: FPS={data['fps']:5.1f}") # 检查退出键 key = cv2.waitKey(1) & 0xFF if key == ord('q'): print("\n退出程序...") break finally: # ===================================================================== # 5. 清理资源 # ===================================================================== # 通知所有工作进程停止 for event in stop_events: event.set() # 等待所有进程结束 for p in processes: p.join(timeout=2) if p.is_alive(): p.terminate() p.join() cv2.destroyAllWindows() print("所有传感器进程已停止") if __name__ == '__main__': # Windows 下多进程必需的初始化 mp.freeze_support() main()