☰
YOLOv5打架行为检测实战:从数据集标注到PyQt界面与边缘部署
2026/10/10 21:41:45 网站建设 项目流程

简介:本资源面向计算机视觉学习者与安防场景开发者,提供一套可直接落地的打架行为检测方案,包含YOLOv5训练好的权重、一万余张标注数据集以及PyQt可视化界面,适合课程设计、毕业设计或行为识别项目快速验证。压缩包共2000个文件,以1994个XML标注文件为主,另含3个Python脚本与3份环境配置及PyQt使用说明PDF,整体约508MB;数据集已按train、val、test划分并附data.yaml,类别为normal与fight两类,txt格式标签可直接供YOLOv5、YOLOv7、YOLOv8、YOLOv9等算法训练。已有160人学习下载。读者可借助预训练权重直接推理检测,也可基于完整目录结构复现训练流程,配合说明文档完成环境配置与界面调用,省去数据采集与标注成本。

1. 从一张监控截图说起:YOLOv5打架行为检测到底在做什么

凌晨两点,值班室的屏幕上弹出一段自动截取的视频片段,画面里两个人从推搡升级到挥拳,系统在第三秒就框出了两个交叠的人体框,并打上“fight”标签。这不是什么高精尖的实验室demo,而是一套用 YOLOv5 训练出来的打架行为检测模型在跑。很多人第一次听到“打架行为检测”,脑子里浮现的是动作识别、时序建模、光流法那一套重装备,但真正落地到园区、工地、校园这些场景时,最稳、最快、最容易维护的方案,反而是把问题降维成目标检测——用 YOLOv5 直接检测画面里“正在打架的人”这个类别。它不追求理解整段动作的语义,只回答一个问题:这一帧里,有没有人正在做出打架姿态。配合 PyQt 界面,值班人员不需要懂命令行,打开软件、选视频、点开始,结果就实时画在画面上。这套组合适合谁?适合手里有几百到几千张标注图、想在一周内跑出一个能演示能迭代的原型的工程师,也适合已经用过 YOLOv5 做安全帽、烟火检测、想再扩一个行为类别的熟手。它不适合追求学术级动作识别精度的团队,但对绝大多数工程落地场景,够用,而且快。

2. 数据集怎么攒:从公开数据到自标注的取舍

2.1 打架行为检测的数据集长什么样

打架检测的数据集和普通目标检测有个本质区别:它的正样本极度依赖“姿态”而不是“物体”。一个人站着是 person,挥拳也是 person,区别在肢体关系。所以标注时不能只框人,要框“正在发生冲突的两个人或一群人”,通常做法是用一个较大的框把参与冲突的人整体包住,或者对每个参与人单独框但统一打上 fight 标签。常见做法是后者,因为 YOLOv5 本身是多框检测,单框包两人容易在密集场景下漏检。数据集来源一般三条路:一是公开的行为识别数据集里抽帧,比如 RWF-2000、Movies Fight 这类,抽帧后人工筛掉模糊和误标;二是自己从监控录像里截取,这是最贴合落地场景的,但要注意隐私合规,只保留人体区域、不涉及可识别身份信息;三是用现有 person 检测模型先跑一遍,把所有人框出来,再人工把其中打架的框改标签,这样能省一半标注时间。我一般会建议至少准备 1500 张图,其中正样本(含打架)不低于 500 张,负样本里要有大量“两个人靠得近但没打架”的干扰图,比如握手、递东西、排队,否则模型会把“靠近”学成“打架”。

2.2 标注格式转换与目录结构

YOLOv5 要的是 YOLO 格式的 txt,每行class x_center y_center width height,全部归一化到 0~1。如果你用 LabelImg 或 CVAT 标的是 VOC 的 xml,需要转一道。下面这个脚本是我常用的转换逻辑,处理了边界裁剪和空文件跳过:

