基于YOLO11的疲劳检测系统:从训练到部署的完整实战

用Python和深度学习技术打造一个完整的疲劳检测系统,支持图片检测、实时监控和智能预警。本文带你从零开始构建一个专业的疲劳驾驶检测应用。

🎯 项目背景

疲劳驾驶是道路交通安全的重大隐患,据统计,疲劳驾驶造成的交通事故占总事故的20%以上。本项目利用最新的YOLO11深度学习算法,实现了:

  • 👁️ 智能检测:实时识别眼部状态(睁眼/闭眼)和嘴部状态(张嘴/闭嘴)
  • 🚨 疲劳预警:基于PERCLOS算法智能判断疲劳状态
  • 🌐 Web界面:友好的用户界面,支持图片检测和实时监控
  • 📊 数据分析:统计疲劳事件和驾驶行为分析
  • 🔧 一键部署:智能环境检查,快速启动应用

🔧 技术架构

核心技术栈

  • 深度学习框架:YOLO11 (Ultralytics)
  • 后端框架:Flask
  • 前端技术:HTML5 + JavaScript + Bootstrap
  • 计算机视觉:OpenCV
  • 数据处理:NumPy
  • 疲劳算法:PERCLOS (眼部闭合百分比)

📦 项目结构

复制代码
疲劳检测项目/
├── fatigue_app.py                    # Flask Web应用
├── quick_start.py                    # 智能启动脚本
├── train_fatigue.py                  # 模型训练脚本
├── convert_fatigue_voc_to_yolo.py   # 数据格式转换
├── test_fatigue_model.py            # 模型测试脚本
├── templates/
│   └── fatigue_index.html           # Web前端页面
├── static/                          # 静态资源
├── models/                          # 预训练模型
│   └── yolo11n.pt                   # YOLO11模型
├── Fatigue/                         # 原始数据集
│   ├── JPEGImages/                  # 图片文件
│   └── Annotations/                 # XML标注文件
├── fatigue_yolo_dataset/            # YOLO格式数据集
├── fatigue_results/                 # 训练结果
└── detection_results/               # 检测结果

🚀 核心代码实现

1. 数据格式转换

将VOC格式数据转换为YOLO格式:

python 复制代码
def convert_annotation(xml_path, classes):
    """转换单个XML标注文件"""
    tree = ET.parse(xml_path)
    root = tree.getroot()
    
    # 获取图像尺寸
    size = root.find('size')
    width = int(size.find('width').text)
    height = int(size.find('height').text)
    
    annotations = []
    for obj in root.findall('object'):
        class_name = obj.find('name').text
        if class_name not in classes:
            continue
            
        class_id = classes.index(class_name)
        
        # 获取边界框并转换为YOLO格式
        bbox = obj.find('bndbox')
        xmin, ymin = float(bbox.find('xmin').text), float(bbox.find('ymin').text)
        xmax, ymax = float(bbox.find('xmax').text), float(bbox.find('ymax').text)
        
        # 转换为YOLO格式 (x_center, y_center, width, height)
        x_center = (xmin + xmax) / 2.0 / width
        y_center = (ymin + ymax) / 2.0 / height
        w = (xmax - xmin) / width
        h = (ymax - ymin) / height
        
        annotations.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}")
    
    return annotations

2. 疲劳检测模型训练

使用YOLO11进行疲劳检测模型训练:

python 复制代码
def train_model():
    """疲劳识别训练主函数"""
    # 加载预训练模型
    model = YOLO('models/yolo11n.pt' if Path('models/yolo11n.pt').exists() else 'yolo11n.pt')
    
    # 开始训练
    results = model.train(
        data='fatigue_data.yaml',      # 数据集配置
        epochs=200,                    # 训练轮数
        imgsz=640,                     # 输入图像尺寸
        batch=16,                      # 批次大小
        device='0',                    # GPU设备
        lr0=0.01,                      # 初始学习率
        patience=30,                   # 早停耐心值
        project='fatigue_results',     # 项目文件夹
    )
    
    return results

3. 疲劳检测Flask Web应用

构建Web检测服务:

python 复制代码
from flask import Flask, render_template, request, jsonify
from ultralytics import YOLO
import cv2
import numpy as np

app = Flask(__name__)
model = YOLO('fatigue_results/yolo11n_fatigue_20250711_222310/weights/best.pt')
classes = ['open_eye', 'closed_eye', 'open_mouth', 'closed_mouth']

