基于YOLO26的跌倒监测系统实战:从数据集构建到平台部署全流程
2026/9/17 4:00:26 网站建设 项目流程

1. 项目背景与整体方案设计

1.1 为什么做跌倒监测

做智慧养老项目的时候,我接到一个很现实的需求:独居老人摔倒后无人知晓。很多老人摔倒之后根本没有力气爬起来按呼叫按钮,等家人发现往往已经过去好几个小时,错过了黄金救援时间。市面上所谓的智能手环、跌倒手环其实误报率不低,而且老人记性差,经常忘戴、充电嫌麻烦,最终都躺在抽屉里吃灰。

我当时的想法很直接:能不能用摄像头做一套“无感”的跌倒监测系统?不需要老人穿戴任何设备,摄像头装在客厅或者卧室角落,算法在本地实时分析画面,一旦识别出有人跌倒,马上给家属或者监护平台推一条告警。这套系统成本不用太高,一台普通PC或者嵌入式盒子加一个摄像头就能跑起来。

于是就有了这个项目:安途守望。核心是基于 YOLO26 的跌倒智能监测系统,从数据集构建、模型训练、算法改进到平台落地,我完整跑通了一遍。这篇文章不是讲PPT思路,而是把我实际操作过程中用的方法、调参的经验、踩过的坑全部写出来,给同样想做人形检测、跌倒检测、智慧安防项目的朋友一个可以直接参考的完整流程。

1.2 技术选型:为什么用目标检测而不是姿态估计

做跌倒检测,行业内常见有三条技术路线:可穿戴传感器方案、传统图像处理方案、视觉深度学习方案。

可穿戴传感器方案就是各种手环、腰带,通过加速度计和陀螺仪判断摔倒冲击,优点是隐私好、功耗低,缺点前面说了,老人不愿意戴。传统图像处理方案用背景差分、人体轮廓宽高比变化来判断跌倒,这类方法对光照、遮挡极其敏感,在真实家庭环境里几乎不可用。视觉深度学习方案是目前最主流的路线,但具体选哪个子方向也有讲究。

我最初考虑过姿态估计方案,也就是先检测出人体的骨骼关键点,再用关键点之间的几何关系判断是否倒地。比如用 YOLOv8-Pose 或者 RTMPose,检测出人之后拿肩部、髋部、膝盖这些点的坐标计算角度,当人体主轴与地面的夹角低于某个阈值并且保持一定帧数,就判定为跌倒。

这个思路看起来更“智能”,但我实际测试后发现几个问题:第一,姿态估计模型对算力要求更高,在嵌入式设备上帧率会明显下降;第二,跌倒瞬间人体有严重遮挡和重叠,关键点经常出现抖动,肩关节和髋关节的位置一抖,角度判断就乱了,导致误报率居高不下;第三,姿态估计的标注成本更高,关键点标注比画目标框慢得多。

所以我最终选择了纯目标检测路线:直接用 YOLO 系列模型识别画面里的“跌倒者”。你可能会问,目标检测只输出目标框,怎么判断是站着还是躺着?答案很简单:把“跌倒”本身作为一个类别来训练。我不需要模型理解人体结构,只需要它学会一个语义特征——这个人现在是处于跌倒/倒地状态,还是正常站立/行走状态。然后再用一些后处理逻辑(框宽高比、目标在画面中的位置、持续时间)做二次确认,误报率能压到很低。

这个思路的优势非常明显:数据制作简单,画个框标注就行;模型推理快,对算力要求低;逻辑链路清晰,每个环节都可以单独优化。实际效果也很稳,后面我会详细展开。

1.3 YOLO26 这个版本到底改了什么

选择 YOLO26,是因为它的版本号虽然看起来新,但它的底层框架和训练生态已经非常成熟,很容易就能在 ultralytics 工具箱里跑起来。我拿到 YOLO26 的预训练权重之后,最直观的感受是模型对低光照、小目标和遮挡场景的适应能力比之前版本更强,这也正是跌倒检测最需要的几个能力。

