☰
YOLOv8摔倒检测实战:从数据标注到部署的完整代码与避坑指南
2026/9/26 2:59:11 网站建设 项目流程

简介:这份资源是面向深度学习初学者与安防、体育分析方向开发者的YOLOv8摔倒检测完整代码包,用于快速搭建可识别视频或图像中人物摔倒事件的目标检测系统,可应用于老年人监护、公共场所安全监控及运动员训练反馈等场景。压缩包为zip格式,整体约894.56MB,内含训练与推理代码、标注数据集、模型配置文件、预训练权重及数据增强、结果可视化等辅助脚本,覆盖从模型定义、数据预处理到损失函数与优化器设置的完整流程。目前已有853人学习下载,适合具备一定深度学习基础、希望理解mAP等评估指标并动手调参的读者。借助这份代码,读者可对照配置文件理解网络结构与超参数,利用权重文件直接推理或微调,并参考辅助脚本完成数据增强与预测结果解析,从而快速将摔倒检测方案部署到实际应用中。

1. 摔倒检测为什么选 YOLOv8:从场景痛点到落地方案

做养老监护、工地安全、医院病房这类场景的工程师,大概率都碰过同一个需求:摄像头画面里有人摔倒,系统得在几秒内报警。传统做法要么用姿态估计先抽骨架再判角度,要么用背景建模加轮廓分析,前者依赖关键点精度、后者一遇到光照变化就翻车。YOLOv8 把这件事简化成了一个目标检测问题——把"摔倒"和"未摔倒"当成两个类别直接训,推理一次出框,工程链路短、部署门槛低,这也是它在这类项目里被反复选中的原因。

这份 YOLOv8 摔倒检测完整代码,覆盖的是从数据组织、模型训练到推理落地的整条链路,适合两类人:一类是刚接触目标检测、想拿一个真实场景跑通全流程的新手;另一类是想把摔倒检测塞进自己现有监控系统、需要一份能改能调的基线代码的从业者。它不解决"摔倒动作的细粒度分类",也不做时序判断,单帧检测的边界就在这里,后面会专门讲怎么补。

2. 环境搭建与数据准备:把 YOLOv8 跑起来的前置条件

2.1 环境配置:CPU 版本也能先跑通

很多人卡在第一步不是代码问题,是环境。YOLOv8 依赖 ultralytics 这个库,它对 PyTorch 版本有要求,装错了就是一堆 import 报错。如果你手头只有一台没有独显的机器,比如 Ubuntu 20.04 的 CPU 版本,照样能跑训练和推理,只是慢。我一般会先建一个干净的虚拟环境,避免和系统里的其他包打架。

# 创建并激活虚拟环境,Python 建议 3.8 到 3.10 python -m venv yolov8_env source yolov8_env/bin/activate # Windows 用 yolov8_env\Scripts\activate # 安装 PyTorch,CPU 版本走官方 CPU 源 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics,它会自动带上 opencv、numpy 等依赖 pip install ultralytics

这段命令的逻辑是:先隔离环境,再装 PyTorch 底座,最后装 ultralytics。参数上,--index-url指定 CPU 版下载源,如果你有 NVIDIA 显卡,把这一行换成对应 CUDA 版本的官方命令即可,比如 cu118。装完用yolo checks验证,它会打印出 PyTorch、CUDA 是否可用、以及当前 ultralytics 版本。如果这一步报No module named 'ultralytics',八成是虚拟环境没激活,或者 pip 装到了系统 Python 里。

提示:GTX 1660 Ti 这类 6G 显存的卡跑 YOLOv8n 训练是够的,但 batch size 别超过 8,否则显存溢出报 CUDA out of memory。

2.2 数据集组织:摔倒检测的标注与目录结构

YOLOv8 认的是 YOLO 格式的标注,每张图对应一个 txt,每行是类别 中心x 中心y 宽 高,坐标都归一化到 0 到 1。摔倒检测一般分两类:fall和no_fall,也有人把站立、坐下单独拆类,但类别越多、样本越难均衡,新手建议先两类跑通。

标注工具用 Labelme 也行,但它默认出的是 JSON,得转一道;更省事的是直接用 labelImg 或 Roboflow 导出 YOLO 格式。目录结构按下面这样摆,ultralytics 才能自动识别:

fall_dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 对应训练标注 txt │ └── val/ # 对应验证标注 txt └── data.yaml # 数据集配置文件

data.yaml是训练入口,内容长这样:

path: ./fall_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 nc: 2 # 类别数 names: ['no_fall', 'fall'] # 类别名,顺序要和标注里的类别 id 对上

这里最容易踩的坑是类别顺序。标注时fall是 0 还是 1,必须和names列表一致,否则训练出来的模型会把摔倒识别成没摔倒,而且 loss 看着还挺正常,属于典型的玄学翻车。我一般会在标注完先抽三张图,用脚本把框画出来肉眼核对一遍再开训。

