
简介面向计算机及相关专业学生这是一份可支撑毕业设计、课程设计、期末大作业或项目实战的深度学习完整项目基于YOLOv5人体检测与OpenPose姿态检测实现摔倒识别适合希望获得可运行项目范式、快速产出演示系统的学习者参考。资源提供完整源码与训练好的模型覆盖行人定位、多人姿态估计、摔倒行为判定等核心环节能够帮助读者在真实场景中理解目标检测与姿态估计模型的串联过程并快速搭建用于医院、养老院、家庭看护等场景的跌倒预警原型。zip压缩包约40.25MB当前已有166人学习/下载适合具备一定Python基础、正在学习计算机视觉或深度学习的开发者使用。通过该项目还可掌握模型调用、参数调整、推理结果可视化以及围绕骨骼关键点特征进行异常行为判断的思路并可在现有基础上进一步扩展行为识别、跌倒报警等落地功能。1. 摔倒检测为什么非得是 yolov5openpose单目摄像头下的工程真相独居老人摔倒后无人发现、工地高处坠落无人预警、患者在医院走廊倒地——这类场景的共同点是只有一路普通监控摄像头没有穿戴设备不能指望人一直盯着屏幕。深度学习领域里针对这件事的主流做法就是把人体检测和目标姿态估计串成一条流水线先用训练好的模型确定画面里哪里有人再用姿态估计模型读取骨架信息最后用几何逻辑判断这个人是不是摔了。yolov5 负责“人在哪”openpose 负责“人摆成什么样”两者结合之后摔倒检测才从“看着像”变成“算得出来”。这个项目是很多深度学习初学者的第一个综合性实战它不靠单一模型硬解而是把目标检测、关键点回归、时序判断三个环节串在一个推理链路里。你还会接触到模型转换、阈值调参、误报排查这些真实部署才有的问题。下面按我自己的落地顺序把环境和代码拆开讲清楚新手跟着能跑通老手可以跳过铺垫直接看避坑清单。2. 方案选型和判断逻辑yolov5 与 openpose 在摔倒检测里的分工2.1 为什么不能用单一模型硬刚检测和姿态估计的边界先回答一个常见疑问直接用 openpose 检测人再拿关键点判断摔倒行不行理论上行工程上难受。openpose 这类姿态估计模型本身就包含人的检测但它在画面里人较少且姿态复杂时表现不稳定尤其是身体互相遮挡的场景。摔倒恰恰常发生在人群遮挡中——比如病房里有人站在倒地者旁边工地里几个人走在一起。yolov5 先做目标级过滤只保留置信度足够的“人”再把人框裁剪出来单独送入姿态模型姿态模型的输入干扰就少了一大半。另一个现实理由是算力。yolov5s 在 640x640 输入下CPU 上大约每帧 80~150msGPU 上轻松到 5ms 以内。而 openpose 的原版 Caffe 实现即便在 GPU 上也要 100ms 以上CPU 上按秒算。如果在全图上跑姿态估计视频流根本追不上实时。先用目标检测把人框剪出来让 openpose 只处理框内区域推理时间能缩短到原来的三分之一甚至更少。这就是为什么这个项目的标准架构不是“二选一”而是“串联”。2.2 摔倒的几何特征把“摔倒了”翻译成模型能算的数字摔倒判定不是模型直接输出“摔倒/未摔倒”而是基于姿态关键点做规则判断。openpose 输出每个人的 18 个COCO 格式或 25 个BODY_25 格式关键点坐标。摔倒检测常用以下四类特征组合打分特征计算方式物理意义人体框宽高比检测框宽度 / 高度站立时远小于 1倒地时接近或大于 1躯干与地面夹角脖子到髋部连线与水平线的夹角站立接近 90°倒地接近 0°头部中心高度变化头部关键点 y 坐标的连续差摔倒瞬间头部 y 值快速下降各部位垂直分布肩部、髋部、脚踝关键点的 y 值离散程度倒地后跨关键点高度差变小单用任何一个特征都会翻车比如下蹲会导致框宽高比变大搬东西弯腰会导致躯干夹角变小但这些行为的人还在继续活动。工程做法是给每个特征分配权重算出“摔倒分数”再用时序逻辑确认——下一小节展开。2.3 推理链路编排先检测、再裁剪、后判断在常见实现里每一帧的处理顺序是这样的yolov5 跑一次前向得到所有人体的边界框对每个边界框按坐标在原图上裁剪出 ROI将 ROI 缩放后送入 openpose 模型得到关键点坐标根据关键点计算摔倒特征值结合上一帧状态确定是否触发警报为什么要裁剪而不是直接把整帧关键点拿出来用一是因为 openpose 的输入尺寸通常被限制在 368x368 或 656x368直接缩全帧会丢失小目标的关键点细节二是因为裁剪后姿态模型对单人的专注度更高关键点抖动更小。这个链路在很多真实项目里也被改为并行处理用低帧率的姿态模型配合高帧率的目标检测减少计算压力。# 伪代码摔倒检测主循环的常见结构 while cap.isOpened(): ret, frame cap.read() frame cv2.resize(frame, (1280, 720)) # 1. yolov5 检测人体, 只保留 labelperson results yolo_model(frame) boxes results.xyxy[0].cpu().numpy() person_boxes [box for box in boxes if results.names[int(box[5])] person] # 2. 遍历每个 person 框, 裁出 ROI 后送 openpose for box in person_boxes: x1, y1, x2, y2 int(box[0]), int(box[1]), int(box[2]), int(box[3]) roi frame[y1:y2, x1:x2] # 3. openpose 姿态推断, 得到关键点数组 keypoints pose_model(roi) # shape: [num_person, 18, 3], 最后一维是 x, y, conf # 4. 摔倒判断 score fall_score(keypoints[0]) # 返回 0~1 的摔倒概率值 if score 0.6: trigger_alert(frame, box)这个流程的关键在于第 4 步的fall_score()函数它才是整套系统的“决策大脑”。下面给出一种我在项目里验证过的打分实现它综合了框宽高比、躯干倾角和头部速度三项各自归一化后加权求和。3. 跑通摔倒检测最小系统环境搭建与核心代码3.1 环境准备conda、yolov5 依赖与 openpose 的模型权重你拿到的项目包通常已经带了预训练权重但环境还是得自己配。我建议用 conda 独立环境隔离避免把系统 Python 搞乱。# 创建 python 3.8 环境yolov5 官方支持 3.8~3.10) conda create -n fall-detection python3.8 conda activate fall-detection # 安装 pytorchCUDA 版本根据显卡驱动选不确定就先装 CPU 版跑通流程 conda install pytorch1.12.1 torchvision0.13.1 torchaudio0.12.1 cpuonly -c pytorch # yolov5 依赖 pip install -r yolov5/requirements.txt # 视觉与数据处理 pip install opencv-python numpy pandas参数说明如果你有 NVIDIA 显卡cpuonly改成cudatoolkit11.3对应的安装命令没有显卡就先 CPU 跑yolov5s 检测单人的速度还能接受openpose 会慢一些先验证流程没问题再换 GPU。Python 版本不要用 3.11 以上部分旧版 openpose 实现依赖的numpy接口不兼容。3.2 yolov5 人体检测最小可用脚本yolov5 官方提供了 torch.hub 方式加载模型不强制从源码 clone但项目里通常会包含yolov5/目录因为训练脚本和超参数文件都在里面。# detect_person.py import torch import cv2 def load_yolo(weights_pathyolov5s.pt, devicecpu): 加载 yolov5 模型 weights_path: 权重文件路径, 可以是官方预训练或自己训练的 device: cpu 或 cuda:0 model torch.hub.load(./yolov5, custom, pathweights_path, sourcelocal, force_reloadFalse) model.conf 0.35 # 置信度阈值: 低于此值的目标被丢弃 model.iou 0.5 # NMS IoU 阈值: 重叠大于此值只保留置信度高的 model.classes [0] # COCO 中类别 0 是 person, 只保留人体 model.to(device) return model def detect_person(model, img_bgr): 输入 BGR 图像, 返回 person 边界框列表 每个框为 [x1, y1, x2, y2, confidence] # yolov5 接收 RGB 图像, 这里做通道转换 img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) results model(img_rgb, size640) # size640 是输入分辨率, 越大越准但越慢 df results.pandas().xyxy[0] boxes [] for _, row in df.iterrows(): if row[class] 0: # person boxes.append([int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]), row[confidence]]) return boxes逻辑说明model.classes [0]是一种省算力的技巧模型只输出 person 类别不必全类别输出后再过滤。model.conf 0.35表示检测框置信度低于 0.35 就会丢掉背景复杂或图中有密集小人时可以调到 0.25但误检率会上升。size640是向模型输入的边长如果你的摄像头画面里人很小调到 960 能改善小目标召回代价是推理时间增加约一倍。3.3 openpose 姿态推断从人体框到关键点这里以项目中常见的轻量级 pytorch openpose 实现为例重点是把输出转换为标准坐标系之后再接摔倒逻辑。# predict_pose.py import cv2 import numpy as np import torch from models.with_mobilenet import PoseEstimationWithMobileNet from modules.keypoints import extract_keypoints from modules.pose import Pose, propagate_ids def load_pose_model(weights_pathcheckpoint_iter_370000.pth): 加载轻量级 openpose 模型 该模型基于 mobilenet, 速度和精度平衡适合本地部署 model PoseEstimationWithMobileNet() checkpoint torch.load(weights_path, map_locationcpu) model.load_state_dict(checkpoint[state_dict]) model.eval() return model def infer_pose(model, roi_bgr): 输入人体 ROI (BGR), 输出 18 个关键点坐标 返回 shape: [18, 3], 每行为 [x, y, confidence] # 统一输入尺寸 roi cv2.resize(roi_bgr, (368, 368)) roi roi.astype(np.float32) / 255.0 roi cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) roi torch.from_numpy(roi.transpose((2, 0, 1))).unsqueeze(0) with torch.no_grad(): stages_output model(roi) # 网络输出 heatmap 和 PAF (部件亲和场) heatmaps stages_output[-2].cpu().numpy()[0] pafs stages_output[-1].cpu().numpy()[0] # 峰值提取与关键点配对 keypoints extract_keypoints(heatmaps, pafs) return keypoints参数说明checkpoint_iter_370000.pth是轻量 openpose 常用的预训练权重名不同版本迭代次数不一样你拿到的项目里通常会把这个文件放在weights/目录下。model(roi)输出的是 two-stage 结构最后一层是 PAF 特征图倒数第二层是关键点热力图。extract_keypoints是开源实现里的工具函数它会把热力图上的峰值点做非极大值抑制并通过 PAF 的向量场把属于同一个人的关键点连接起来。如果程序报这个函数不存在说明你用的不是同一个版本直接调用项目包里的modules.keypoints即可。最关键的一点是openpose 输出的关键点坐标是相对于输入 ROI 的使用时必须加上 ROI 在原图中的偏移量否则后面计算高危区域的坐标会错位。3.4 摔倒判断逻辑把几何特征变成可调的分数摔倒判断是项目的核心得分点。下面是综合宽高比、躯干倾角、头部下降速度三种信号的打分函数。# fall_logic.py import numpy as np import math # COCO 关键点索引 NECK 1 # 颈部 HIP 8 # 髋部中心原版 COCO 没有髋尾, 这里用左髋右髋中点代替 LEFT_HIP 9 RIGHT_HIP 10 NOSE 0 # 鼻子, 代表头部 def calc_torso_angle(kpts): 躯干倾角: 颈部到髋部连线的水平夹角度 站立时接近 70~90 度, 倒地时接近 0~30 度 if kpts[NECK][2] 0.2 or kpts[LEFT_HIP][2] 0.2: return 90.0 # 关键点缺失时按站立处理, 保守不误报 neck_x, neck_y kpts[NECK][:2] hip_x (kpts[LEFT_HIP][0] kpts[RIGHT_HIP][0]) / 2 hip_y (kpts[LEFT_HIP][1] kpts[RIGHT_HIP][1]) / 2 angle math.degrees(math.atan2(hip_y - neck_y, hip_x - neck_x)) return abs(angle) def fall_score(box, kpts, prev_head_y, head_speed_threshold50): 摔倒综合打分, 返回 0~1 box: 当前帧人体框 [x1, y1, x2, y2, conf] kpts: 关键点数组 [18, 3] prev_head_y: 上一帧头部 y 坐标 # 特征 1: 宽高比 w box[2] - box[0] h box[3] - box[1] ratio w / (h 1e-5) # 站立约 0.3~0.5, 横躺可能大于 1.0 ratio_score min(ratio / 1.2, 1.0) # 大于 1.2 视为满分特征 # 特征 2: 躯干倾角 angle calc_torso_angle(kpts) angle_score 1.0 - min(angle / 45.0, 1.0) # 夹角越小, 分数越高 # 特征 3: 头部下降速度 head_y kpts[NOSE][1] head_speed 0 if prev_head_y is not None: head_speed head_y - prev_head_y # 正数表示头部在下降 speed_score min(max(head_speed / head_speed_threshold, 0), 1.0) # 加权融合: 角度权重最大, 避免单靠躺倒触发 total 0.5 * angle_score 0.3 * ratio_score 0.2 * speed_score return total逻辑说明三个特征里躯干倾角权重最高宽高比次之速度信号只作为辅助。原因在于速度特征对噪声敏感——弯腰捡东西时头部也会快速下降但躯干倾角变化不明显加权处理后分数不会越界。head_speed_threshold50是按 720p 视频、30fps 估算的像素位移阈值如果摄像头是俯视角度这个阈值要减小到 20 左右。关键点置信度低于 0.2 时该点不做几何计算避免噪声估出的坐标污染角度值。主循环里加入一个“连续确认”机制当摔倒分数连续 3 帧超过 0.6 才触发告警因为真实摔倒后的躯干倾角变化会维持数十帧。4. 让模型适配你的摄像头数据准备与关键参数调优4.1 数据集策略摔倒不是一张图片而是状态序列很多初学者把摔倒检测当成图像分类任务来训练给它喂“站立的图”和“躺倒的图”结果模型一到真实视频就失效。原因是摔倒是一个过程跌倒瞬间的人体形态和躺倒后的静止形态差异很大。正确的数据集构造方式是以“状态序列”为单位每个样本包含 5~15 帧连续动作标注分为“站立”“蹲下/弯腰”“正在摔倒”“倒地”四类。yolov5 只负责检测人所以标注重点在人体框上——但如果你微调模型就得确保训练集中包含了“正在摔倒”这种介于站立和躺倒之间的半程形态。公开数据集方面常见的选择是 Le2i Fall Detection Dataset 和 UR Fall Detection Dataset。前者拍摄于室内房间视角多变后者包含深度图序列适合做多模态扩展。这两类数据集的图像质量普遍偏低建议用你自己的摄像头重新采集一部分画面做迁移微调否则场景光照差异会让模型在白天窗边、夜间灯下表现飘忽。4.2 yolov5 训练参数照着这份设置跑第一轮如果你打算用自己的数据微调 yolov5 的 person 类别建议从预训练权重yolov5s.pt开始不要从零训练。yolov5 的训练脚本在项目目录的train.py里常用参数如下python train.py \ --weights yolov5s.pt \ --data fall.yaml \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0 \ --workers 4 \ --cache ram参数说明--data fall.yaml需要三个字段train和val指向图像目录nc填类别数默认为 1person 单类names填[person]。--epochs 100对单类检测足够了跑 300 轮反而会增加过拟合。--cache ram在显存足够时把图像缓存到内存中显著缩短训练时间。还有一个容易被忽略的--multi-scale参数如果摄像头安装了广角镜头开启这个参数可以让模型适应不同尺度的人体。训练完成后用export.py导出模型为 TorchScript 或 ONNX 格式部署时推理速度更快。4.3 推理侧的三个高频调整点置信度、NMS 与姿态关键点阈值模型训练完只是第一步推理侧的参数才是你真正频繁碰的东西。下表对照了默认值和摔倒场景的推荐值参数yolov5 默认值摔倒检测推荐值原因置信度阈值 model.conf0.250.35~0.45摔倒场景误报警代价高宁可漏检也不要错检NMS IoU 阈值 model.iou0.450.5~0.6多人近距离时宽松 IoU 让重叠人体框保留更多输入尺寸 size640640 或 960960 改善小目标远处的人检出但帧率下降openpose 关键点置信度过滤无0.2~0.3低于此值的关键点不参与运算去除抖动置信度阈值是整套系统最敏感的设置。在养老院室内场景0.5 的光照和监控视角下0.35 与 0.45 的差异每天可能带来 2~3 次误报。我的做法是先把阈值调到 0.25 运行一周记录误报帧再逐步提高直到误报消失取中间值。yolov5 的 NMS 参数同样重要。默认的 0.45 在多人重叠场景里有问题人 A 的框与人 B 的框高度重叠NMS 会删掉其中一个摔倒者的框可能被相邻者吞掉。把model.iou调到 0.6能让重叠的人体框共存代价是同一人身上可能出现两个嵌套框需要按置信度做二次筛选。5. 摔倒检测的四个高频坑与排查清单5.1 现象openpose 在 CPU 上慢得像幻灯片每帧要 3~5 秒原因轻量 openpose 虽然比原版快但 CPU 推理仍需要数百毫秒到数秒。如果你没有裁剪 ROI 而直接在全图上推理算力消耗成倍增加。还有个容易被忽略的因素是模型内部执行了多次图像缩放输入尺寸越大耗时越长。解决首先确认检测链路是否做了 ROI 裁剪——先用 yolov5 把人体框裁减到cv2.resize的 ROI 上再送入 openpose。其次把 openpose 输入从 656x368 降到 320x240精度下降但速度能提升 3 倍。最后可以考虑每 2 帧跑一次姿态估计目标检测保持每帧执行。真实摔倒过程至少持续 600ms30fps 下隔帧采样完全够用。5.2 现象人蹲下整理鞋带、弯腰捡东西被误判为摔倒原因宽高比和躯干角度两个特征在下蹲时都会朝“摔倒”方向偏移。下蹲时躯干角通常在 30 度左右宽高比接近 0.8两个特征叠加后总分可能达到 0.6 的触发线。真实摔倒与下蹲的差异在于时间尺度和速度摔倒时头部 y 坐标变化速度极快且帧间方向一致。解决给速度特征增加一个“突变持续时间”条件——要求连续 5 帧头部速度都超过阈值时才计入分数单纯一帧的快速头部移动不参与加权。同时把触发分数提高到 0.7并加入髋部中心高度变化辅助判定髋部在摔倒时快速降低下蹲时相对缓慢。实测这一改动能把误报率降低一半以上。5.3 现象关键点坐标严重抖动角度和宽高比计算时好时坏原因openpose 输出的关键点坐标是浮点数不同帧间存在 ±5 像素的噪声。摔倒判定对躯干角度极其敏感5 像素的抖动在远距离可造成 10 度以上的角度偏差。解决对关键点坐标做一级低通滤波最简单的是指数移动平均EMA。# keypoint_smoothing.py import numpy as np smooth_kpts None alpha 0.4 # 新帧占比, 越小越平滑但延迟越大 def smooth(raw_kpts): global smooth_kpts if smooth_kpts is None: smooth_kpts raw_kpts.copy() else: # 置信度低于 0.2 的关键点不做平滑, 直接用原始值 mask raw_kpts[:, 2] 0.2 smooth_kpts[mask] raw_kpts[mask] smooth_kpts[~mask] alpha * raw_kpts[~mask] (1 - alpha) * smooth_kpts[~mask] return smooth_kpts参数说明alpha0.4兼顾平滑度和时效性调小到 0.2 画面会更稳定但摔倒的快速姿态变化会被滞后。出生时摔倒检测目标是抓“突变”所以 alpha 不宜小于 0.3。5.4 现象摄像头从平视改成俯视安装后所有判断全部失效原因平视场景下人摔倒后宽高比会明确大于 1但俯视 60 度安装时站立的人本身宽高比就可能接近 0.8~1.2摔倒前后宽高比变化不明显躯干角度的几何定义也变化了。如果你使用别人预训练好的模型和原项目阈值几乎必定翻车。解决俯视相机场景下重心速度与地面距离成为更可靠的特征。头部到髋部中点的投影距离突然缩短、髋部 y 值快速增大这两个信号的鲁棒性优于角度。改判据时建议重写fall_score()函数不要微调原阈值。另外模型标注需要加入“侧卧”“俯卧”“仰卧”三种姿势的遮挡情况俯视下关键点大量遮挡openpose 会丢点这时候宁可漏报也不要依据残缺关键点强行判断。5.5 现象训练时进程崩溃报错与 numpy、json 相关原因yolov5 对numpy版本有要求新版 numpy 移除了某些接口导致旧代码报错。同时pycocotools在 Windows 上不安会导致训练无法启动。解决固定numpy1.22.4确保 pycocotools 安装成功Windows 下用pip install pycocotools-windows。opencv 注意不要用 4.8 以上版本部分推理代码对cv2.CascadeClassifier的兼容性不理想。6. 把误报压下去时序确认与二次验证的进阶技巧运行稳定之后你会发现自己最大的敌人不是模型精度而是边缘计算设备上的帧率波动。以树莓派或边缘盒子为例yolov5s 在 CPU 上约 8fpsopenpose 约 2fps两者串行时实际帧率可能掉到 1.5fps。这时摔倒过程仅能捕获 2~3 帧时序确认的窗口就要缩短——连续 2 帧确认比连续 3 帧更适合低帧率设备。我踩过的一个坑是把摔倒检测结果直接接入微信/短信告警结果模型把“拖地时拖把的影子”当成另一人摔倒半夜电话响不停。后来加了二次验证触发告警后把摔倒前后各 15 帧的关键点坐标、速度峰值、角度变化曲线存为 JSON 日志同时在画面中摔倒位置截取一张带关键点绘制的图片人工核查时只看图片就能判断误报原因。这组日志不仅帮我调好了阈值还成了论文里最有说服力的实验截图。另一个值得投入的方向是摔倒后的静止检测——真摔之后人往往无法立刻站起来。在摔倒判定触发后继续跟踪检测框内人体关键点的相对位移若在 10 秒内检测到髋部高度抬升超过初始值的 50%则取消告警。这个机制能有效过滤“跌倒后立刻爬起”的误报也让系统更贴近真实监护需求。我自己做这个项目时把误报率从每天 7 次压到了每天不到 1 次靠的就是这个静止确认逻辑外加把置信度阈值从 0.4 微调到 0.45。取阈值这事确实有几分玄学但按“先跑一周统计分布再做微调”的路子走基本不会走偏。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。