news 2026/9/24 21:11:35

基于YOLOv8的跌倒检测模型训练:数据集、源码与部署全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLOv8的跌倒检测模型训练:数据集、源码与部署全流程

简介:这份资源面向计算机视觉入门与进阶开发者、安防监控及健康看护方向的算法实践者,提供一套可直接复现的跌倒检测训练方案,解决从零搭建行为识别模型时数据匮乏、流程繁琐的问题。压缩包共1438个文件,约78.41MB,其中1428张jpg为跌倒场景标注图像,构成训练与验证主体;6个py脚本负责数据加载、模型训练与推理;2个md文档说明使用流程;另含1个pt权重与1个onnx导出模型,便于直接部署或二次微调。资源基于YOLOv8实现,特征提取采用CSPDarknet结构,通过跨阶段部分连接降低参数量并提升特征提取效率;检测头借鉴YOLOv4-Head设计,并采用Anchor-Free方式直接预测目标中心点与宽高比例,减少先验框数量,在速度与精度间取得平衡。目前已有1020人学习,适合希望快速跑通跌倒检测全流程、积累数据集与源码经验的读者参考。

1. 跌倒检测为什么值得用 YOLOv8 重做一遍

跌倒检测这件事,做过的人都知道,难点从来不在「有没有模型」,而在「模型能不能在真实场景里稳住」。传统做法要么用可穿戴传感器,要么用姿态估计加时序判断,前者用户不愿意戴,后者在遮挡、逆光、多人场景下经常翻车。而把跌倒当成一个目标检测问题来做,用 YOLOv8 直接框出「人跌倒」这个类别,反而绕开了骨架点抖动和时序建模的玄学,工程上更好落地。

这套「基于 YOLOv8 训练跌倒检测模型 + 数据集 + 源码」的思路,本质是把跌倒检测降维成单阶段检测任务:输入一帧图像,输出跌倒框和置信度,再接一个简单的持续帧判定就能报警。它适合两类人:一类是想快速跑通一个能演示、能部署的跌倒检测原型的开发者;另一类是想拿它当模板,迁移到工地安全、养老监护、医院病房等场景的工程师。下面我按自己实际训练和调参的顺序,把数据集、训练、验证、踩坑一条线讲清楚。

2. 数据集怎么准备:从标注格式到 YOLOv8 目录结构

2.1 跌倒检测数据集的类别设计与来源

跌倒检测数据集最容易被低估的是类别定义。很多人一上来就标「跌倒」和「未跌倒」两类,结果训练出来的模型把「蹲下」「弯腰捡东西」「坐下」全判成跌倒,误报率高得没法用。我一般会分成三类:fall(跌倒)、person(正常站立或行走的人)、lying(躺卧但非跌倒,比如床上休息)。这样模型学到的是「跌倒」和「躺下」的边界,而不是简单的人形检测。

数据来源上,公开的跌倒数据集通常规模不大,常见做法是拿 UR Fall Detection Dataset、Le2i 这类公开数据打底,再自己用手机在走廊、卧室、卫生间门口补拍。补拍时注意三点:一是模拟跌倒要真的倒地,不要半蹲;二是要覆盖不同光照,尤其是夜间红外或弱光;三是每个场景至少拍 20 段,每段 3 到 5 秒,抽帧后能得到几百张有效图。抽帧不要每秒都抽,跌倒过程前后 1 秒内抽 5 到 8 帧就够,否则大量静止帧会让模型偏向「人站着」这个多数类。

2.2 用 Labelme 标注并转成 YOLO 格式

标注工具用 Labelme 或 labelImg 都行,关键是要输出 YOLO 需要的归一化 txt。Labelme 默认输出 JSON,需要转一道。下面这个脚本是我常用的转换逻辑,输入是 Labelme 的 JSON 目录,输出是 YOLO 的 images 和 labels 分离结构。