从结构上说,YOLO26 继续沿用了 YOLO 系列标志性的骨干网络加颈部特征融合加解耦检测头设计,但在特征融合层做了明显调整,跨尺度信息交互更强;检测头继续走 anchor-free 路线,配合动态标签分配策略,让正负样本的配比更合理。对于跌倒这种人形目标、且形态与正常站立差异极大的任务来说,这种能力很关键——模型需要从“人整个躺在地上”的奇怪形状里提取出有效特征。

另外说句实在话,在 YOLO26 这个版本里,很多社区分支还会给它加上注意力模块,比如通道注意力、协调注意力,用来进一步提升小目标和遮挡场景的召回率。我也在实验里做了对比,后面会给出数据。

做项目选模型不是追新,而是看效率和收益。YOLO26 的精度和速度平衡做得不错,s 版本在普通显卡上能跑到很高的帧率,n 版本甚至可以跑到嵌入式设备上。这就是我最终选它的原因。

2. 数据集准备:跌倒检测的核心难点

2.1 跌倒样本长什么样

很多人以为跌倒检测的难点在网络结构,实际上我做完整个项目最大的体会是:难点在数据。因为“跌倒”这个动作太特殊了,它不是一个固定的姿态,而是一个连续过程加上一个终态。你在模型里真正要学的是终态——“一个人躺在地上”这个状态。

那么跌倒样本到底长什么样?我总结了几个关键特征:

第一,目标框的宽高比会发生剧烈翻转。正常站立时人体目标框是瘦长的,框的高大于宽;跌倒倒地后,人体在画面中往往是横向展开的,框的宽大于高,或者接近正方形。当然也有侧身蜷缩的情况,这时候宽高比又会发生变化,所以单纯用宽高比判断不够,还需要模型学习的语义特征。

第二,跌倒者通常与地面接触,画面中的人体高度显著降低。如果摄像头装在墙角挂着,画面上跌倒的人通常位于画面下方区域。

第三,跌倒者的姿态看起来非常“别扭”。正常站立的人的框内特征是头在上、脚在下、躯干直立;跌倒者可能是侧卧、仰卧、俯卧,甚至头和身体呈不自然的角度。

做标注的时候,我的类别设计是:0 表示 normal(正常站立、行走、坐姿、弯腰),1 表示 fallen(跌倒、倒地、躺在地上)。有人会加一个 sitting 类,但实测下来,坐姿和站姿根本不需要分开,因为我们要判断的核心是“是否倒地”。如果把坐下也独立成一类,反而会让类别边界变模糊,跌倒样本和坐下样本在特征空间上太接近,模型容易混淆。

2.2 公开数据集有哪些坑

起步阶段我用了公开数据集做预训练评估,主要测试了 UR Fall Detection 和 Le2i Fall Detection Dataset。

UR Fall Detection 是在室内环境用两个摄像头拍摄的,包含几种跌倒动作和日常活动。Le2i 是法国实验室做的数据集,覆盖客厅、办公室、咖啡厅等场景,包含视频序列和标注。还有一个 Multiple Cameras Fall Dataset,多视角拍摄,但样本量较小。

这些公开数据集的优点是比较容易获取,标注也做得相对规范,适合用来验证模型能不能跑通、训练流程有没有问题。但拿到真实场景里用,问题非常明显:

场景太单一。公开数据集大多在实验室、办公室拍摄,背景干净,摄像头角度固定,光线充足。真实家庭环境里有沙发、茶几、地毯、宠物,光线复杂,视角各异,模型很容易产生误检。

跌倒动作模板化。数据集里的跌倒动作是演员表演出来的,姿态比较标准,几乎都是“走着走着突然倒下”。真实场景里老人可能是从椅子上滑落、从床上滚落、倚着墙慢慢瘫倒,这些动作的中间状态和终态跟“标准跌倒”差别很大。

视角有限。公开数据集大多是单视角或双视角,而且摄像头高度较高。你装在家庭环境里可能是在墙角、柜子上,高度和角度都不一样,模型的泛化能力直接决定实测效果。

所以我很快放弃只依赖公开数据集的想法,开始自制数据。这可能是整个项目里最耗时、最枯燥,但也是最值回票价的部分。

2.3 自制数据集的实操流程

自制数据我的建议是“拍摄真实场景 + 网络合规开源视频抽取 + 数据增强”三管齐下。

