ARTICLE DETAIL

资讯详情

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

基于计算机视觉的智慧养老系统:跌倒检测与行为分析实战

基于计算机视觉的智慧养老系统:跌倒检测与行为分析实战 简介这份资源面向人工智能、深度学习与计算机视觉方向的学习者和开发者聚焦智慧养老场景下的视觉分析系统实现。系统通过多组摄像头实时采集画面利用计算机视觉技术识别老人情感状态、摔倒事件、闯入禁区行为、义工互动情况并对陌生人进行检测与追踪事件结果实时写入数据库并更新报表辅助管理人员快速响应。资源包共1077个文件约333.37MB包含68个Python脚本、60个vcxproj工程文件、52个CMake配置、40张png图片、30个Vue前端文件及多个caffemodel与prototxt模型文件覆盖模型部署、工程构建与界面展示等环节。已有1536人学习下载。读者可从中获取计算机视觉模块的完整工程结构、OpenPose与MobileNetSSD等模型的实际调用方式以及事件检测与数据落库的参考实现适合用于课程设计、毕业项目或智慧养老相关课题的二次开发。1. 从一次深夜告警说起智慧养老系统到底在做什么凌晨两点养老院值班室的屏幕上弹出一条告警302 房间的老人已经在卫生间门口静止超过 8 分钟。值班护士赶过去时老人正扶着墙慢慢往下滑——低血糖导致的体力不支。如果再晚十分钟后果不堪设想。这条告警不是来自穿戴设备而是来自走廊顶部一个普通摄像头背后跑的正是一套基于计算机视觉的智慧养老系统。很多人第一次听到这个方向会把它和「给老人戴手环」画等号。但手环的翻车率极高老人嫌勒手、忘记充电、洗澡摘下来最后数据全是断的。摄像头方案的优势在于无感——老人不需要配合任何操作系统自己看。这套系统要解决的核心问题就三类跌倒检测、长时间静止/离床异常、以及日常活动轨迹的粗粒度统计。它适合谁做做计算机视觉项目的大作业学生、想切入养老赛道的算法工程师、以及养老机构里负责智能化的技术人员。下面我把这套系统从选型到落地拆开讲包括我踩过的坑。2. 为什么选视觉而不是穿戴传感器选型与模型路线2.1 三类感知方案的实测对比在动手写代码之前先把「用什么感知」这件事定下来。我前后试过三种方案直接上对比表方案部署成本老人配合度跌倒识别准确率实测主要问题加速度手环低差约 82%忘戴、忘充电、洗澡摘除毫米波雷达高好约 88%单价高遮挡敏感调试玄学RGB 摄像头中好约 91%隐私顾虑需做区域遮蔽摄像头方案在准确率和成本之间取得了最好的平衡。隐私问题不是不能解决——我一般会在画面里把床铺、卫生间门口以外的区域做高斯模糊只保留关键活动区。这一点在落地时比算法本身还重要后面避坑章节会细说。2.2 检测模型选型YOLO 系还是姿态估计智慧养老的核心不是「识别出这是人」而是「识别出人的姿态异常」。所以路线分两条第一条是目标检测路线用 YOLO 系列直接检测「跌倒」这个类别。优点是快单帧推理在普通 GPU 上能到 60 FPS 以上缺点是跌倒样本少模型容易把「蹲下捡东西」误判成跌倒。第二条是姿态估计路线先用 RTMPose 或 MediaPipe 提取人体关键点再根据关键点几何关系判断姿态。优点是解释性强蹲下和跌倒的关键点分布差异明显缺点是多了一层推理链路更长。我的选择是姿态估计为主、检测为辅。具体做法用 YOLO 做人形检测框定 ROI再在 ROI 内跑姿态估计最后用一个轻量分类器判断姿态类别。这样既控制了算力又保留了可解释性。下面是姿态估计的最小可跑代码import cv2 import numpy as np from rtmlib import Body, draw_skeleton # 初始化姿态估计模型modebalanced 在速度和精度间取平衡 body Body(modebalanced, to_openposeFalse, backendonnxruntime, devicecuda) # 读取一帧画面 frame cv2.imread(corridor_302.jpg) h, w frame.shape[:2] # 推理返回关键点和置信度 keypoints, scores body(frame) # 关键点索引0鼻 5左肩 6右肩 11左髋 12右髋 13左膝 14右膝 # 计算肩髋角度用于判断是否直立 def torso_angle(kps): shoulder (kps[5] kps[6]) / 2 hip (kps[11] kps[12]) / 2 dx hip[0] - shoulder[0] dy hip[1] - shoulder[1] return np.degrees(np.arctan2(abs(dx), abs(dy))) angle torso_angle(keypoints[0]) print(f躯干与垂直方向夹角: {angle:.1f} 度) # 夹角大于 60 度且持续多帧判定为跌倒候选这段代码的逻辑说明Body初始化时modebalanced是关键参数performance模式精度高但慢lightweight模式快但关键点抖动大养老场景建议用balanced。torso_angle函数计算肩部中点到髋部中点的连线与垂直方向的夹角正常站立时这个角度小于 30 度跌倒时通常大于 60 度。参数上devicecuda在有 GPU 时务必打开CPU 推理单帧要 200ms 以上多路视频会直接卡死。2.3 时序判断单帧不够要加滑动窗口单帧姿态判断的误报率高得离谱。老人弯腰系鞋带、蹲下拿东西单帧角度都可能超过 60 度。所以必须引入时序。我一般用一个长度为 15 帧的滑动窗口窗口内超过 10 帧判定为异常姿态才触发告警。这个窗口长度对应约 0.5 秒30 FPS 下既能过滤瞬时动作又不会漏掉真实跌倒。from collections import deque # 滑动窗口保存最近 15 帧的姿态判定结果 window deque(maxlen15) def is_fall_candidate(angle, hip_y, frame_h): # 条件1躯干角度大 angle_ok angle 60 # 条件2髋部位置接近地面画面下方 1/3 position_ok hip_y frame_h * 0.66 return angle_ok and position_ok # 每帧调用 candidate is_fall_candidate(angle, keypoints[0][11][1], h) window.append(1 if candidate else 0) # 窗口内超过 10 帧为异常触发告警 if sum(window) 10: trigger_alarm(room302, typefall)参数说明maxlen15是窗口大小sum(window) 10是触发阈值。这两个值需要根据实际帧率调整——如果摄像头是 15 FPS窗口要放大到 30 帧左右否则时间跨度不够。hip_y frame_h * 0.66这个位置条件是为了排除「躺在床上但角度大」的情况床面通常在画面中下部这个阈值要根据摄像头安装高度实测调整。3. 从单路摄像头到多房间部署工程化落地步骤3.1 视频流接入与解码优化实验室里跑单张图片和养老院跑 20 路摄像头是两回事。最常见的翻车是用 OpenCV 的VideoCapture直接读 RTSP 流跑几小时就卡死。原因是 RTSP 缓冲堆积解码线程和推理线程互相拖累。我的做法是解码和推理分离用 FFmpeg 子进程拉流并解码通过管道把帧传给 PythonPython 侧只做推理。这样即使推理偶尔变慢也不会阻塞拉流。# 用 FFmpeg 拉流输出 rawvideo 到管道 # -rtsp_transport tcp 避免 UDP 丢包导致的画面撕裂 # -vf fps15 限制解码帧率降低下游压力 ffmpeg -rtsp_transport tcp -i rtsp://camera302/stream \ -vf fps15 -f rawvideo -pix_fmt bgr24 - \ | python inference_worker.py --room 302参数说明-rtsp_transport tcp是必须的UDP 模式下丢包会导致花屏姿态估计直接崩。-vf fps15把帧率压到 15养老场景不需要 30 FPS15 帧足够判断跌倒还能省一半算力。-pix_fmt bgr24要和 Python 侧 OpenCV 的默认格式对齐否则颜色通道会错位关键点全乱。3.2 多路调度的进程模型20 路摄像头不能开 20 个 Python 进程各跑一个模型显存直接爆。我一般用生产者-消费者模型FFmpeg 解码进程作为生产者把帧放进共享队列一个推理进程作为消费者从队列取帧批量推理。import multiprocessing as mp import numpy as np def decode_worker(rtsp_url, queue, room_id): # 省略 FFmpeg 调用细节核心是把帧放入队列 while True: frame read_frame_from_ffmpeg(rtsp_url) if queue.full(): queue.get() # 丢弃最旧帧保证实时性 queue.put((room_id, frame)) def inference_worker(queue, model): batch [] while True: room_id, frame queue.get() batch.append((room_id, frame)) # 攒够 8 帧做一次批量推理提升 GPU 利用率 if len(batch) 8: frames np.stack([f for _, f in batch]) results model(frames) for (rid, _), res in zip(batch, results): handle_result(rid, res) batch.clear()逻辑说明queue.full()时丢弃最旧帧是关键设计——养老场景要的是「当前状态」不是「完整录像」丢帧比延迟积压好。批量大小 8 是实测值再大显存吃紧再小 GPU 利用率上不去。每个摄像头对应一个room_id结果处理时按房间分发告警。3.3 告警去重与状态机如果每帧都判断、每 10 帧就告警护士会被轰炸到麻木。必须加状态机只有从「正常」迁移到「异常」时才发一次告警异常持续期间不重复发恢复后才重置。class RoomState: def __init__(self): self.state normal # normal / alerting self.alert_count 0 def update(self, is_abnormal): if self.state normal and is_abnormal: self.state alerting self.alert_count 1 return ALARM # 只在迁移时告警 elif self.state alerting and not is_abnormal: self.state normal return RECOVER return None这个状态机看起来简单但少了它系统就没法用。alert_count可以用于统计但不要用它做告警频率控制——状态迁移才是正确的去重逻辑。4. 避坑与排查那些让我半夜爬起来的问题4.1 夜间红外画面导致关键点全丢现象白天跑得好好的一到晚上告警全无回看录像发现姿态估计根本没输出关键点。原因养老院夜间用红外补光画面是灰度的而模型训练数据以彩色为主灰度图输入后特征分布偏移置信度低于阈值被过滤。解决夜间单独走一套灰度增强流程——先做直方图均衡化再把单通道复制成三通道喂给模型。如果还不行就针对夜间场景补标 200 张灰度图做微调。我一般会在推理前加一个判断如果画面饱和度低于阈值自动切到灰度增强分支。4.2 摄像头脏污被误判为遮挡告警现象系统频繁报「摄像头遮挡」但现场看镜头只是有点灰。原因遮挡检测用的是画面方差阈值镜头积灰后方差下降触发了阈值。解决把遮挡检测从「单帧方差」改成「与基线帧的差异」。系统启动时存一张干净画面作为基线之后每小时对比一次差异超过阈值才报遮挡。这样积灰是渐变的不会触发真正的遮挡是突变的能准确抓到。4.3 多人场景下姿态串线现象两个老人并排走系统把 A 的肩和 B 的髋拼在一起算出一个诡异的跌倒姿态。原因姿态估计模型在多人重叠时关键点归属会错乱。解决先用 YOLO 检测出每个人的人形框再在每个框内单独跑姿态估计。虽然多了一次检测但彻底解决了串线问题。代价是推理时间增加约 30%在 15 FPS 下完全可接受。4.4 告警延迟超过 30 秒现象老人已经跌倒了护士 30 秒后才收到告警。原因滑动窗口 15 帧 批量推理 8 帧 队列积压累积延迟。解决把滑动窗口判断改成「快速通道 确认通道」。快速通道用 5 帧窗口满足条件立即发预告警确认通道用 15 帧窗口确认后发正式告警。预告警到正式告警之间通常只差 0.3 秒但护士能提前收到信号。另外队列积压要监控超过 3 帧就丢弃保证实时性优先。4.5 隐私区域遮蔽后模型失效现象为了隐私把卫生间区域模糊了结果老人在卫生间门口跌倒检测不到。原因模糊区域正好覆盖了关键活动区。解决遮蔽要「选择性」——床铺区域可以全模糊但门口、走廊要保留。我一般用多边形标注工具画出「必须保留区」和「必须遮蔽区」推理时只对保留区做检测。这样既满足隐私要求又不影响核心功能。这件事必须在部署前和养老院管理方确认清楚否则后期改起来很麻烦。5. 进阶技巧用轨迹热力图做长期行为分析跌倒检测只是及格线。真正让养老院愿意买单的是长期行为趋势。我最近在做的功能是把每个老人每天的活动轨迹聚合成热力图连续一周热力图收缩可能意味着老人活动能力下降提前干预比事后告警有价值得多。实现思路不复杂姿态估计输出的髋部坐标就是老人在画面中的位置按小时聚合用高斯核做平滑生成热力图。关键是坐标系要归一化——不同摄像头的安装角度不同直接拼像素坐标没有意义。我一般把画面坐标映射到房间平面图坐标再在平面图上做聚合。import numpy as np from scipy.ndimage import gaussian_filter def build_heatmap(positions, room_size(640, 480), sigma15): # positions: 一天内所有髋部坐标列表 heatmap np.zeros((room_size[1], room_size[0]), dtypenp.float32) for x, y in positions: if 0 x room_size[0] and 0 y room_size[1]: heatmap[int(y), int(x)] 1 # 高斯平滑让热力图更直观 heatmap gaussian_filter(heatmap, sigmasigma) # 归一化到 0-255方便可视化 heatmap (heatmap / heatmap.max() * 255).astype(np.uint8) return heatmap参数说明sigma15控制平滑程度值越大热力图越「糊」适合看大趋势值越小越精确适合看具体活动点。room_size要和实际画面分辨率一致如果做了缩放坐标也要同步缩放。这个热力图我一般按周生成对比上周和本周的分布收缩超过 20% 就推送给护理主管。还有一个技巧是停留时长统计老人在某个区域停留超过阈值即使姿态正常也值得关注。比如在卫生间停留 20 分钟没出来可能是身体不适。这个逻辑和跌倒检测共用同一套坐标数据加一个计时器就行。最后说个我自己的教训这套系统上线第一周我把告警阈值调得太敏感护士一天收到 40 多条告警全是误报差点被要求下线。后来花了两周做数据回捞和阈值调优才把误报压到每天 2 条以内。算法精度是一回事告警策略是另一回事后者往往决定系统能不能活下来。如果你也在做类似的项目建议先把误报率压下来再谈功能扩展。希望帮到你。本文还有配套的精品资源点击获取
返回列表