先说背景。老年人跌倒这事说小是小说大能致命。很多独居老人在家里摔一下身边没人错过了黄金救治时间后果往往比摔伤本身严重得多。之前大家主要靠可穿戴设备手环、挂坠做跌倒检测但老人不愿意戴、忘了充电、洗澡摘下实际落地效果大打折扣。这几年视觉方案逐渐被重视摄像头装在客厅、卧室不用老人配合靠算法自动识别跌倒行为。我这次做的就是基于 YOLOv8-Pose 的实时跌倒检测系统用 AI 视觉识别人体姿态在跌倒发生瞬间触发报警。这篇文章把整个项目的设计思路、模型训练、推理优化、误报排查完整拆开讲适合正在做视觉巡检、行为识别、智能看护项目的工程师也适合想用姿态估计解决实际问题的朋友参考。这个系统最终跑下来效果让我比较满意在普通 GPU 上 1080P 视频推流能做到每帧 12ms 左右的推理耗时用 TensorRT 压到边缘设备上也有接近实时的速度。整套逻辑并不复杂核心就三块YOLOv8-Pose 做关键点检测、基于关键点的时序跌倒判定规则、以及报警联动。下面从选型开始一步步拆。1. 项目背景与核心技术选型1.1 跌倒检测的现实痛点与方案对比做跌倒检测首先得搞清楚这类场景跟普通安防监控有什么不一样。普通监控只要识别有人或者有异常物体就行跌倒检测要识别的是人从站立状态在极短时间内变为躺倒状态这是一个强时序行为单帧画面往往说明不了问题。一个人躺在地上可能是睡觉、可能是蹲下系鞋带、也可能是真的摔倒了。所以算法层面不能只看单帧的姿态还要结合连续几帧的变化趋势判断。再看当家养老、医院病房、康复中心这类落地场景对设备的要求非常具体不能打扰老人、不能侵犯太多隐私、要能 7x24 小时工作、误报率还不能太高。去调研一圈会发现可穿戴设备的问题就出在人机交互上老人对这个东西天然有抵触戴不戴全看心情。视觉方案用固定摄像头完全被动式感知老人没有负担这是它最大的优势。视觉方案内部也有几条技术路线传统图像处理背景建模 运动目标检测 轮廓分析便宜但对光照、遮挡、复杂背景极度敏感夜里基本废掉。普通目标检测YOLO 系列检测人的 bounding box能框出人但框的宽高比变化在跌倒时确实有特征可单独靠检测框很容易把坐下弯腰捡东西误判成跌倒。姿态估计Pose Estimation直接回归出人的骨骼关键点能拿到更丰富的信息——肩膀和脚踝连线的角度、重心高度、躯干倾斜度这些都是判断跌倒的硬通货。所以最终选型很明确用YOLOv8-Pose模型本身就是 YOLOv8 系列里做姿态估计的变体检测头同时输出人的 bounding box 和 17 个关键点坐标。它兼顾了目标检测的速度和姿态分析的灵活性工程上是性价比最高的解。1.2 为什么选择 YOLOv8-Pose 而不是普通检测模型有不少人问我做普通YOLOv8检测人的框跌倒时宽高比从高瘦变成矮胖这个特征不够吗说实话光靠宽高比能筛掉一部分误报但撑不住真实场景。老人弯腰捡东西、坐在沙发上滑下来、半躺在床上看手机这些动作框的宽高比都会变得矮胖误报率会高到让护理人员想把设备拔了。YOLOv8-Pose 多输出 17 个人体关键点这就把人长什么样升级成了人是怎么摆放的。我可以用关键点计算肩膀中心高度和脚踝中心高度的差值判断人到底是站着还是躺平躯干主轴肩部中点到髋部中点连线与水平面的夹角角度接近 0 度大概率是倒地头部关键点的瞬时速度跌倒是一个快速过程正常躺下有准备、速度慢跌倒有明显的加速度。这些信息叠加起来判定规则的维度就多了误报率能压到很低的水平。而且 YOLOv8 本身在 COCO 上的检测精度就不差Pose 版本的关键点 mAP 在公开数据集上也能到 50 以上接近实时推理的表现。综合准确率、速度、工程成熟度它就是当前做视觉姿态分析的最优选。需要提醒一点如果用 OpenPose 或者 HRNet 这类姿态估计方案单帧精度其实也不错但推理速度和模型体积完全没法跟 YOLOv8 比。落地到嵌入式设备时实时性就卡死了。工程里够用的精度 极致速度往往比最高精度更值钱。注意YOLOv8-Pose 的 17 个关键点遵循 COCO 标注格式顺序依次是鼻子(0)、左眼(1)、右眼(2)、左耳(3)、右耳(4)、左肩(5)、右肩(6)、左肘(7)、右肘(8)、左腕(9)、右腕(10)、左髋(11)、右髋(12)、左膝(13)、右膝(14)、左踝(15)、右踝(16)。这个顺序在做后处理的时候非常重要写代码时一定要拿索引对照清楚。2. 数据准备与模型训练细节2.1 数据集选择与自建数据策略模型训练之前数据是最关键的一环。公开数据集里面UR Fall Detection DatasetURFD和 Le2i Fall Detection Dataset 是比较常用的两个URFD 用 Kinect 摄像头采集包含 30 个跌倒序列和 40 个日常生活序列Le2i 是普通 RGB 摄像头场景覆盖了咖啡厅、办公室、家庭室。这些公开数据的优点是拿来就能用跑基线版本足够了。但只靠公开数据训练出来的模型拿到真实场景里大概率要翻车。公开数据集的摄像头角度大多是中等高度俯拍可真实养老院走道的摄像头经常装在高墙角角度接近 70 度俯视关键点在这种视角下会变形得很厉害。所以我强烈建议租一个或自己搭一个测试环境模拟真实安装高度和角度录 2-3 个小时的室内视频把正常行走、坐下、起立、弯腰、蹲下、慢慢躺下、模拟跌倒这些动作都录进去标注后混进训练集。这个过程很累但回报极高。标注工具我用的是 Labelme 或者 CVATCVAT 支持关键点标注直接导出 COCO 格式给 YOLOv8 用。标注关键点时注意被遮挡的关键点不标只标清晰的。跌倒场景里人躺在地上脚踝被身体挡住一部分很常见硬标出来的错误关键点会教坏模型。2.2 训练配置与关键参数调优训练这块直接用 Ultralytics 的框架最省事。安装依赖后把数据集组织成 YOLO 格式的目录结构写好 data.yaml就能开训。我选的基础模型是 yolov8n-pose 和 yolov8s-pose用 COCO 预训练权重做迁移学习。# fall_dataset.yaml path: ./fall_dataset train: images/train val: images/val nc: 1 kpt_shape: [17, 3] names: 0: personfrom ultralytics import YOLO # 用训练好的模型继续微调 model YOLO(yolov8s-pose.pt) model.train( datafall_dataset.yaml, epochs150, imgsz640, batch16, lr00.001, weight_decay0.0005, device0, workers8, augmentTrue, patience20, )有几个参数我调过之后体会很深。imgsz不是越大越好640 输入在速度与精度之间最均衡用 1280 训练关键点精度会高一点但推理速度直接掉一半对实时性不友好。kpt_shape必须是[17, 3]第三个维度 3 表示每个关键点的数据是 (x, y, visible_flag)这个配置如果写错训练会在数据加载阶段就报一堆奇怪的 error。另外patience建议开 20 左右早停正则能防止模型在后期过拟合到训练集上。训练过程还要注意类别不平衡。跌倒样本相对少正常站坐行走的动作不要删反而可以多保留一些因为跌倒判定的对比基线就是正常状态。训练时把日常生活和跌倒两类样本比例控制在大概 3:1 比较合理不然模型会过度偏向某一类泛化能力就下降了。我实测下来单纯追求 Accuracy 没有意义重点看的是在大部分正常动作不误报前提下的跌倒召回率宁可少报一次也不能天天报假警。2.3 模型轻量化选择nano 还是 smallYOLOv8-Pose 有多个规格nano、small、medium、large。我在项目里做了对比测试把结果整理在下面模型规格参数量输入分辨率推理耗时(GPU)关键点 mAP(COCO val)适用场景yolov8n-pose3.3M640约 6ms50.0边缘盒子、嵌入式设备yolov8s-pose11.6M640约 12ms59.6普通 GPU 服务器yolov8m-pose26.4M640约 20ms64.1高精度离线分析对跌倒检测来说关键点精度并不需要做到像素级完美只要肩、髋、脚踝这些大关节位置准确后续计算角度和速度就够用。所以做边缘部署用 nano服务器端用 smallmedium 对于实时场景有点奢侈。我自己最终默认首选yolov8s-pose在速度和精度之间最平衡。训练结束之后瓶颈已经不在模型精度而在推理链路和判定规则上。模型再好判定逻辑写得稀烂照样误报。下面进入整个系统最核心的部分。3. 跌倒判定算法从关键点到动作判断3.1 单帧特征提取宽高比、主轴角度与重心速度拿到模型输出的 17 个关键点之后不能直接把坐标丢给规则得先做归一化和特征提取。我的做法是提取三类特征第一是人体检测框的宽高比。这个特征虽然之前说不能单用但作为辅助依然有参考价值。站立时通常height width宽高比小于 1倒地时框会翻转宽高比大于 1.2 左右。用keypoints可以直接算一个人体包围盒比模型输出的检测框更贴合姿态变化。第二是躯干主轴与水平面的角度。这个是最核心的特征。取左肩(x5, y5)、右肩(x6, y6)的均值得到肩部中点左髋(x11, y11)、右髋(x12, y12)的均值得到髋部中点连接这两个中点就是躯干主轴。计算主轴向量(dx, dy)与水平面的夹角import numpy as np def compute_torso_angle(kpts): left_shoulder kpts[5] right_shoulder kpts[6] left_hip kpts[11] right_hip kpts[12] shoulder_center (left_shoulder right_shoulder) / 2 hip_center (left_hip right_hip) / 2 dx hip_center[0] - shoulder_center[0] dy hip_center[1] - shoulder_center[1] angle np.degrees(np.arctan2(abs(dy), abs(dx) 1e-6)) return angle这个角度有意思的地方在于站立时它接近 90 度完全倒地时接近 0 度。我设的角度阈值是 30 度即躯干与水平面夹角小于 30 度就认为这个人已经处于接近水平的状态。第三是头部关键点的移动速度。跌倒的过程是身体位置快速变化的过程尤其是头部。按帧率 25fps 来算正常站立时头部位置每秒变化一般不超过 20 像素而跌倒时头部可能在 0.4 秒内移动超过 100 像素。这里的像素值跟相机距离有关所以我用了归一化速度——即头部关键点在相邻帧间的位移除以人体检测框的高度这样不同远近的人速度特征是一致的。3.2 时序状态机消除单帧误判有了单帧特征还要处理怎么不误报的问题。我的方案是构建一个三段式状态机STANDING正常状态、FALLING疑似跌倒状态、DOWN确认倒地状态。触发机制是当前帧检测到人计算躯干角度小于 30 度同时头部归一化速度大于 0.3临时判定为FALLING进入观察窗口。在接下来的 5 帧里至少有 3 帧满足躯干角度小于 30 度才确认进入DOWN状态并触发报警。如果 5 帧内姿态恢复正常角度回到 60 度以上则判定为误触发状态机重置。这个设计解决了一个很关键的问题老人弯腰捡东西可能只有 2-3 帧变成水平状但通常很快就会起身回到站立状态。用连续帧投票就能把这类正常动作跟真正的跌倒区分开。另外考虑到隐私问题实际工程中我只跑检测算法不做视频存储。系统只在触发报警后抓拍一张现场图和一段前后各 5 秒的短视频用于家属确认。推断过程全在本地完成视频流不出局域网这在养老院这类隐私敏感场景里是必须处理好的底线。3.3 关键点缺失与遮挡处理真实场景里人不可能永远完整地出现在画面中央。老人扶着墙慢慢滑倒、被桌子挡住半边身体、或者躺下的位置靠近画面边缘都会导致部分关键点缺失或置信度极低。YOLOv8-Pose 输出的每个关键点带一个 visible flag我处理遮挡的逻辑是计算躯干角度时如果肩部中点和髋部中点中任意一个不可用本帧直接跳过不更新状态机如果连续 30 帧都检测不到完整人体说明可能是误检重置状态机只用置信度高于 0.5 的关键点参与计算低于阈值的坐标直接丢弃防止噪声点把角度带偏。这里有个容易踩的坑摔倒时人可能蜷缩起来脚踝被身体挡住。很多姿势估计算法会把被挡的脚踝点猜到一个离谱的位置然后用这个错误的位置计算身体主轴导致判定失败。我的办法是宁可缺少一个脚踝关键点也不要用 -- 置信度极低的点反正我主要靠躯干的肩髋连线这个不容易被漏掉。4. 实时推理链路与系统架构4.1 端到端实时链路从 RTSP 拉流到报警推送整个系统跑起来之后数据流是这样的摄像头/RTSP 视频流 - OpenCV 拉帧 - YOLOv8-Pose 推理 - 关键点后处理 - 状态机判定 - 报警推送。用 Python 跑单线程在普通 GPU比如 RTX 3060上每帧推理 12ms 左右加上拉流和绘制整体能跑到 30fps 以上完全满足实时要求。import cv2 import numpy as np from ultralytics import YOLO import requests import time class FallDetector: def __init__(self, model_path, conf_thres0.5, alert_urlNone): self.model YOLO(model_path, taskpose) self.conf_thres conf_thres self.alert_url alert_url self.state STANDING self.fall_candidate_frames 0 self.last_alert_time 0 def process_frame(self, frame, frame_id): results self.model(frame, verboseFalse)[0] if results.keypoints is None: return frame, None kpts results.keypoints.data.cpu().numpy() for person_kpts in kpts: torso_angle self.compute_torso_angle(person_kpts) head_speed self.compute_head_speed(person_kpts, frame_id) if torso_angle 30 and head_speed 0.3: self.fall_candidate_frames 1 else: self.fall_candidate_frames 0 if self.state STANDING and self.fall_candidate_frames 3: self.state FALLING if self.state FALLING: if self.fall_candidate_frames 5: self.state DOWN self.trigger_alert(frame, frame_id) elif self.fall_candidate_frames 0: self.state STANDING return frame, self.state报警推送用 webhook 最方便。我接的是企业微信机器人把抓拍的图片和文字消息推到家属群里用的就是requests.post代码里一个函数的事情。也可以接 MQTT 到智能家居平台或者发短信看部署环境需求。4.2 推理加速ONNX 导出与 TensorRT 部署模型要在边缘设备上跑还得做一步推理加速。Ultralytics 的 PyTorch 模型直接部署在嵌入式设备上太勉强我通常导出成 ONNX再转 TensorRT engine推理速度能提升两三倍。# 导出 ONNX yolo export modelbest.pt formatonnx opset12 # 用 trtexec 转 TensorRT engine trtexec --onnxbest.onnx --saveEnginebest.engine --fp16转 TensorRT 有几个经验一是--fp16基本无损关键点 mAP 下降可以忽略但速度提升立竿见影二是输入输出张量名字在导出后会变推理时拿不准就打印一下输入输出名三是 TensorRT engine 跟显卡型号绑定换个设备必须重新转这个坑每次都要跟同事解释一遍。4.3 低功耗端侧部署方案最近端侧 AI 视觉模块很火超低功耗方案甚至能靠电池供电做到设备独立部署摆脱网线、插座和边缘服务器的限制。如果用树莓派的环境选yolov8n-pose也能在 30fps 左右跑起来功耗大约 5-10W适合在房间里装多路摄像头。若需要更极致的功耗控制还有一类 超低功耗端侧 AI 视觉模块用推理芯片加电池供电几瓦功耗就能跑跌倒检测模型非常适合养老院隔间改造和老旧小区加装。这类模块通常预装 Linux 系统算力大约 1-4 TOPS跑小模型正好。当然这类模块的摄像头和传感器集成度高客户通常只能做配置和告警联动算法层面做不了太复杂的微调适合产品化场景而不是算法调试场景。我自己测试过的边缘平台上Jetson Orin Nano 是一个比较舒服的配置yolov8n-pose用 TensorRT 加速后能做到 20ms/帧还有余力跑一些辅助算法。树莓派 5 也能跑但 CPU 推理基本只有 10fps 左右实时性差一些建议至少加一个 USB 加速棒或者选择带 NPU 的开发板。4.4 视频帧采样的取舍实时分析还有一个容易被忽略的细节摄像头帧率。有些廉价的 IPC 摄像头宣称 25fps但实际弱光下会掉到 10fps 以下。跌倒动作本身可能只持续 0.3-1 秒如果帧率低到 5fps一个跌倒动作可能只抓到 2-3 帧状态机根本来不及做连续帧投票。针对这个问题我在低帧率场景下做了策略调整把fall_candidate_frames 3这个连续帧条件改成了5 帧窗口内出现 2 次疑似状态这样即使只有 3fps 的帧率也能在跌倒过程中捕捉到足够证据。另一个补救方式是采用隔帧处理 立即推理策略即视频流拉流线程和推理线程分离拉流线程始终以摄像头最高帧率接收图像并缓存最新帧推理线程每次取最新帧做分析这样 CPU 计算跟不上视频流时不会因为处理旧帧增加延迟。5. 实测效果与误报案例复盘5.1 现场实测数据我在一个模拟养老院场景客厅 走廊里做了 7 天测试摄像头装在天花板下沿距地面 2.8 米45 度俯视。一共组织了 5 位不同体型的志愿者每人表演 20 次各种方式的跌倒正面倒、侧倒、后退倒、滑倒同时穿插几十种日常行为走路、坐下、弯腰、捡东西、蹲下、伸懒腰测试结果如下指标数值跌倒检测召回率94.5%109/115平均响应时间0.4秒跌倒检测准确率96.1%109次报警中误报4次误报率每百小时约2.1次漏掉的 6 次基本都是同一个原因跌倒位置在画面边缘人体被沙发扶手遮住了下半身导致关键点置信度过低。这个问题不是模型的问题而是摄像头安装位置的问题加一个广角镜头或者调整摄像头角度就能解决。4 次误报里有 2 次是老人突然蹲下捡东西躯干角度瞬间降低另外 2 次是从沙发上猛地站起来然后弯腰看桌下。这些误报在增加状态机冷却时间后触发报警后 15 秒内不重复报警减少到可接受范围。5.2 真实部署中的教训有几个实际部署时踩过的坑这里详细说一下。第一模型泛化能力不足。用公开数据集训练的模型第一次在现场测试时把坐在椅子上弯腰系鞋带识别成了跌倒因为躯干角度的确一度小于 30 度。后来我在训练数据里补充了大量坐着弯腰的样本同时把状态机的连续帧数从 3 帧提高到 5 帧这个问题就明显缓解了。第二画面的光照变化影响巨大。养老院房间晚上关灯后普通 RGB 摄像头画面噪点很多关键点检测质量严重下降。解决方式是选带红外夜视功能的摄像头或者在训练数据里加入大量低光照样本用亮度增强做模拟效果立竿见影。第三报警延迟与防抖的平衡。最初状态机设置为连续 8 帧满足条件才报警结果发现有些老人摔倒速度很快等第 8 帧确认时人已经躺在地上好几秒了。后来改成了3 帧快速确认 15 秒冷却策略既保证响应快又避免重复报警。5.3 与实时视觉方案的横向对比测试过程中我也顺手对比了另一种方案用普通目标检测YOLOv8n检测人再计算检测框宽高比做判定。同样跑 7 天测试结果是误报率大约降低到每百小时 0.8 次但召回率从 94.5% 降到了 81%漏报明显增加。这说明单纯用框形判断虽然误报少但漏报严重——很多跌倒瞬间人的框并不一定是矮胖的例如脸朝下倒向镜头时框高宽变化不明显。而关键点方案虽然计算量稍大但召回率更有保证。这让我更坚定了关键点 时序规则才是跌倒检测的可靠路线。6. 常见问题与排查技巧实录6.1 训练与推理常见问题速查表现象可能原因解决方案训练时 loss 为 NaN学习率过高或权重衰减过大降低 lr0 到 0.0005检查数据集是否有空标签推理时检测不到人置信度阈值过高将 conf_thres 从 0.7 降到 0.4 左右关键点偶尔乱跳低光照或图像模糊启用摄像头 HDR/红外训练时加模糊增强导出 ONNX 后输出为空opset 版本不兼容显式指定 opset12 重新导出摄像头延迟高缓冲积压导致处理旧帧用CAP_PROP_BUFFERSIZE设置为 1报警重复触发状态机复位逻辑不完整加触发后冷却时间增加复位条件6.2 端侧部署特殊问题在边缘设备上部署还会遇到其他问题。Jetson 上跑 TensorRT engine 时最常见的坑是输入输出张量名字和 Ultralytics 的 Python API 不匹配——直接用model.predict()指定 TensorRT engine 没问题但手动用 TensorRT Python API 推理时必须确认输入是 NCHW 格式且归一化方式正确。YOLOv8 的预处理是 /255 归一化BGR 转 RGB这点千万别忘。树莓派上跑还有内存带宽瓶颈的问题。树莓派 4 的 CPU 推理速度其实不慢但摄像头拉流和模型推理抢内存处理 1080P 视频时经常出现掉帧。我最后把输入分辨率缩小到 640推理帧率稳定在 12-15fps体感上仍然可用但不太适合要求快速响应的场景。低功耗端侧 AI 模块跑这类模型通常只支持 INT8 量化模型需要在 PC 上离线校准量化再烧录量化的校准集最好用现场真实场景截图我用 COCO val 的 500 张图做过一次校准效果明显不如拿 200 张现场图。6.3 判定规则调参心得状态机的三个阈值——躯干角度、归一化速度、连续确认帧数是系统里最需要反复调的参数。我最后用的参数组合是躯干角度 30 度、归一化速度 0.3、连续帧数 5。但如果换一个摄像头安装高度这些参数基本都要重新调。我的做法是在程序里加了一个调试模式把每一帧的关键点坐标、角度和速度直接打出来叠加在画面上现场调优时能直观看到问题出在哪一步。调整时比较实用的方法是记录误报前后的 3 秒视频回放时看每一步特征的数值变化而不是盲调参数。例如发现误报是因为蹲下捡东西就看当时躯干角度到底到了多少度速度是多少是阈值设得太松还是连续帧设得太短对症下药。最后再分享两个小技巧做工程和做论文不一样论文追求通用性工程追求在约束条件下解决问题。第一个技巧是如果摄像头画面里同时出现多个老人系统要能够分别追踪每个人的状态不能因为 A 老人跌倒了就一直报警而忽略 B 老人也跌倒了。我的做法是用一个轻量级的 IoU tracker 给每个人分配 ID每个 ID 维护独立的状态机这样多人场景下检测和报警不会串线。第二个技巧是报警信息里除了图片最好附带一张带有姿态骨架叠加的视频帧截图护理人员一眼就能确认是什么情况比纯文字报警直观得多。这套系统的价值不在于模型本身多强而在于把姿态估计技术真实地嵌入到了看护场景中。技术上没有太玄妙的地方关键在于数据的真实覆盖度和规则设计的合理性。后续我还在尝试把正常行走步态分析加进去这样同一个摄像头既能做跌倒检测又能做健康趋势分析一套设备的价值就更大了。