import json import os from pathlib import Path # 类别顺序必须和训练时 data.yaml 里的 names 一致 CLASS_MAP = {"fall": 0, "person": 1, "lying": 2} def labelme_to_yolo(json_dir, out_img_dir, out_lbl_dir): json_dir = Path(json_dir) out_img_dir = Path(out_img_dir) out_lbl_dir = Path(out_lbl_dir) out_img_dir.mkdir(parents=True, exist_ok=True) out_lbl_dir.mkdir(parents=True, exist_ok=True) for js_file in json_dir.glob("*.json"): with open(js_file, "r", encoding="utf-8") as f: data = json.load(f) img_w = data["imageWidth"] img_h = data["imageHeight"] lines = [] for shape in data["shapes"]: label = shape["label"] if label not in CLASS_MAP: continue cls_id = CLASS_MAP[label] # Labelme 是两点矩形,取左上和右下 (x1, y1), (x2, y2) = shape["points"] x_min, x_max = min(x1, x2), max(x1, x2) y_min, y_max = min(y1, y2), max(y1, y2) # YOLO 格式:中心点 x,y 和宽高,全部除以图像尺寸归一化 cx = (x_min + x_max) / 2.0 / img_w cy = (y_min + y_max) / 2.0 / img_h bw = (x_max - x_min) / img_w bh = (y_max - y_min) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") # 没有有效标注的图直接跳过,避免空标签文件干扰训练 if not lines: continue txt_path = out_lbl_dir / (js_file.stem + ".txt") with open(txt_path, "w") as f: f.write("\n".join(lines)) # 图片复制到 images 目录,保持同名 src_img = json_dir / data["imagePath"] if src_img.exists(): import shutil shutil.copy(src_img, out_img_dir / src_img.name) if __name__ == "__main__": labelme_to_yolo("./raw_json", "./dataset/images", "./dataset/labels")

这段代码的关键点有三个。第一,CLASS_MAP的 id 必须和后面data.yaml里的names顺序严格对应,错一位模型就全乱。第二,坐标归一化用的是图像原始宽高,不是缩放后的尺寸,Labelme 的imageWidthimageHeight就是原图尺寸,直接用。第三,空标签文件一定要跳过,YOLOv8 遇到没有目标的图会当成背景样本,少量可以,大量会让召回率掉得很难看。

2.3 划分训练集验证集与 data.yaml 写法

转换完之后按 8:1:1 划分 train、val、test。我一般写个小脚本随机抽,但要注意同一个视频抽出来的帧不能同时出现在 train 和 val,否则验证指标虚高。正确做法是按视频段划分,而不是按帧随机划分。目录结构最终长这样:

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

对应的fall_data.yaml

path: ./dataset train: images/train val: images/val test: images/test nc: 3 names: 0: fall 1: person 2: lying

nc是类别数,names的 key 必须从 0 开始连续。很多人在这里写错,比如写成1: fall,训练不报错但类别全偏。写完 yaml 后建议用yolo checks先跑一遍环境自检,确认路径能被正确解析。

3. 用 YOLOv8 训练跌倒检测模型:参数、命令与损失曲线

3.1 环境配置与模型选型

环境这块,Ubuntu 20.04 和 Windows 都能跑,CPU 版本也能训练,只是慢。如果手头是 GTX 1660 Ti 这类 6G 显存的卡,建议用yolov8s而不是yolov8x,输入尺寸设 640,batch 设 8 到 16,基本能稳住。安装就一条命令:

pip install ultralytics

装完yolo version能打印出版本号就说明环境通了。模型权重不用自己找,yolov8n.ptyolov8s.pt在首次训练时会自动下载。如果网络受限,可以提前把 pt 文件放到项目根目录,训练时用绝对路径指定。

选型上我的经验是:跌倒检测的类内差异其实不大(都是人形倒地),yolov8n在数据量 3000 张以上时就能到可用的 mAP,yolov8s是精度和速度的平衡点。如果部署到 RK3588 或 Hi3516 这类边缘板,优先选yolov8n,后面转 ONNX 和量化都更省事。

