yolov5+openpose摔倒检测项目实战:原理、部署与参数调优
发布时间:2026/10/8 1:09:06 锦皓数字建站

简介面向计算机专业毕业设计、课程设计与期末大作业场景的深度学习实战资源整合yolov5人体检测与openpose姿态估计算法实现视频或图片中人体摔倒行为的自动识别。项目由导师指导完成评审得分98分具备完整的项目源码和训练模型适合需要快速搭建人体姿态分析系统、理解目标检测与关键点提取联合应用逻辑的学生参考。资源共包含40余个文件以Python源码、模型权重、配置文件及说明文档为主压缩包整体约40.25MB便于直接下载解压后对照学习。目前已吸引167人浏览学习可帮助学习者快速掌握摔倒检测的技术流程、模型调用方式与关键代码结构同时也可作为高分之选的高分项目范例进行扩展与二次开发。1. 为什么“yolov5openpose”组合能撑起一个摔倒检测项目摔倒检测在计算机视觉里不算新课题但真正落地过的人都知道难点不在于“识别一个人”而在于“稳定判断一个人是不是摔了”。单纯用目标检测模型你能拿到人框却拿不到姿态单纯用姿态估计模型人一多、遮挡一重关键点就乱飘。这个项目把 yolov5 的人体检测和 openpose 的姿态估计串成一条流水线yolov5 先把人从画面里框出来openpose 再在框内提取姿态关键点最后由几何规则判定摔倒与否。评审分 98 分靠的不是堆模型而是这套分工明确的检测链路。适合正在做毕业设计、课程设计的学生也适合想跑通一套完整 CV 落地流程的开发者。下面按我实际拆过的路径从原理、环境、判定逻辑到踩坑逐层说清楚。2. 两阶段流水线的基本原理为什么先检测人再估计姿态2.1 yolov5 在人检任务里的选型理由yolov5 是目前工程落地里性价比最高的一档检测器。它把模型按深度和宽度分成 s、m、l、x 四个尺度摔倒检测这类单人或多人的室内监控场景用 yolov5s 就够。s 版本在 COCO 上的 mAP 大约 37推理速度在 GTX 1060 上能跑到 50 FPS 以上这对后续接姿态估计很重要因为姿态估计更吃算力检测器如果能做到又快又稳整个管线的帧率才有保障。选 yolov5 而不是更早的 yolov3 或更重的 EfficientDet核心原因是它的预处理和后处理接口简单。项目检测类别只需要 person 这一类跑推理时直接读data/coco128.yaml或者自定义一个只有 person 类别的 yaml把nc改成 1重新训练或者直接沿用 COCO 预训练权重里的 person 类别输出。实际部署时你会用到它的detect.py核心参数就是--weights、--source、--conf-thres、--iou-thres。# yolov5 推理的核心调用路径示例 import torch model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.classes [0] # 只保留 person 类别COCO 中 person 的 id 是 0 results model(frame) # 输入 BGR 帧返回检测结果 boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls]这段代码里model.classes [0]是关键过滤逻辑只保留 person 类的检测框否则椅子、桌子这些物体会被一起框出来后面的姿态估计会白算。results.xyxy返回的是归一化前的像素坐标后续传给 openpose 之前需要做一次坐标系转换。2.2 openpose 姿态估计的底层逻辑openpose 的核心原理是 PAFPart Affinity Fields部分亲和场。简单说它不只预测每个关键点的热力图还预测关键点之间的连接方向和置信度然后通过图匹配把这些连接拼成完整骨架。输出是 18 个或 25 个关键点的坐标与置信度常见的是 COCO 格式的 17 点包括鼻子、颈部、双肩、双肘、双腕、双髋、双膝、双踝。摔倒检测里最有用的四个点是双髋、双膝、双踝。判定摔倒的几何特征全部围绕这几个点展开。比如人站立时髋关节离地面大约 90 厘米摔倒后髋关节高度会骤降到 20 厘米以下再比如站立时躯干颈部到髋部连线的延长线与地面的夹角接近 90 度摔倒后这个夹角会跌到 30 度以下。这些指标用 openpose 输出的原始坐标就能算不依赖额外的训练数据。openpose 的模型体积是个现实问题。原版 caffe 模型有 200 多 MB在 CPU 上跑 640x480 的一帧需要几百毫秒帧率只有个位数。工程里常见的做法是替换成 lightweight-openpose 或 tensorrt 加速版本前者只有 14MB 左右精度略有下降但摔倒检测这种场景足够用。2.3 两条模型串起来的完整管线整个检测管线分四步yolov5 跑目标检测拿到人物框 → 把每个人物框裁剪出来 → 对裁剪图执行 openpose 姿态估计 → 拿姿态关键点到主帧坐标还原再做摔倒判定。这里最容易翻车的是坐标系还原因为裁剪图里算出来的关键点坐标是相对裁剪图的必须映射回原图否则判定逻辑里的“离地高度”和“躯干角度”全是错的。# 坐标映射的核心逻辑从裁剪图坐标回到原图坐标 def map_keypoints_to_original(kp_cropped, box): x1, y1, x2, y2 box scale_x (x2 - x1) / crop_width # 裁剪图宽度与原框宽度的比例 scale_y (y2 - y1) / crop_height kp_original [] for (kx, ky, conf) in kp_cropped: orig_x x1 kx * scale_x orig_y y1 ky * scale_y kp_original.append((orig_x, orig_y, conf)) return kp_original这段代码的scale_x和scale_y必须分开算不能图省事用同一个缩放系数因为裁剪时不一定保持原始宽高比。很多开源项目里摔倒误报率高问题就出在这里。等比缩放只适用于x1x2这类极端情况日常视频里人的检测框宽高比一直在变。3. 环境搭建与源码复现从下载到跑通第一帧检测3.1 环境版本组合与安装细节这个项目的依赖分两块yolov5 的依赖和 openpose 的依赖。yolov5 官方推荐 Python 3.8 以上、PyTorch 1.7 以上openpose 的 pytorch 实现比如 tf-pose-estimation 或 lightweight-openpose要求 PyTorch 版本不能太新否则部分算子会报错。我实际跑通的一组组合是组件版本说明Python3.8兼容性最好torchvision 编译不出幺蛾子PyTorch1.10.0CPU/GPU 版本均可GPU 需 CUDA 11.3opencv-python4.5.5视频读取与图像预处理numpy1.21关键点坐标运算Cython0.29.32openpose 的 setup 编译需要安装时先装 torch 再装其他依赖。pip install torch1.10.0cu113 torchvision0.11.0cu113 -f https://download.pytorch.org/whl/torch_stable.html是 GPU 版本的安装命令。如果只有 CPU 机器把cu113去掉就行。3.2 源码目录结构与权重文件放置项目解压后的目录通常是这样的fall_detection/ ├── models/ # yolov5 模型定义与权重 │ ├── yolov5s.pt │ └── openpose/ │ ├── pose_model.pth │ └── pose_cfg.yaml ├── utils/ # 通用工具坐标映射、可视化 ├── detection/ # yolov5 推理封装 ├── pose/ # openpose 推理封装 ├── judge/ # 摔倒判定规则 ├── test_data/ # 测试视频与图片 ├── main.py # 主入口 └── requirements.txt第 1 次跑通前先确认 yolov5s.pt 存在这是官方预训练权重只有 14MB。openpose 的权重如果是原版 caffe 模型需要pose/coco/pose_iter_440000.caffemodel这个文件约 200MB下载后放在指定目录路径不对会在加载时报FileNotFoundError。3.3 主流程代码跑通第一帧# main.py 的最小可运行版本 import cv2 from detection.yolo_detector import YoloDetector from pose.openpose_estimator import OpenPoseEstimator from judge.fall_judger import FallJudger yolo YoloDetector(weightsmodels/yolov5s.pt, conf_thres0.4) pose OpenPoseEstimator(model_pathmodels/openpose/pose_model.pth) judger FallJudger(fall_height_ratio0.45, angle_thresh35) frame cv2.imread(test_data/person_standing.jpg) boxes yolo.detect(frame) # 返回 [x1,y1,x2,y2,conf] 列表 for box in boxes: crop frame[int(box[1]):int(box[3]), int(box[0]):int(box[2])] keypoints pose.estimate(crop) # 返回 17x3 的数组 [x,y,conf] orig_kps map_keypoints_to_original(keypoints, box) is_fall judger.judge(orig_kps) if is_fall: cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 0, 255), 2) cv2.putText(frame, FALL, (box[0], box[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) cv2.imwrite(test_data/result.jpg, frame)这段代码里有三个参数值得注意。conf_thres0.4控制 yolov5 的人检置信度低于 0.4 的框会被过滤太高了容易漏检太低了误检框多openpose 会被带着多算好几轮空姿态。pose.estimate(crop)输入的裁剪图建议做一次 resize 到 368x368这是 openpose 最常用的输入分辨率直接传原始裁剪尺寸也能跑但姿态精度会波动。judger.judge(orig_kps)里传入的必须是原图坐标这一点前面强调过。3.4 第一次跑通后的自检清单跑通第一帧后不要急着接视频流先做三个验证。第一打印关键点坐标确认最大 x 不超过图像宽度、最大 y 不超过图像高度如果超出说明坐标映射有 bug。第二把关键点画在原图上检查颈部到髋部的连线应该落人身体中心线上如果斜着飘出去说明 openpose 输入的分辨率处理有问题。第三统计单帧耗时。yolov5s 加 lightweight-openpose 在 GPU 上应该在 30ms 以内如果超过 100ms检查是不是没用半精度推理或者模型版本选大了。4. 摔倒判定逻辑与参数调优从关键点坐标到每一个阈值4.1 判定指标的数学定义摔倒检测的判定指标通常有三个互相配合使用。第一个是髋部离地高度比计算公式是髋部平均高度除以人体身高。用绝对高度不方便因为摄像头角度和安装高度会直接影响像素坐标改成比例后鲁棒性更好。第二个是躯干倾斜角取颈部到双髋中点的连线与水平线之间的夹角站立时接近 90 度摔倒后接近 0 度。第三个是重心垂直速度用相邻帧髋部高度的差分除以帧间隔时间突然快速下降本身就是一个强信号。def compute_fall_metrics(keypoints, prev_keypoints, fps): # keypoints: 17x3索引按 COCO 顺序 hip_left keypoints[11] # 左髋 hip_right keypoints[12] # 右髋 neck keypoints[1] # 颈部 hip_center_y (hip_left[1] hip_right[1]) / 2 neck_y neck[1] hip_to_ground hip_center_y # 以图像底部为地面 body_height abs(hip_center_y - neck_y) # 指标1髋部高度比 height_ratio body_height / hip_to_ground if hip_to_ground 1e-6 else 0 # 指标2躯干倾斜角度 delta_x (hip_left[0] hip_right[0]) / 2 - neck[0] delta_y hip_center_y - neck_y body_angle abs(math.degrees(math.atan2(delta_y, delta_x))) # 指标3垂直速度像素/秒 if prev_keypoints is not None: prev_hip_y (prev_keypoints[11][1] prev_keypoints[12][1]) / 2 fall_speed (hip_center_y - prev_hip_y) * fps else: fall_speed 0 return height_ratio, body_angle, fall_speed这三个指标的含义要区分清楚。height_ratio衡量的是人体“竖着占多少比例”站立时身体高度与髋部高度接近 1:1比值趋近 1摔倒平躺时身体高度贡献给了 x 方向髋部高度接近地面比值会很小。body_angle单独看不可靠因为弯腰捡东西时角度也会大幅缩小所以必须结合垂直速度一起判断。4.2 多帧缓冲与状态机单帧判定必然误报这是所有摔倒检测项目的共性教训。合理解法是引入一个长度为 N 的滑动窗口只有连续 N 帧中至少有 M 帧触发摔倒条件才输出一次摔倒事件。这里 N 取 5、M 取 3 是个比较稳的起点。class FallJudger: def __init__(self, height_ratio_thresh0.45, angle_thresh35, speed_thresh450, buffer_size5, trigger_count3): self.buffer [] self.buffer_size buffer_size self.trigger_count trigger_count def judge(self, kps, prev_kps, fps): ratio, angle, speed compute_fall_metrics(kps, prev_kps, fps) # 核心条件髋部高度比变小 躯干角度变平 垂直速度够大 is_falling (ratio self.height_ratio_thresh and angle self.angle_thresh and speed self.speed_thresh) self.buffer.append(1 if is_falling else 0) if len(self.buffer) self.buffer_size: self.buffer.pop(0) if sum(self.buffer) self.trigger_count: self.buffer.clear() # 触发后清空防止同一次摔倒重复报警 return True return False这段代码里最关键的是buffer.clear()不清空的话同一段摔倒视频会连续报警十几次。实际部署时清空后还需要加一个冷却期比如 10 秒内不再触发等姿态恢复正常再重新武装。4.3 阈值整定的实践经验阈值不是拍脑袋定的用测试视频跑一遍把三个指标的时间序列打印出来看站立、走路、弯腰、坐下、摔倒这几类动作分别在什么范围。动作类型height_ratiobody_anglefall_speed站立0.85 ~ 1.175 ~ 90 50弯腰0.30 ~ 0.5525 ~ 4550 ~ 150坐下0.35 ~ 0.6040 ~ 6080 ~ 250摔倒0.10 ~ 0.405 ~ 30400 ~ 1000从这张表能看出height_ratio和angle都无法单独区分弯腰和摔倒必须加上speed这个维度。最终选阈值时height_ratio_thresh0.45能覆盖大部分摔倒场景angle_thresh35会放过一些侧倒场景侧倒时躯干角度可能还有 40 度需要根据你的摄像头安装角度微调。5. 避坑与常见问题排查漏检、姿态错乱、显存溢出的真实原因5.1 openpose 偶尔完全拿不到关键点现象yolov5 框到了人但 openpose 输出的 keypoints 全是 0置信度也是 0。原因裁剪出来的人像区域太小或者 openpose 内部对输入做了固定 resize小区域被放大后严重失真。还有一种可能是关键点置信度阈值设太高比如 0.6 以上部分遮挡场景下关键点置信度天然低。解决给 openpose 的输入加一个最小尺寸限制。裁剪区域短边小于 100 像素时直接放弃该帧的姿态估计让下一帧再试。置信度阈值默认降到 0.2 到 0.3 之间摔倒检测更看重召回而不是精确漏一个关键点比多一个噪点更致命。# 在裁剪后做尺寸规整 if min(crop.shape[0], crop.shape[1]) 100: continue # 跳过这种无法可靠估计姿态的框 crop_resized cv2.resize(crop, (368, 368), interpolationcv2.INTER_LINEAR)5.2 站着的人被频繁判定为摔倒现象画面里有人正常行走偶尔出现一次摔倒误报看输出指标是 body_angle 突然掉到 30 度。原因yolov5 检测框偶尔会丢一只脚导致 openpose 只估计出上半身关键点髋部位置被抬高height_ratio 和 angle 同时失真。解决判定加上关键点完整性检查。至少要有 10 个置信度大于 0.3 的关键点才进入摔倒判定而且双髋、双膝、双踝这六个点里不能少于 4 个。缺失超过一半就跳过当前帧无脑输出“疑似摔倒”只会把项目变成狼来了的故事。5.3 GPU 显存不足跑几秒就 OOM现象在 8GB 显存的卡上跑视频流后台日志显示 CUDA out of memory。原因openpose 的默认输入分辨率大yolov5 的 batch size 没控制加上 PyTorch 默认不用半精度显存瞬间被吃光。解决三件事输入分辨率降到 320x320yolov5 推理时设置batch1openpose 模型换成 lightweight 版本再把推理代码包一层torch.cuda.amp。with torch.cuda.amp.autocast(): keypoints pose.estimate(crop_resized)半精度在这类任务上精度损失很小显存占用直接减半是性价比最高的优化手段之一。5.4 检测框剧烈抖动姿态也跟着跳现象人物框每帧左右横跳关键点坐标噪声很大速度指标算出来的值忽高忽低。原因yolov5 的 NMS 阈值设置不合适同一个人的多个框没有被合并干净或者检测到的是两个人靠得很近的情况。解决iou-thres从默认的 0.45 提到 0.5 到 0.55让重叠的框更激进地合并。速度指标对坐标噪声非常敏感给关键点坐标做一个宽度为 3 帧的移动平均滤波能显著减少速度误报。# 三帧移动平均滤波平滑关键点坐标 smoothed (prev2 prev1 current) / 35.5 侧躺摔倒完全检测不到现象人对着摄像头侧面摔倒躯干夹角始终在 50 度以上你设定的 angle 阈值永远不会触发。原因侧躺时躯干在图像平面上的投影角度不够平这是二维估计的天然盲区。解决不要依赖单一视角的角度改用“髋部高度比 垂直速度”双条件并联逻辑。侧躺摔倒时髋部高度同样会大幅下降速度同样会骤升这两个指标不受视角旋转影响。只有这两个条件都触发时才判定摔倒angle 只作为辅助参考。6. 进阶把摔倒检测接进视频流与告警管线前面的测试都是单帧图片真正要落地还得接视频流和告警。这里给一套我已经跑通的方案用 OpenCV 的VideoCapture读摄像头流做一个双线程结构主线程读帧和画框子线程跑检测主线程只负责显示和推流。这个设计的核心是不要阻塞读帧否则视频会越跑越卡。def process_stream(rtsp_url, fall_callback): cap cv2.VideoCapture(rtsp_url) fps cap.get(cv2.CAP_PROP_FPS) while True: ret, frame cap.read() if not ret: break boxes yolo.detect(frame) for box in boxes: kps get_keypoints(frame, box) if judger.judge(kps, prev_kps, fps): fall_callback(frame, box) # 告警回调 judger.cooldown_until time.time() 10 prev_kps kps告警推送最简单是走钉钉或企业微信的 webhookrequests.post一发就完事。我把一张图片存下来连同时间戳和摄像头编号组一条消息推出去实测全链路延迟在 1 秒以内。再往上走一步是提速。yolov5 转 ONNX 再走 TensorRTopenpose 同理整套流程在 RTX 3060 上能从 20 帧提高到 40 帧左右。代价是模型变得难以调试所以我一贯的原则是先跑通纯 PyTorch 版本确认判定逻辑没问题再做转换。直接上半精度和 TensorRT 排错你会分不清是模型问题还是代码问题。另外提醒一句模型权重文件的路径不要写绝对路径项目换机器就得改。放进 models 目录后用相对路径引用这是这个项目里最常见的低级翻车。从那以后我每次部署新环境都强制走一遍完整的跌倒测试集用五段标注好时刻的视频去验证触发时刻误差确认没问题再交付。希望这份笔记能帮你把摔倒检测项目顺利跑通少走我走过的那些弯路。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。