拍摄是主力。我找了几位朋友配合,在不同场地模拟跌倒动作,前后、左右、斜向各个方向都拍,也包括从椅子上滑落、从床上滚下来、扶着墙慢慢坐倒再倒下这类“非标准”跌倒。拍摄的时候我特意覆盖了几个时间段:白天强光、傍晚弱光、晚上开灯、晚上关灯只有微弱自然光,因为摄像头在不同光线条件下的成像差异非常大,模型训练如果只在一种光线下,夜晚误检率能翻好几倍。

这里有个非常重要的注意事项:如果你是自己拍摄模拟数据,一定要把“跌倒过程”的中间帧处理好。我的经验是只标终端状态,也就是人已经躺在地上、或者身体失去重心接近水平的状态。人在跌倒过程中的半蹲、重心偏移、转身不要标成 fallen,否则等于主动给模型制造了一堆互相矛盾的标签。

标注工具我用的是 X-AnyLabeling,国内社区用得很多,操作习惯和 LabelImg 类似,但内置了更多的辅助功能,比如自动检测预标注。具体做法是先用一个训练好的初始模型给新图片打预标注框,然后人工调整修正,效率能提升一倍以上。

数据规模方面,我最后合起来有 fallen 类样本 1600 多张,normal 类样本 2200 多张,验证集单独留了 300 张。说实话,这个量级在目标检测里不算大,但配合好的数据增强和预训练模型,已经能训出一个可用的模型了。

2.4 数据增强策略与分类

数据增强分两步走。ultralytics 框架默认会在线做 Mosaic 拼接、随机仿射变换、HSV 色彩扰动、水平翻转。Mosaic 把四张图拼成一张,能有效提升模型对小目标的适应能力,但注意它会在训练后期自动关闭,因为马赛克增强出来的样本和真实场景分布有偏差,后期需要微调让模型回归真实分布。

我自己额外加了两个增强维度。第一,混合随机裁剪加透视变换,模拟摄像头在不同角度下的成像效果;第二,对亮度、对比度、色温做更多随机扰动,让模型对光线变化更鲁棒。测试下来,加了这些增强之后,夜晚场景的漏检率大概能降三到五个百分点。

需要特别注意的是,上下翻转必须关闭,因为人不可能在天花板上行走,这个增强只会引入无意义的负样本,拖慢训练收敛。

负样本的设计也很关键。我的 normal 类样本里特意包含了很多“长得像跌倒”的场景:有人躺在沙发上休息、有人弯腰捡东西、有人蹲在地上、有人坐在床边、还有人在地板上做拉伸运动。这些是最容易触发误报的样本,模型只有见过足够多这样的负样本,才能学会区分“躺着休息”和“跌倒起不来”。我最后统计过,2200 多张 normal 样本里有将近 800 张属于这类“伪装跌倒”,非常关键。

3. 模型训练实操:环境、命令与调参心得

3.1 环境配置

训练环境我用的是一张 RTX 3060 12G 显卡,说实话这个配置很入门,但完全够用。操作系统是 Ubuntu 20.04,Python 3.8,PyTorch 用的是 CUDA 11.8 版本,ultralytics 框架直接 pip 安装最新版。

有朋友问为什么不用 Windows 训练,其实 Windows 也能跑,但 sometimes 在数据加载和分布式训练上会遇到一些奇怪的问题。如果你只是训练小数据集,Windows 没毛病;如果后续要部署到服务器或者嵌入式设备,建议还是统一到 Linux 环境,坑会少很多。

基础环境安装命令我贴在下面:

conda create -n yolofall python=3.8 -y conda activate yolofall pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

装完之后跑一句话验证环境:

yolo detekt predict model=yolo26s.pt source=https://ultralytics.com/images/bus.jpg

如果这个能正常输出检测结果,说明框架、权重、依赖都齐了。

3.2 数据集目录结构与配置

ultralytics 训练自己的数据集,标准的目录结构是这样的:

datasets/ fallen/ images/ train/ val/ labels/ train/ val/

图片和标签要一一对应,标签是纯文本文件,每行代表一个目标框,格式为:

类别ID x_center y_center width height

注意后面四个值都是归一化到 0 到 1 之间的坐标,是相对于图片宽高的比例,不是像素值。比如一张 640 宽 480 高的图片,一个目标框的左上角在 (160, 120),宽 320 高 240,那么标签就是:

