1. 项目概述:当YOLOv8遇上吸烟检测
去年给某工业园区做安防系统升级时,客户提出个特殊需求——要在摄像头画面中实时检测违规吸烟行为。传统方案要么误报率高,要么延迟严重,最终我们用YOLOv8+PyQt搭建的这套系统,在测试中达到了94%的识别准确率。不同于通用物体检测,吸烟动作的识别有三大难点:烟支目标小(通常只占画面3%-8%)、存在遮挡(手部/面部遮挡)、姿态多变(持烟/吸烟/弹烟灰等)。这套方案从数据采集到部署落地共耗时3周,核心代码不到800行,今天就把完整实现过程拆解给大家。
系统工作流程分四个阶段:① 用仿真引擎生成5000+张带标注的吸烟图像;② YOLOv8模型选型与训练技巧;③ 使用OpenCV处理RTSP视频流;④ PyQt构建带违规记录功能的GUI界面。实测在NVIDIA Jetson Xavier NX上能稳定跑15FPS,比原版YOLOv5s快2.3倍。下面我会重点说明几个关键环节的避坑要点。
关键指标:输入分辨率640x640,模型尺寸17.3MB,mAP@0.5达到0.89,推理耗时65ms/帧(含前后处理)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据集构建:解决小目标检测的样本困境
2.1 混合数据源采集策略
纯真实场景采集吸烟数据存在两大障碍:隐私问题和样本单一性。我们的解决方案是:
- 仿真数据:用Blender搭建虚拟场景,通过调整烟支长度(5-15像素)、持烟角度(0-180°)、烟雾浓度等参数,生成3000张带bounding box标注的合成图像
- 公开数据集:整合Tobacco800(文档吸烟检测)、FDDB(面部遮挡场景)和自采的200张工厂监控画面
- 数据增强:重点针对小目标添加随机裁剪(最小保留30%烟支)、运动模糊(模拟快速动作)、亮度抖动(应对逆光场景)
python复制# 使用Albumentations的关键增强配置
transform = A.Compose([
A.RandomSizedCrop(min_max_height=(480, 640), height=640, width=640, p=0.5),
A.MotionBlur(blur_limit=7, p=0.3),
A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.5),
A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20, val_shift_limit=10, p=0.3)
], bbox_params=A.BboxParams(format='yolo'))
2.2 标注规范与质量把控
吸烟检测需要特殊标注规则:
- 可见部分超过烟支长度1/3时才标注
- 被手部遮挡时以指尖和烟头位置估算边界框
- 烟雾不单独标注(易引发误报)
- 多人场景下只标注实际吸烟者
使用CVAT标注工具时,我们自定义了吸烟场景的标签属性:
occlusion_level:遮挡程度(0-3级)holding_style:持烟姿势(fingers/palm/lips)smoke_visible:是否可见烟雾
实测发现,标注时添加这些属性可使mAP提升5-8%,特别是在处理半遮挡情况时效果显著
3. YOLOv8模型调优实战
3.1 模型选型与轻量化
对比YOLOv8不同尺寸的表现:
| 模型版本 | 参数量(M) | mAP@0.5 | 推理速度(ms) | 适用场景 |
|---|---|---|---|---|
| YOLOv8n | 3.2 | 0.82 | 28 | 树莓派4B |
| YOLOv8s | 11.4 | 0.87 | 45 | 边缘计算盒 |
| YOLOv8m | 26.2 | 0.89 | 68 | 工控机 |
| YOLOv8l | 43.7 | 0.90 | 92 | 服务器 |
最终选择YOLOv8s并进行以下改进:
- 替换Backbone为MobileNetV3,减少30%计算量
- 在Neck部分添加CBAM注意力模块,提升小目标检测能力
- 修改损失函数:
- 使用Wise-IoU替代CIoU
- 分类损失加入Focal Loss
yaml复制# yolov8s-smoke.yaml 关键修改
backbone:
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, MobileNetV3, [128]] # 替换原C2f
- [-1, 1, CBAM, [256]] # 添加注意力模块
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [..., 1, Detect, [nc, anchors, loss_fn='wiou']] # 修改损失函数
3.2 训练技巧与参数配置
采用两阶段训练策略:
第一阶段(冻结Backbone)
- 初始lr=0.01,cosine衰减
- 优化器:SGD(momentum=0.937)
- Batch Size=32
- 增强:Mosaic(概率0.5)、MixUp(概率0.2)
第二阶段(全网络微调)
- 初始lr=0.001
- 优化器:AdamW
- 添加CutMix增强
- 使用EMA权重(decay=0.9999)
关键训练指令:
bash复制yolo detect train data=smoke.yaml model=yolov8s-smoke.yaml epochs=300 imgsz=640 \
--batch 32 --device 0 --cache ram --close_mosaic 10 --noval
注意:当验证集mAP连续3个epoch不增长时,自动触发ReduceLROnPlateau
4. 摄像头流处理与系统集成
4.1 多路RTSP视频流处理
工业场景常需处理4-8路摄像头,我们采用生产者-消费者模式:
python复制class StreamProcessor:
def __init__(self, rtsp_urls):
self.buffer = Queue(maxsize=30)
self.rtsp_urls = rtsp_urls
def producer(self):
caps = [cv2.VideoCapture(url) for url in self.rtsp_urls]
while True:
frames = []
for cap in caps:
ret, frame = cap.read()
if ret:
frames.append((id(cap), frame))
self.buffer.put(frames)
def consumer(self):
while True:
frames = self.buffer.get()
results = model(frames, stream=True) # YOLOv8流式推理
self.update_gui(results)
关键优化点:
- 使用OpenCV的VIDEOIO_FFMPEG参数提升解码效率
- 为每路流单独设置重连机制(TCP fallback)
- 动态调整解码分辨率(基于GPU负载)
4.2 PyQt界面设计要点
系统界面包含三大功能模块:
- 实时监控面板:
- 采用QGraphicsScene实现低延迟渲染(<15ms)
- 违规目标用红色闪烁框标注
- 事件记录系统:
- 使用SQLite存储违规截图和时间戳
- 支持按摄像头ID/时间段筛选
- 报警联动模块:
- 通过QSerialPort连接声光报警器
- 支持触发IO信号输出
python复制class MainWindow(QMainWindow):
def __init__(self):
self.video_label = QLabel()
self.log_table = QTableWidget(0, 4)
self.setup_ui()
def update_frame(self, results):
annotator = Annotator(frame)
for box in results.boxes:
if box.cls == 0: # 吸烟类别
annotator.box_label(box.xyxy[0], f"Smoking {box.conf:.2f}")
self.log_event(box)
qimg = QImage(annotator.result(), ...)
self.video_label.setPixmap(QPixmap.fromImage(qimg))
5. 部署优化与性能调校
5.1 边缘设备部署技巧
在Jetson系列设备上的优化手段:
- 使用TensorRT加速:
bash复制yolo export model=best.pt format=engine device=0 half=True - 调整GPU时钟频率:
bash复制sudo jetson_clocks --fan - 内存优化:
- 启用CMA连续内存分配
- 限制OpenCV缓存大小
实测性能对比:
| 优化手段 | 功耗(W) | 帧率(FPS) | 内存占用(MB) |
|---|---|---|---|
| 原始模型 | 12.3 | 9.2 | 780 |
| TensorRT | 10.1 | 15.7 | 420 |
| +DLA加速 | 8.6 | 18.3 | 380 |
5.2 常见问题排查指南
问题1:漏检手持香烟
- 检查标注是否包含各种持握姿势
- 尝试减小模型stride(从32改为16)
- 增加test-time augmentation
问题2:误报笔/筷子等长条物体
- 在数据集中添加负样本(持笔场景)
- 调整分类损失权重
- 添加后处理规则(要求检测到面部+手部)
问题3:视频流延迟累积
- 设置cv2.CAP_PROP_BUFFERSIZE为1
- 改用GStreamer管道
- 启用硬件解码(如Jetson上的NVDEC)
这套系统在纺织厂部署后,违规吸烟事件同比下降73%。关键收获是:小目标检测不能只依赖模型能力,需要从数据、标注、前后处理全链路优化。如果要在更小设备(如树莓派)运行,建议改用YOLOv8n+蒸馏方案,虽然mAP会降到0.8左右,但帧率能保持在8-10FPS。
