简介这份资源面向计算机视觉方向的本科毕业生与深度学习入门者提供一套可直接运行的摔倒检测完整方案解决从人体关键点提取到动作分类的工程落地问题。项目以YOLOv5完成人体检测结合OpenPose提取骨骼关键点再通过动作分类网络判断跌倒行为适合作为毕业设计、课程设计或姿态识别练手项目。压缩包共193个文件约40.29MB包含39个Python脚本、21个pyc缓存、17个yaml与6个yml配置、2个pt权重及2个jit模型另有75张jpg与11张jpeg样本图、若干txt与xml标注说明覆盖训练、推理与配置全流程。资源已获导师认可并通过严格调试可直接运行若需检测其他姿势可按说明收集图片、运行runOpenpose.py生成关键点图再分类放入data/train与data/test最后执行action_detect/train.py完成再训练。目前已有1243人学习下载适合希望快速掌握姿态检测与跌倒识别完整链路的学习者参考。1. 摔倒检测这件事为什么 YOLOv5 加 OpenPose 是毕业设计里最稳的组合做计算机毕业设计选题时摔倒检测是个高频方向但真正动手才发现坑不少。纯靠 YOLOv5 做目标检测只能框出“人”这个类别判断不了人是站着还是躺着纯靠 OpenPose 拿骨架又缺少人体框的定位画面里人一多就串线。把两者串起来——YOLOv5 负责“找到人”OpenPose 负责“看清姿态”再用几何规则判断摔倒——这套方案在毕业设计里落地成本低、可解释性强、答辩时讲得清楚。这篇笔记就按这个思路从环境配置、数据准备、模型推理到摔倒判定逻辑把每一步的参数和踩坑点讲透适合正在做人工智能专业毕业设计、想用 Python 快速跑通一个完整项目的同学。2. 环境配置与两个模型的加载conda 隔离、权重放置、推理入口2.1 为什么必须用 conda 隔离环境YOLOv5 和 OpenPose 对 PyTorch、NumPy、OpenCV 的版本要求经常打架。我见过太多人直接在 base 环境里 pip install结果 YOLOv5 能跑OpenPose 一 import 就报undefined symbol。常见做法是建一个独立环境把两个模型的依赖锁在各自能接受的版本区间里。# 创建独立环境Python 版本选 3.8兼容性最好 conda create -n fall_detect python3.8 -y conda activate fall_detect # 先装 PyTorchYOLOv5 和 OpenPose 都依赖它 # CUDA 11.3 对应 torch 1.10.1这是经过验证的稳定组合 pip install torch1.10.1cu113 torchvision0.11.2cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html # 装 OpenCV 和常用科学计算库 pip install opencv-python4.5.5.64 numpy1.21.6 scipy1.7.3 matplotlib3.5.3参数说明python3.8不是随便选的OpenPose 的 Python API 在 3.9 以上经常编译失败torch1.10.1对应 CUDA 11.3如果你的显卡驱动只支持 CUDA 11.0就降到torch1.9.0cu111。装完后用python -c import torch; print(torch.cuda.is_available())验证返回 True 才算环境通了。2.2 YOLOv5 权重放置与推理脚本YOLOv5 源码从官方仓库拉下来后权重文件yolov5s.pt放在weights/目录下。推理时不需要改源码直接调detect.py或者自己写一个封装函数。import torch import cv2 import numpy as np # 加载 YOLOv5 模型这里用 yolov5s速度快适合毕业设计的实时性要求 model torch.hub.load(ultralytics/yolov5, custom, pathweights/yolov5s.pt) model.conf 0.5 # 置信度阈值低于 0.5 的框直接丢弃 model.iou 0.45 # NMS 的 IoU 阈值防止同一个人被框多次 model.classes [0] # 只检测 person 类COCO 数据集里 person 的索引是 0 def detect_person(img): 输入 BGR 图像返回每个人的边界框列表 [(x1,y1,x2,y2), ...] results model(img) boxes results.xyxy[0].cpu().numpy() # 格式 [x1, y1, x2, y2, conf, cls] person_boxes [] for box in boxes: x1, y1, x2, y2 map(int, box[:4]) person_boxes.append((x1, y1, x2, y2)) return person_boxes逻辑说明model.conf0.5是经验值太低会框到背景里的杂物太高会漏掉远处的人model.classes[0]把非人类目标全部过滤减少后续 OpenPose 的计算量。返回的person_boxes直接喂给 OpenPose 做单人姿态估计这样 OpenPose 就不用在整张图上跑速度能提升 3 到 5 倍。2.3 OpenPose 的两种接入方式与选择OpenPose 在毕业设计里有两条路一是编译官方 C 版本再调 Python API二是直接用开源的轻量级 Python 实现。官方版本精度高但编译极其痛苦我一般推荐用openpose-python或者mediapipe作为替代后者在树莓派 5 上都能跑。import mediapipe as mp mp_pose mp.solutions.pose pose mp_pose.Pose( static_image_modeFalse, # 视频流模式复用上一帧结果更快 model_complexity1, # 0/1/21 是精度和速度的平衡点 smooth_landmarksTrue, # 平滑关键点减少抖动 min_detection_confidence0.5, min_tracking_confidence0.5 ) def get_keypoints(img, box): 在 YOLOv5 框出的区域内做姿态估计返回 33 个关键点坐标 x1, y1, x2, y2 box roi img[y1:y2, x1:x2] # 裁剪出人体区域 roi_rgb cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) result pose.process(roi_rgb) if result.pose_landmarks: h, w roi.shape[:2] keypoints [] for lm in result.pose_landmarks.landmark: # 把归一化坐标还原到原图坐标系 kx int(lm.x * w) x1 ky int(lm.y * h) y1 keypoints.append((kx, ky, lm.visibility)) return keypoints return None参数说明model_complexity1在 1080p 视频上单帧约 15msstatic_image_modeFalse开启视频流模式后MediaPipe 会利用上一帧的跟踪结果实际耗时降到 8ms 左右。smooth_landmarksTrue对摔倒检测很关键因为摔倒瞬间关键点抖动剧烈不平滑会误判。3. 摔倒判定逻辑从骨架坐标到“摔倒/未摔倒”的规则设计3.1 用躯干角度做第一层判断拿到 33 个关键点后最直观的特征是躯干与地面的夹角。人站立时肩膀到髋部的连线接近垂直摔倒时这条线接近水平。import math def calculate_torso_angle(keypoints): 计算躯干与垂直方向的夹角单位度 # MediaPipe 关键点索引11左肩12右肩23左髋24右髋 left_shoulder keypoints[11] right_shoulder keypoints[12] left_hip keypoints[23] right_hip keypoints[24] # 取肩膀和髋部的中心点 shoulder_center ((left_shoulder[0] right_shoulder[0]) / 2, (left_shoulder[1] right_shoulder[1]) / 2) hip_center ((left_hip[0] right_hip[0]) / 2, (left_hip[1] right_hip[1]) / 2) # 计算向量 dx shoulder_center[0] - hip_center[0] dy shoulder_center[1] - hip_center[1] # 与垂直方向的夹角dy 取负是因为图像坐标系 y 轴向下 angle abs(math.degrees(math.atan2(dx, -dy))) return angle逻辑说明站立时angle接近 0 到 30 度摔倒时躯干倾斜angle会超过 60 度。但仅靠角度不够因为弯腰捡东西也能到 60 度。所以需要第二层判断。3.2 用人体框宽高比做第二层过滤YOLOv5 给出的人体框站立时高度远大于宽度摔倒时宽度接近甚至超过高度。def calculate_aspect_ratio(box): 计算人体框的宽高比 x1, y1, x2, y2 box w x2 - x1 h y2 - y1 if h 0: return 0 return w / h def is_fall(box, keypoints, angle_thresh60, ratio_thresh0.8): 综合角度和宽高比判断是否摔倒 angle calculate_torso_angle(keypoints) ratio calculate_aspect_ratio(box) # 两个条件同时满足才判定为摔倒降低误报 if angle angle_thresh and ratio ratio_thresh: return True return False参数说明angle_thresh60是经验阈值低于 60 度基本是正常活动ratio_thresh0.8表示框的宽度达到高度的 80% 以上。两个条件用and连接宁可漏报也不误报因为毕业设计答辩时误报比漏报更难解释。3.3 加入时间窗口做第三层确认单帧判断容易受遮挡和抖动影响实际部署时要用一个滑动窗口连续多帧都判定为摔倒才触发报警。from collections import deque class FallDetector: def __init__(self, window_size5, trigger_count3): self.window deque(maxlenwindow_size) self.trigger_count trigger_count def update(self, box, keypoints): fall is_fall(box, keypoints) self.window.append(fall) # 窗口内超过 trigger_count 帧判定为摔倒才最终确认 if sum(self.window) self.trigger_count: return True return False逻辑说明window_size5表示看最近 5 帧trigger_count3表示至少 3 帧判定为摔倒。这样即使某一帧关键点跳变也不会立刻报警。在 30fps 的视频里5 帧约 0.17 秒响应速度足够快。4. 数据准备与模型微调用自定义数据集提升场景适配4.1 毕业设计数据集从哪来公开数据集里UR Fall Detection Dataset 和 Le2i Fall Detection Dataset 是最常用的两个。UR Fall 包含 30 个摔倒视频和 40 个日常活动视频Le2i 有 191 个视频。如果要做更贴近实际的场景可以用手机在宿舍、走廊拍几十段视频用 LabelImg 标注人体框再用 YOLOv5 做微调。# 用 LabelImg 标注后生成 YOLO 格式的标签文件 # 目录结构 # dataset/ # images/ # train/ (存放训练图片) # val/ (存放验证图片) # labels/ # train/ (存放对应的 .txt 标签) # val/ # 创建 data.yaml 配置文件 cat data.yaml EOF path: ./dataset train: images/train val: images/val nc: 1 names: [person] EOF参数说明nc: 1表示只有一个类别 personnames列表里写类别名。标签文件每行格式是class_id x_center y_center width height坐标都要归一化到 0 到 1 之间。4.2 YOLOv5 微调的关键超参数# 在 YOLOv5 目录下执行训练 python train.py \ --data data.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --lr0 0.01 \ --lrf 0.01 \ --patience 20 \ --device 0参数说明--epochs 100对毕业设计的数据量足够太多会过拟合--batch-size 16取决于显存8G 显存跑 640 尺寸最多 16--lr0 0.01是初始学习率--lrf 0.01是最终学习率因子YOLOv5 默认用余弦退火--patience 20表示 20 轮验证集指标不提升就早停。训练完后在runs/train/exp/weights/best.pt拿到微调权重。4.3 摔倒检测的评估指标怎么算毕业设计答辩时老师一定会问准确率、召回率。摔倒检测不能只看整体准确率因为正常样本远多于摔倒样本。指标计算公式含义毕业设计建议值准确率(TPTN)/(TPTNFPFN)整体判对的比例90%召回率TP/(TPFN)摔倒样本被检出的比例85%精确率TP/(TPFP)报警中真正摔倒的比例80%F1 分数2PR/(PR)精确率和召回率的调和平均82%TP 是正确检出的摔倒FN 是漏掉的摔倒FP 是把正常活动误报为摔倒。如果召回率低就降低angle_thresh如果精确率低就提高ratio_thresh。5. 避坑与排查那些让我熬夜的翻车现场5.1 现象OpenPose 关键点全部挤在画面左上角原因YOLOv5 返回的框坐标是相对于原图的但裁剪 ROI 后 OpenPose 输出的归一化坐标没有加回偏移量。解决在get_keypoints里把lm.x * w x1和lm.y * h y1加上确保坐标映射回原图。5.2 现象视频里人一多就串线A 的骨架连到 B 身上原因OpenPose 在整张图上做多人姿态估计时靠 PAF 做匹配人挨得近就容易错。解决先用 YOLOv5 框出每个人再对每个框单独跑 OpenPose从“多人”变成“多个单人”串线问题直接消失。5.3 现象训练 loss 降到 0.01 但验证集准确率只有 60%原因数据集太小或者标注质量差模型过拟合。解决检查标签文件里有没有框超出图像边界用albumentations做数据增强把--epochs降到 50 并加--dropout 0.2。5.4 现象树莓派 5 上跑 YOLOv5 只有 2fps原因树莓派没有 CUDAPyTorch 走 CPU 推理yolov5s 在 CPU 上就是慢。解决换成yolov5n权重用 ONNX Runtime 或者 NCNN 做推理加速输入尺寸从 640 降到 320帧率能到 8 到 10fps。5.5 现象摔倒后 3 秒才报警错过最佳响应时间原因滑动窗口window_size5加上trigger_count3在低帧率下需要 1 秒以上。解决把window_size降到 3trigger_count设为 2同时提高视频采集帧率到 25fps 以上。6. 把检测结果推到前端一个可演示的 Flask 页面与调参技巧毕业设计最终要演示光在命令行跑不够直观。我一般用 Flask 搭一个最小 Web 界面把检测结果实时画在视频流上。from flask import Flask, render_template, Response import cv2 app Flask(__name__) camera cv2.VideoCapture(0) # 0 是默认摄像头也可以换成视频文件路径 detector FallDetector(window_size3, trigger_count2) def gen_frames(): while True: success, frame camera.read() if not success: break boxes detect_person(frame) for box in boxes: kps get_keypoints(frame, box) if kps: fall detector.update(box, kps) color (0, 0, 255) if fall else (0, 255, 0) x1, y1, x2, y2 box cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) if fall: cv2.putText(frame, FALL!, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) ret, buffer cv2.imencode(.jpg, frame) yield (b--frame\r\n bContent-Type: image/jpeg\r\n\r\n buffer.tobytes() b\r\n) app.route(/) def index(): return render_template(index.html) # 一个简单的 img src/video_feed 页面 app.route(/video_feed) def video_feed(): return Response(gen_frames(), mimetypemultipart/x-mixed-replace; boundaryframe) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)逻辑说明gen_frames是一个生成器每读一帧就做一次检测把结果画上去再编码成 JPEG 推给前端。detector.update内部维护滑动窗口只有连续多帧判定摔倒才画红框。debugFalse在部署时一定要关否则多线程会出问题。调参时我习惯先固定angle_thresh60只调ratio_thresh从 0.6 开始每次加 0.1观察误报和漏报的平衡点。如果场景里椅子多ratio_thresh要提到 0.9 以上因为人坐在椅子上时框的宽高比也会接近 0.8。另外min_detection_confidence在光线暗的走廊要降到 0.3否则 OpenPose 直接丢帧。这套方案我在三个毕业设计里用过最深的教训是不要一上来就追求端到端的深度学习分类模型YOLOv5 加 OpenPose 加规则判断的组合调试成本低、每一步都可解释答辩时老师问“为什么阈值设 60 度”你能答上来比黑匣子模型稳得多。希望帮到你。本文还有配套的精品资源点击获取