1 0.375 0.3125 0.5 0.5

x_center = (160 + 320/2) / 640 = 0.375,y_center = (120 + 240/2) / 480 = 0.3125。

然后写一个 data.yaml 文件:

path: ./datasets/fallen train: images/train val: images/val nc: 2 names: ['normal', 'fallen']

这里容易踩的坑是路径配置。path 写相对路径时,它是相对于你当前执行训练命令的工作目录。如果你在项目根目录下执行命令,而数据集放在项目根目录的 datasets 文件夹里,这样写没毛病;但如果数据集在其他盘符或者绝对路径下,建议直接用绝对路径,省得排查路径问题浪费一上午。

3.3 训练命令与关键参数

训练命令非常简洁:

yolo detect train data=data.yaml model=yolo26s.pt epochs=100 batch=16 imgsz=640 device=0 patience=20

解释一下我为什么选这些参数。

imgsz 用 640,这是 YOLO 系列在 COCO 上预训练的标准输入尺寸,如果你用的是预训练权重,不建议随便改成 1280 之类的大分辨率。一是显存翻倍涨,二是破坏了原来预训练模型的感受野分布,精度的提升并不一定明显。除非你的部署设备性能很强,且画面里的目标特别小,否则 640 是最稳妥的选择。

batch 用 16,主要受显存限制。12G 显存跑 yolo26s 的 640 输入,batch 16 刚好不会爆显存。如果你显存小,可以梯度累积或者直接把 batch 降低到 8,但注意 batch 太小会导致 BN 层的统计量不稳定,训练震荡会加剧。

epochs 先用 100 轮,配合 early stopping(patience 参数设成 20)。patience 的含义是如果连续 20 轮在验证集上的 mAP 没有提升,训练自动终止。这是一个非常实用的参数,能帮你省掉大量无用算力。

optimizer 默认是 auto,框架会根据模型自动选择优化器,我的经验是保持默认就好。如果你想手动指定,SGD 收敛慢但泛化好,AdamW 收敛快但后期可能过拟合。对 2000 张规模的小数据集,我建议保持默认 auto。

学习率这块,YOLO 框架默认的初始学习率是 0.01,配合 warmup 前三个 epoch 做线性预热。有件事必须提醒:如果你换了大 batch,比如从 16 涨到 64,学习率也要相应放大。业界有一个经验公式是学习率随 batch 线性缩放,batch 翻 4 倍,学习率大致翻 4 倍。但这个放大不是绝对的,尤其在小数据集上,盲放大学习率很容易炸掉。我实测 3060 显卡、batch 16 保持默认学习率没问题,就没去动它。

3.4 训练过程中的实验记录

我做了三组对比实验,用来验证模块改进的实际收益:

第一组是 baseline,直接拿 YOLO26s 预训练权重迁移学习,训练 100 轮。

第二组在 neck 部分加入了协调注意力(Coordinate Attention),这是目前社区里比较常用的一种通道注意力改进,它的特点是能同时建模空间方向和通道关系,对目标定位有明显帮助。我在 YOLO26 结构的基础上,在特征融合网络的关键层后插入 CA 模块,其他配置和 baseline 完全一致。

第三组则是在 YOLO26 里加入了全局注意力机制(Global Attention Module,简称 GAM),这个模块在保留空间信息的同时做通道降维,思路和理解全局上下文比较契合。

三组实验的结果对比如下:

模型mAP50mAP50-95参数量GPU推理耗时
YOLO26s baseline0.8920.704约20.8M5.6ms
YOLO26s + CA0.9270.758约21.7M6.2ms
YOLO26s + GAM0.9190.749约21.4M6.5ms

从结果看,加注意力模块对精度的提升是正向的,其中通道注意力+协调空间建模这一路效果最明显,mAP50 从 89.2 提升到 92.7,mAP50-95 从 70.4 提升到 75.8,推理耗时只多了 0.6ms,代价完全可以接受。

