ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv8-pose实时摔倒检测:目标检测+姿态估计的Python实战

YOLOv8-pose实时摔倒检测:目标检测+姿态估计的Python实战 简介这是一份基于计算机视觉的目标检测与姿态分析实时防摔倒预警系统完整项目使用Python实现并配套实验报告面向计算机视觉、人工智能等专业的在校学生、教师及入门开发者既可支撑课程设计、毕业设计或初期项目演示也适合作为学习目标检测与人体姿态分析的实践素材。资源共1794个文件除Python主程序外还包含大量C依赖库头文件hpp与源文件cpp、JSON配置、CUDA算子、模型描述文件prototxt/nnmodel、AVI测试视频以及Word/Markdown文档等整体约230.34MB。目前已有225人学习下载项目结构完整解压后可直接复现运行也便于二次开发。实验报告详细说明了目标检测与姿态分析的实现流程视频样本可直观验证防摔倒预警效果代码模块化程度高适合深入理解从模型推理到实时预警的完整链路。1. 课程设计为什么值得选检测、姿态、预警一条链源码和实验报告都能交差深夜的独居老人家里比人眼更靠谱的往往是墙角的摄像头。实时防摔倒预警系统要做的事很直接用目标检测锁定画面里的人用姿态分析读出骨骼关键点两者配合在倒下的几秒内触发报警。这个课程设计题目把计算机视觉里三块硬骨头——目标检测、姿态分析、实时视频处理——串成一个能现场演示的 Python 工程最终交付 python 源码和实验报告正好覆盖计算机视觉大作业最看重的两个点能跑、能讲原理。适合想做完整项目而不只调库的同学也适合作为毕设的前置预研。下文按我搭建同类方案的实际顺序把模型选型、代码骨架、摔倒判定算法和踩坑记录一次说透。2. 这个题的技术核心检测框回答「人在哪」姿态分析回答「人怎么摆」很多人第一次拿到这个题目第一反应是用目标检测框住人框变扁了就是摔倒。这个思路听起来合理实际跑起来误报率高到没法用原因后面细说。真正的分水岭在于目标检测只能给出目标的位置和类别而摔倒是一个姿态事件必须由人体关键点构成的骨架来判定。2.1 为什么单靠目标检测框做摔倒判定会翻车先泼一盆冷水目标检测框做摔倒判定有三个绕不过去的问题。第一摔倒前后检测框的类别不会变始终是 person。模型不关心这个 person 是站着还是躺着它只负责回答这里有个人的概率是多少。第二检测框的宽高比变化并不能唯一对应摔倒。人蹲下系鞋带、坐在椅子上、弯腰捡东西宽高比都会从竖直变成接近水平这三类全是正常行为在养老场景里如果频繁误报照护人员会直接把系统关掉——这是防摔倒系统在实际部署里最大的失败模式。第三检测框本身没有关节信息。躺在地上和坐在矮凳上从检测框层面看几乎没有区别但两者需要的响应完全不同。所以单靠框的几何变化做判定本质上是在用一个信息量不够的输入做关键决策。姿态分析补上的恰好是这个缺口它输出人体关节点的像素坐标最常见的 COCO 17 点定义是——0 鼻子、1/2 左右眼、3/4 左右耳、5/6 左右肩、7/8 左右肘、9/10 左右腕、11/12 左右髋、13/14 左右膝、15/16 左右踝。摔倒的本质就是躯干从近乎竖直变成近乎水平这个变化用肩部中心和髋部中心的相对位置就能定量描述。2.2 姿态估计方案选型MediaPipe、OpenPose、YOLOv8-pose 怎么选做姿态分析有三个常见方案我直接给对比都是从业者常用的。方案关键点数目检测与姿态是否一个模型CPU 实时性课程设计友好度MediaPipe Pose33 点含面部关键点内部两步对外一个接口较好CPU 能跑实时中上手快但姿态点置信度不如 YOLO 直观多人场景要额外处理OpenPose18/25/135 点是差基本要 GPU低依赖重装环境能劝退一半人YOLOv8-pose17 个 COCO 点是n 模型在 CPU 约 10-15fps高一个模型同时出检测框和骨架实验报告好写我一般会选 YOLOv8-pose理由有三层。第一它把目标检测和姿态估计合并成一次前向传播。模型检测出人的检测框之后在框内回归 17 个关键点一次推理同时拿到 (x1, y1, x2, y2, class, conf) 和 17 个 (x, y, confidence)。代码不用维护两套推理逻辑实验报告里也能把目标检测和姿态分析两个模块串成一条线来讲。第二它是 YOLO 生态后续想微调或者换数据集直接沿用目标检测常用标注工具和数据集处理流程不用换工具链。这点在你实验不满意、想重新训练检测头时特别值钱。第三置信度输出直观。YOLOv8-pose 对每个关键点都输出一个可见性置信度后面摔倒判定里可以拿这个值决定这个点的坐标可不可信。2.3 输入帧到关键点的数据流一个前向传播同时拿框和骨架整个系统的数据流是这样的摄像头读入一帧 640x480 的 BGR 图送入 YOLOv8-pose 模型模型输出检测框列表和关键点张量。关键点张量的形状是 [num_person, 17, 3]最后一个维度的三个数分别是 x、y 和置信度。注意这里 x、y 是相对于原图尺寸的像素坐标不是归一化坐标后面做特征计算时要先转成 float否则整数除法会直接把你坑了。拿到关键点之后先做一个轻量的时序平滑再交给摔倒判定模块。为什么不直接判定因为单帧关键点有噪声尤其肩膀和髋部在衣服宽松、光照差的情况下会抖动平滑能显著降低误报。平滑窗口和系数我放在第 4 章讲这里先把概念立住——摔倒判定用的不是某一帧的姿态而是连续几十帧的姿态趋势。3. 工程骨架把 YOLOv8-pose 接到视频流的最小可跑代码这一章直接给可抄的代码骨架。工程分三个文件主循环、姿态估计封装、摔倒判定模块。先把检测和判定分离后面换模型或者调算法都不用在主循环里动刀。3.1 目录安排与三行依赖安装常见做法是把工程这样组织fall_detection/ ├── main.py # 视频流主循环读帧、推理、显示、触发报警 ├── pose_estimator.py # 封装 YOLOv8-pose输出检测框和关键点 ├── fall_detector.py # 摔倒判定状态机 ├── requirements.txt └── weights/ └── yolov8n-pose.pt # 预训练权重首次运行自动下载依赖只要三个在 requirements.txt 里写清楚ultralytics opencv-python numpy然后用一条命令装齐pip install -r requirements.txt装完先确认一下模型文件能不能自动下载。运行下面这条命令如果输出里出现下载进度条说明网络通畅如果卡住就去 ultralytics 的 release 页面手动下载 yolov8n-pose.pt 放进 weights 目录。这个坑我踩过一次离线环境拿不到权重整个项目就瘫了所以建议第一步先把权重准备好。3.2 PoseEstimator 封装推理一次拿到框和 17 个关键点# pose_estimator.py # 封装 YOLOv8-pose一次前向传播输出检测框和 17 个 COCO 关键点 from ultralytics import YOLO import numpy as np class PoseEstimator: def __init__(self, model_pathweights/yolov8n-pose.pt, conf0.4, devicecpu): # n 模型在 CPU 上实时性最好课程设计阶段够用 self.model YOLO(model_path) self.conf conf self.device device def estimate(self, frame): results self.model.predict( sourceframe, confself.conf, deviceself.device, verboseFalse, # 不打印推理日志保证终端清爽 ) # 没有检测到人直接返回 None if len(results) 0 or results[0].keypoints is None: return None kps results[0].keypoints.data # [num_person, 17, 3] boxes results[0].boxes.xyxy # [num_person, 4] if len(kps) 0: return None return boxes, kps这段代码的逻辑很直白 predict 返回一个列表列表里每个元素对应一张输入图的结果。我们每次只送一帧所以取 results[0]。keypoints.data 是关键点张量最后一维的 3 个值分别表示 x、y 和可见性置信度boxes.xyxy 是检测框左上角和右下角的像素坐标。conf 参数控制的是目标检测阶段的置信度阈值默认 0.4 比较均衡。调高到 0.6误检变少但对遮挡严重的摔倒场景可能漏检调低到 0.2能把半遮挡的人捞回来但也会引入一些假的检测框。我的建议是课程设计先用 0.4 跑通最后做评估时再按数据调。device 参数在 CPU 机器上写 cpu有 N 卡可以写 0 走 CUDA。这里要提醒一句即使你本地有 GPU交作业演示的机器未必有所以代码里统一用 CPU 路径测试避免现场翻车。3.3 主循环里串起来抽帧、推理、画骨架、按 q 退出# main.py # 视频流主循环读帧 - 姿态估计 - 摔倒判定 - 可视化 import cv2 from pose_estimator import PoseEstimator from fall_detector import FallDetector def main(): # 摄像头用 0视频文件直接改成路径 cap cv2.VideoCapture(0) # 统一输入尺寸关键点坐标稳定后面阈值标定才有意义 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) estimator PoseEstimator(model_pathweights/yolov8n-pose.pt, conf0.4, devicecpu) detector FallDetector() while cap.isOpened(): ret, frame cap.read() if not ret: break out estimator.estimate(frame) if out is not None: boxes, kps out # 这里做摔倒判定kps 取第一个人多人场景需要按跟踪 ID 处理 for i in range(len(kps)): if detector.update(kps[i]): # 触发报警实际项目中这里接保存截图、推送通知 cv2.imwrite(alert.jpg, frame) # 画检测框和骨架的代码省略cv2.rectangle 和 cv2.line 即可 # 实验报告里的可视化截图就是从这一步来的 cv2.imshow(fall-detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() if __name__ __main__: main()主循环的关键点是每读一帧就推理一次检测到人后逐个人调用摔倒判定。imwrite 保存报警截图这行别删实验报告里报警记录一节需要它当证据。多人场景下这个写法会有问题——每个人没有稳定 ID同一人跨帧会被当成不同人。课程设计阶段可以用最大检测框近似解决只对画面里面积最大的人做判定想做得正经就得引入跟踪器我在最后一章讲进阶方向。4. 摔倒判定算法三要素、状态机与阈值标定一次讲完骨架拿到了接下来是核心问题什么样的关键点分布算摔倒这一章给出可解释、可调参的几何判定方法。4.1 三个特征归一化髋部高度、躯干倾角、帧间下落速度摔倒判定我用三个特征都是从业者最常用的几何量。第一个是归一化髋部高度。取左右髋关键点的中点作为髋部中心左右肩关键点的中点作为肩部中心然后算髋部中心相对肩部中心的纵向距离再除以肩髋距离做归一化。为什么要归一化因为同一个摄像头下人离镜头近和远绝对像素高度差别很大但只要用肩髋距离做尺度站立时这个比值接近 1平躺时接近 0跟人的远近基本无关。第二个是躯干倾角。肩部中心和髋部中心连一条线这条线与竖直方向的夹角。站立时接近 0 度完全躺平时接近 90 度。这个特征对坐在椅子上和躺在地上的区分很有用——坐着时躯干仍然基本竖直倾角很小摔倒后躯干趋于水平。第三个是帧间下落速度。用当前帧髋部中心 y 坐标减去上一帧的 y 坐标得到一帧内的像素位移。人在正常走路时这个值很小摔倒时会在两三帧内出现一个大的正向跳变也就是髋部突然向下。这个特征用来提前捕捉正在倒下的过程而不是等躺平了才发现——提前几帧报警对实际场景意义很大。三个特征各有分工下落速度负责第一时间发现异常倾角负责确认姿态性质归一化髋部高度负责确认最终状态。三者合一阈值判定才有足够的区分度。4.2 FallDetector 实现平滑、状态机、冷却时间# fall_detector.py # 摔倒判定状态机平滑 - 特征计算 - 累计触发 - 冷却 import numpy as np # COCO 17 点关键点索引 LEFT_SHOULDER, RIGHT_SHOULDER 5, 6 LEFT_HIP, RIGHT_HIP 11, 12 class FallDetector: def __init__(self, height_thresh0.45, angle_thresh65, down_frames30, cooldown150): self.height_thresh height_thresh # 归一化髋部高度阈值 self.angle_thresh angle_thresh # 躯干倾角阈值单位度 self.down_frames down_frames # 连续命中的帧数30帧约1秒 self.cooldown cooldown # 触发后的冷却帧数防重复报警 self.smoothed None # EMA 平滑后的关键点 self.prev_hip_y None # 上一帧髋部中心 y 坐标 self.down_count 0 # 连续命中计数器 self.cooldown_count 0 # 冷却计数器 self.alerted False def smooth(self, kps): # EMA 指数移动平均压制单帧关键点抖动 if self.smoothed is None: self.smoothed np.array(kps, dtypenp.float32) else: # 0.6 保留历史0.4 采纳当前帧平滑和延迟要权衡 self.smoothed 0.6 * self.smoothed 0.4 * np.array(kps, dtypenp.float32) return self.smoothed def compute_features(self, kps): shoulder (kps[LEFT_SHOULDER][:2] kps[RIGHT_SHOULDER][:2]) / 2 hip (kps[LEFT_HIP][:2] kps[RIGHT_HIP][:2]) / 2 # 肩髋距离做归一化尺度站立时 hip_norm 接近 1平躺接近 0 body_len np.linalg.norm(shoulder - hip) 1e-5 hip_norm (hip[1] - shoulder[1]) / body_len # 躯干与竖直方向的夹角站立约 0 度平躺约 90 度 angle np.degrees(np.arctan2( abs(hip[0] - shoulder[0]), abs(hip[1] - shoulder[1]) )) return hip_norm, angle, hip[1] def update(self, kps): kps self.smooth(kps) hip_norm, angle, hip_y self.compute_features(kps) # 冷却期不重复报警防止同一事件反复触发 if self.cooldown_count 0: self.cooldown_count - 1 self.alerted False return False # 帧间下落速度髋部中心向下的像素位移 velocity_px 0 if self.prev_hip_y is not None: velocity_px hip_y - self.prev_hip_y self.prev_hip_y hip_y # 两个触发条件满足其一就开始累计 # 1. 已经处于倒地姿态髋部低 躯干水平 # 2. 正在快速下落帧间位移大 躯干明显倾斜 lying hip_norm self.height_thresh and angle self.angle_thresh falling velocity_px 15 and angle self.angle_thresh if lying or falling: self.down_count 1 else: self.down_count 0 if self.down_count self.down_frames: self.alerted True self.cooldown_count self.cooldown return True return False代码里最值得讲的是 lying 和 falling 两个条件的配合。lying 捕捉已经躺下的状态它的优点是稳不会因为速度误判缺点是有滞后人已经倒在地上才报警。falling 捕捉正在倒下的瞬间能提前两三帧报警但只看速度容易把猛地蹲下也判成摔倒。两个条件做成 OR 关系再用 down_frames 连续计数来过滤偶发抖动。速度阈值 15 像素这个值不是拍脑袋定的它假设视频在 30fps 下运行、输入尺寸 640x480。如果 fps 掉到 15同样的物理下落速度在每帧里的像素位移会翻倍这个阈值反而要调小如果换了 1280x720 的输入位移也会变大。所以代码里我把它写死成经验值做实验报告时你应该把它列为可调参数而不是固定常数。4.3 初始阈值与标定套路四个核心参数的初始值和调节方向我整理成了表格。参数含义初始值调节方向height_thresh归一化髋部高度阈值0.45调小减少误报调大提升漏报召回angle_thresh躯干倾角阈值65 度调大减少误报调小捕捉慢速倒下down_frames连续命中帧数30调大更稳但报警更慢调小响应更快cooldown报警冷却帧数150调大减少重复报警调小不丢第二次事件标定套路比参数本身更重要。我的做法是录一段 5 分钟的场景视频让同学依次做正常走路、弯腰捡东西、坐在椅子上、蹲下、模拟摔倒这几类动作把每类动作里三个特征的数值范围打出来看分布有没有重叠。正常情况下弯腰捡东西的髋部高度会下降但躯干倾角不会超过 45 度坐在椅子上的下落速度远小于摔倒模拟摔倒则是三项特征同时越界。找到重叠区之后把阈值设在重叠区中间偏安全的一侧——宁可晚一秒报警也不要频繁误报。5. 参数调优与避坑把实时防摔倒系统搞翻车的 5 个细节这一章写血泪经验。以下每个坑我都实际碰到过或者帮别人排查过按现象、原因、解决三步说清楚。5.1 坑一目标检测模型微调崩了——loss 爆炸或一直不降现象用预训练权重跑得好好的想用自己拍的数据微调一下检测头结果 loss 越训越高或者卡在某个值附近震荡最后模型输出全乱。原因八成出在数据标注和数据格式上。你要接触 yolov8 目标检测数据集处理的标准流程先用 LabelImg 这类目标检测常用标注工具画框导出成 YOLO 格式的 txt 文件再按 8:1:1 切分训练集、验证集、测试集最后写一个 data.yaml 给模型喂路径。最容易翻车的有三种情况一是类别编号从 1 开始而不是从 0 开始模型把前景当背景二是标注框里混了被遮挡一半的人模型学到的特征被带偏三是训练集和验证集来自同一段视频的不同帧验证集失去意义loss 曲线看起来很好但现场泛化一塌糊涂。解决先冻结 backbone 只训检测头和姿态头学习率降到 1e-4 这个量级训练 20 个 epoch 看 loss 能不能稳定下降同时检查 data.yaml 里的类别索引和标注文件的类别编号是否一致。课程设计阶段如果只是演示强烈建议直接用预训练权重别碰微调。5.2 坑二CPU 上推理只有 5fps现场演示变成幻灯片现象在自己笔记本上跑有 GPU 还算顺畅换到教室电脑上 fps 掉到 5画面卡得没法看摔倒判定也因为帧率太低完全失灵。原因YOLOv8-pose 的默认输入尺寸是 640x640CPU 跑这个分辨率本身就不轻松再加上主循环里读帧、推理、绘图全在一个线程里串行性能瓶颈全压在推理上。解决先把输入尺寸降到 480x480推理速度能提升近一倍然后做跳帧策略——每隔一帧做一次姿态估计中间那帧复用上一次的关键点对摔倒判定这种低频事件完全够用如果用的是 Intel CPU把模型导出成 OpenVINO 格式推理速度还能再上一个台阶。除了性能还要注意帧率变化会影响 4.1 节的速度阈值fps 掉一半时速度阈值要跟着调。5.3 坑三关键点抖动引来的误报警现象人站着伸了个懒腰或者转身时衣服褶皱让肩膀关键点跳了一下系统直接报摔倒一天误报七八次。原因单帧关键点的噪声比你想象的大。YOLOv8-pose 在遮挡、光照变化、衣服纹理不明显时关键点会在真实位置附近来回跳。我们的 FallDetector 虽然做了 EMA 平滑但平滑只压制随机抖动如果某一帧关键点跳得太远EMA 会把这个误差拖进后续好几帧。解决把 down_frames 从 30 提高到 45要求连续命中 1.5 秒才报警代价是报警延迟增加同时检查平滑系数alpha0.6 已经比较保守再高就会让真实的摔倒动作也变钝。另外可以在报警前加一个二次确认触发报警后再取当前帧做一个轻量的二分类判断——躯干接近水平且髋部低于膝盖位置才真正确认。这个二次确认能干掉绝大多数抖出来的误报。5.4 坑四摔倒瞬间姿态关键点大面积丢失现象人倒向沙发或桌子方向身体一半被家具挡住YOLOv8-pose 输出的 17 个关键点里髋部或肩部置信度很低坐标完全不可信摔倒判定模块因为 features 算错而漏报。原因关键点丢失不是 bug而是模型的正常行为。训练数据里大部分人是直立行走状态遮挡样本不够一旦目标被家具遮挡一半模型对关键点的回归就变得不可靠。解决在 compute_features 里加一个置信度闸门——左右髋、左右肩四个关键点的置信度都大于 0.3 才计算几何特征否则跳过这一帧。这样宁可漏判几帧也算错几帧好。另外可以加一个兜底规则如果检测框宽度大于高度且框的下沿在画面底部说明人在地上即使关键点不全也触发预报警。兜底规则必须有因为实景里摔倒后被子、轮椅、茶几都可能挡住关键点。5.5 坑五阈值玄学——换个摄像头角度就全乱套现象在实验室用一个约 1.5 米高的摄像头俯角 45 度阈值调好之后一切正常把同一套代码搬到宿舍摄像头放在桌面平视角度结果正常走路都触发报警或者人摔倒了不报。所有参数都检查过代码逻辑完全一样报警行为却完全不同。原因姿态特征和摄像头视角强相关。俯角大时人躺在地上髋部高度归一化值能明显区分站立和倒地平视视角下人躺倒后肩部和髋部在画面里可能还有一定纵高差归一化髋部高度达不到阈值。这就是阈值玄学——参数本身没有绝对正确它绑定的是安装角度。解决安装摄像头时固定高度和俯角我一般定在 1.8 米高度、45 度俯角这个角度对室内场景最友好换场景必须重新标定而不是沿用上一套参数。标定方法就用 4.3 节的五分钟场景视频把该场景的正常动作和摔倒动作都录一遍再调阈值。实验报告里最好把这个标定过程写成场景适配一节老师看到这种细节一般会给加分。6. 验证与进阶用混淆矩阵评估再往 LSTM 和多路视频走6.1 自建测试集与评估路线系统搭完最常被问的问题是你怎么证明它有效。通过课堂演示一两次不够要做一个正式评估找同学录三段视频——正常活动走路、坐下、弯腰、蹲下、模拟摔倒三个方向各几次、干扰动作猛地躺到沙发上、床上每段视频里哪个时间点发生了什么都标注出来然后用系统跑一遍统计四类结果。指标计算方法目标值召回率真实摔倒中触发报警的比例100% 的理想值至少 90%误报次数非摔倒动作触发报警的次数越小越好目标每 10 分钟不超过 1 次报警延迟从倒地到报警的秒数理想在 1-2 秒内这个评估过程同样会成为实验报告里最有说服力的一节。把混淆矩阵画出来再写一句误报集中在猛地躺下和蹲下两类动作预期通过增加样本量微调阈值解决完成度就很高。6.2 方法与设备升级方向固定阈值判定在慢速摔倒、坐空、被搀扶时下滑这几类场景里会失灵因为几何特征的变化不够剧烈。进阶路线是拿关键点时序数据训练一个 LSTM 分类器——输入连续 30 帧的 17 点坐标序列输出正常/摔倒两个类别。时序模型能学到倒下的动态模式对缓慢滑倒的召回比固定阈值好很多但代价是需要标注数据。另一个方向是换深度摄像头比如 RealSense 或者 Kinect直接用深度图做三维目标检测输出人体在空间中的真实坐标。三维关键点的优势是彻底摆脱视角问题不管摄像头俯角多少人的高度和躯干角度都是三维空间里的真实值阈值不再玄学。如果预算有限也可以保留单目方案但把多路摄像头拼接起来用 ByteTrack 这类跟踪器给每个人分配稳定 ID这样多人场景就不会相互干扰。最后说一个我的个人习惯任何阈值改动都在实验报告里留记录哪天报警行为变了翻记录比翻代码快得多。第一次做这类系统时我调好阈值就在实验室里跑起来结果同事弯腰捡笔触发了一次报警当时还不知道从哪排查后来才发现是阈值定得太紧。按现象、原因、解决留排查笔记之后再遇到翻车就能按图索骥。这个方向值得做但要把标定过程当工程来做而不是调出一个数就收工——希望帮到你。本文还有配套的精品资源点击获取
返回列表