class FatigueDetector:
    def __init__(self):
        self.closed_eye_count = 0
        self.frame_count = 0
        
    def detect_fatigue(self, detections):
        """基于PERCLOS算法检测疲劳状态"""
        closed_eyes = sum(1 for d in detections if d['class'] == 'closed_eye')
        
        self.frame_count += 1
        if closed_eyes > 0:
            self.closed_eye_count += 1
            
        if self.frame_count >= 30:  # 每30帧计算一次
            perclos = self.closed_eye_count / self.frame_count
            is_fatigue = perclos > 0.7
            
            # 重置计数器
            self.closed_eye_count = 0
            self.frame_count = 0
            
            return {'is_fatigue': is_fatigue, 'perclos': perclos}
        return None

fatigue_detector = FatigueDetector()

@app.route('/')
def index():
    return render_template('fatigue_index.html')

@app.route('/detect', methods=['POST'])
def detect():
    """处理疲劳检测请求"""
    file = request.files['image']
    
    # 读取和检测图像
    file_bytes = file.read()
    nparr = np.frombuffer(file_bytes, np.uint8)
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    results = model(img, conf=0.5)
    
    # 处理检测结果
    detections = []
    for result in results:
        if result.boxes is not None:
            for box in result.boxes:
                x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
                confidence = box.conf[0].cpu().numpy()
                class_id = int(box.cls[0].cpu().numpy())
                
                detections.append({
                    'bbox': [int(x1), int(y1), int(x2), int(y2)],
                    'confidence': float(confidence),
                    'class': classes[class_id]
                })
    
    # 疲劳状态检测
    fatigue_result = fatigue_detector.detect_fatigue(detections)
    
    return jsonify({
        'success': True,
        'detections': detections,
        'count': len(detections),
        'fatigue_result': fatigue_result
    })

if __name__ == '__main__':
    app.run(debug=True, port=5002)

📊 训练与评估结果

主要性能指标

基于21轮训练的最终结果:

  • mAP50 : 98.7% - 在IoU=0.5时的平均精度
  • mAP50-95 : 62.8% - 在IoU=0.5:0.95时的平均精度
  • Precision : 96.2% - 检测精确率
  • Recall : 97.5% - 检测召回率
  • 模型大小: 5.4MB (轻量级YOLO11n)
  • 输入分辨率: 640×640像素
  • 检测速度: <50ms/帧 (GPU)

训练参数配置

yaml 复制代码
# 核心训练参数
epochs: 200              # 训练轮数
batch: 16               # 批次大小
imgsz: 640              # 图像尺寸
lr0: 0.01               # 初始学习率
momentum: 0.937         # SGD动量
weight_decay: 0.0005    # 权重衰减
patience: 30            # 早停耐心值

# 数据增强参数
hsv_h: 0.015           # 色调增强
hsv_s: 0.7             # 饱和度增强
hsv_v: 0.4             # 明度增强
fliplr: 0.5            # 左右翻转概率
mosaic: 1.0            # 马赛克增强

# 损失函数权重
box: 7.5               # 边界框损失权重
cls: 0.5               # 分类损失权重
dfl: 1.5               # 分布焦点损失权重

训练可视化结果

  • 训练批次样本


  • 标签分布

  • 标签相关性

训练样本展示了模型学习的眼部和嘴部状态检测能力

🔬 疲劳检测算法

PERCLOS算法原理

PERCLOS (Percentage of Eyelid Closure) 是目前最广泛使用的疲劳检测算法:

python 复制代码
def calculate_perclos(closed_frames, total_frames):
    """计算PERCLOS值"""
    perclos = closed_frames / total_frames
    
    # 疲劳程度判断
    if perclos > 0.8:
        return "严重疲劳"
    elif perclos > 0.5:
        return "中度疲劳"  
    elif perclos > 0.2:
        return "轻度疲劳"
    else:
        return "正常状态"

多模态疲劳检测

结合多种生理特征提高检测准确率:

  1. 眼部特征

    • 眨眼频率 (BLINK)
    • 眼部闭合时间 (CLOSNO)
    • 眼部闭合百分比 (PERCLOS)
  2. 嘴部特征

    • 打哈欠检测
    • 嘴部张开程度
  3. 头部姿态

    • 头部倾斜角度
    • 点头频率

🎨 前端界面设计