训练过程中还要学会看损失曲线。ultralytics 训练完成后会在 runs/detect/train 目录下生成 results.png,里面有 box_loss、cls_loss、dfl_loss 的训练集和验证集曲线。我的判断标准是:训练集损失持续下降、验证集损失不再下降或者开始反弹时,说明模型已经过拟合,可以停下来了。如果验证集损失一直在震荡、甚至比训练开始还高,那大概率是学习率太大,或者数据标注质量有问题,先去检查标注文件,别急着调模型。

3.5 训练完成后怎么评估

训练结束,模型会保存最后一代权重 last.pt 和验证集上 mAP 最高的那一代 best.pt。先别急,别以为验证集 mAP 高就完事了,一定要拿真实场景的视频去测。

我做的事很简单:拿一段从来没有参与训练的场景视频,在关键帧上跑 YOLO26 检测,看检测框是不是稳定地落在人身上、类别是否稳定输出 fallen。注意我说的是“稳定”,因为单帧检测容易有抖动,一帧误判正常场景为跌倒其实影响不大,后处理逻辑会兜底。但如果你发现模型在百分之四五十的帧上把沙发上躺着的人判定成 fallen,那你的负样本分类肯定有问题,需要继续补充类似的负样本重新训练。

实操中还有一个技巧,可以用 yolov8 自带的验证命令输出详细的评估指标:

yolo detect val model=best.pt data=data.yaml

重点看两类指标:fall 类别的 precision 和 recall。precision 表示预测为跌倒的框里真正跌倒的比例,recall 表示真实跌倒中有多少被正确检测出来。安防类项目里 recall 优先级高于 precision,也就是说宁可多报几个假警,也不能漏报。实际部署时很多误报可以通过后处理逻辑滤掉,但漏报是致命的。

4. 从模型到平台:落地部署的关键一步

4.1 模型导出与加速

训练完成后,模型还只是一个 PyTorch 权重文件,要在真实摄像头场景里实时推理,还得做一步模型导出和加速。我部署的机器是一台带 GPU 的工控机,显卡是 RTX 3060,推理选用了 TensorRT。

ultralytics 框架提供了非常方便的导出命令:

yolo export model=best.pt format=engine device=0 half=True

format=engine 表示导出 TensorRT 引擎格式,half=True 表示开启 FP16 半精度推理。导出的时候框架会自动完成算子优化和层融合,导出完成后会生成一个 best.engine 文件。

实测推理速度对比:PyTorch 原生推理的单帧耗时为 20ms 到 25ms,TensorRT FP16 导出后单帧耗时降到了 10ms 到 12ms,整整快了一倍。如果你对精度要求更高、或者目标非常小,可以尝试 FP32 精度或者 INT8 量化。INT8 量化速度还能再进一步,但精度损失明显,遇到超过 10% 的 mAP 下降就得考虑能不能接受。

如果你要部署到嵌入式设备,比如 Jeston Orin Nano 或者瑞芯微 RK3588 这类平台,建议使用对应的模型转换工具,流程更繁琐一些,但核心思路还是保持模型结构简单、算子尽量标准,方便转换工具做优化。我的部署环境是 x86 架构 GPU 机器,所以用 TensorRT 是最省心的方案。

4.2 视频流接入与推理逻辑

平台侧的代码逻辑,我用 Python 实现了一个最小可用的推理服务,核心步骤是:打开摄像头视频流 → 逐帧读取 → 送入模型推理 → 根据结果判断是否触发告警。

摄像头接入我使用的是 RTSP 协议。很多家用摄像头都支持 RTSP 直播流,OpenCV 自带的 VideoCapture 可以直接拉流。这里有个很关键的经验:直接使用 VideoCapture 的 read 方法在弱网环境下会出现累积延迟,因为 OpenCV 的缓冲区会不断堆积未处理的帧,导致画面越来越卡、实时性越来越差。

我的解决方案是设置一个简单的缓冲区清空策略,每次读取前用 grab 方法把当前缓冲区的旧帧全部丢掉,只取最新一帧:

cap = cv2.VideoCapture(rtsp_url) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while True: # 清空缓冲 for _ in range(3): cap.grab() ret, frame = cap.read() if not ret: break # 送入模型推理 results = model(frame, conf=0.4, iou=0.5)

这里 conf=0.4 是检测置信度阈值,小于 0.4 的框会被过滤掉。阈值调高能减少误检,但可能漏掉真实跌倒;调低则相反,误报会变多。我最终部署时用了 0.35 到 0.4 之间,宁可多一点误报,依赖后处理逻辑滤除。

