
做视觉算法这些年我经手过车牌识别、工地安全帽检测、还有工厂的工服穿戴识别但真正让我觉得“这段代码写出来是能救命的”是去年给一家社区养老机构落地的那套基于 YOLOv8-Pose 的实时老年人跌倒检测系统。老人洗澡、起夜、在客厅走动任何场景里摔倒了没人及时发现后果往往不只是擦破皮那么简单——髋部骨折、颅内出血很多老人就是摔一跤之后身体急转直下。这套系统的目标很直接用摄像头画面实时识别画面里的“人”再用姿态估计拿回身体各关节的关键点坐标通过关键点的几何变化和运动趋势判断“这个人是不是摔倒了”一旦确认就立刻触发告警推送。整套项目从数据准备、模型训练到跌倒判定算法和告警链路我都完整跑通了一轮也踩了不少坑。市面上讲 YOLOv8 检测的文章很多但真正把“基于姿态估计做跌倒识别”从头到尾讲透的不多。这篇文章我就按照实际落地的顺序把技术选型、数据准备、训练、判定逻辑、实时部署和现场排坑全部写清楚给正在做类似 AI 视觉项目的朋友一个可以直接参考的样板。1. 项目背景老人跌倒这事为什么非得靠AI视觉1.1 跌倒风险到底有多大跌倒不是小概率事件。很多医学统计资料都指出跌倒是老年人因伤就诊和死亡的首要原因之一尤其是 65 岁以上的老人一年内至少跌倒一次的比例相当高。我接触的那家养老机构护理员就跟我讲过真实案例有位老人凌晨两点起夜在卫生间门口滑倒等天亮巡房才发现人躺在地上五六个小时髋部骨折加失温后面护理难度陡增。这种场景下最缺的不是“事后抢救”而是“事发时第一时间发现”。传统做法是让老人按求助按钮、或者让护理员定时巡查。但按钮需要老人有意识去按摔倒后昏迷、疼痛应激的情况下根本按不了定时巡查有窗口期几分钟的空档就可能酿成严重后果。所以这个项目本质上要解决的是一个“无人值守时段内的自动监护”问题对响应速度的要求是秒级而不是分钟级。1.2 穿戴设备为什么不够用有人可能会问市场上不是有智能手环、跌倒报警吊坠吗确实有但实际使用中问题一大堆。最致命的是老人不配合佩戴。我调研过几家养老机构护理员反映很多老人觉得手环戴着硌手、洗澡要摘、睡觉不愿意戴结果就是设备在抽屉里吃灰真出事的时候根本没用。另外穿戴设备跌倒检测多基于加速度计和陀螺仪对“突然撞击”这种特征识别还可以但对缓慢滑倒、倚着墙慢慢滑坐下去这类低冲击跌倒误报漏报都很严重。摄像头方案的优势在于“无感”。老人不需要穿戴任何东西装上摄像头之后该干嘛干嘛系统在后台持续分析画面。对于养老院、社区日间照料中心、独居老人家中这些固定场景摄像头本身就是很成熟的安防基础设施在它之上叠加 AI 能力部署成本也低。当然隐私是个敏感话题这个我后面单独讲现场处理得好的话完全可以做到既监护又合规。1.3 为什么是姿态估计而不是普通目标检测第一版方案我其实用过普通的目标检测就是直接用 YOLOv8 的 person 类别加一个自定义的“fall”类别把摔倒当成一种目标来识别。但实际测试下来有两个问题一是摔倒姿态千变万化有正面趴、背面仰、侧躺蜷缩、半跪半倒靠检测框去框“摔倒的人”很难有统一的视觉特征二是普通检测模型对“人躺在地上”和“人坐在地上”“人蹲着捡东西”这些相似姿态区分能力很差误报率居高不下。换成姿态估计之后思路本质变了。我不再关心“人看起来像什么”而是关心“人的骨架几何形态是什么”。姿态估计模型输出 17 个关键点坐标包括肩膀、手肘、手腕、髋部、膝盖、脚踝有了这些点我可以计算躯干倾斜角、髋膝弯曲角、人体中心点的高度、跌落速度等一系列结构化特征再用这些特征去判断跌倒。这套方法的泛化能力强很多因为不管穿什么衣服、什么体型人的骨骼结构是稳定的几何特征是有明确物理含义的。2. 技术选型YOLOv8-Pose 是怎么胜出的2.1 主流姿态估计方案横向对比姿态估计不是只有一个选择。我对比过的方案至少有四类OpenPose、MediaPipe Pose、HRNet 系列以及 Top-Down 一类的两阶段检测器。OpenPose 是经典方案关键点检测效果扎实但原始模型非常重在 CPU 上跑实时推理基本不用想依赖也重部署麻烦。MediaPipe Pose 轻量且移动端友好但它更像是个纯跟踪器摄像头稍微晃动、多个人相互遮挡时关键点容易跳变对于跌倒这种需要高稳定性的场景不够可靠。HRNet 精度高但计算量摆在那实时性是个坎。综合下来YOLOv8-Pose 是目前工程落地最舒服的选择。它本身是单阶段模型一次性输出检测框和关键点速度极快Ultralytics 这个库封装得很好训练、验证、导出一套流程非常顺模型从 nano 到 x 有五个尺寸可以根据算力灵活选择关键点格式对齐 COCO 的 17 点定义生态兼容性也好。我拿它在 RTX 3060 上测过一次yolov8n-pose 推理一帧只要几毫秒就算用 Jetson 这类边缘设备也能轻松跑满 25 帧实时视频流。2.2 精度和速度的平衡点选模型尺寸这件事我踩过一点弯路。最开始想当然上了 yolov8l-pose觉得精度高结果在部署机上帧率掉到十几帧画面明显卡顿而且关键点抖动也变大了因为每帧处理时间不均匀。后来换成 yolov8s-pose精度下降幅度其实很小mAP 大概掉了两三个点但推理速度翻了两倍以上整体体验反而更好。对跌倒检测这个场景还有一个容易被忽略的点帧率稳定比单帧精度更重要因为判定逻辑依赖时序特征帧率一抖速度计算就失真误判就来了。如果现场的算力实在太弱比如只有一块 RK3588 或者类似级别的边缘盒子我会建议直接上 yolov8n-pose再结合 TensorRT 或 RKNN 做加速。实际上在骨架识别这个任务上nano 级别的模型关键点精度对应用来说已经够用了。真正的判定准确率大头在后端的跌倒判定算法上不在模型本身的 mAP 上。2.3 硬件与摄像头怎么配硬件配置要看场景。我在养老机构做的那套前端用的是普通的海康/大华 RTSP 网络摄像头带红外夜视功能分辨率 200 万像素就可以关键是帧率要能到 25 帧不然跌倒这种快速动作会糊。摄像头的安装位置非常讲究最好装在房间角落的天花板附近往下俯视这样视场角能覆盖大部分活动区域而且被家具遮挡的概率小。正对床或者正对沙发安装的话人坐在沙发上的侧影和倒地的姿态容易混淆误报会很头疼。推理端我一开始用的是带独显的迷你主机后来为了控制功耗和体积换成了 Jetson Orin Nano实测功耗十几瓦性能完全足够。这里多说一句如果项目要对外交付最好把推理端做成一个小盒子而不是一台游戏主机不然现场工程师会想打人。摄像头和盒子之间走局域网摄像头供电最好用 PoE一根网线搞定供电和传输少很多现场故障点。3. 数据集准备跌倒样本到底从哪来3.1 公开数据集怎么用训练姿态估计模型第一步是搞清楚数据从哪来。公开的跌倒数据集有几个比较出名的Le2i Fall Detection Dataset、UR Fall Detection Dataset、UP-Fall Dataset。我自己主要用了 Le2i它有室内多场景的跌倒视频涵盖客厅、办公区、演讲厅等动作包含前扑、后仰、侧倒、坐下、弯腰等对训练识别“跌倒 vs 类似动作”很有帮助。UR Fall Detection 是多传感器数据有穿戴设备和深度摄像头两种单纯导姿态估计的话深度图用不上但它的普通视频也可以辅助。不过我必须说一句实话公开数据集的规模都不大场景也比较单一直接拿来训练在真实养老院场景里泛化一定不够。Le2i 的室内场景和养老院的实际布局差别很大光照、遮挡、摄像头高度都不一样。我的做法是拿公开数据做预训练基础再用自采数据做微调。公开数据的作用是让模型先学会人类的骨架结构而真正让模型适应现场的是自采数据。3.2 自采数据的采集规范自采数据这个环节很多人会偷懒觉得拿公开数据集训一版就够了。但我要说跌倒检测这种应用数据和现场不符就是灾难。我当时的做法是租了一间和养老机构户型相似的房间架好摄像头按实际安装高度和角度固定然后请志愿者做动作采集。动作要覆盖正面倒地、背面倒地、左侧倒、右侧倒、原地慢慢滑坐、走路绊倒、从椅子上滑落还要大量采集“非跌倒”的干扰动作比如蹲下捡东西、弯腰系鞋带、坐在沙发上、躺床上休息、健身拉伸。负样本的重要性怎么强调都不过分。模型对“跌倒”和“蹲下”“坐下”在几何上是很接近的如果没有足够的负样本训练出来的模型会把所有“人体变矮”的动作都判成跌倒。我第一批训练的模型误报率超过 30%后来把负样本补到和正样本接近 1:1 之后误报率才明显降下来。每个动作最好重复拍 5 到 10 次一次拍 10 到 30 秒中间换换角度和位置这样数据多样性才够。3.3 标注与增强的细节标注我用的是 CVAT免费开源支持关键点标注导出格式也能直接转成 Ultralytics 需要的 YOLO pose 格式。这里有个关键点关键点标注一定要标全 17 个点遮挡的点可以不标但不要胡乱猜位置因为模型会学到错误的坐标分布。我自己标注的速度大概是每人每分钟处理十几帧效率不算高所以建议先拿一个预训练的 yolov8s-pose 模型做自动标注然后再人工修正明显错误的点这样能省一半以上的时间。数据增强方面Ultralytics 自带了很多增强策略比如 Mosaic、随机翻转、HSV 扰动、平移缩放。但我特别提醒一点如果开了随机翻转一定要确认关键点的左右顺序也跟着翻转YOLO 格式里关键点的左右是固定的翻转后左肩就跑到画面右边去了模型会学乱。我在训练配置里直接把 flip 关掉了或者严格确认翻转后关键点索引同步交换这个细节很多人不注意训练出来关键点错乱排查半天。4. 模型训练从预训练权重到可用模型4.1 训练环境与关键参数训练环境不算挑剔我用的是一张 RTX 4070 显卡显存 12G训练 yolov8s-pose 完全够。如果显存小把 batch size 调小就行。数据集目录结构按 Ultralytics 的要求组织images 和 labels 两个文件夹下面分 train 和 val然后写一个 dataset.yaml内容大概是这样的path: /data/fall_dataset train: images/train val: images/val kpt_shape: [17, 3] names: 0: person注意 kpt_shape 是 [17, 3]表示 17 个关键点每个点是 x, y, visibility。如果标注时没标 visibility第三维可以直接给 1 或者 2表示可见或遮挡但存在。训练命令我用的是官方推荐的方式yolo detect train ... # 这是目标检测的 yolo pose train data/data/fall_dataset/dataset.yaml \ modelyolov8s-pose.pt \ epochs150 \ imgsz640 \ batch16 \ patience20 \ lr00.01 \ weight_decay0.0005 \ device0这里我解释一下几个关键参数的选择理由。模型用 yolov8s-pose.pt 做预训练权重而不是从随机权重开始能省很多训练时间而且关键点检测的底层特征本来就是通用的。epochs 设 150 是有耐心训练的考虑配合 patience20 做早停防止过拟合。imgsz640 是精度和速度的平衡点再往上调到 768 或 1024 精度提升有限但推理耗时明显增加。4.2 训练过程与评估指标训练过程中要重点盯的指标不是总 loss而是关键点相关的 loss 和验证集上的 mAP。Ultralytics 在训练结束会生成 results.png 和 confusion_matrix.png我会重点关注 box_mAP50、pose_mAP50 和 pose_mAP50-95 这几项。pose_mAP50 到 0.9 以上基本就说明关键点位置已经比较准了。还需要看的指标是 precision 和 recall对跌倒检测来说recall 比 precision 更重要——漏报一次的代价远高于误报一次宁可多告警让护理员确认也不能漏掉真正的跌倒。所以我会把置信度阈值设得低一点用后端的判定逻辑去消化误报。训练集和验证集的划分也有讲究。我是按“场景”划分的而不是按“帧”随机划分。如果同一个人的同一段视频一部分帧进了训练集一部分进了验证集验证集分数会虚高部署上去就露馅。按场景划分虽然会让验证分数稍微难看一点但更接近真实部署的表现。4.3 模型导出与压缩训练完的 PyTorch 权重不能直接上生产要先导出。导出目标取决于部署端。如果是带 CUDA 的机器导出成 TensorRT engine 收益最大如果是 Jetson同样用 TensorRT如果是其他国产边缘芯片就导出 ONNX 再转对应格式。命令很简单yolo pose export modelruns/pose/train/weights/best.pt formatengine device0或者导出 ONNXyolo pose export modelruns/pose/train/weights/best.pt formatonnx opset12我自己的经验是TensorRT 导出后推理速度能比 PyTorch 快 2 到 3 倍而且显存占用更低。有个坑要提一下TensorRT 的 engine 文件和 CUDA 版本强绑定换了环境就得重新导出现场部署之前一定要确认目标机器的驱动和 CUDA 版本不然打好的包到现场跑不起来非常被动。5. 跌倒判定拿到关键点之后才是重头戏模型训练好只是第一步真正决定这个系统好不好用的是拿到 17 个关键点之后怎么判定跌倒。这也是整篇文章我觉得最有价值的部分。5.1 几何特征怎么算拿到的关键点坐标是像素坐标我重点用几个关键点左右肩膀关键点 5、6、左右髋部关键点 11、12、左右膝盖13、14。基于这些点可以计算几个核心几何特征。第一个是躯干倾斜角即肩膀中心到髋部中心连线与竖直方向的夹角。人正常站立时这个角度接近 0 度跌倒过程中躯干会迅速趋向水平角度快速增大到 60 度以上。第二个是人体中心点高度我用髋部中心的 y 坐标代替。第三个是检测框的宽高比站立时框是高的宽高比小于 1倒地后框变成宽的宽高比大于 1。代码大概长这样import numpy as np COCO_SHOULDERS [5, 6] COCO_HIPS [11, 12] def extract_features(keypoints, confs): # keypoints: shape (17, 2) # 取置信度较高的点缺失就返回 None shoulders keypoints[COCO_SHOULDERS] hips keypoints[COCO_HIPS] shoulder_center shoulders.mean(axis0) hip_center hips.mean(axis0) # 躯干向量 torso_vec shoulder_center - hip_center # 竖直方向取 (0, -1)即图像坐标系向上 vertical np.array([0.0, -1.0]) norm np.linalg.norm(torso_vec) cos_angle np.dot(torso_vec, vertical) / (norm 1e-6) torso_angle np.degrees(np.arccos(np.clip(cos_angle, -1.0, 1.0))) # 中心高度归一化到图像高度 hip_height hip_center[1] # y 越大越靠下 return torso_angle, hip_height这几个特征本身还不能直接判跌倒因为“坐在地上”躯干角也很大中心高度也低。所以要结合时间维度看运动过程。5.2 时序特征怎么判跌倒的本质是一个“快速从高位到低位”的过程。我维护一个滑动窗口保存最近 15 到 20 帧的特征序列然后计算两个关键运动指标中心点垂直速度和躯干角变化率。垂直速度就是相邻帧髋部中心 y 坐标的差分跌倒时这个差值会突然变大也就是人体在短时间内快速下沉。躯干角变化率反映的是“突然倾倒”的动作特征正常坐下虽然躯干角也会变大但变化是平滑的、缓慢的。我用一个综合判分逻辑当垂直下沉速度超过阈值 v_thresh同时躯干角超过角度阈值 a_thresh连续若干帧满足条件就判定为跌倒。注意“连续若干帧”这个设计很关键它能过滤掉单帧的抖动和检测噪声我一般设置为连续 3 到 5 帧触发延迟只有两三百毫秒护理员完全感知不到这个延迟但误报能少很多。这里放一段我实际用的判定核心逻辑from collections import deque class FallDetector: def __init__(self, window15, trigger_frames4, v_thresh12.0, angle_thresh60.0): self.history deque(maxlenwindow) self.trigger_frames trigger_frames self.v_thresh v_thresh self.angle_thresh angle_thresh self.frame_high_risk 0 def update(self, torso_angle, hip_height): self.history.append((torso_angle, hip_height)) if len(self.history) 6: return False # 垂直速度当前中心高度和几帧前的差值 prev_hip self.history[0][1] hip_vel prev_hip - hip_height # y 向下为正下沉时 velocity 为正 # 躯干角变化 angle torso_angle is_risk (hip_vel self.v_thresh) and (angle self.angle_thresh) if is_risk: self.frame_high_risk 1 else: self.frame_high_risk max(0, self.frame_high_risk - 1) return self.frame_high_risk self.trigger_frames阈值怎么定我给的 v_thresh12、angle_thresh60 是在 640 分辨率图像下的经验值但千万别直接抄。每个场景的摄像头高度、画面尺度不一样同样的动作在画面上移动的像素数也不同。正确做法是先录几段模拟跌倒和正常活动的视频把特征值打出来看看分布再来定阈值。我第一版就是直接抄了别人项目的参数结果是误报漏报一起爆发后来老老实实回归数据分析把参数调到了现场适配的值。5.3 防误报的落地策略防误报是这个项目的生死线。误报太多护理员第一次会去看第二次会去看第十次就没人信了狼来了的故事。我的经验是分层处理。第一层是时序确认刚才说了连续 N 帧确认单帧异常不告警。第二层是“倒地后持续状态”确认判定跌倒的同时还要看后续几秒内人体一直处于低位和水平状态这是为了排除“快速蹲下又站起来”这类动作。第三层是场景规则比如检测到人在床上时宽高比异常就不该触发跌倒因为躺床上是正常状态检测到人在沙发区域时对坐姿的容忍度要放宽。这些规则可以用简单的区域坐标判断不需要额外的模型。还有一个很实用的技巧告警前拍照留证。触发告警时把当前帧和前后几帧存下来护理员在手机上能看到现场画面片段一眼就能判断是不是误报。这个功能不仅提升信任度事后追溯责任、复盘事件也用得上养老机构非常看重这个。6. 实时推理与告警链路6.1 视频流接入与推理管线实时推理这块我踩过的坑主要在网络和线程上。摄像头都是 RTSP 协议直接用 OpenCV 的 VideoCapture 去读流如果放在推理主线程里同步读只要网络一抖动整个推理循环就卡住告警延迟立马升高。正确做法是单独开一个采集线程用队列把帧缓冲起来推理线程只管从队列取帧这样网络抖动最多丢帧不会阻塞推理。推理部分我用 Ultralytics 的接口简单封装一下import cv2 from ultralytics import YOLO model YOLO(best.engine) # TensorRT 导出的引擎 def process_frame(frame): results model(frame, conf0.4, verboseFalse) for r in results: boxes r.boxes.xyxy.cpu().numpy() kpts r.keypoints.xy.cpu().numpy() confs r.keypoints.conf.cpu().numpy() # 对每个人计算几何特征并送入 FallDetector return annotated_frame多路摄像头的话每一路单独开一组采集线程和推理线程互不干扰。但要注意推理线程大概率成为瓶颈如果 GPU 算力不足可以先把帧率压到 15 帧再送推理或者把输入分辨率缩到 640对实时性的影响比卡死小得多。我自己在单路场景下用 25 帧全速推理没问题四路时就降到每路 15 帧体验依然可接受。6.2 告警通道设计与分级告警是落地环节里最直接体现价值的模块。我设计的是三级告警就地声光告警、推送告警、人工复核。就地声光告警最简单在养老机构的值班室放一个蜂鸣器或者语音播报盒子检测到跌倒直接语音播报房间号成本几十块钱效果立竿见影。推送告警我用的是企业微信机器人 webhook跌倒事件触发后把告警图片和文字说明推送到护理组群里护理员的手机实时能收到。代码很简单import requests def send_wechat_alert(message, image_pathNone): data { msgtype: text, text: {content: message} } requests.post(WEBHOOK_URL, jsondata, timeout5)至于短信和电话告警我建议只在无人值班场景比如独居老人家中才用因为成本高而且误报会造成打扰。我在社区独居老人项目里用的是短信微信双通道老人家属是主要接收人频控上做了限流同一事件 10 分钟内不重复发送。6.3 隐私保护与数据本地化这一节我得认真说。给老人房间装摄像头隐私是绕不开的问题处理不好项目根本推不动。我的方案是三条线第一推理全部在本地边缘盒子完成视频流不出局域网不做云端上传这个在架构上就保证了原始视频不外流。第二只在检测到跌倒事件时保存前后各 5 秒的视频片段平时不保存避免持续录像侵犯隐私。第三系统层面加了访问权限只有管理员能查录像操作日志留痕。实际推进中我发现把隐私方案讲清楚反而成了项目的加分项。很多家属一开始听“装摄像头”是抵触的但当我说明“视频只在本地算、平时不存储、只有报警才留证”之后接受度明显提高。技术上的“本地化”不只是一个架构选择还是项目能否通过伦理审查和家属沟通的关键。7. 常见问题与排查技巧实录7.1 误报与漏报问题误报和漏报是这类系统最头疼的问题。误报的第一大来源我之前提过是“快速蹲下”“从椅子上猛地站起来”这类动作垂直速度和躯干角变化都很快。我后来给系统加了一个“起身抑制”逻辑如果前置状态是坐姿躯干角大且中心高度低那么短时间内中心高度上升过程中不触发跌倒判定只有从高到低的快速运动才判。这个逻辑简单但非常有用。漏报主要发生在两种场景一是老人被遮挡比如走到床后面关键点不全二是光照太暗模型关键点置信度低。遮档问题只能靠摄像头安装位置去优化尽量选高视角减少遮挡面。暗光问题我换了带红外的摄像头但红外模式下画面是黑白的模型如果在彩色数据上训练红外画面推理效果会明显下降。这个坑我踩过解决方法是采集一些红外画面数据做微调或者在部署时把画面简单转成灰度再增强效果会有改善。7.2 性能与稳定性问题跑一段时间后推理速度变慢是常见问题。我遇到过一次显存泄漏排查发现是推理循环里反复创建新的结果对象没有释放导致显存越占越多。解决方法是复用模型句柄推理结果用完后及时 del。另外如果长时间跑 TensorRT engine偶发程序崩溃多半是显存碎片问题最简单的办法是定时重启进程配合看门狗做自动拉起。我的部署脚本里加了一个 systemd 服务崩溃自动重启完全无人值守。还有视频流断线的问题。公网 RTSP 或者无线摄像头偶尔会断流。我的处理是在采集线程里做心跳检测连续 5 秒没取到新帧就重连重连三次都失败就发一条设备离线告警。这个功能一开始没有后来现场工程师半夜被叫起来修摄像头回来就跟我说必须加。7.3 现场部署的坑最后说几个现场部署容易忽略的细节。第一是时间同步告警图片上要打时间戳所有设备必须 NTP 对时不然事后查证的时间线是乱的。第二是电源稳定性边缘盒子最好配 UPS普通民用电网波动可能导致设备频繁重启系统不稳定比检测不准更影响口碑。第三是模型参数不要一刀切每个房间的视角、高度、家具布局都不一样我给每个摄像头单独配了一套阈值参数虽然调试工作量大了点但效果明显比全局一套参数好。还有一个容易被忽视的点系统交付后要留一个“后门”便于维护。我的做法是在盒子上开了一个 SSH 服务和远程运维通道只允许特定 IP 访问这样线上出了问题能远程看日志、改参数不用每次都跑现场。对于多项目部署日志统一回传、告警集中管理这些能力花时间做也值得。回到这次项目本身我个人最大的体会是跌倒检测系统的技术难点其实不在模型而在工程。YOLOv8-Pose 把关键点检测的门槛降得足够低真正的壁垒是你对场景的理解、对判定逻辑的设计、对现场问题的响应能力。做这类系统的朋友我建议前期多花时间泡在真实场景里看老人怎么走路、怎么坐下、怎么起床这些观察最终都会变成你算法里的参数和规则。另外说一句实在的第一版上线不要追求零误报追求的是“漏报为零、误报可控”先把信任建立起来再一步步优化体验。