import os import xml.etree.ElementTree as ET # 输入VOC xml目录,输出YOLO txt目录 voc_dir = "annotations_xml" out_dir = "labels_yolo" classes = ["person", "fight"] # 类别顺序要和训练时一致 os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(voc_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(voc_dir, xml_file)) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.iter("object"): cls_name = obj.find("name").text if cls_name not in classes: continue cls_id = classes.index(cls_name) bbox = obj.find("bndbox") x1 = float(bbox.find("xmin").text) y1 = float(bbox.find("ymin").text) x2 = float(bbox.find("xmax").text) y2 = float(bbox.find("ymax").text) # 裁剪到图像边界内,防止标注越界导致归一化出错 x1 = max(0, min(x1, w)) y1 = max(0, min(y1, h)) x2 = max(0, min(x2, w)) y2 = max(0, min(y2, h)) if x2 <= x1 or y2 <= y1: continue # 跳过无效框 xc = (x1 + x2) / 2.0 / w yc = (y1 + y2) / 2.0 / h bw = (x2 - x1) / w bh = (y2 - y1) / h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") if lines: txt_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(lines))

逻辑说明:先读图像宽高做归一化分母,再对每个 object 取类别名映射到 id,坐标裁剪防止越界,最后按 YOLO 格式写入。参数上classes列表的顺序必须和后面训练时 data.yaml 里的 names 完全一致,否则标签全错。空文件不写,因为 YOLOv5 训练时遇到空 txt 会当作纯背景图,这是有用的,但如果你不希望背景图参与,就在生成后手动删掉。目录结构按 YOLOv5 官方推荐:

dataset/ images/ train/ val/ labels/ train/ val/

train 和 val 按 8:2 分,注意同一个视频抽出来的帧不能同时出现在 train 和 val,否则验证指标会虚高,这是血泪经验。

2.3 数据增强里哪些该开哪些不该开

YOLOv5 自带 mosaic、mixup、HSV 增强、随机翻转。打架检测里,mosaic 和 mixup 要谨慎:mosaic 把四张图拼一起,容易让两个不相关的人被拼成“近距离接触”,模型可能学到假的冲突关系。我一般会把 mosaic 关掉或把概率降到 0.3 以下,mixup 直接关。HSV 增强可以开,因为监控画面色偏常见。随机翻转要分场景:如果打架动作有方向性(比如右勾拳),水平翻转会造出不符合人体工学的样本,但实际监控里左右都有,所以水平翻转可以开,垂直翻转绝对不要开,人不会倒着打架。这些参数在hyp.scratch-low.yaml里改,训练时用--hyp指定。

3. 训练自己的打架检测模型:超参数怎么设才不翻车

3.1 从预训练权重起步还是从头训

除非你有十万张以上的标注图,否则一定从预训练权重起步。YOLOv5 在 COCO 上学到的人体特征对打架检测非常有用,因为打架首先得是人。常见做法是下载yolov5s.pt或yolov5m.pt,s 版本推理快适合 PyQt 实时显示,m 版本精度高一点但帧率会掉。我一般先用 s 跑通全流程,如果验证集漏检严重再换 m。命令如下:

python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/fight.yaml \ --weights yolov5s.pt \ --hyp data/hyp.fight.yaml \ --project runs/fight \ --name exp1

参数说明:--img 640是输入分辨率,监控画面里人通常占画面比例不大,640 够用,如果小目标多可以上 1280 但显存翻倍;--batch 16在 8G 显存上比较稳,爆显存就降到 8;--epochs 100配合早停,实际可能 60 轮就收敛;--data指向你的 yaml,里面写 train/val 路径和类别名;--hyp指向自定义增强配置。训练过程中重点看mAP@0.5和val/box_loss,如果 box_loss 震荡不降,多半是学习率太大或标注框有大量越界。

3.2 打架检测的锚框需不需要重新聚类

YOLOv5 默认锚框是基于 COCO 的,COCO 里人体框的宽高比和监控里打架的人体框有差异——打架时人可能弯腰、伸展,框更扁或更方。所以建议用自己的数据跑一遍 k-means 聚类。YOLOv5 仓库里有utils/autoanchor.py,训练时加--noautoanchor关闭自动锚框,然后手动跑:

python utils/autoanchor.py --data data/fight.yaml --weights yolov5s.pt