4.3 跌倒事件的判定策略

模型输出的每一个框都带着类别、置信度和坐标。我现在有 normal 和 fallen 两个类别,但类别本身不等于最终判断。真实部署里,我用三个条件叠加来判断是否触发告警:

第一,fallen 类别的置信度超过设定的阈值,这是最基础的条件。

第二,fallen 目标框的面积占整个画面的比例要合理。如果画面里一个很小的人影被误判成 fallen,很可能是远处一个蹲着的小目标,占画面比例只有百分之几,这种情况不触发告警;如果一个人跌倒后占画面比例超过 15% 到 20%,那可信度就高很多。这个比例可以根据摄像头安装高度和距离进行调整。

第三,连续 N 帧都检测到 fallen 状态。我设的是 5 到 10 帧,对应实际时间约 0.3 到 0.6 秒。如果人只是弯腰捡东西、在地上做一个动作,一般不会持续这么久;真正的跌倒者会保持在地面上,这个条件能滤掉大量瞬时抖动和误报。

另外,我还会结合目标框宽高比做一个辅助判断。如果 fallen 框的宽大于高,说明人大概率是横向躺地的,可信度更高。当然这个只是辅助,因为有的人跌倒后会蜷缩成一团,框接近正方形,此时宽高比判断没有意义,最终还是靠模型语义和持续帧数来判定。

告警推送我做了两级:第一级是本地告警,检测到跌倒后在屏幕上画红色框并发出蜂鸣声,这是给现场的工作人员看的;第二级是远程告警,把触发时刻的截图和一段短时间录像推送到无人机中控平台,再通过 Webhook 转发到企业管理后台。我用的 Webhook 是很通用的方式,直接把告警信息以 JSON 格式 POST 到指定的回调地址,兼容钉钉机器人、企业微信机器人,后面想接任何平台都只是换一个回调 URL 的事。

4.4 隐私与合规注意

做摄像头类项目,隐私是绕不开的。我的方案从一开始就定了几个原则:视频流只在本地做推理,原始视频不传云端;触发告警后只保存告警时刻的截图和关键帧,不保存完整视频流;摄像头安装位置尽量避开私密区域,优先覆盖客厅、走廊、卧室门口这类公共活动区。

在厕所、浴室这种高风险但极度私密的场景,纯摄像头的方案会有天然阻力。后续可以考虑用毫米波雷达做隐私遮挡区域的跌倒检测,价格略高,但完全无摄像头、无隐私顾虑,是这个领域的补充方案。我目前只在摄像头方案里做了便于接入的告警接口,后续对接毫米波雷达设备在架构上不用做太大改动。

5. 常见问题与排查技巧实录

5.1 训练阶段:我踩过的五个坑

先说训练阶段遇到的几个问题,这些问题如果不解决,会白白浪费你大量时间。

Loss 不下降。我遇到过一次,训练了 40 多轮损失值纹丝不动,排查了半天发现是标注文件坐标有一个越界值——某个框的宽高归一化后等于 1.5,超出了 0 到 1 的范围,导致损失函数数值异常。解决方法是写一个脚本遍历所有标签文件,检查坐标是否越界、目标框是否过小。这个检查步骤建议放在训练前强制执行。

显存溢出。12G 显存跑 batch 16 刚好,但如果你同时打开了 Mosaic 且开启了大尺度随机仿射,图片的中间结果会临时放大,显存开销会瞬间飙高。解决方法是适当降低 batch 到 8,或者把 imgsz 降到 576,代价是精度略有下降。

验证集 mAP 很高但实地测试很差。这种情况十有八九是过拟合到了训练场景。模型把训练集的背景特征也学进去了,换一个环境就崩。解决方向是扩充训练场景的多样性,尤其是对不同光线、不同家具布局的泛化。

夜间检测效果差。这是数据问题,不是模型问题。训练样本里如果没有足够多的夜间数据,模型在夜间的表现必然拉胯。解决方法是专门采集夜间数据补充训练,同时配合摄像头自带的红外补光或者低照度增强模式。