3.2 训练命令与关键参数含义

训练命令本身很短,但参数决定成败:

yolo detect train \ data=fall_data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ patience=30 \ workers=8 \ project=runs/fall \ name=exp1

逐个说。epochs=150是上限,真正停在哪由patience=30决定,30 轮验证指标不升就早停,避免过拟合。lr0=0.01是初始学习率,跌倒数据量不大时我会降到 0.005,否则前期 loss 震荡明显。lrf=0.01是最终学习率系数,配合余弦退火,让后期微调更稳。imgsz=640是输入分辨率,如果跌倒目标在画面里占比很小(比如监控远景),可以提到 960,但显存和速度要重新权衡。workers=8是数据加载线程,Windows 下建议设 0 或 2,否则容易卡死。

训练启动后重点看三个东西:box_loss是否稳定下降、mAP50是否在涨、cls_loss有没有突然飙高。cls_loss飙高通常意味着类别标注有冲突,比如同一张图里falllying框重叠严重,这时候要回去查标注。

3.3 画损失函数曲线和混淆矩阵

训练完runs/fall/exp1/下会有results.csv,里面记录了每轮的 loss 和指标。想单独画损失曲线,用下面这段:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/fall/exp1/results.csv") df.columns = [c.strip() for c in df.columns] # 列名可能带空格 fig, ax1 = plt.subplots(figsize=(10, 5)) ax1.plot(df["epoch"], df["train/box_loss"], label="train box_loss") ax1.plot(df["epoch"], df["val/box_loss"], label="val box_loss") ax1.set_xlabel("epoch") ax1.set_ylabel("box loss") ax1.legend(loc="upper right") ax2 = ax1.twinx() ax2.plot(df["epoch"], df["metrics/mAP50(B)"], color="green", label="mAP50") ax2.set_ylabel("mAP50") ax2.legend(loc="lower right") plt.title("YOLOv8 fall detection training curve") plt.tight_layout() plt.savefig("fall_loss_curve.png", dpi=150)

逻辑说明:左轴画 box_loss,右轴画 mAP50,这样能直观看到 loss 下降和指标上升是否同步。如果 loss 一直降但 mAP 不涨,多半是过拟合或者验证集分布和训练集差太多。参数上dpi=150够发报告用,figsize按需调。混淆矩阵 YOLOv8 会自动生成在exp1/confusion_matrix.png,重点看fall被误判成lying的比例,这个数高就说明两类边界没学好,需要补「半躺」「侧倒」这类难样本。

4. 推理、验证与部署前必须做的几件事

4.1 用验证集跑指标并逐类看 P/R

训练完不要只看一个总 mAP,要逐类看。命令:

yolo detect val \ model=runs/fall/exp1/weights/best.pt \ data=fall_data.yaml \ imgsz=640 \ conf=0.25 \ iou=0.5

输出里会分别列出fallpersonlying的 precision、recall、mAP50。跌倒检测里fall的 recall 比 precision 更重要,漏报一次跌倒的代价远大于误报。如果fall的 recall 低于 0.85,优先补数据而不是调阈值。conf=0.25是推理置信度阈值,验证阶段先用默认,部署时再按场景调。

4.2 单张图和视频推理的最小代码

验证通过后,用 Python 跑单张图确认效果:

from ultralytics import YOLO model = YOLO("runs/fall/exp1/weights/best.pt") # 单张图推理,save=True 会把带框结果存到 runs/detect/predict results = model.predict( source="test_fall.jpg", imgsz=640, conf=0.3, iou=0.45, save=True ) for r in results: for box in r.boxes: cls_id = int(box.cls[0]) conf = float(box.conf[0]) print(model.names[cls_id], round(conf, 3))

