资讯动态

基于YOLOv11的吸烟行为识别检测系统实战解析

发布时间:2026/10/21 13:47:54 来源:尧图企业网站定制
抽烟行为识别这类需求这几年在工矿企业、校园、加油站、餐饮后厨甚至社区安防里都特别常见。传统的人工盯监控方式既费人力又容易漏报所以“用深度学习模型自动检测画面里的人有没有吸烟”就成了很实用的落地方向。我最近正好完整做了一个基于YOLOv11的吸烟识别检测系统把模型训练、数据集整理、界面打包、登录注册全串了起来前后折腾了小两周。这篇文章把我的做法、参数、踩过的坑全部写出来想复现的朋友可以直接照着抄。这套系统说白了就是三件事第一用YOLOv11训练一个能识别“吸烟行为”的目标检测模型第二把模型封装成可交互的桌面软件用PyQt5做了登录注册和主检测界面第三支持图片、视频、摄像头三种输入方式检测结果实时框选显示。项目源码和训练好的权重都在手里改改路径就能跑起来。适合正在做毕业设计、公司内部安防Demo、或者想快速上手YOLOv11落地项目的人参考。1. 项目定位与方案选型1.1 为什么选YOLOv11而不是其他模型目标检测领域能用的模型其实很多Faster R-CNN、SSD、YOLO系列、DETR等等。但我做这个项目的时候几乎没有犹豫直接选了YOLOv11。原因是这个场景有很明确的特点实时性要求高摄像头画面要能跟得上目标不算特别复杂只需要区分“吸烟”和“未吸烟”部署环境是普通办公电脑不能指望动不动就上几十G显存的服务器。YOLOv11相比之前版本在C2f模块、损失函数和训练策略上都有改进同等算力下精度和速度的平衡做得比较好。尤其是yolo11s这种小模型一张普通显卡上跑实时推理完全没问题CPU推理也能用只是帧率会低一些。模型权重文件也就十几到几十兆打包进软件很合适。如果换成Faster R-CNN精度可能不错但推理速度明显跟不上摄像头实时检测。用YOLOv5的话也可以但既然要新做项目直接用新版本能少走很多弯路官方提供的预训练权重和API也更顺手。1.2 系统整体功能拆解整个系统我在设计上分成了四个独立模块这样做的好处是后续改任何一个模块都不影响其他部分。用户模块负责登录、注册、用户信息维护。登录成功后才能进入主界面避免软件被随便使用。这里我没有引入复杂的用户体系直接用SQLite数据库存用户名和密码哈希值足够应付小型项目。检测引擎模块是核心负责加载YOLOv11模型、对输入的图片或视频帧做推理、解析检测结果。它只关心“模型输入输出”不关心界面长什么样这样以后想替换成YOLOv12或者其他检测模型只需要改这一个模块。界面交互模块负责显示摄像头画面、图片、检测框、置信度、FPS等同时接收用户的操作指令。使用PyQt5实现PyQt5自带丰富的控件做桌面检测工具非常方便。数据存储模块负责把每次检测的时间、结果、图片路径等信息写到数据库里方便事后查询。这个功能很多人会忽略但实际使用时特别有用比如安防场景要回看某个时间段有没有人吸烟。1.3 UI框架选择背后的考虑界面这块我选了PyQt5而不是PySide、Tkinter或者Web前端。Tkinter做简单Demo可以但想做出带登录页、图片展示、视频流、历史记录这种稍微完整的界面布局和样式会很痛苦。Web前端做出来好看但又多了一套前后端通信逻辑作为桌面软件反而绕远路。PyQt5的好处是控件齐全QSS还可以做样式美化界面不至于太丑。登录页和主界面都用代码动态生成不依赖Qt Designer生成的.ui文件这样别人拿到源码后直接运行就能看到界面少一步转.ui的步骤也更方便我控制布局逻辑。数据库方面用了SQLite这是Python自带支持的文件型数据库。用户信息、检测记录都不大用不上MySQL而且SQLite单文件拷贝即走适合分发。1.4 数据集与标注策略直接关系到模型能不能用很多朋友做目标检测项目喜欢一上来就找公开数据集。但吸烟检测和通用的“检测猫狗”不一样最大问题是“烟”本身太小如果直接标注“香烟”目标模型很难学到有效特征。我最后采取的方案是标注“正在吸烟的人”也就是把人和香烟作为一个整体行为框出来。这样做的好处是目标尺寸相对大模型更容易学习也更符合实际场景需求。标注类别只设置一个smoking。这是一类目标检测模型只需要输出一个类别训练难度比多类别低很多。如果你的场景里还需要识别“电话吸烟”“禁烟区吸烟”之类的细分行为可以在后续阶段增加标签但第一版建议不要搞太复杂。数据集规模建议控制在800到2000张之间太少模型容易过拟合太多标注成本高。我实际用了1200张左右来源包括开源数据集、自己拍摄的视频截图、网络上可合法使用的公开图片以及一部分人工合成的现场照片。数据要尽量覆盖不同光线、不同角度、不同距离、不同人群否则模型换个场景精度下降很明显。2. 环境准备与项目初始化2.1 硬件配置怎么定我实际跑训练的机器是一张8G显存的显卡训练yolo11s模型batch size设成8图片尺寸640×640大概跑100个epoch需要一两个小时。如果你只有CPU也能训练但速度会慢很多建议先用yolo11n这种最小模型验证流程确认代码没问题再上大模型。推理侧的硬件要求就低很多了。我的主力测试机没有独立显卡纯CPU跑yolo11n大概每帧200到400毫秒肉眼看着有点卡但能用。有显卡的话yolo11s在摄像头实时画面上可以跑到30FPS左右流畅度没问题。2.2 Python虚拟环境与依赖安装我强烈建议用虚拟环境不要一股脑把包装到系统Python里。这个项目涉及的包比较多包括PyQt5、ultralytics、opencv、torch、torchvision、numpy、Pillow、sqlite3等版本冲突的坑我后面会专门讲。创建虚拟环境并安装依赖python -m venv venv source venv/bin/activate # Windows系统执行 venv\Scripts\activate pip install --upgrade pip pip install ultralytics torch torchvision opencv-python PyQt5 Pillow这里要注意PyQt5的依赖目录比较大安装可能需要几分钟。opencv-python和opencv-contrib-python不要同时装否则会互相覆盖文件我最初就踩过这个坑。安装完成后做一次快速验证导入关键库不报错即可python -c import torch, ultralytics, PyQt5, cv2; print(环境OK)2.3 项目目录结构规划我习惯把代码、模型、数据、界面资源分开存放。这样不仅看着清楚打包和备份也方便。最终项目目录大致长这样smoke_detection_system/ ├── app.py # 程序入口 ├── main_window.py # 主界面逻辑 ├── login_window.py # 登录注册界面 ├── detector.py # YOLOv11检测模块 ├── database.py # SQLite用户和记录管理 ├── models/ │ ├── yolo11s.pt # 预训练或训练好的模型权重 │ └── best.pt ├── datasets/ │ ├── train/ │ ├── val/ │ └── smoke.yaml ├── ui_style.qss # 界面样式表 └── records.db # 数据库文件2.4 模型权重与预训练文件准备YOLOv11模型的权重文件可以用ultralytics库直接下载。第一次运行时会自动下载预训练权重但国内网络环境有时候下载慢可以手动把权重文件放到models目录下并设置环境变量指向本地路径。先写一段简单的测试代码确认模型能正常跑起来from ultralytics import YOLO model YOLO(models/yolo11s.pt) results model.predict(sourcetest.jpg, conf0.25, saveTrue) print(results[0].boxes)如果这个能跑通说明模型侧没问题接下来就可以进入训练阶段。3. 模型训练与优化实录3.1 数据集标注与yaml配置标注工具我用的开源标注工具导出格式为YOLO的txt格式。每张图片对应一个同名txt文件每一行内容为类别id归一化中心点x归一化中心点y归一化宽度w归一化高度h。比如一张640×480的图上有一个人吸烟检测框左上角(100, 80)右下角(300, 400)那么txt内容就是0 0.3125 0.5 0.3125 0.6667所有的标注文件要和图片名完全一致图片统一放在images目录标签统一放在labels目录。数据集根目录下的smoke.yaml这样写path: datasets/smoke # 数据集根目录 train: images/train val: images/val names: 0: smoking注意yaml里的路径建议写相对路径使用Ultralytics训练时会自动拼接。如果写绝对路径换一台电脑就要全局替换很麻烦。3.2 训练参数怎么选训练脚本我用的是Ultralytics提供的Python API。核心参数如下from ultralytics import YOLO model YOLO(models/yolo11s.pt) model.train( datadatasets/smoke/smoke.yaml, epochs100, imgsz640, batch8, device0, patience20, workers4, projectruns/smoke, nameexp1, pretrainedTrue, )epochs第一次建议设100同时打开patience做早停。如果训练到70个epoch后mAP不再提升会自动停止省时间。batch大小要根据显存调节。8G显存跑yolo11s、640分辨率、batch8是安全的。如果显存不够优先把batch降到4再不行把imgsz降到512不要一上来就调模型结构。workers是数据加载线程数Windows系统下建议不要超过4太高反而容易引发DataLoader报错。3.3 训练过程怎么看指标训练过程中ultralytics会实时输出loss、precision、recall、mAP50、mAP50-95这些指标。很多新手只看mAP但我建议多关注loss曲线。loss不断下降是正常现象。如果训练集loss很低验证集loss却在升高说明过拟合了。这时候早停会起作用也可以在数据层面做增强、增加样本量或者换用带更强正则化的模型配置。mAP50是判断模型是否可用的关键指标。吸烟检测的场景目标不算特别难mAP50能到0.85以上就算合格。如果你的mAP50只有0.5左右先不要怀疑模型先检查数据标注有没有问题、样本类别是否均衡、训练集和验证集有没有污染。训练完成后best.pt和last.pt会保存在runs/smoke/exp目录下。best.pt是验证集表现最好的权重部署时用best.pt不要用last.pt。3.4 模型导出与本地测试训练得到的best.pt可以继续用PyTorch格式直接推理也可以导出成ONNX格式。ONNX的好处是部署时不依赖PyTorch推理速度更快还能方便后续转成TensorRT或者OpenVINO。导出ONNX的命令非常简单model YOLO(runs/smoke/exp/weights/best.pt) model.export(formatonnx, imgsz640)导出的best.onnx放在同目录下。推理时同样使用ultralytics的YOLO接口加载或者用onnxruntime加载。在纯CPU机器上onnx格式通常比PyTorch格式更快一些。本地测试我用的一段最简代码from ultralytics import YOLO model YOLO(runs/smoke/exp/weights/best.pt) results model.predict( sourcetest_video.mp4, conf0.35, iou0.45, saveTrue, save_txtTrue, showFalse, )conf是置信度阈值。阈设得太低会误报严重设得太高会漏检。训练好的模型建议先用0.25看效果再根据实际调。3.5 提升精度的几个实用套路第一个套路是难例挖掘。第一轮训练完把验证集里漏检的图片找出来单独收集类似场景的硬样本重新标注后加入训练集。这个方法往往比盲目增加数据量更有效。第二个套路是数据增强。Ultralytics默认开了不少增强但现场光照复杂时可以自己加。比如调整亮度、对比度、高斯噪声、随机裁剪。增强不是越多越好如果增强过猛模型可能学不到真实分布。第三个套路是调整模型大小。如果你用yolo11n训练出来mAP不高换成yolo11s或者yolo11m在显存允许的情况下精度通常会有明显提升。我最后部署用到的是yolo11s综合效果最合适。第四个套路是后处理调参。推理时不一定要把所有检测框都显示出来。在特定场景下可以提高conf到0.4减少误报或者提高iou到0.5减少重复框。这些阈值不是固定的要根据现场监控距离和角度微调。4. UI界面、登录注册与检测流程实现4.1 登录注册界面设计登录界面我用PyQt5的QWidget搭建包含用户名输入框、密码输入框、登录按钮、注册按钮。整体用垂直布局放在窗口中央再通过QSS控制圆角和背景色。代码结构大致如下class LoginWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle(吸烟检测系统 - 登录) self.setFixedSize(400, 300) self.init_ui() def init_ui(self): layout QVBoxLayout() self.username_edit QLineEdit() self.password_edit QLineEdit() self.password_edit.setEchoMode(QLineEdit.Password) layout.addWidget(QLabel(用户名)) layout.addWidget(self.username_edit) layout.addWidget(QLabel(密码)) layout.addWidget(self.password_edit) self.login_btn QPushButton(登 录) self.register_btn QPushButton(注 册) layout.addWidget(self.login_btn) layout.addWidget(self.register_btn) self.setLayout(layout)登录和注册按钮的信号分别绑到对应槽函数。注册时先检查用户名是否已存在如果不存在就把密码哈希后写入数据库。4.2 密码存储与登录注册逻辑密码绝对不能明文存。我这里用hashlib加盐哈希每人生成随机盐存储格式为“盐值:哈希值”。import hashlib import os def hash_password(password: str) - str: salt os.urandom(16).hex() digest hashlib.sha256((salt password).encode()).hexdigest() return f{salt}:{digest} def verify_password(password: str, stored: str) - bool: salt, digest stored.split(:) check hashlib.sha256((salt password).encode()).hexdigest() return check digest数据库操作集中放在database.py模块里。用户表字段id、username、password_hash、created_at。检测记录表字段id、username、detect_time、image_path、has_smoking、confidence。登录成功后就关闭登录窗口实例化主窗口并显示。这个流程听着简单但要注意主窗口必须在登录窗口关闭后再show否则会出现两个窗口一起闪的问题。4.3 主界面布局与功能按钮主界面用QMainWindow搭建左侧是操作区右侧是视频/图片显示区底部是检测结果信息栏。操作区放三个大按钮打开图片、打开视频、打开摄像头下面是检测开关和置信度滑块。界面显示图片我用的QLabel通过setPixmap把OpenCV读到的BGR帧转成RGB后显示。这里有个容易踩的坑直接用cv2.imread读出来是BGR格式用PyQt显示前要转RGB否则画面颜色发蓝发红急救半天才发现是颜色通道问题。def frame_to_pixmap(frame): rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_frame.shape bytes_per_line ch * w q_img QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888) return QPixmap.fromImage(q_img)4.4 摄像头与视频检测流程摄像头实时检测最大的问题是不能把推理放在主线程里否则界面会卡死。我的做法是用QThread加信号槽线程循环读取摄像头帧送入模型推理把结果框绘制好之后再通过信号把图像发回主界面。class DetectThread(QThread): change_pixmap pyqtSignal(QPixmap) update_info pyqtSignal(str) def __init__(self, model_path): super().__init__() self.model YOLO(model_path) self.cap cv2.VideoCapture(0) self.is_running True def run(self): while self.is_running: ret, frame self.cap.read() if not ret: continue results self.model.predict(frame, conf0.35, verboseFalse) for box in results[0].boxes: x1, y1, x2, y2 map(int, box.xyxy[0]) conf float(box.conf[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, fsmoking {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) self.change_pixmap.emit(frame_to_pixmap(frame))关闭摄像头时先要设置is_running为False再调用wait()等待线程退出否则程序会报“QThread: Destroyed while thread is still running”的错误。图片检测就简单多了加载图片后直接调用model.predict绘制检测框后显示。视频检测是逐帧读取逐帧检测如果视频分辨率高建议先缩放一档否则CPU推理会掉帧很严重。4.5 检测结果保存与历史查询每次检测结束后我会把检测时间、用户、是否有吸烟、置信度最高值、当前帧截图保存路径写入数据库。def save_record(username, image_path, has_smoking, confidence): conn sqlite3.connect(records.db) cur conn.cursor() cur.execute( INSERT INTO detect_records(username, detect_time, image_path, has_smoking, confidence) VALUES(?, datetime(now, localtime), ?, ?, ?) , (username, image_path, int(has_smoking), confidence)) conn.commit() conn.close()主界面上可以加一个“历史记录”按钮弹出新窗口用QTableWidget展示最近记录。点某一行可以直接显示当时的检测截图。这一块对实际项目验收挺加分用户能直观看到系统在工作。5. 常见问题与避坑指南5.1 依赖冲突导致界面起不来这个项目最容易翻车的地方就是ultralytics、torch、opencv版本互相不兼容。最典型的表现是import ultralytics报错或者cv2的版本和torch的Vision模块冲突。我的建议是不要使用最新版全家桶也不要使用太老的版本。先按我这里给的组合安装确认能跑通后再升级。如果之前已经装过别的深度学习环境最好新建一个干净的虚拟环境。5.2 训练时显存不足训练时报“CUDA out of memory”这太常见了。解决步骤依次是把batch从8降到4如果还不行就降到2把imgsz从640降到512把workers降到0排除数据加载占显存的假象。如果显存实在只有4G建议换yolo11n模型先训练出基线不要硬跑yolo11m或yolo11l。有些资料说可以用混合精度训练减少显存ultralytics默认就开了AMP所以这不是一个可自定义的大头。5.3 UI视频检测卡顿卡顿原因通常是推理放在了GUI主线程或者摄像头帧率太高但推理速度跟不上。前者必须用QThread解决后者可以在读取视频帧后做隔帧检测或者把图片尺寸缩小后再推理。还有一个容易被忽略的点UI显示时setPixmap的尺寸如果超过QLabel原始大小PyQt会做缩放而缩放本身也消耗性能。建议先把QImage缩放到和QLabel匹配的尺寸再转成QPixmap显示。5.4 模型精度和误报问题模型在训练集上效果不错一到现场就误报或漏报大多数情况是训练数据和现场场景差距太大。比如训练集全是白天室外图现场是晚上室内灯光模型当然不认。解决办法有两个一是收集现场真实照片加入训练集二是尝试把推理图像做预处理比如统一做亮度归一化。另外摄像头摆放角度也有影响尽量让检测画面和训练样本的视角接近。5.5 常见问题速查表问题现象可能原因解决措施训练时CUDA内存不足batch或imgsz过大调小batch调小imgsz换小模型CPU推理很慢模型大且没有用ONNX换yolo11n导出ONNX隔帧检测界面显示画面偏蓝BGR未转RGBcv2.cvtColor转RGB后再显示关闭摄像头报线程错误线程未正确退出设置停止标志并调用wait()登录后界面闪退数据库表未初始化确保首次运行时执行建表语句mAP50很低标注不准确或样本太少重新检查标注补充困难样本画面中有人但没框出来目标太小或距离太远调整摄像头位置提高输入分辨率误报严重conf阈值太低调高conf到0.4或0.45还有一些小细节值得多说一句模型推理的conf阈值和nms的iou阈值最好做成界面上的滑块方便现场调试。我最初把这两个参数写死结果换了一个场景就疯狂误报后来改成可调节才舒服很多。最后分享一个我觉得最值钱的经验这类检测系统真正决定成败的不是模型结构而是数据集和场景匹配度。YOLOv11本身已经足够强训练代码无非那几十行但数据采集、标注、清洗这份功夫花多少模型效果就回报多少。建议第一版先用现有数据快速跑通全流程再花一周时间专门补现场数据迭代一版模型效果会有质的提升。另外尽量把训练和推理的代码封装得干净一点界面和检测逻辑彻底解耦。我当时为了赶进度在界面代码里直接写了模型推理后来换新模型时改得头皮发麻。如果你现在刚开始做一定先画好模块边界后面维护会轻松非常多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