2.3 数据增强与样本均衡的取舍

摔倒检测的数据集天然不均衡——正常站立、走动的帧远多于摔倒帧。直接训,模型会倾向于全预测no_fall,mAP 看着不低,但真摔倒一个都抓不到。常见做法是在data.yaml同级配一个训练参数文件,或者直接在训练命令里加增强参数。YOLOv8 内置了 mosaic、翻转、HSV 抖动这些,对摔倒场景有用的主要是随机缩放和水平翻转,垂直翻转要慎用,因为倒过来的人容易被误判成摔倒。

样本层面,我一般会把摔倒帧做几倍过采样,或者用 copy-paste 把摔倒的人贴到不同背景上。但要注意,贴图合成如果边缘处理不好,模型会学到"贴图痕迹"而不是摔倒姿态,验证集上虚高、实际场景拉胯。判断方法很简单:拿几段没参与训练的监控视频跑推理,看漏检和误检,比看 mAP 靠谱。

3. 模型训练与参数调优:从预训练权重到收敛曲线

3.1 加载预训练权重开始训练

YOLOv8 官方放出了 n、s、m、l、x 五个尺度的预训练权重,摔倒检测这种场景,n 或 s 就够用,模型小、推理快,适合边缘设备。训练命令一行就能起:

# 用 yolov8n 预训练权重,在自定义数据集上训练 100 轮 yolo detect train \ data=fall_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=8 \ lr0=0.01 \ patience=20 \ project=runs/fall \ name=exp1

逐项说参数:data指向数据集配置;model是预训练权重,第一次跑会自动下载;epochs是训练轮数,摔倒数据量小的话 100 轮通常够;imgsz是输入尺寸,640 是默认值,监控画面里人小的话可以提到 960,但显存和速度都会涨;batch按显存调;lr0是初始学习率,0.01 是官方推荐起点;patience是早停耐心值,20 轮验证指标不涨就停,省时间。project和name决定结果存哪,跑完在runs/fall/exp1/下能看到权重、曲线图和日志。

训练过程中重点盯两个东西:box_loss和cls_loss是否稳定下降,以及验证集的 mAP50 曲线。如果 loss 震荡厉害,先把lr0降到 0.001 试试;如果 mAP 早早卡住不涨,多半是数据问题而不是参数问题。

3.2 损失曲线与指标解读

跑完训练,results.png里会把训练和验证的 loss、precision、recall、mAP 都画出来。摔倒检测最该看的是 recall——漏检一个摔倒的代价,远大于误报一次。如果 recall 明显低于 precision,说明模型偏保守,可以适当降低置信度阈值,或者在训练时给fall类加权。

YOLOv8 画损失函数曲线图不用自己写代码,训练日志里已经存了results.csv,想单独画可以用 pandas 加 matplotlib:

import pandas as pd import matplotlib.pyplot as plt # 读取训练日志,results.csv 在训练输出目录下 df = pd.read_csv('runs/fall/exp1/results.csv') df.columns = df.columns.str.strip() # 列名可能带空格,先清理 plt.figure(figsize=(10, 5)) plt.plot(df['epoch'], df['train/box_loss'], label='train box loss') plt.plot(df['epoch'], df['val/box_loss'], label='val box loss') plt.xlabel('epoch') plt.ylabel('loss') plt.legend() plt.savefig('loss_curve.png', dpi=150)

这段代码读的是训练输出的 CSV,列名里带斜杠,pandas 读进来后建议先 strip 一下空格,否则取列会报 KeyError。画出来能直观看到训练和验证 loss 是否同步下降,如果验证 loss 早早抬头,就是过拟合,得加数据或加正则。

3.3 摔倒检测的置信度与 NMS 参数

推理阶段有两个参数直接决定漏检和误检:置信度阈值conf和 NMS 的iou。默认conf=0.25、iou=0.7,但摔倒场景我一般会把conf降到 0.15 到 0.2,宁可多报也别漏。NMS 的iou在画面里人密集时调低一点,比如 0.5,避免相邻的人框被合并。

# 用训练好的权重做推理,降低置信度阈值提高召回 yolo detect predict \ model=runs/fall/exp1/weights/best.pt \ source=test_video.mp4 \ conf=0.2 \ iou=0.5 \ save=True

source可以是单张图、视频、整个目录,甚至摄像头编号。save=True会把带框的结果存下来。调参没有万能值,得拿实际场景的视频反复试,把漏检和误检的片段截出来对比,才能定下来。

4. 推理部署与常见问题排查

4.1 从脚本推理到服务化封装

训练完的best.pt要真正用起来,得包成能持续处理视频流的服务。最直接的方式是用 OpenCV 读帧,逐帧送进模型,判断有没有fall类且置信度超阈值,超了就触发报警。下面是一个最小可用的推理循环:

import cv2 from ultralytics import YOLO model = YOLO('runs/fall/exp1/weights/best.pt') cap = cv2.VideoCapture('test_video.mp4') FALL_CONF = 0.5 # 报警阈值,比检测阈值高,减少误报 while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model(frame, conf=0.2, verbose=False) for r in results: for box in r.boxes: cls_id = int(box.cls[0]) conf = float(box.conf[0]) # 类别名从模型里取,避免硬编码顺序出错 name = model.names[cls_id] if name == 'fall' and conf > FALL_CONF: x1, y1, x2, y2 = map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, f'FALL {conf:.2f}', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imshow('fall detection', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

逻辑上分三层:读帧、推理、判定报警。关键点是报警阈值FALL_CONF和检测阈值分开,检测用低阈值保召回,报警用高阈值压误报。类别名从model.names取而不是写死数字,这样换数据集也不会错位。实际部署时把imshow换成推流或写文件,再接一个报警回调就行。

4.2 避坑与排查:五个真实踩过的坑

现象一:训练 loss 正常下降,但验证集 mAP 一直是 0。原因:data.yaml里的names顺序和标注 txt 里的类别 id 对不上,或者val路径写错导致验证集为空。 解决:先确认labels/val下有对应 txt,再抽一张图用脚本把框画出来核对类别,路径用绝对路径试一次排除相对路径问题。

现象二:推理时所有框都标成no_fall,摔倒完全不报。原因:训练集里摔倒样本太少,模型退化成只预测多数类。 解决:对摔倒帧过采样,或在训练时用cls权重给fall类加权,同时把推理conf降到 0.15 观察是否出现摔倒框。

现象三:CPU 上推理一帧要好几秒,视频卡成幻灯片。原因:用了yolov8m或更大模型,且没做输入尺寸压缩。 解决:换yolov8n,把imgsz降到 416 或 320,或者用 ONNX Runtime 做 CPU 推理加速,速度能提升两三倍。

现象四:白天检测正常,晚上红外画面几乎全漏。原因:训练数据全是可见光,模型没见过红外成像的灰度分布。 解决:在训练集里混入夜间红外帧,或者对红外画面单独微调一轮,别指望一个模型通吃两种成像。

现象五:相邻两个人挨得近时,框被 NMS 合并成一个。原因:iou阈值太高,重叠框被误判为同一目标。 解决:把推理iou降到 0.5 甚至 0.4,或者在训练时增加密集人群场景的样本。

4.3 单帧检测的边界:时序信息怎么补

YOLOv8 是单帧检测,它判断的是"这一帧里有没有摔倒姿态",但真实摔倒是一个过程——人从站立到倒地有连续几帧。单帧模型容易把"蹲下""弯腰捡东西"误判成摔倒,因为它看不到前后动作。要压这类误报,常见做法是加一层时序逻辑:连续 N 帧都检测到fall才触发报警,或者用简单的跟踪算法给每个人分配 ID,看同一个 ID 的姿态是否持续异常。这不是 YOLOv8 本身能解决的,但工程上必须补,否则误报率会让你在验收时很难受。

5. 进阶技巧:把摔倒检测推到能交付的程度

模型训出来只是起点,能不能交付看的是误报率和响应速度。我一般会做三件事。第一件是滑动窗口投票:维护一个长度为 10 的队列,记录最近 10 帧里fall出现的次数,超过 6 次才报警,这样偶发的单帧误检会被过滤掉。第二件是区域屏蔽:监控画面里有些区域根本不可能有人摔倒,比如天花板、走廊尽头,用多边形把这些区域标出来,框中心落在里面就忽略。第三件是分级报警:置信度 0.5 到 0.7 记一次预警,超过 0.7 直接报警,给值班人员一个缓冲。

验证方法上,别只看 mAP。找几段真实监控视频,人工数出摔倒次数,跑一遍系统统计漏检和误报,算出实际召回率和误报率,这才是能写进验收报告的数字。如果条件允许,把模型导出成 ONNX 或 TensorRT,在目标设备上测端到端延迟,从读帧到报警的总耗时控制在 1 秒内,才算真正可用。

from collections import deque # 滑动窗口投票,过滤单帧误检 history = deque(maxlen=10) FALL_RATIO = 0.6 # 窗口内摔倒帧占比超过这个值才报警 def should_alarm(fall_detected): history.append(1 if fall_detected else 0) if len(history) < history.maxlen: return False return sum(history) / len(history) >= FALL_RATIO

这段逻辑很轻,但效果立竿见影。maxlen和FALL_RATIO要根据场景帧率调,25 帧的视频里 10 帧窗口大概覆盖 0.4 秒,够滤掉抖动又不会太迟钝。从那以后我每次做摔倒检测,都会先把这套投票逻辑接上再谈模型精度,否则单帧模型的误报能把整个系统的可信度拖垮。希望帮到你。

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

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

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

立即咨询