conf=0.3比验证时略高,是为了减少演示时的误报。iou=0.45控制 NMS 合并程度,跌倒场景里人和跌倒框可能重叠,iou 设太高会把两个框都留下,设太低会误合并,0.45 是我试下来比较稳的值。视频推理把source换成视频路径即可,YOLOv8 会自动逐帧处理并输出视频。

4.3 从检测到报警:持续帧判定逻辑

单帧检测出fall不代表真跌倒,可能只是弯腰。工程上要加一个持续帧判定:连续 N 帧中至少 M 帧检出fall才触发报警。常见做法是 N=15、M=10,按 25fps 算大约 0.6 秒。这个逻辑不复杂,但能大幅降低误报。如果部署到边缘设备,建议把这段判定放在后处理里,而不是塞进模型,方便按场景调参。

5. 跌倒检测训练里最容易翻车的几个坑

5.1 现象:mAP 很高但实际误报不断

原因:验证集和训练集来自同一批视频,按帧随机划分导致同一段视频的相邻帧同时进了 train 和 val,模型其实在「背答案」。解决:按视频段划分数据集,确保同一段视频只出现在一个子集里,重新训练后 mAP 通常会掉几个点,但那个数才是真实的。

5.2 现象:训练到一半 loss 变成 nan

原因:学习率过高,或者标注里有宽高为 0 的框。Labelme 转换时如果两个点重合,bwbh会是 0,YOLOv8 计算 loss 时除零。解决:在转换脚本里加一行过滤,if bw <= 0 or bh <= 0: continue,同时把lr0从 0.01 降到 0.005。

5.3 现象:模型把「蹲下」全判成跌倒

原因:数据集里缺少蹲下、弯腰这类负样本,模型没见过这些姿态,只能往最近的fall类靠。解决:专门补拍蹲下捡东西、系鞋带、坐下的片段,标成person,每类至少 200 张,重新训练后误报会明显下降。

5.4 现象:GPU 显存够但训练报 CUDA out of memory

原因:batch设太大,或者imgsz提到 960 后没同步降 batch。解决:6G 显存下imgsz=640batch=16是上限,提到 960 就把 batch 降到 4 或 8。也可以用yolo detect train ... amp=False关掉混合精度排查是不是 AMP 引起的显存异常。

5.5 现象:转 ONNX 后推理结果和 PyTorch 不一致

原因:导出时没指定opset或动态轴,或者预处理归一化方式不同。解决:导出命令加opset=12dynamic=False,并在 ONNX 推理端严格复用 YOLOv8 的预处理:BGR 转 RGB、除以 255、letterbox 填充。这三步错一步,框的位置就会整体偏移。

6. 把跌倒检测推到边缘设备:量化与阈值联调的一个实用技巧

模型训练完只是半成品,真正落地到 RK3588、Hi3516 这类板子上,量化是绕不过去的。我的习惯是先在 PC 上用 ONNX Runtime 验证 FP32 精度,再做 INT8 量化,对比量化前后fall类的 recall 掉了多少。如果掉超过 5 个点,就回到训练阶段补数据,而不是硬调量化参数。

具体做法是导出一份 ONNX,用onnxruntime跑一遍验证集,记录每类的 P/R:

import onnxruntime as ort import numpy as np import cv2 sess = ort.InferenceSession("best.onnx", providers=["CPUExecutionProvider"]) input_name = sess.get_inputs()[0].name def preprocess(img_path, size=640): img = cv2.imread(img_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (size, size)) img = img.astype(np.float32) / 255.0 img = np.transpose(img, (2, 0, 1)) # HWC -> CHW return np.expand_dims(img, axis=0) out = sess.run(None, {input_name: preprocess("test_fall.jpg")}) print(np.array(out[0]).shape) # 确认输出维度符合预期