它会输出建议的 anchors 并计算 BPR(best possible recall)。如果 BPR 低于 0.98,说明默认锚框确实不匹配,把输出的 anchors 填到模型 yaml 里。这一步很多人跳过,结果就是小目标打架框回归不准,mAP 卡在 0.5 上不去。注意聚类用的图应该是训练集子集,别用验证集,否则等于偷看答案。

3.3 训练中必须盯的三个指标

第一个是metrics/mAP_0.5,这是最直观的,但别只看它,因为打架检测里负样本多,mAP 高不代表误报低。第二个是val/obj_loss,如果它一直降但 mAP 不涨,说明模型在学背景,可能是正样本太少。第三个是混淆矩阵,训练完在runs/fight/exp1/下会生成confusion_matrix.png,重点看 fight 被误判成 person 的比例,如果很高,说明模型分不清“人”和“打架的人”,这时候要么加更多打架正样本,要么把 person 类去掉只留 fight 和 background。我一般会保留 person 类,因为 PyQt 界面上同时显示人和打架框,值班人员能看清上下文。

4. PyQt 界面怎么接:从推理到可视化

4.1 界面线程与推理线程必须分开

PyQt 最大的坑就是卡界面。如果你在按钮点击槽函数里直接跑 YOLOv5 推理,视频一播放主线程就冻结,窗口直接“未响应”。正确做法是用 QThread 把推理循环扔到子线程,通过信号槽把带框的图像传回主线程显示。下面是最小可用的结构:

from PyQt5.QtCore import QThread, pyqtSignal import cv2 import torch class DetectThread(QThread): frame_signal = pyqtSignal(object) # 传numpy图像 def __init__(self, model, source): super().__init__() self.model = model self.source = source self.running = True def run(self): cap = cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame = cap.read() if not ret: break # 推理,model是加载好的YOLOv5 results = self.model(frame, size=640) annotated = results.render()[0] # 画框后的图 self.frame_signal.emit(annotated) cap.release() def stop(self): self.running = False self.wait()

逻辑说明:frame_signal用 object 类型传 numpy 数组,主线程收到后转成 QImage 再塞给 QLabel。self.running控制循环退出,窗口关闭时调用stop()避免线程泄漏。参数上size=640要和训练时一致,不一致精度会掉。注意results.render()返回的是列表,取第 0 个。这个结构跑 1080p 视频在普通 i5 上能到 15~20 FPS,够值班看。

4.2 检测结果怎么画才不糊

YOLOv5 自带的 render 画的是细线框,在监控大屏上远看还行,但 PyQt 窗口里如果缩放显示,线会糊。我一般自己写绘制,用 OpenCV 的rectangle加putText,框线宽按图像宽度动态算,比如thickness = max(2, int(frame.shape[1] / 640))。颜色上 fight 用红色,person 用绿色,这样值班人员一眼能区分。文字背景加个实心矩形,不然白字在亮背景上看不清。还有一点:如果连续多帧都检测到 fight,不要每帧都弹报警,加一个简单的计数器,连续 5 帧命中才触发声音或日志,否则单帧误检会把人烦死。

4.3 模型加载与界面初始化的顺序

PyQt 启动时加载 YOLOv5 模型要几百毫秒到几秒,如果放在主窗口构造函数里,界面会白屏一会儿。常见做法是先把界面显示出来,再在子线程里加载模型,加载完发信号启用“开始检测”按钮。模型加载用torch.hub.load或直接DetectMultiBackend,注意指定device为cpu或cuda:0,如果机器没显卡就老老实实 cpu,别硬等 cuda 报错。还有,模型路径别写死绝对路径,用os.path.join(os.path.dirname(__file__), "weights/fight.pt"),不然换台机器就翻车。

5. 避坑与排查:打架检测落地时最容易翻车的五件事

5.1 现象:模型把两个人靠近就判成打架

原因:训练集里负样本缺少“近距离但非打架”的图,模型把空间距离当成了冲突特征。解决:专门收集握手、递烟、排队、搀扶这类图,标成 person 或 background,数量至少和打架正样本持平,重新训练。如果已经训完不想重来,可以在推理后加一个简单规则:两个 fight 框的 IoU 超过阈值且持续多帧才报警,但这只是补救。

5.2 现象:PyQt 界面播放视频越来越卡,内存一直涨

