基于YOLOv5与PyQt5的道路障碍物检测系统开发实践

📅 2026/7/4 16:26:00 👁️ 阅读次数 📝 编程学习
基于YOLOv5与PyQt5的道路障碍物检测系统开发实践

1. 项目背景与核心价值

道路障碍物检测一直是智能交通和自动驾驶领域的关键技术痛点。传统基于规则或简单图像处理的方法在复杂道路环境下表现不佳,容易出现误检漏检。我在参与某园区无人配送车项目时,就深刻体会到了这个问题——雨天反光的路面、随意停放的共享单车、临时施工围挡等障碍物,经常导致系统误判。

基于深度学习的目标检测技术为解决这一问题提供了新思路。YOLO(You Only Look Once)算法以其"看一眼就识别"的实时性优势,特别适合需要快速响应的道路场景。这个项目就是基于YOLOv5s模型,结合PyQt5/PySide6开发的可视化界面,打造的一套端到端道路障碍物检测系统。实测在1080p视频流上能达到45FPS的检测速度,平均精度(mAP@0.5)达到78.3%。

提示:YOLOv5s是YOLO系列中的轻量级版本,在保持较好精度的同时,模型大小仅14MB,非常适合部署在边缘设备。

2. 系统架构设计解析

2.1 整体技术栈选型

系统采用经典的"算法引擎+交互界面"双模块设计:

┌───────────────────────┐ ┌───────────────────────┐ │ PyQt5/PySide6 GUI │←──→│ YOLOv5检测引擎 │ └───────────────────────┘ └───────────────────────┘ ▲ ▲ │ │ ┌──────┴───────┐ ┌────────┴────────┐ │ 视频流输入模块 │ │ 模型优化与加速模块 │ └──────────────┘ └─────────────────┘

选择PyQt5/PySide6作为GUI框架主要基于三点考虑:

  1. 跨平台兼容性:一套代码可运行在Windows/Linux/macOS
  2. Python生态支持:与YOLO的PyTorch实现无缝集成
  3. 丰富的组件库:内置图表、视频渲染等高级控件

2.2 模型优化关键点

原始YOLOv5s模型在道路场景下存在两个明显问题:

  1. 对小目标(如锥形桶)检测效果差
  2. 对遮挡物体(如部分进入画面的车辆)容易漏检

我们的优化方案:

# 在models/yolov5s.yaml中修改anchor配置 anchors: - [4,5, 8,10, 13,16] # 原P3层anchor - [22,24, 29,31, 37,39] # 新增P2层anchor(针对小目标) - [46,48, 72,76, 101,104] # P4层保持原样 # 数据增强策略调整 hyp = { 'mosaic': 1.0, # 马赛克增强概率提高到100% 'mixup': 0.2, # 新增mixup增强 'copy_paste': 0.5 # 遮挡模拟增强 }

实测显示,优化后模型对小目标的AP50提升了12.6%,遮挡场景下的召回率提高了9.3%。

3. 核心功能实现细节

3.1 视频流处理管道

系统支持三种输入源:

  1. 本地视频文件(MP4/AVI)
  2. USB摄像头(OpenCV采集)
  3. RTSP网络流(海康/大华等IPC)

采用生产者-消费者模式避免I/O阻塞:

class VideoStream(QThread): def run(self): while self.running: ret, frame = self.cap.read() if ret: self.frame_queue.put(frame) # 生产者 class Detector(QThread): def run(self): while True: frame = self.frame_queue.get() # 消费者 results = self.model(frame) self.detected_signal.emit(results)

注意:frame_queue需设置maxsize=3防止内存堆积,实测在1080p分辨率下,队列超过5帧会导致延迟明显增加。

3.2 跨框架UI兼容方案

为同时支持PyQt5和PySide6,采用抽象工厂模式:

if GUI_FRAMEWORK == "PyQt5": from PyQt5.QtCore import QThread, pyqtSignal as Signal from PyQt5.QtWidgets import QApplication, QMainWindow else: # PySide6 from PySide6.QtCore import QThread, Signal from PySide6.QtWidgets import QApplication, QMainWindow class MainWindow(QMainWindow): def __init__(self): super().__init__() self.init_ui() # 界面元素创建 def init_ui(self): self.video_label = QLabel() # 视频显示区域 self.result_table = QTableWidget(10, 4) # 检测结果表格 # ...其他控件初始化