简洁现代的Web界面:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>疲劳检测系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .upload-area {
            border: 2px dashed #007bff;
            border-radius: 10px;
            padding: 40px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
        }
        .upload-area:hover {
            background-color: #f8f9fa;
        }
        .detection-result {
            margin-top: 20px;
            padding: 20px;
            border-radius: 8px;
        }
        .fatigue-high { background-color: #ffebee; border-left: 4px solid #f44336; }
        .fatigue-medium { background-color: #fff3e0; border-left: 4px solid #ff9800; }
        .fatigue-low { background-color: #e8f5e8; border-left: 4px solid #4caf50; }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="text-center my-4">😴 疲劳检测系统</h1>
        
        <div class="row">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">📸 图片检测</h5>
                        <div class="upload-area" onclick="document.getElementById('fileInput').click()">
                            <i class="fas fa-cloud-upload-alt fa-3x mb-3"></i>
                            <p>点击或拖拽上传图片</p>
                            <input type="file" id="fileInput" accept="image/*" style="display: none;">
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="col-md-6">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">📹 实时检测</h5>
                        <button class="btn btn-primary" onclick="startCamera()">启动摄像头</button>
                        <video id="video" width="100%" height="240" style="display: none;"></video>
                    </div>
                </div>
            </div>
        </div>
        
        <div id="results" class="mt-4"></div>
    </div>

    <script>
        // 图片上传检测
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) {
                const formData = new FormData();
                formData.append('image', file);
                
                fetch('/detect', {
                    method: 'POST',
                    body: formData
                })
                .then(response => response.json())
                .then(data => {
                    displayResults(data);
                });
            }
        });

        function displayResults(data) {
            const resultsDiv = document.getElementById('results');
            
            if (data.success) {
                let html = `<div class="detection-result">
                    <h5>检测结果</h5>
                    <p>检测到 ${data.count} 个目标</p>
                `;
                
                if (data.fatigue_result) {
                    const level = data.fatigue_result.fatigue_level;
                    html += `<div class="fatigue-${level}">
                        <h6>疲劳状态: ${data.fatigue_result.is_fatigue ? '疲劳' : '正常'}</h6>
                        <p>PERCLOS值: ${(data.fatigue_result.perclos * 100).toFixed(1)}%</p>
                    </div>`;
                }
                
                html += '</div>';
                resultsDiv.innerHTML = html;
            }
        }

        // 摄像头实时检测
        function startCamera() {
            navigator.mediaDevices.getUserMedia({ video: true })
                .then(stream => {
                    const video = document.getElementById('video');
                    video.srcObject = stream;
                    video.style.display = 'block';
                    video.play();
                })
                .catch(err => {
                    console.error('无法访问摄像头:', err);
                });
        }
    </script>
</body>
</html>

🔧 部署

快速部署

bash 复制代码
# 1. 克隆项目
git clone https://github.com/your-repo/fatigue-detection.git
cd fatigue-detection

# 2. 安装依赖
pip install -r requirements.txt

# 3. 快速启动
python quick_start.py

💡 核心亮点

1. 高精度检测

  • mAP50达98.7%:业界领先的检测精度
  • 多类别检测:支持眼部、嘴部4种状态
  • 实时性能:单帧检测<50ms

2. 智能疲劳判断

  • PERCLOS算法:科学的疲劳评估标准
  • 多模态融合:眼部+嘴部状态综合分析
  • 自适应阈值:根据个人特征调整判断标准

3. 完整工程方案

  • 数据处理:VOC到YOLO格式转换
  • 模型训练:优化的超参数配置
  • Web部署:Flask + Bootstrap响应式界面
  • 智能启动:一键环境检查和应用启动

4. 实用功能特性

  • 图片检测:支持批量图片处理
  • 实时监控:摄像头实时疲劳检测
  • 数据统计:疲劳事件记录和分析
  • 预警系统:多级疲劳状态提醒

📝 总结

这个疲劳检测项目展示了如何将最新的深度学习技术应用到实际安全场景中。通过YOLO11算法的强大检测能力,结合科学的PERCLOS疲劳评估算法,我们构建了一个完整的疲劳检测系统。

项目的核心优势:

  • 高精度检测:mAP50达98.7%的检测准确率
  • 实时性能:单帧检测时间<50ms
  • 科学算法:基于PERCLOS的疲劳判断标准
  • 完整方案:从数据处理到Web部署的全流程
  • 工程实用:支持图片检测和实时监控