这段代码的重点是预处理必须和训练时一致,尤其是BGR2RGB/255.0,少一个结果就偏。量化时用板子厂商的工具链,校准集从验证集里抽 200 张,覆盖白天、夜间、多人场景。量化完在板端跑一遍,重点看fall的 recall 和单帧耗时,如果 recall 掉太多,优先检查校准集是不是偏了。

阈值联调有个实用技巧:不要只调conf,把conf和持续帧判定的 M 值一起调。比如板端算力有限只能跑 10fps,那 N=15 就对应 1.5 秒,太慢,改成 N=8、M=6,同时把conf从 0.3 提到 0.4,用置信度换响应速度。这个组合我在实际项目里调过很多次,比单独调一个参数有效得多。

最后说个我自己的习惯:每次训练完,我都会把best.ptdata.yamlresults.csv和当时的训练命令一起存进一个带日期的文件夹,命名比如fall_yolov8s_20240612。跌倒检测这行,数据一换、参数一改,结果就完全不同,没有后悔药,只能靠记录复现。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 21:10:33

动态图神经网络DGNN实战:异常流量检测从pcap到线上部署

简介&#xff1a;这份资源面向计算机、人工智能及网络安全方向的学习者与研究人员&#xff0c;提供一套基于动态图神经网络的异常流量检测完整实现方案&#xff0c;用于解决传统静态拓扑方法在动态网络环境中准确率与效率不足的问题。压缩包共141个文件&#xff0c;约34.94MB&a…

作者头像 李华
网站建设 2026/9/24 21:10:21

一条命令批量生成100条视频:Hypit多Agent视频生产管线实战

1. 从一条命令说起&#xff1a;这个开源项目到底在解决什么问题第一次看到“一条命令复刻100条爆款视频”这个说法&#xff0c;我的反应是&#xff1a;要么是标题党&#xff0c;要么背后有一套相当成熟的模板化生产管线。花了两天把项目源码和配套的Agent工作流跑通之后&#x…

作者头像 李华
网站建设 2026/9/24 21:09:23

Windows终端开发环境:Nushell+coreutils+Fresh配置实战

说实话&#xff0c;在 Windows 上正经写代码的人&#xff0c;多少都经历过一段“终端自卑期”。PowerShell 5.1 默认那个蓝色窗口&#xff0c;编码动不动就乱码&#xff0c;想用个grep发现只有findstr&#xff0c;想删个目录还得记rmdir /s /q&#xff0c;跟 Linux 上干活的老哥…

作者头像 李华
网站建设 2026/9/24 21:08:59

AI推理网关路由架构与策略实践:应对多模型调用混乱

做AI推理网关这件事&#xff0c;说白了就是一句话&#xff1a;当你的大模型后端从一两个变成七八个&#xff0c;调用入口必须有一个统一的路由架构&#xff0c;把流量按策略分到最合适的推理服务上。这篇是“大模型推理优化系列”的第一篇&#xff0c;我会把AI推理网关的路由架…

作者头像 李华
网站建设 2026/9/24 21:08:57

Git Worktree + Skill:多任务并行开发的高效工作流实践

1. 多任务并行开发的真实痛点&#xff1a;从"切分支噩梦"说起1.1 一次紧急修复引发的分支切换事故先讲个真实经历。上个月某个周五下午&#xff0c;我正在一个功能分支上开发新模块&#xff0c;代码改到一半&#xff0c;涉及六个文件&#xff0c;逻辑已经在大脑里串起…

作者头像 李华
网站建设 2026/9/24 21:08:21

从提示词到AI Skill:5步工程化流程打造可复用技能

1. 为什么提示词学会就废&#xff1f;先搞懂 Skill 到底解决什么问题这两年我见了太多人&#xff0c;提示词收藏了上百条&#xff0c;真正能稳定复用的没几条。今天问 ChatGPT 要一个方案觉得不错&#xff0c;明天换个需求重新写一段提示词&#xff0c;效果又飘了。问题不在于你…

作者头像 李华