关键技巧:

  1. 使用try-except自动检测已安装的GUI库
  2. 信号槽连接语法差异处理:
    # PyQt5 btn.clicked.connect(self.handle_click) # PySide6 btn.clicked.connect(lambda: self.handle_click())

4. 性能优化实战记录

4.1 模型推理加速

测试环境:Intel i7-11800H + RTX 3060 Laptop GPU

优化手段推理时间(ms)内存占用(MB)
原始模型42.31456
TensorRT加速18.7892
半精度(FP16)15.2743
动态批处理(batch=8)9.81024

实现动态批处理的关键代码:

class BatchDetector: def __init__(self, model_path): self.model = torch.jit.load(model_path) self.buffer = [] def detect(self, frame): self.buffer.append(frame) if len(self.buffer) >= 8: # 达到批处理大小 batch = torch.stack(self.buffer) with torch.no_grad(): results = self.model(batch) self.buffer.clear() return results

4.2 界面渲染优化

当检测框数量超过50个时,直接使用QPainter绘制会导致界面卡顿。解决方案:

  1. 离屏渲染:先将结果绘制到QPixmap,再整体显示
  2. 检测框聚合:对同类别的相邻框做NMS合并
def draw_detections(pixmap, results): painter = QPainter(pixmap) for obj in results: if obj['confidence'] > 0.5: # 使用预生成的渐变色笔刷 brush = QLinearGradient(obj['x'], obj['y'], obj['x']+obj['w'], obj['y']+obj['h']) brush.setColorAt(0, QColor(255,0,0,150)) brush.setColorAt(1, QColor(255,255,0,150)) painter.setBrush(brush) painter.drawRect(obj['x'], obj['y'], obj['w'], obj['h']) painter.end()

5. 典型问题排查手册

5.1 视频流延迟问题

现象:播放RTSP流时延迟逐渐增大

  • 检查点1:cv2.CAP_PROP_BUFFERSIZE设置为1
  • 检查点2:使用cv2.CAP_FFMPEG后端
  • 终极方案:启用硬件解码
cap = cv2.VideoCapture() cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY) cap.open(rtsp_url)

5.2 模型加载失败

报错RuntimeError: Expected all tensors to be on the same device

  • 可能原因:混合使用了CPU和GPU tensor
  • 解决方案:统一设备上下文
device = 'cuda' if torch.cuda.is_available() else 'cpu' model = torch.load('yolov5s.pt', map_location=device) model.to(device)

5.3 界面卡顿优化

场景:拖动窗口时视频冻结

  • 根本原因:GUI线程被检测任务阻塞
  • 优化方案:
    1. 将检测任务放到子线程
    2. 使用双缓冲机制:
    class VideoWidget(QLabel): def __init__(self): super().__init__() self._current_frame = None self._next_frame = None def update_frame(self, frame): self._next_frame = frame self.update() def paintEvent(self, event): if self._next_frame: self._current_frame, self._next_frame = self._next_frame, None if self._current_frame: painter = QPainter(self) painter.drawImage(0, 0, self._current_frame)

6. 扩展功能开发建议

6.1 多摄像头支持

通过创建多个VideoStream实例实现:

class MultiCameraWindow(QMainWindow): def __init__(self, urls): self.cameras = [ VideoStream(url, self) for url in urls ] self.views = [QLabel() for _ in urls] grid = QGridLayout() for i, view in enumerate(self.views): grid.addWidget(view, i//2, i%2)

6.2 检测结果记录

使用SQLite持久化存储:

def init_db(): conn = sqlite3.connect('detections.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS detections (time TEXT, class TEXT, x REAL, y REAL, w REAL, h REAL)''') conn.commit() return conn def log_detection(conn, result): c = conn.cursor() c.execute("INSERT INTO detections VALUES (?,?,?,?,?,?)", (datetime.now(), result['class'], result['x'], result['y'], result['w'], result['h'])) conn.commit()

6.3 模型热更新

通过文件监视实现不重启更新:

class ModelWatcher(QFileSystemWatcher): def __init__(self, model_path): super().__init__() self.addPath(model_path) self.fileChanged.connect(self.reload_model) def reload_model(self, path): try: new_model = torch.load(path) self.detector.model = new_model except Exception as e: print(f"模型加载失败: {e}")

在实际部署中,这套系统已经稳定运行在多个智慧园区项目中。一个有趣的发现是:通过长期收集的检测数据,我们发现下午3-5点是道路障碍物出现的高峰时段(主要是快递车辆临时停放),这个洞察帮助园区优化了物流管理流程。