尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

yolov5与openpose摔倒检测实战:从环境搭建到参数调优

yolov5与openpose摔倒检测实战:从环境搭建到参数调优 简介人体姿态估计与目标检测是计算机视觉中两个基础且重要的技术方向在智能安防、养老监护等领域应用广泛。摔倒检测作为其中的典型场景通常依赖目标检测框的形态变化或骨骼关键点的几何特征来实现。本文从YOLOv5目标检测与OpenPose姿态估计的基本原理出发详细阐述基于检测框宽高比和关键点角度的摔倒判定方法并给出完整的Python工程实践包括环境搭建、模型加载、后处理逻辑、简易目标跟踪及常见误报场景的调参思路。通过对比两种技术路线的适用条件与性能差异帮助开发者在不同摄像头角度、实时性要求下进行合理选型提升系统鲁棒性。 yolov5摔倒检测、openpose跌倒检测这类项目最近在技术社区里讨论热度一直不低也不少人私信问源码怎么跑通、两种方案到底选哪个。我干脆把之前做过的完整流程整理成一篇博文从环境搭建到最终拿到检测结果把所有值得注意的坑都摊开讲。先说结论这个项目本质是“目标检测/姿态估计 摔倒判定规则”的组合核心难点不在模型训练而在判定逻辑的鲁棒性。博文里会分别拆解基于yolov5的检测框方案和基于openpose的关键点方案并给出可直接跑的Python源码思路。1. 项目整体设计与两套方案的取舍1.1 为什么同一份需求要准备两套检测方案摔倒检测听起来就是“看人有没有倒下去”但真要落地第一步就卡在“怎么定义摔倒”。同一个动作从侧面看是摔倒从顶视摄像头看可能只是蹲下年轻人弯腰捡东西和老人跌倒在画面里可能一模一样。所以我在设计时没有只押注一种技术路线而是同时做了两套基于Python的检测方案方案Ayolov5目标检测方案。用yolov5先框出画面里的所有人再分析人体框的宽高比变化、中心点位移速度来判断有没有发生跌倒。这也是标题里“yolov5摔倒检测”所指的实现方式。方案Bopenpose姿态估计方案。用openpose提取人体的骨骼关键点坐标根据关键点构成的躯干夹角、头臀距离、髋部中心高度等几何特征判断目标是否处于跌倒状态。这对应标题里的“openpose跌倒检测”。两套方案共用同一个输入输出框架支持读取视频文件和图片文件。调用时通过命令行参数切换实际项目里可以都保留一条命令换方案非常方便。从工程角度说yolov5方案的检测速度更快适合实时监控场景openpose方案的关键点信息更丰富判定依据更充分误报率理论上更低但模型计算量更大。二者搭配起来既能做实时预警又能做精确复核。1.2 方案选型的底层逻辑很多新手容易一上来就陷入“哪个模型更牛”的纠结。我的建议是先想清楚你的使用场景再决定。如果摄像头安装高度在2.5米以内、基本是平视或略微俯视角度那么yolov5方案完全够用。人体框在站立时通常高大于宽摔倒躺下后宽大于高单帧宽高比变化就很明显配合帧间连续性判断误报率可以控制得很低。如果是病房、养老院这类场景希望检测到“慢慢滑倒”“扶着墙壁坐倒在地”这类动作宽高比可能不会瞬间翻转这时候openpose的关键点角度变化就更有价值。如果摄像头是正上方顶视两种方案都难做因为骨骼关键点大量重叠遮挡需要额外的深度信息或者专门训练的顶视模型这部分以后有机会单独聊。Python生态在这里的优势很明显yolov5和openpose都有成熟的预训练模型不用自己标注数据训练开箱即用。这也是我推荐从这两个模型入手做摔倒检测的原因比起从零训练一个行为识别网络工程上靠谱得多。2. 环境搭建与依赖版本锁定2.1 Python环境配置的必踩坑清单先说环境因为这一关拦住了太多人。项目基于Python建议使用3.8或3.9版本这两个版本对yolov5和openpose相关依赖的兼容性最稳定。我用的是Python 3.8.10实测从没出过依赖冲突。安装依赖时最容易翻车的几个点# 基础依赖 pip install opencv-python pip install numpy pip install onnxruntime pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118第一坑PyTorch版本和CUDA版本必须匹配。如果机器有NVIDIA显卡装上CUDA版本的torch才能用GPU加速如果只是CPU跑装CPU版torch就行。判断方法是打开Python执行import torch print(torch.cuda.is_available())输出True说明GPU可用False则只能CPU推理。CPU跑yolov5s模型也能跑到每帧100-200毫秒视频检测能接受实时性要求高就得靠GPU了。第二坑不要一股脑pip install -r requirements.txt。yolov5官方仓库的requirements里有些依赖和你已经装好的版本会冲突我建议只手动安装缺的部分缺什么装什么。第三坑openpose部分原版是Caffe框架配置极其痛苦。我强烈建议直接用ONNX版模型onnxruntime推理跨平台兼容性好不用折腾Caffe和CUDA。模型文件放在项目models目录下即可。2.2 yolov5权重与openpose模型的准备yolov5部分直接下载官方预训练权重即可推荐yolov5s.pt。它只有大约14MB检测速度和精度平衡最佳摔倒检测够用了。如果你追求更高的精度可以换yolov5m.pt但推理耗时会增加约一倍。openpose部分需要下载COCO格式的关键点模型对应输出18个关键点鼻子、脖子、双肩、双肘、双手腕、双髋、双膝、双脚踝等。模型是一个ONNX文件约200MB左右放好路径后使用下面的代码加载import cv2 import onnxruntime as ort # 初始化openpose模型会话 session ort.InferenceSession( models/pose_coco.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider] )注意如果机器只有CPU建议把providers参数改成只有“CPUExecutionProvider”否则onnxruntime在初始化时会尝试加载CUDA库没装CUDA环境可能直接崩溃。3. yolov5摔倒检测方案的原理与实现3.1 检测框宽高比最直观的摔倒特征yolov5方案的核心就是利用检测框中人的姿态变化来判断摔倒。站立的人检测框是竖长的宽高比小倒下的人检测框变成横宽的宽高比大。这个原理看起来简单但实现时有个关键细节必须避免把“坐着”“蹲着”“躺下休息”误判为摔倒。我的做法是双条件叠加判定宽高比超阈值设定一个初始阈值比如0.9。当检测框的宽度除以高度大于这个值时说明目标形态已经是“横向”的了。中心点骤降连续跟踪同一个人的检测框当框中心点的Y坐标在短时间内出现大幅下降比如10帧内下降超过原高度的30%说明这个人发生了快速的姿态变化是“跌”而不是“本来就坐着”。只有两个条件同时满足时才判定为摔倒并连报3帧确认。对应核心Python代码如下def compute_bbox_features(box): 计算检测框的宽高比和中心点 x1, y1, x2, y2 box w x2 - x1 h y2 - y1 ratio w / max(h, 1e-5) cx (x1 x2) / 2 cy (y1 y2) / 2 return ratio, (cx, cy), h class FallJudge: def __init__(self, ratio_thresh0.9, drop_ratio0.3, confirm_frames3): self.ratio_thresh ratio_thresh self.drop_ratio drop_ratio self.confirm_frames confirm_frames self.prev_center {} self.fall_count {} self.is_fall {} def update(self, track_id, box): 更新单个目标的摔倒状态 ratio, (cx, cy), h compute_bbox_features(box) fall_flag False if track_id in self.prev_center: prev_cx, prev_cy self.prev_center[track_id] # 中心点Y坐标下降比例 drop (cy - prev_cy) / max(h, 1e-5) if ratio self.ratio_thresh and drop self.drop_ratio: self.fall_count[track_id] self.fall_count.get(track_id, 0) 1 else: self.fall_count[track_id] 0 if self.fall_count[track_id] self.confirm_frames: fall_flag True else: self.fall_count[track_id] 0 self.prev_center[track_id] (cx, cy) return fall_flag这段代码里的track_id是目标跟踪ID用来区分不同的人。如果没有跟踪模块可以用检测框的IoU匹配来生成临时ID后面第5节会讲。3.2 yolov5源码调用与输出标注yolov5的推理我直接加载官方权重不依赖完整仓库这样可以减少很多文件依赖。用torch.hub加载即可import torch # 加载模型本地权重文件路径指定为weights/yolov5s.pt model torch.hub.load(ultralytics/yolov5, custom, pathweights/yolov5s.pt, force_reloadFalse) # 推理 results model(frame) boxes results.xyxy[0].cpu().numpy()results.xyxy[0]是检测结果数组每行格式是[x1, y1, x2, y2, conf, class_id]。我们要做的就是从里面过滤出class_id 0person类的检测框然后交给上面的FallJudge处理。筛选人的框person_boxes [] for det in boxes: x1, y1, x2, y2, conf, cls det if int(cls) 0 and conf 0.5: person_boxes.append([x1, y1, x2, y2])筛选后在图上绘制检测框和实时框宽高比摔倒时绘制红色框并输出报警文字。这段画框逻辑我习惯单独封装成函数方便在GUI和视频保存之间复用。4. openpose关键点跌倒检测方案的原理与实现4.1 骨骼关键点特征更精确的摔倒判据openpose方案的关键是把人体姿态抽象成18个关键点的坐标然后利用这些坐标计算躯干与水平面的夹角、髋部中心的高度等判据。来看具体判定逻辑。摔倒时人的躯干基本与地面平行而站立时躯干与地面接近垂直。我们用肩部中心点和髋部中心点连线计算这条线与水平面X轴的夹角import math def compute_body_angle(keypoints): 根据openpose的18个关键点计算躯干与地面的夹角 # COCO关键点索引5左肩6右肩11左髋12右髋 left_shoulder keypoints[5] right_shoulder keypoints[6] left_hip keypoints[11] right_hip keypoints[12] # 计算肩部中心和髋部中心 shoulder_center_x (left_shoulder[0] right_shoulder[0]) / 2 shoulder_center_y (left_shoulder[1] right_shoulder[1]) / 2 hip_center_x (left_hip[0] right_hip[0]) / 2 hip_center_y (left_hip[1] right_hip[1]) / 2 # 计算躯干向量与水平面的夹角弧度转角度 dx hip_center_x - shoulder_center_x dy hip_center_y - shoulder_center_y angle abs(math.degrees(math.atan2(abs(dy), abs(dx)))) # 如果关键点不可见坐标为0返回None if shoulder_center_x 1 and shoulder_center_y 1: return None if hip_center_x 1 and hip_center_y 1: return None return angle站立时躯干基本竖直dy远大于dx计算出来的角度接近90度摔倒时躯干躺平dx远大于dy角度接近0度。所以我设定角度小于30度时视为疑似倒地。只靠角度一个特征还不够我叠加了第二个特征颈部关键点到髋部中心的距离变化。人在站立时颈部高髋部也高两点的Y坐标都比较小倒地后整个躯干放平颈部与髋部中心的Y坐标都比较接近地面。这里用“髋部中心Y坐标是否超过画面高度的一半”作为一个辅助判断。综合判定条件如下def is_fall_by_openpose(keypoints, frame_height, angle_thresh30): openpose摔倒判定 angle compute_body_angle(keypoints) if angle is None: return False # 髋部中心的Y坐标 left_hip keypoints[11] right_hip keypoints[12] hip_center_y (left_hip[1] right_hip[1]) / 2 # 角度小 髋部中心低于画面一半高度 if angle angle_thresh and hip_center_y frame_height * 0.5: return True return False注意角度阈值“30度”是我在测试场景下标定的值不同摄像头视角需要微调。如果摄像头是俯视安装这个角度会持续偏小误报会明显增加。4.2 openpose前处理与关键点提取完整流程openpose的ONNX模型输入是368x368的RGB图输出是多个热度图和PAF图需要后处理才能得到关键点坐标。这里我直接把最常用的后处理逻辑写明白。核心步骤是三步缩放输入图、推理、对热度图取峰值点。def preprocess(frame): 图像预处理缩放到openpose要求的368x368 h, w frame.shape[:2] scale 368 / max(h, w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(frame, (new_w, new_h)) # 如果缩放后仍小于368补边 canvas np.zeros((368, 368, 3), dtypenp.uint8) canvas[:new_h, :new_w] resized # HWC转CHW、归一化 blob canvas.astype(np.float32) / 255.0 blob blob.transpose(2, 0, 1) blob np.expand_dims(blob, axis0) return blob, scale, new_h, new_w def get_keypoints(heatmaps, scale, new_h, new_w): 从热度图中提取关键点坐标 # heatmaps形状: (1, 18, 46, 46) heatmaps heatmaps[0] keypoints [] h, w heatmaps.shape[1], heatmaps.shape[2] for i in range(18): heatmap heatmaps[i] _, max_val, _, max_loc cv2.minMaxLoc(heatmap) if max_val 0.1: x int(max_loc[0] * (new_w / w)) y int(max_loc[1] * (new_h / h)) keypoints.append((x, y, max_val)) else: keypoints.append((0, 0, 0)) return keypoints这段代码是我简化后的版本完整版还需要处理PAFPart Affinity Fields用于关键点之间的连接但在只做摔倒检测、不需要绘制骨骼连线的情况下单靠热度图峰值已经够用。每个关键点有三维信息x坐标、y坐标、置信度。拿到关键点后还有一个容易被忽视的细节关键点置信度过滤。当某个关键点置信度低于0.5时说明这个点没有被可靠识别用它计算角度会引入很大误差。所以我要在compute_body_angle里加一层校验关键点置信度太低的直接返回None宁可漏检一次也不要误报。5. 视频与图片检测的完整流程实现5.1 目录结构与命令行设计我习惯把整个项目组织成模块化结构让视频检测和图片检测共用同一套核心逻辑FallDetection/ ├── weights/ │ ├── yolov5s.pt │ └── pose_coco.onnx ├── yolo_detect.py # yolov5方案入口 ├── openpose_detect.py # openpose方案入口 ├── fall_judge.py # 摔倒判定逻辑两种方案共用 ├── track_utils.py # 简易目标跟踪IoU匹配 └── data/ ├── test_video.mp4 └── test_image.jpg命令行设计上入口脚本支持三个参数# 检测视频使用yolov5方案 python yolo_detect.py --source data/test_video.mp4 # 检测图片使用openpose方案 python openpose_detect.py --source data/test_image.jpg # 指定置信度阈值 python yolo_detect.py --source data/test_video.mp4 --conf 0.4所有检测结果会同时输出到屏幕窗口和保存为带标注的新文件方便后续复盘分析。5.2 视频流循环与图片单帧处理的统一封装视频检测的本质是“逐帧读取 检测 判定 绘制”。我封装了一个统一的处理函数让视频和图片共用def run_detection(model_type, source): 统一检测入口 if source.endswith((.jpg, .png, .jpeg)): frame cv2.imread(source) process_frame(frame, model_type) cv2.waitKey(0) else: cap cv2.VideoCapture(source) while True: ret, frame cap.read() if not ret: break process_frame(frame, model_type) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()process_frame内部先根据model_type调用yolo或openpose前处理拿到检测结果后交给fall_judge.py中的判定类最后绘制标注。这里有一个提升准确率的关键技巧不要对每一帧单独判定而是引入“连续N帧确认”机制。单帧的宽高比或角度存在抖动比如人弯腰系鞋带可能连续两三帧都满足疑似条件但很快就恢复站立。如果用单帧判定就会误报。我们让判定状态持续累积只有连续3帧甚至5帧都满足条件时才输出“摔倒”报警。5.3 简易目标跟踪避免多人场景的ID错乱前面yolov5方案的判定逻辑里用到了track_id这个ID从哪里来最省事的办法是用检测框的IoU做帧间匹配。具体思路是当前帧的每个检测框和上一帧的所有检测框计算IoUIoU最大的且超过0.3的被认定为同一个目标。我用代码实现了一个简易Trackerdef iou(box1, box2): 计算两个检测框的IoU x1 max(box1[0], box2[0]) y1 max(box1[1], box2[1]) x2 min(box1[2], box2[2]) y2 min(box1[3], box2[3]) inter_area max(0, x2 - x1) * max(0, y2 - y1) box1_area (box1[2] - box1[0]) * (box1[3] - box1[1]) box2_area (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter_area / (box1_area box2_area - inter_area 1e-5) class SimpleTracker: def __init__(self, iou_thresh0.3): self.iou_thresh iou_thresh self.tracks {} self.next_id 0 def update(self, boxes): updated_tracks {} for box in boxes: best_id None best_iou 0 for track_id, track_box in self.tracks.items(): score iou(box, track_box) if score best_iou: best_iou score best_id track_id if best_id is not None and best_iou self.iou_thresh: updated_tracks[best_id] box else: updated_tracks[self.next_id] box self.next_id 1 self.tracks updated_tracks return updated_tracks这段简易代码在遮挡不严重的情况下够用。如果场景中人很多且互相遮挡频繁还是建议接入DeepSORT这类专门的跟踪算法但项目的核心是摔倒检测简易跟踪能极大降低误报就没有必要引入过重的依赖。6. 常见问题与排查技巧实录6.1 误报高发场景与参数调优我在实测时遇到最多的误报场景基本来自三个人蹲下/坐下宽高比会变大但中心点Y坐标不一定发生“骤降”因为坐下是一个相对平稳的过程。yolov5方案的“中心点骤降”条件可以过滤掉这类误报。弯腰捡东西躯干角度瞬时变小但不会持续连续N帧确认机制能过滤掉大部分情况。有人躺下睡觉/休息这个最难区分因为和摔倒的形态特征几乎一致。我目前的做法是加入“时间维度的恢复判断”如果一段时间后目标重新站立则更新状态不报警如果长时间保持倒地再输出报警。对于真正的无人值守场景宁可多报也不漏报。参数调优建议参数位置初始值调整方向bbox宽高比阈值yolo方案0.9误报多则调大漏报多则调小中心点下降比例yolo方案0.3检测“慢慢滑倒”则调小连续确认帧数共用3误报多则调大关键点角度阈值openpose方案30度俯视摄像头则调大关键点置信度阈值openpose方案0.5画面模糊则调小6.2 运行时报错的排查思路这个项目新手常遇到的报错我整理了一份速查报错一torch.noattribute或module not found大概率是依赖缺失或版本冲突。先确认环境里有没有装错包尤其注意torch和torchvision版本必须对应。报错二onnxruntime加载模型失败检查模型文件路径是否正确模型文件是否下载完整。可以用下面一行代码快速验证import onnxruntime sess onnxruntime.InferenceSession(weights/pose_coco.onnx) print(模型加载成功)报错三cv2.imshow无法显示窗口在服务器上跑项目时没有桌面环境会导致该报错。解决办法是把显示相关的代码注释掉只保存结果文件。报错四检测不到任何结果先降低置信度阈值到0.1试试如果仍然没有检测框基本可以确定是模型加载出了问题而不是阈值问题。用一张带完整人身的图片单独测试模型。6.3 我在实际使用中总结的几个小技巧再分享几个提升体验的细节这些在文档里通常不会写处理速度的优化onnxruntime支持半精度推理如果模型支持FP16可以在InferenceSession初始化时设置enable_fp16True速度能提升30%左右。视频抽帧检测如果只需要事后分析而不是实时监控可以用cv2.CAP_PROP_POS_MSEC做跳帧处理比如每5帧检测一次处理速度直接提升数倍。报警联动检测到摔倒后可以加一个subprocess调用系统命令发语音提示或者把报警帧保存成单独的截图到alarm目录这样事后查证很方便。加一行cv2.imwrite(falarm/{timestamp}.jpg, frame)就能实现。我个人在实际操作中的体会是这个项目真正有价值的部分不在于用多高深的技术而在于把模型输出变成可靠业务规则的过程。同样的yolov5权重和openpose模型不同的人写出来的摔倒判定效果可能差很多差别就在对场景的理解和规则的精细程度。建议你拿到源码后先用自己场景的摄像头素材测试优先调整判定阈值再考虑要不要更换模型或引入跟踪算法。本文还有配套的精品资源点击获取
返回列表