尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv5人员跌倒检测实战:数据集标注、模型训练与部署避坑指南

YOLOv5人员跌倒检测实战:数据集标注、模型训练与部署避坑指南 简介面向深度学习目标检测学习者和智慧养老场景开发者这套资料提供基于yolov5训练老人跌倒检测模型的完整工程涵盖预训练权重、专用数据集与全部源码可用于复现实验、二次开发或部署测试。压缩包共331个文件以图片样本、配置文档、训练脚本、权重文件、测试视频及标注文件为主整体约208MB结构覆盖数据、模型、训练与验证各环节。已有309人学习下载适合初学者快速理解yolov5跌倒检测全流程也适合工程师在现有基础上调整策略。资源内嵌官方配置与训练脚本并附带训练日志、结果统计及容器部署配置支持从数据准备到模型评估的闭环操作。其中小型跌倒数据集可作为基线便于结合数据增强或迁移学习进一步提升泛化能力。1. 为什么人员跌倒检测绕不开yolov5先想清楚自己要训什么做智慧养老或者工地安全的人大概率在某个晚上搜过“用yolov5训练人员跌倒模型”这句话。监控24小时开着值班员不可能一直盯着屏幕老人夜里从床上摔下来、工人在脚手架旁边倒地等发现往往过了半小时。标题里这份打包好的数据集加源码想解决的就是“给监控装一双自动报警的眼睛”。我的判断是这条路能走通但难点不在模型能不能训出来而在数据标得对不对、阈值怎么调、跌倒判定怎么写。这篇就按这个顺序把这几个坑趟一遍。适合手里有一块显卡、想两三天跑出第一版、再把模型接到摄像头后面做报警的人如果你只想看原理这篇也可以当一份落地参考。2. 把跌倒数据集整理成YOLO格式类别策略、标注脚本与yaml配置2.1 跌倒检测是时序任务但数据准备要按单帧来做跌倒和行人检测有一个本质区别行人检测的目标是“人”这个静态类别跌倒检测的目标是“从站立变成躺地”这个动态事件。检测模型本身只处理单帧图像它无法知道“这个人前一秒站着这一秒倒了”。所以在数据准备阶段就要做一个取舍把“跌倒”建模成“倒地姿态的人”用单帧检测框住倒在地上的人再靠后面的时序逻辑区分真跌倒和蹲下、躺下。这个取舍直接影响标注方案。常见做法是只标一个类别名字可以叫 fallen 或者 fall把所有倒地、躺地、趴地的人框出来。不推荐第一版就标 standing 和 fallen 两类因为站立姿态的样本人种、服装、角度差异太大需要喂很多数据才能学稳而且两类之间边界模糊反而把简单任务做复杂。我一般先标单类把站立的人全部忽略让模型专注学习“非正常倒地人体”的视觉特征。别小看这一步我见过有人用两类标注训了一个礼拜mAP看起来不错推到真实场景后蹲在地上干活的人全被框成了fallen后来改成单类加时序判定误报直接降了一半。2.2 数据集的构成正样本、负样本和场景多样性训练一个能用的跌倒检测器数据比模型结构重要。第一版数据集建议包含三类内容真实跌倒的正样本、容易混淆的负样本、不同场景下的成像差异。正样本要覆盖姿态多样性仰卧、侧卧、俯卧、趴地、靠墙滑倒、从椅子上跌落。每种姿态至少要有几十个样本。场景上优先覆盖你的实际部署环境养老院房间、走廊、卫生间、工地、楼梯口。如果只有公开数据集比如 Le2i 或者 UR Fall Detection 这类公开跌倒数据集也要自己抽帧看一眼因为公开数据集的相机视角和标注口径不一定和你的场景一致直接拿来训容易在视角迁移上翻车。网络视频截帧也是一个来源但要注意版权商用项目最好不要用来源不明的视频。负样本是这一类项目最容易忽略的部分。蹲下捡东西、弯腰系鞋带、坐在矮凳上、躺沙发、躺在床上这些都会让人的外接框从瘦高变矮胖和跌倒姿态在单帧上几乎无法区分。负样本一般建议占训练集的三成到五成数量不够就把日常监控里正常活动的片段抽帧加进去。数据规模上第一版单类检测正样本 800 到 1500 张负样本 400 到 800 张就足够训出一个效果可用的模型。别一上来追求几万张标注成本高收益却很低。2.3 标注工具与VOC转YOLO格式脚本标注建议直接用 LabelImg 或 Label Studio导出格式选 YOLO。YOLO 格式的标签是 txt 文件每一行代表一个目标格式为类别id 中心点x 中心点y 宽度 高度其中坐标值都归一化到 0 到 1 之间。很多数据集下载下来是 Pascal VOC 的 XML 标注需要转换。下面这个脚本就是把 VOC 的 XML 转成 YOLO 格式 txt 的常用实现# voc2yolo.py — 把Pascal VOC的XML标注转成YOLO格式txt import xml.etree.ElementTree as ET import os def convert(xml_path, out_dir, classes): tree ET.parse(xml_path) root tree.getroot() # 取原图尺寸归一化必须用它不能偷懒取固定值 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) if img_w 0 or img_h 0: print(f跳过异常文件: {xml_path}) return lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 中心坐标和宽高都要除以原图宽高 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if not lines: return out_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines))这个脚本有三个边界点需要你重点检查第一img_w 和 img_h 必须来自 XML 里的原始尺寸不能从标注工具的界面截图猜否则归一化坐标全部错位训练时 loss 会异常这类错误非常隐蔽。第二x2 必须大于 x1y2 必须大于 y1一旦出现反向框宽高算出负数模型训练直接报错。第三标签 txt 和图片 jpg 必须同名且在同一级目录YOLOv5 训练时靠同名匹配图片和标签一个对不上就会在“Scanning labels”阶段被跳过。转换完成后的目录结构YOLOv5 默认是这样读取的fall_data/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml2.4 data yaml配置几个必须注意的相对路径坑数据集和标签准备好之后写一份 yaml 告诉 YOLOv5 去哪里找数据# data/fall.yaml — 跌倒检测数据配置 train: /home/user/fall_data/images/train val: /home/user/fall_data/images/val nc: 1 names: [fallen]train 和 val 指向的是 images 目录YOLOv5 会自动到同级 labels 目录找对应标签。这里最大的坑是相对路径如果你在项目根目录运行 train.py写 data/fall.yaml 没问题但如果换一个终端、换一个工作目录启动训练相对路径就会失效报 FileNotFoundError。我一般都会写绝对路径省掉这类玄学报错。另外 val 一定要从训练集里分出去不要用同一批图片既训练又验证否则指标虚高一上真场景就打回原形。如果预算允许再从实际摄像头里抽几十张没见过的画面单独建一个 test 集这个后面第6章会讲。3. 用YOLOv5训练自己的跌倒模型环境配置与训练超参数调优3.1 环境配置conda装依赖只花半个下午yolov5环境配置本身不复杂常见做法是先建一个独立的 conda 环境避免把系统 Python 搞乱conda create -n yolov5 python3.9 -y conda activate yolov5 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里有一个额外注意点requirements.txt 里默认装的 torch 是 CPU 版本还是 CUDA 版本取决于你安装时的平台。如果你有 N 卡建议先跑一句nvidia-smi看驱动支持的最高 CUDA 版本然后去 PyTorch 官方按对应 cu 版本单独装 torch再装 requirements 里剩下的依赖。很多人卡在“训练时显示 CUDA unavailable”九成是这一步没配对。装完可以用python -c import torch; print(torch.cuda.is_available())验证输出 True 再继续。3.2 第一次训练的最小命令和逐参数说明环境通了之后第一次训练建议拿标题里这份数据集直接跑。最小训练命令长这样python train.py \ --data data/fall.yaml \ --cfg yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --imgsz 640 \ --device 0 \ --cache逐个说一下参数的含义和调整思路--data指向刚才写好的 fall.yamlYOLOv5 从这里读训练集和验证集路径。--cfg指定模型结构。yolov5s 是 balanced 配置显存不够可以换成 yolov5n精度优先可以换 yolov5m。结构越大训练越慢推理也越慢跌倒检测这种实时场景不建议起步就上 x。--weights加载预训练权重。用官方在 COCO 上预训练好的 yolov5s.pt 做初始化迁移学习能省大量训练时间。如果只想从零训可以写成--weights 但我不推荐数据量不大时从零训练容易欠拟合。--batch-size显卡显存决定。以 8G 显存为例yolov5s 在 640 分辨率下 batch 16 差不多是上限。显存不够先降 batch不要先降分辨率。--epochs第一版跑 100 轮看曲线走势再决定加还是减。--imgsz输入分辨率。640 是默认值。如果你的摄像头画面里人比较小可以试 1280mAP 会涨但训练和推理速度都会明显下降。--device0 表示第一块显卡CPU 训练可以写--device cpu但速度会慢到怀疑人生。--cache把图片缓存进内存减少每轮训练读硬盘的 IO 时间前提是你的内存够大几十 G 数据量下这个参数很管用。3.3 训练曲线怎么读跌倒场景更该盯召回率训练结束后结果存在runs/train/exp/目录里打开results.png能看到 loss 曲线和指标曲线。三个 lossbox_loss、obj_loss、cls_loss整体应该是前几十轮快速下降后面趋于平缓。如果 loss 还在明显下降说明 100 轮不够可以接着训或直接用 last.pt 继续。跌到场景里最值得盯的指标是召回率不是 mAP。mAP0.5 高只能说明“检测框位置对了”但对误报和漏报的平衡给不了直观感受。实际监控场景中漏掉一次跌倒可能比误报十次更严重所以我一般会去看验证集上的 recall 值低于 0.8 就先把阈值往低调宁可多报警让后端时序逻辑去过滤。3.4 超参数选择模型体量、图像分辨率和数据增强的取舍yolov5超参数有两个层面一个是网络结构配置一个是训练增强超参。结构层面我的选择参考是这张表模型配置精度速度我一般的应用场景yolov5n偏低最快边缘盒子、树莓派这类低算力设备yolov5s均衡快本地摄像头接入的常规服务器yolov5m更高较慢对精度要求高且推理机器有较好显卡增强超参在data/hyps/hyp.scratch.yaml里主要关注三个hsv_h 控制色调增强fliplr 控制水平翻转概率mosaic 控制四图拼接增强。跌倒检测里水平翻转可以保留因为左右对称不会改变跌倒语义但垂直翻转必须关掉倒立的人不是跌倒场景YOLOv5 默认不做垂直翻转不要手动去开。mosaic 增强在数据量少时很有用可以让模型在小目标和大目标之间更稳定如果你发现训练后期 mAP 波动大可以先关掉 mosaic 重训一轮对比。分辨率层面640 是性价比最优解只有当画面里的人占比很小比如走廊远端跌倒的远景才值得把 imgsz 提到 1280这是一种用计算量换小目标精度的做法别一开始就上。4. 训练与部署避坑指南四个常见的翻车现场4.1 现象一mAP很高一到真实场景就漏检现象是验证集 mAP0.5 到了 0.9看起来挺好的模型接到摄像头后人躺在地上半天检测不出来。原因基本是训练集和真实场景分布不一致。验证集也是从训练集里分出来的代表不了真实环境。常见差异包括摄像头安装高度不同导致的人体比例差异、夜间或弱光下的成像风格变化、被桌椅或床沿遮挡一半的身体。解决思路分两步。第一步把真实场景里采集的视频抽帧加入训练集并重新划分验证集让模型看到目标场景的光线和遮挡模式。第二步如果真实场景里人确实小用--imgsz 1280重新训一轮。我一般会在训练完之后做一次抽样推理直接从监控视频里抽 100 帧跑 detect.py肉眼扫一遍结果这一步比看任何指标都真实。4.2 现象二弯腰、蹲下、坐地全被误报成跌倒这是跌倒检测项目里最经典的翻车现场。弯腰捡东西时人的外接框从瘦高变成矮胖变形方向和跌倒非常接近单帧图像上几乎无法区分。原因是模型只学了“矮胖人体框”这个表象没有学到“贴地”这个关键语义。解决要分两层。数据层在负样本里专门加入弯腰、蹲下、坐在矮凳上、躺沙发这几类动作让模型知道这些是负例。如果用的是单类 fallen这些负样本就是无标注图片YOLOv5 对没有标签的图片会自动忽略目标训练时它们会作为背景参与损失计算能有效压低误报。推理层增加时序判定逻辑这一点第5章展开核心思路是跌倒不仅形态变了而且中心点高度在短时间内快速下降弯腰和蹲下的中心点变化幅度和速度远小于真实跌倒。4.3 现象三训练时显存不够或者数据读取慢到怀疑人生Train 过程报 CUDA out of memory大家的第一反应都是降 batch但有一个更隐蔽的原因显存里可能同时驻留了其他进程。先跑nvidia-smi看显卡占用情况如果有别的训练任务占着先协调资源。排除占用后再按顺序调参数先把--batch-size降到 8还不行就检查是否开着 tensorboard 或 wandb这些工具会额外吃显存和内存。如果数据读取成了瓶颈每轮训练都要等很久两个手段最有效--cache把图片加载进内存以及把--workers从默认值调高让数据加载线程跑得比 GPU 计算快。还有一种情况是图片尺寸差异太大模型在 resize 阶段耗时严重可以在数据准备阶段统一把图片缩放到接近 640 的短边减少运行时缩放压力。4.4 现象四loss先降后飞最后发现是标注数据有脏标签训练到二三十轮时 loss 突然反弹这种先降后飞的曲线往往不是学习率问题而是标签有异常。最常见的有三种标签框超出图像边界导致宽高比失衡、同一个目标被重复标注两个互相矛盾的框、类别 id 超出 nc 数量。YOLOv5 在训练前会扫描标签格式错误会直接报错但坐标越界和重复框不一定报错会悄悄污染训练过程。解决的办法是训练前写一个标签校验脚本把每张图的框都画出来人工抽查一遍重点看有没有超出边界的框以及有没有该标没标的漏网之鱼。我习惯用 detect.py 的--save-txt打开可视化输出或者直接用 OpenCV 跑一遍标注叠加虽然多花半小时却能省下后面好几天的排查时间。5. 从检测框到跌倒判定宽高比、中心点骤降与报警去重的实现5.1 单帧检测不够跌倒是一个持续几帧的事件模型输出的只是每个人体框的坐标和置信度要把它变成“跌倒”这个事件必须加时序逻辑。原因很直白一个人躺在地上不动单帧看起来和睡觉、躺着休息没有区别一个人蹲下系鞋带单帧的宽高比也接近倒地姿态。但如果在连续帧里这个人的外接框高度突然变小、重心位置快速下移、倒地后一段时间内保持低姿态我们就可以比较自信地判定为跌倒。常见的判定方案是滑动窗口加计分。每一帧拿到检测框后计算两个特征宽高比 w/h 和中心点的 y 坐标。宽高比变大说明人从直立变成横躺中心点 y 坐标相对前几帧明显下移说明人在短时间内倒地。连续多帧满足这两个条件才触发报警。这个思路的实现成本低而且比单帧 threshold 判断稳得多。5.2 一个时序判定器的Python实现下面这个类可以直接接在 YOLOv5 的 detect.py 输出后面使用# fall_judge.py — 基于检测框的时序跌倒判定器 from collections import deque class FallDetector: def __init__(self, ratio_thresh1.2, drop_pix40, confirm_frames3, window15): self.ratio_thresh ratio_thresh # 宽高比阈值超过认为倒地 self.drop_pix drop_pix # 中心点最低下移像素数 self.confirm_frames confirm_frames # 连续满足条件的帧数 self.window deque(maxlenwindow) self.score 0 self.smooth_ratio None def update(self, bbox): # bbox 来自YOLOv5输出: [x1, y1, x2, y2, conf, cls] x1, y1, x2, y2 bbox[:4] w x2 - x1 h y2 - y1 if h 0: return False ratio w / h cy (y1 y2) / 2.0 # 指数平滑抑制检测框抖动带来的毛刺 if self.smooth_ratio is None: self.smooth_ratio ratio else: self.smooth_ratio self.smooth_ratio * 0.8 ratio * 0.2 self.window.append((self.smooth_ratio, cy)) if len(self.window) self.confirm_frames 1: return False # 取前几帧作为基准对比当前帧 prev list(self.window)[:-1] prev_ratio sum(p[0] for p in prev) / len(prev) prev_cy sum(p[1] for p in prev) / len(prev) ratio_delta self.smooth_ratio - prev_ratio cy_delta cy - prev_cy if ratio_delta 0.4 and cy_delta self.drop_pix: self.score 1 else: self.score max(0, self.score - 1) return self.score self.confirm_frames这里几个参数值得单独说明。ratio_thresh是绝对宽高比阈值成年人站立时检测框一般高大于宽w/h 约 0.3 到 0.6倒地后 h 变小、w 变大w/h 通常超过 1.0。建议至少 1.2太低会把坐姿也框进来。drop_pix40指中心点下移像素数这个值依赖你的输入分辨率摄像头画面里人的尺寸大就调大比如 640 分辨率下 40 到 80 像素都是合理范围需要拿真实视频试几轮。confirm_frames3表示连续 3 帧满足条件才算跌倒太低容易被单帧抖动骗过太高会延迟报警3 到 5 是常见取值。宽高比变化的阈值 0.4 表示当前帧比前几帧平均宽高比大出 0.4这个值是经验值如果你发现蹲下误报多可以上调到 0.5 甚至 0.6。5.3 阈值与冷却时间误报率的最后一道闸门时序判定器只能过滤掉“没有倒地动作”的误报真实场景中还会遇到另一种情况一个人真的摔倒了模型也连续 3 帧判定成功但系统每次判定都推一条报警一晚上推几十条值班员直接麻木。所以报警端必须有冷却逻辑常见做法是记录上一次报警时间冷却时间内不再重复推送# alarm_dedup.py — 报警去重示例 import time last_alarm 0 cooldown 30 # 同一目标30秒内不重复报警 # 每帧拿到目标框后调用 if detector.update(bbox): now time.time() if now - last_alarm cooldown: # 触发推送截图、记录时间、发通知 print(fall alarm triggered) last_alarm now冷却时间可以结合业务需求调整养老院场景一般 20 到 30 秒因为老人倒地后不会瞬间自己爬起来这个窗口足够覆盖后续处置动作如果用在工地安全可以缩短到 10 秒毕竟要尽快响应。还有一个细节是置信度阈值检测器输出的 conf 默认 0.25跌倒场景我建议下调到 0.15 到 0.2宁可多检几个框让时序判定器去过滤也不要让漏检过一个真实跌倒。漏报一个的代价远大于误报十个这是所有跌倒检测项目通用的取舍。6. 验证模型的最后一公里场景实测集与留一份后悔药6.1 每个权重都留备份别随手覆盖训练完成之后runs/train/exp/weights/里会有best.pt和last.pt好习惯是把每一版权重都复制出来用带日期的编号命名mkdir -p backup cp runs/train/exp/weights/best.pt backup/fall_v1_20250110.pt cp runs/train/exp2/weights/best.pt backup/fall_v2_20250112.pt这个习惯我是在吃了亏之后才养成的。有一版模型我调了数据增强重新训练指标比上一版好就把上一版权重覆盖了。后来换到实测场景发现新版误报明显想回退到旧版只能翻缓存重新训练。从此之后所有训练完的权重都先备份再继续调下一版这个操作两秒钟省的是半天到一天的重训时间。6.2 场景实测集比mAP更值得信任mAP 只能证明模型在验证集上的表现验证集和训练集同分布说明不了真实部署效果。我最后的习惯是每版模型训出来后拿手机或监控拍 10 到 20 段真实场景视频覆盖正常走路、弯腰、蹲下、坐下、躺沙发和真实跌倒。这些视频不进训练集专门用于测试。python detect.py \ --weights backup/fall_v2_20250112.pt \ --source scene_test/ \ --conf-thres 0.2 \ --save-txt然后把输出结果按视频逐段过一遍数一下漏检和误报。这个过程花不了多少时间但比任何指标都直观。当你把模型从“在验证集上 mAP 很高”推进到“在真实场景里漏报为零、误报可接受”这个项目才真正算能交付。我也希望你最开始就跑通这个闭环再回头调整数据增强和阈值方向会清晰很多少走我走过的那些弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表