标注错误。这里说的不是单个框标错,而是标注规范不一致。前面提到过,如果某个标注员把“蹲下”标成 fallen,而另一个标注员把同样的场景标成 normal,模型就会崩溃。我的经验是制定非常明确的标注规范,并且在标注完成后抽样检查,尤其对边界情况重点复核。

5.2 部署阶段:推理速度与稳定性问题

部署阶段我遇到比较头疼的问题是视频流掉线和帧率波动。

RTSP 拉流最怕网络抖动。最开始我用 OpenCV 直接读流,网络一抖动,read 就会阻塞,程序卡死。后来我把取流逻辑放到独立线程里,用队列缓存最新的帧,推理主线程只取队列里最新的一帧,这样即使网络短暂卡顿,也不影响推理线程的运行。取流线程崩溃时自动重连,加上一个简单的重连退避机制,实测稳定性大大提升。

帧率方面,如果 TensorRT 推理已经很快了,瓶颈反而在 OpenCV 的图像编解码上。把图像从 BGR 转 RGB、resize 到 640 这些操作都会吃掉不少 CPU。我的经验是在推理线程内部直接使用 ultralytics 的 predict 接口,它会自动做预处理,比手动拆解更快。如果追求极限性能,可以创建一个固定的输入缓冲区,避免每帧都重新分配内存。

5.3 告警误报漏报怎么调

误报多的时候,优先调整的是连续帧阈值而不是置信度阈值。我习惯先保持模型置信度低阈值,把 recall 拉满,然后用帧数过滤来压误报。帧数拉得越高,告警越准,但确认时间越长,从检测到告警的延迟越大。对于跌倒场景,5 帧到 10 帧的确认延迟是完全可以接受的。

漏报多的时候,先检查是不是光照太暗。如果夜间漏报严重,优先处理图像质量,比如开启摄像头的宽动态模式。其次再考虑降低置信度阈值,以及扩充夜间数据重新训练。

5.4 排查问题速查表

现象可能原因解决方案
训练 loss 不下降标注越界、标签格式错脚本校验标签坐标范围
训练后期过拟合数据集太小、增强不足增加负样本、加增强、降低训练轮数
夜间漏检严重夜间训练样本不足补采夜间数据、提升图像亮度
误报集中在沙发/床负样本缺少躺卧场景补充沙发、床上休息负样本
告警延迟高连续帧阈值过大降低帧数阈值到 3 到 5 帧
推流卡顿延迟OpenCV 缓冲堆积独立取流线程+清空缓冲
模型在边缘设备上跑不动模型太大换 n 版本、INT8量化或知识蒸馏

6. 从监控到行为分析:我踩过的坑与后续规划

做这个项目最大的感受是,训练一个检测模型只是整个系统里最简单的一环,真正花时间的是数据治理、推理优化和告警闭环。模型精度再高,如果视频流三天两头卡顿、Webhook 推送偶尔丢失、现场部署环境网络不稳定,这套系统依然不可用。我把大概 60% 的时间花在了数据整理和工程部署上,只有 40% 在模型训练和改进上,这个比例我觉得是跌倒检测项目合理的投入方式。

还有一个校园停车场管理的思路可以延伸。这个系统的人形检测能力其实是通用的,只要你把数据集换一下,比如换成摔倒的电动车、违规停车等目标,训练一个模型后,整个推理平台、告警平台、管理后台都不用改。所以我把代码里模型推理部分和告警部分做了清晰的解耦,后续接入新的检测类别只需要替换模型权重和类别映射表,平台层零改动。

后面我计划把两件事做了。第一是把模型从检测端点扩展到行为分析,比如在跌倒检测的同时识别出“长时间停留”“徘徊”“进入禁区”等行为,这些行为特征在养老和公共安全场景都有需求,实现方式可以复用当前的目标检测框架,加上目标跟踪模块即可。第二是增加边缘错误恢复机制,比如盒子断电重启后自动拉流、自动回连告警服务、自动清理过期缓存,争取做到现场无人值守也能连续运行几个月不用重启。

如果你也在做类似的视觉检测项目,我的建议是别把太多时间花在追求 SOTA 模型上,把精力放到数据质量和工程稳定性上,这两个短板补上来之后,系统的可用度会有一个质的提升。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询