原因:每帧都创建新的 QImage 和 QPixmap,旧对象没释放,Python 垃圾回收跟不上。解决:在子线程里复用 numpy 缓冲,主线程显示时用QLabel.setPixmap后手动del旧 pixmap,或者用QImage直接构造不经过QPixmap。更彻底的做法是限制显示帧率,比如每两帧显示一次,人眼看起来一样流畅。

5.3 现象:训练 loss 正常下降但验证 mAP 一直是 0

原因:data.yaml 里的val路径写错,或者 val 的 labels 目录为空,YOLOv5 找不到验证集就跳过评估。解决:检查data/fight.yaml里 train 和 val 的路径是相对于数据集根目录还是绝对路径,建议统一用绝对路径。再确认 val 的 images 和 labels 文件名一一对应,缺一个都会导致该图被忽略。

5.4 现象:换一台机器推理,检测框全偏了

原因:训练时图像做了 letterbox 填充,推理时如果直接 resize 不填充,坐标映射会错。YOLOv5 的DetectMultiBackend默认会做 letterbox,但如果你自己写预处理就容易漏。解决:要么直接用官方推理接口,要么自己写时严格按scale = min(640/w, 640/h)算缩放和 padding,再把框坐标反算回去。这个坑我踩过两次,框整体偏移几十像素,查了一晚上。

5.5 现象:PyQt 打包成 exe 后模型加载失败

原因:PyInstaller 没把weights和models目录打进去,或者torch.hub.load在打包环境里找不到缓存路径。解决:用--add-data把权重和模型配置目录加进去,代码里用sys._MEIPASS拼路径。另外 torch 打包体积巨大,可以用--exclude-module去掉不用的模块,但别删 torch 核心,否则推理直接崩。

6. 进阶技巧:把打架检测模型塞进 RK3568 或树莓派4B

如果你已经跑通了 PC 上的 YOLOv5 + PyQt,下一步大概率是想把它挪到边缘设备。RK3568 和树莓派4B 是热搜里出现最多的两个平台,但它们的算力差一个量级。RK3568 有 NPU,支持 INT8 量化,YOLOv5s 量化后能跑到 15 FPS 以上;树莓派4B 只有 CPU,跑原版 YOLOv5s 大概 2~3 FPS,基本没法实时看视频,只能做定时抓拍。所以选型上,要实时就上 RK3568,要低成本演示就树莓派加低分辨率。

RK3568 的路径是:PyTorch 训练 → 导出 ONNX → 用 RKNN-Toolkit2 转成 rknn 模型 → 板端用 rknn_api 推理。导出 ONNX 时注意把--img固定成 640,动态轴关掉,否则 RKNN 转换会报错。量化需要准备一批校准图,一般从训练集里抽 200 张就够,校准图要覆盖白天、夜晚、室内、室外,不然量化后夜间误检会飙升。下面是一个导出 ONNX 的命令:

python export.py \ --weights runs/fight/exp1/weights/best.pt \ --include onnx \ --img 640 640 \ --batch 1 \ --simplify

--simplify会调用 onnx-simplifier 去掉冗余节点,RKNN 转换时更顺。转 RKNN 的脚本在 RKNN-Toolkit2 的 examples 里改,重点配置mean_values和std_values,要和训练时的归一化一致,YOLOv5 默认是 0~1 归一化,所以 mean 填 0,0,0,std 填 255,255,255。板端推理后处理要自己写 NMS,因为 RKNN 输出的是原始特征图,别指望它帮你解码。

树莓派4B 上如果非要跑,建议用 YOLOv5n 或者把输入降到 320,然后开 OpenVINO 或 NCNN 推理框架,比原版 PyTorch 快 2~3 倍。但说实话,树莓派做实时打架检测体验很差,我一般只拿它做离线视频分析,一晚上跑完一天的录像,第二天看报警片段。最后说个习惯:不管上哪个边缘设备,先在 PC 上用同一段视频对比量化前后的检测结果,如果量化后 mAP 掉超过 5 个点,就回去检查校准集是不是太单一。这个对比步骤能帮你省下大量在板子上反复烧录的时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询