ARTICLE DETAIL

资讯详情

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

免费本地AI监控值守方案:YOLO目标检测与RTSP告警推送实战

免费本地AI监控值守方案:YOLO目标检测与RTSP告警推送实战 各位做弱电、安防、运维的朋友是不是都有过这种经历监控大屏铺满一整面墙几十上百个画面来回切眼睛盯久了酸涩发胀半夜施工现场怕进贼小区车库怕剐蹭仓库怕起火冒烟值班室不敢离人只能靠咖啡硬撑。好不容易闭眼眯一会儿第二天回放录像一查关键画面恰恰就在你打盹那几分钟。传统监控系统最大的问题不是“录得不够清楚”而是“看得不够及时”。摄像头本身没有判断能力它把画面录下来但有没有人闯入、有没有烟雾火焰、有没有车辆违停全靠人眼去轮巡、去发现。可人的注意力是有限的夜班更是生理上扛不住。最近我在工地上反复折腾搭了一套免费的本地AI监控值守方案把原来“人盯屏”的模式改成了“AI盯屏”。摄像头还是原来的摄像头录像机还是原来的录像机只是多加了一层本地AI推理服务就能做到24小时自动画面分析检测到异常事件立刻截图、录段视频、推送告警到手机。关键是全程本地运行不上传视频流不依赖云服务也不需要每年交订阅费。这篇文章把整套方案从原理到落地完整梳理了一遍包括硬件选型、软件环境、代码实现、告警推送以及排查思路。零基础的朋友可以按步骤从上往下搭已经在做监控项目的朋友可以直接跳到你最关心的章节。1. 本地AI监控到底在解决什么问题1.1 传统监控的痛点传统监控系统可以简单理解成三个环节采集、传输、存储。摄像头采集画面通过网线或光纤传到录像机录像机把视频流存储到硬盘里。这个流程是“被动记录型”的它不关心画面里发生了什么。这就导致几个很现实的问题实时性差画面有没有异常只能靠人盯着屏幕。夜间效率低夜班盯屏非常消耗精力人很容易疲劳、漏看。回放成本高出事后翻录像几个小时甚至十几个小时的海量录像人工查找效率极低。误报漏报并存如果靠运动检测移动侦测来提醒风吹树叶、灯光变化、飞虫爬过都会触发误报真实入侵反而被大量无效告警淹没。移动侦测为什么误报率高因为它只是对比“这一帧”和“上一帧”的像素差异只要画面里任意区域发生变化它就会报警。它不区分变化的是一个人、一辆车、一只猫还是树叶被风吹动。换句话说传统移动侦测根本没有“理解画面”的能力。1.2 本地AI能做什么本地AI方案引入了一台带GPU或高性能CPU的计算设备在本地运行目标检测、行为识别、烟火识别等算法模型。它把视频流实时解码成图像帧每一帧都交给AI模型做推理模型会输出画面里有哪些目标、目标在什么位置、置信度是多少。这样系统就从“会录像”进化到了“会判断”能力传统方案本地AI方案画面记录✅ 支持✅ 支持人形检测❌ 无靠移动侦测✅ 识别“人”而不是“画面变化”车辆识别❌ 无✅ 识别车辆、车牌区域烟火检测❌ 无✅ 识别火焰、烟雾特征区域入侵仅全画面✅ 可划定重点区域告警推送部分支持误报率高✅ 按置信度过滤推送截图和短视频视频数据处理本地或云端✅ 全程本地处理举一个最典型的场景夜间工地大门门口一只野猫路过触发传统移动侦测系统推送了一条告警20分钟后有人翻越围栏系统反而没有识别出来。换成AI方案后野猫经过时模型识别出目标类别置信度低不会触发告警人翻越围栏时模型识别出“人”这个类别同时判断该人位于“禁区”区域内于是立刻告警。1.3 为什么选择本地部署而不是云服务市面上也有不少云AI监控产品但实际落地时会遇到几个绕不开的问题视频流上云隐私风险高摄像头画面涉及工地、仓库、小区、园区视频数据传到云端一旦平台出现安全问题画面内容就可能泄露。订阅费用持续性强很多云AI服务按路数、按天、按月收费路数一多每年费用不少。依赖公网稳定性厂区、工地网络波动时云服务直接断开本地录像正常但AI分析停摆。延迟变量多视频上传、云端推理、结果回传链路长了告警延迟可能从秒级变成数十秒级别。本地AI部署则完全不同。视频流从摄像头到本地推理设备全程走内网毫秒级延迟隐私数据不出硬件设备。断网了摄像头本地录像继续AI分析继续告警可以先存数据库等网络恢复后再推送到手机。1.4 这个方案适合谁用弱电工程商、安防集成商想给自己的监控项目增加智能化卖点。物业、园区、仓库、工地值班人员想减少夜间盯屏压力。个人开发者、极客玩家家里装了摄像头想自己搞一套智能告警。2. 整体方案与技术选型2.1 系统架构整套系统可以分为四个层次视频源层现有的网络摄像头IPC或录像机NVR通过RTSP协议输出视频流。接入层本地AI推理服务器负责拉取RTSP视频流解码成连续帧。这里可以用流媒体网关统一处理也可以用OpenCV直接解码。分析层AI模型对每一帧做推理检测人、车、烟火等目标并结合预先设置的布防区域、布防时段做判断。告警层触发目标事件后系统自动截图、录制短视频、保存事件记录并通过Server酱、钉钉机器人、企业微信机器人等渠道推送到手机微信或App。如果是单路摄像头的个人项目可以简化一台带GPU的电脑Python脚本拉流 → 推理 → 告警三个环节串起来就够了。如果是多路摄像头的工程级项目建议引入流媒体服务器做统一接入。摄像头 RTSP 流先推到本地流媒体服务AI分析程序统一从流媒体服务取流避免多路摄像头因码流过大多路并发导致网络拥堵。2.2 核心组件对比组件推荐方案说明流媒体网关MediaMTX / ZLMediaKitMediaMTX较轻量适合个人和小型项目ZLMediaKit功能更强适合工程集成视频解码OpenCV / FFmpegOpenCV适合快速实现FFmpeg适合自定义抽帧策略AI目标检测YOLOv8 / YOLOv9 / RT-DETR社区生态好模型文件开源推理速度快推理加速ONNX Runtime GPU版 / TensorRT / OpenVINO根据你的显卡类型和框架习惯选择告警推送Server酱、钉钉/企业微信机器人、邮件、MQTT需要根据现场网络环境选存储本地磁盘保存截图和短视频最小成本方案量大可接MinIO对象存储YOLO系列是当前工程里用得最广的目标检测模型。YOLOv8以及后来的YOLOv9、YOLO11版本训练生态完善官方提供了预训练权重可以直接检测人、车、猫、狗等80种常见目标。对于弱电监控场景我们最常用的是其中的“person”人类别以及“car”“truck”“bus”车类别。2.3 技术选型的几个原则结合我实际搭建过程中的经验选型时有几个原则值得参考第一能用本地现成能力就不要造轮子。目标检测模型直接下载YOLO的预训练权重即可不用自己标注数据集训练除非你有特殊的检测目标。第二推理设备选择要平衡成本和性能。只带4路以内1080P摄像头一张GTX 1650/RTX 3060级别的显卡就可以带动十几路以上的场景再考虑性能更强的显卡或边缘计算盒子。第三告警链路要尽量简单可靠。告警推送通道优先选择微信/钉钉机器人这类接口清晰的Webhook方案不要自己搭App太重了。第四整个系统要支持降级运行。AI推断服务挂了摄像头原本的录像功能不能受影响系统设计时各模块要解耦。3. 环境准备与硬件建议3.1 硬件清单本地AI监控方案的核心硬件是AI推理设备。不同规模的监控点数对硬件要求差别很大。下面给出几个参考档位应用规模摄像头路数推荐硬件备注入门体验1-2路普通PC GTX 1650/RTX 3050显卡内存16GWindows/Linux均可中小场景4-8路二手工作站 RTX 3060/4060显卡建议用Linux系统服务更稳定工程级8-16路服务器 RTX 4070及以上显卡可加多卡注意散热和电源功率极低功耗1-2路树莓派5 / Jetson Orin Nano适合室外弱电机柜功耗低注意实际选择时不能只看摄像头路数还要看摄像头的分辨率、帧率和码流大小。4路4K摄像头和4路720P摄像头的解码压力差距很大。3.2 软件环境软件环境以本文示例为准版本号不写死因为不同时期安装得到的最新版本会有差异。你需要按实际情况调整。推荐使用Ubuntu 22.04 LTS作为AI推理服务器的系统原因如下生态成熟NVIDIA驱动、CUDA、Python环境安装资料齐全。没有Windows自动更新、杀毒软件等干扰因素。适合7x24小时长时间运行。Python版本建议使用3.10或3.11。推理框架使用PyTorchGPU版检测模型使用YOLOv8系列。在开始安装前先确认你的显卡驱动是否正常。Ubuntu下可以用nvidia-smi命令查看nvidia-smi如果输出类似下面的信息说明驱动正常----------------------------------------------------------------------------- | NVIDIA-SMI 535.146.02 Driver Version: 535.146.02 CUDA Version: 12.2 | |--------------------------------------------------------------------------- | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | || | 0 NVIDIA GeForce RTX 3060 Off | 00000000:01:00.0 On | N/A | ---------------------------------------------------------------------------如果驱动没有安装先安装NVIDIA驱动和CUDA环境再继续后面的步骤。3.3 软件安装步骤创建一个工作目录建议结构如下project/ ├── config.yaml # 系统配置 ├── main.py # 主程序 ├── detector.py # 目标检测封装 ├── alert.py # 告警推送模块 ├── recorder.py # 截图录像模块 ├── requirements.txt # 依赖清单 └── models/ # AI模型文件存放目录安装Python依赖。创建一个requirements.txtopencv-python ultralytics numpy pyyaml requests然后执行安装pip install -r requirements.txt如果你的电脑有NVIDIA显卡建议先安装对应版本的CUDA和cuDNN再安装PyTorch GPU版。PyTorch安装命令可以在官网生成这里给一个参考示例具体以官网为准pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121安装完成后快速验证GPU是否可以被PyTorch调用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果输出True和你的显卡型号说明GPU环境已就绪。3.4 摄像头RTSP地址准备在写代码之前先确认摄像头的RTSP取流地址。不同品牌的摄像头RTSP地址格式不同但绝大多数走标准RTSP协议。常见格式是rtsp://用户名:密码IP地址:554/Streaming/Channels/101海康威视摄像头常见的取流地址格式rtsp://admin:password192.168.1.64:554/Streaming/Channels/101其中101代表主码流第一通道102代表子码流第一通道。AI分析一般建议用子码流分辨率稍低但帧率稳定解码压力小保存告警视频时再临时切换主码流保证画面清晰。大华摄像头常见的取流地址格式rtsp://admin:password192.168.1.64:554/cam/realmonitor?channel1subtype0如果不知道摄像头的RTSP地址有两个办法查看摄像头品牌对应的SDK文档或官方说明。使用VLC播放器测试地址是否可用。在VLC中打开网络流输入RTSP地址如果画面正常播放说明地址有效。建议先用VLC确认地址能通再开始写代码。这样可以避免代码写完了、发现半天排查的是RTSP地址错误。4. 核心原理拆解4.1 视频流如何被“喂”给AI摄像头输出的RTSP流是连续的H.264或H.265编码视频。AI模型不能直接处理编码后的视频流必须先解码还原成单张图像帧然后逐帧分析。这个链路是RTSP视频流 → FFmpeg/OpenCV解码 → 图像帧 → AI模型推理 → 检测结果OpenCV的VideoCapture类可以直接拉取RTSP流底层调用FFmpeg做解码。每次调用cap.read()会返回一帧BGR格式的图像。这种方式的优点是实现简单缺点是一帧一帧串行处理如果AI推理速度跟不上视频帧率会导致处理积压。实际工程里有两种解决思路抽帧分析不分析每一帧而是每隔N帧分析一次比如每秒分析2-5帧。监控场景不需要每帧都分析目标在画面里停留的时间通常以秒计。多线程/多进程拉流解码线程和AI推理线程分离解码线程不断读帧放入队列推理线程从队列取帧分析。4.2 目标检测模型的工作方式目标检测模型的任务是回答两个问题画面里有什么它们在哪个位置YOLO系列模型采用单阶段检测思路训练完成后会输出每个目标的类别、置信度、边界框坐标。边界框就是目标在画面中的矩形区域通常用四个值表示中心点x坐标、中心点y坐标、宽度、高度。我们使用Ultralytics官方库加载模型时代码可以很简单from ultralytics import YOLO model YOLO(yolov8n.pt) # 加载YOLOv8n模型n代表nano尺寸最小、速度最快 results model(frame) # 对一帧图像做推理推理结果里每个检测目标包含以下关键属性cls类别ID。YOLOv8 COCO预训练模型里0代表人2代表车7代表卡车。conf置信度取值范围0到1越大代表模型越确信。xyxy或xywh目标的边界框坐标。如果画面里出现一个人模型会输出类似这样的结果Boxes(Xyxy): [ x1, y1, x2, y2, conf, cls] [ 142.3, 89.6, 380.1, 512.0, 0.8873, 0.0]这段结果的含义是在图像坐标(142, 89)到(380, 512)这个区域内有一个“人”模型置信度88.73%。4.3 布防区域与布防时段检测到目标还不够我们还需要做两道过滤第一道目标类别过滤。画面里可能有行人、车辆、动物、树叶。监控场景下你关心的可能只有“人”和“车辆”其他目标全部忽略。代码实现上直接检查检测结果的类别ID是否在允许列表里ALLOW_CLASSES [0, 2, 7] # 人、小汽车、卡车第二道布防区域过滤。画面不是整幅都需要监控。比如工地大门口你只关心门口东侧这块区域没有其他地方。这时可以在画面上人工划定一个矩形区域只有目标中心点落在矩形区域内才触发告警。假设摄像头是1920x1080分辨率想监控画面中央偏左的矩形区域可以用归一化坐标来配置zone: x1: 0.2 # 左上角x坐标占画面宽度的20% y1: 0.3 # 左上角y坐标占画面高度的30% x2: 0.8 # 右下角x坐标占画面宽度的80% y2: 0.9 # 右下角y坐标占画面高度的90%代码里判断目标中心点是否在区域内def is_in_zone(cx, cy, zone): x1 zone[x1] * frame_width y1 zone[y1] * frame_height x2 zone[x2] * frame_width y2 zone[y2] * frame_height return x1 cx x2 and y1 cy y2第三道布防时段过滤。白天工作人员正常进出不需要告警夜间无人时段才进入布防状态。布防时段可以直接在配置文件里写schedule: enabled: true start: 22:00 # 晚上10点开始布防 end: 06:00 # 早上6点解除布防实际操作里很多值班场景是“全天布防”加上“白名单时间段静默”。比如白天不告警只记录不推送夜间告警并推送。这样既保留了完整的证据链又不会让告警轰炸值班手机。4.4 告警去重如果AI每秒分析2帧画面里有个人站了10秒就会触发20次检测。如果每次都推送告警手机微信会被刷屏。解决办法是事件锁存机制一旦检测到目标并推送告警记录当前告警时间和目标位置在接下来的冷却时间内比如60秒如果检测到的还是同一个区域的目标则不重复推送只更新事件状态。这个逻辑用一段伪代码描述last_alert_time {} def should_alert(camera_id, zone_id, target_type, now): key f{camera_id}_{zone_id}_{target_type} if key in last_alert_time: diff (now - last_alert_time[key]).seconds if diff COOL_DOWN_SECONDS: return False last_alert_time[key] now return True多个目标进入画面会产生多个告警事件。但在值班人员手机上同一时间段、同一区域的同类目标合并成一条提示“检测到1个/2个人进入布防区域”会更友好。除了时间冷却还可以加一个区域冷却人一直在区域内走动虽然位置变化但始终在同一个布防区只要没离开该区域就不重复报警。离开区域或者目标消失后再恢复该区域的报警能力。4.5 告警图片和短视频怎么保存事件触发后自动保存两张图片和一段视频检测到目标时保存一张原始画面截图。在截图基础上绘制边界框和类别文字保存一张标注图。从触发时间点往前回溯几秒、往后持续几秒保存一段短视频作为证据。短视频录制有一个细节AI检测到目标时已经是当前帧往前回溯需要用到环形缓冲区技术。程序一直把最近10秒的视频帧缓存在内存队列里一旦触发告警就把队列里的历史帧取出来加上新来的几秒帧一起编码成MP4文件。这样保存下来的视频能完整还原“目标出现前-目标出现-目标移动”的全过程。Python里可以用OpenCV的VideoWriter把帧序列写成本地文件。5. 完整实战搭建本地AI自动值守系统下面进入完整实战部分。我们以一台Ubuntu 22.04主机、单路RTSP摄像头为例实现以下功能拉取RTSP视频流。使用YOLOv8模型检测画面中的人。人出现在布防区域内自动截图、录像。触发告警后通过Server酱推送到手机微信。整个项目代码会分成几个文件方便以后扩展多路摄像头。5.1 项目配置创建config.yaml配置文件camera: rtsp_url: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 name: 大门入口 model: weights: yolov8n.pt conf_threshold: 0.5 target_classes: [0] # 0代表person zone: x1: 0.1 y1: 0.1 x2: 0.9 y2: 0.9 alert: cooldown_seconds: 30 serverchan_sendkey: YOUR_SENDKEY # Server酱的SendKey在sct.ftqq.com获取 screenshot_dir: ./data/screenshots video_dir: ./data/videos schedule: enabled: true start: 00:00 end: 23:59配置项说明conf_threshold置信度阈值低于这个值的目标被忽略。设为0.5时有较好的平衡如果你发现误报比较多可以提到0.6或0.7。target_classes要检测的类别列表。COCO数据集中0代表人2代表小汽车7代表卡车。zone布防区域这里默认是全画面你可以改成自己关心的局部区域。cooldown_seconds同一目标重复告警的冷却时间。serverchan_sendkeyServer酱的SendKey。Server酱是一个微信推送服务扫码登录后生成SendKey通过HTTP请求就能把消息推送到微信。这个对于个人项目非常方便。5.2 封装目标检测模块创建detector.py文件# -*- coding: utf-8 -*- 目标检测模块封装YOLOv8模型加载和推理 from ultralytics import YOLO class ObjectDetector: def __init__(self, weights_path, conf_threshold0.5): self.model YOLO(weights_path) self.conf_threshold conf_threshold def detect(self, frame): 对输入帧做目标检测 :param frame: OpenCV读取的BGR图像 :return: 检测结果列表每个元素为 (cls_id, conf, x1, y1, x2, y2) results self.model(frame, verboseFalse) detections [] for result in results: boxes result.boxes if boxes is None: continue for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) if conf self.conf_threshold: continue x1, y1, x2, y2 box.xyxy[0].tolist() detections.append((cls_id, conf, x1, y1, x2, y2)) return detections这个类只负责一件事给一帧图像返回检测到的所有目标。主程序不需要关心模型细节方便以后替换成其他模型。5.3 封装告警模块创建alert.py文件# -*- coding: utf-8 -*- 告警模块支持Server酱微信推送 import requests import time import os class AlertManager: def __init__(self, sendkey, cooldown_seconds30): self.sendkey sendkey self.cooldown_seconds cooldown_seconds self.last_alert_time {} def should_alert(self, camera_id, zone_id, target_type): 判断当前目标是否应该触发告警时间冷却去重 key f{camera_id}_{zone_id}_{target_type} now time.time() if key in self.last_alert_time: diff now - self.last_alert_time[key] if diff self.cooldown_seconds: return False self.last_alert_time[key] now return True def send_serverchan(self, title, content): 通过Server酱推送消息到微信 url fhttps://sctapi.ftqq.com/{self.sendkey}.send data { title: title, desp: content } try: resp requests.post(url, datadata, timeout10) result resp.json() if result.get(code) 0: print(f[告警] 推送成功: {title}) else: print(f[告警] 推送失败: {result.get(message)}) except Exception as e: print(f[告警] 推送异常: {e})5.4 保存图片和录像创建recorder.py文件# -*- coding: utf-8 -*- 录像模块保存告警截图和短视频 import cv2 import os import time from collections import deque class FrameBuffer: 环形缓冲器保存最近N秒的视频帧用于告警前回溯 def __init__(self, max_frames100): self.buffer deque(maxlenmax_frames) def add(self, frame): self.buffer.append(frame.copy()) def get_frames(self): return list(self.buffer) class EventRecorder: def __init__(self, screenshot_dir./data/screenshots, video_dir./data/videos): self.screenshot_dir screenshot_dir self.video_dir video_dir os.makedirs(screenshot_dir, exist_okTrue) os.makedirs(video_dir, exist_okTrue) def save_screenshot(self, frame, event_namealert): 保存一张告警截图 filename f{event_name}_{int(time.time())}.jpg filepath os.path.join(self.screenshot_dir, filename) cv2.imwrite(filepath, frame) return filepath def save_video(self, frames, event_namealert, fps20): 将帧列表保存为MP4文件 if not frames: return None filename f{event_name}_{int(time.time())}.mp4 filepath os.path.join(self.video_dir, filename) height, width frames[0].shape[:2] writer cv2.VideoWriter( filepath, cv2.VideoWriter_fourcc(*mp4v), fps, (width, height) ) for frame in frames: writer.write(frame) writer.release() return filepath5.5 主程序创建main.py文件# -*- coding: utf-8 -*- 本地AI监控值守服务主程序 import cv2 import yaml import time from detector import ObjectDetector from alert import AlertManager from recorder import EventRecorder, FrameBuffer from datetime import datetime def load_config(pathconfig.yaml): with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def is_in_schedule(cfg, nowNone): 判断当前时间是否在布防时段内 schedule cfg.get(schedule, {}).get(enabled, False) if not schedule: return True start cfg[schedule][start] end cfg[schedule][end] now now or datetime.now().strftime(%H:%M) return start now end def is_in_zone(cx, cy, frame_w, frame_h, cfg): 判断目标中心点是否在布防区域内 zone cfg.get(zone, {}) if not zone: return True x1 zone.get(x1, 0) * frame_w y1 zone.get(y1, 0) * frame_h x2 zone.get(x2, 1) * frame_w y2 zone.get(y2, 1) * frame_h return x1 cx x2 and y1 cy y2 def draw_detections(frame, detections, class_names): 在画面上绘制检测框 for cls_id, conf, x1, y1, x2, y2 in detections: label class_names.get(cls_id, str(cls_id)) cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) text f{label} {conf:.2f} cv2.putText(frame, text, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) return frame def main(): cfg load_config() # 初始化各模块 detector ObjectDetector( weights_pathcfg[model][weights], conf_thresholdcfg[model][conf_threshold] ) alert AlertManager( sendkeycfg[alert][serverchan_sendkey], cooldown_secondscfg[alert][cooldown_seconds] ) recorder EventRecorder( screenshot_dircfg[alert][screenshot_dir], video_dircfg[alert][video_dir] ) class_names { 0: person, 1: bicycle, 2: car, 3: motorcycle, 5: bus, 7: truck } target_classes set(cfg[model][target_classes]) # 打开RTSP视频流 cap cv2.VideoCapture(cfg[camera][rtsp_url]) if not cap.isOpened(): print(f无法打开摄像头: {cfg[camera][rtsp_url]}) return # 设置解码缓存减少延迟 cap.set(cv2.CAP_PROP_BUFFERSIZE, 3) # 事件触发帧缓冲 frame_buffer FrameBuffer(max_frames50) event_frames [] recording False record_frames_count 0 event_id None print(f开始监控: {cfg[camera][name]}) print(fRTSP地址: {cfg[camera][rtsp_url]}) while True: ret, frame cap.read() if not ret: print(读取视频帧失败尝试重连...) cap.release() time.sleep(3) cap cv2.VideoCapture(cfg[camera][rtsp_url]) continue # 始终保存到环形缓冲区 frame_buffer.add(frame) # 判断是否在布防时段内 if not is_in_schedule(cfg): time.sleep(1) continue # 目标检测 detections detector.detect(frame) frame_h, frame_w frame.shape[:2] triggered_detections [] for cls_id, conf, x1, y1, x2, y2 in detections: if cls_id not in target_classes: continue cx (x1 x2) / 2 cy (y1 y2) / 2 if not is_in_zone(cx, cy, frame_w, frame_h, cfg): continue triggered_detections.append((cls_id, conf, x1, y1, x2, y2)) if triggered_detections: camera_id cfg[camera][name] target_type ,.join([class_names.get(c[0], str(c[0])) for c in triggered_detections]) if alert.should_alert(camera_id, default, target_type): print(f[事件] 检测到目标: {target_type}, 数量: {len(triggered_detections)}) # 保存截图 shot_path recorder.save_screenshot(frame, event_namealert) print(f[事件] 截图已保存: {shot_path}) # 准备录像环形缓冲区里的历史帧 当前帧 event_frames frame_buffer.get_frames() # 绘制检测框并保存标注图 annotated draw_detections(frame.copy(), triggered_detections, class_names) annot_path recorder.save_screenshot(annotated, event_nameannotated) print(f[事件] 标注图已保存: {annot_path}) # 推送告警 alert.send_serverchan( titlef{cfg[camera][name]} 检测到{target_type}, contentf时间: {datetime.now().strftime(%Y-%m-%d %H:%M:%S)}\n f检测目标: {target_type}\n f数量: {len(triggered_detections)}\n f置信度: {max([d[1] for d in triggered_detections]):.2f}\n f截图: 见服务器目录 {shot_path} ) # 进入录像状态持续采集35帧约2秒 recording True record_frames_count 0 if recording: record_frames_count 1 event_frames.append(frame) if record_frames_count 35: video_path recorder.save_video(event_frames, event_namealert) print(f[事件] 录像已保存: {video_path}) recording False event_frames [] # 显示实时画面可选需要桌面环境 # cv2.imshow(Monitor, frame) # if cv2.waitKey(1) 0xFF ord(q): # break time.sleep(0.1) cap.release() cv2.destroyAllWindows() if __name__ __main__: main()5.6 运行与验证在项目目录下执行python main.py正常情况下会看到类似输出开始监控: 大门入口 RTSP地址: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 [事件] 检测到目标: person, 数量: 1 [事件] 截图已保存: ./data/screenshots/alert_1738123456.jpg [事件] 标注图已保存: ./data/screenshots/annotated_1738123456.jpg [事件] 录像已保存: ./data/videos/alert_1738123456.mp4 [告警] 推送成功: 大门入口 检测到person验证步骤让一个人走进摄像头布防区域观察程序是否输出事件日志。检查data/screenshots目录下是否生成了截图文件。检查data/videos目录下是否生成了MP4文件。查看手机微信是否收到了Server酱推送的消息。如果在室内没有真实摄像头可以先用电脑摄像头或手机摄像头做测试。也可以下载一个测试视频文件把rtsp_url替换成本地视频文件路径OpenCV同样支持读取视频文件。这样即使没有摄像头也能先跑通整套检测逻辑。5.7 把服务部署成后台常驻进程上面的代码直接用python main.py运行会占用一个终端窗口。一旦终端关闭程序就停了。实际使用中需要让它作为后台服务7x24小时运行。推荐使用systemd服务来管理。创建/etc/systemd/system/ai-monitor.service文件[Unit] DescriptionLocal AI Monitor Service Afternetwork.target [Service] Typesimple WorkingDirectory/home/your_user/project ExecStart/usr/bin/python3 /home/your_user/project/main.py Restartalways RestartSec5 EnvironmentPYTHONUNBUFFERED1 [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable ai-monitor sudo systemctl start ai-monitor查看服务状态和日志sudo systemctl status ai-monitor journalctl -u ai-monitor -f用systemd管理的好处是程序崩溃后会自动重启开机自动启动不用人工干预。6. 进阶优化多路摄像头与烟火检测6.1 多路摄像头并发处理前面单路摄像头的代码可以直接扩展为多路。最简单的改造方法是为每路摄像头创建一个独立线程每个线程里跑一套检测逻辑import threading def monitor_camera(cfg): # 每路摄像头的检测主循环 pass threads [] for cam_cfg in all_cameras: t threading.Thread(targetmonitor_camera, args(cam_cfg,)) t.start() threads.append(t) for t in threads: t.join()但需要注意多路摄像头同时推理时GPU显存和算力会被分摊。在实际项目中更适合的方案是使用消息队列或任务队列框架统一管理多路视频流。6.2 烟火识别除了目标检测火灾预警是监控场景的高频需求。实现烟火识别有两条路使用现成的烟火检测模型很多开源模型仓库里都有基于YOLO训练的烟火检测权重。使用传统图像处理做辅助比如颜色分析、帧差法检测动态区域、频率分析等。传统方法成本低但准确率有限。简单示例是用HSV颜色空间过滤火焰颜色import cv2 import numpy as np def detect_fire_color(frame): hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 火焰颜色范围 lower np.array([0, 100, 100]) upper np.array([40, 255, 255]) mask cv2.inRange(hsv, lower, upper) fire_ratio cv2.countNonZero(mask) / (frame.shape[0] * frame.shape[1]) return fire_ratio 0.005 # 阈值自行调整但在复杂环境下这种方法的误报率较高。工程化方案建议直接使用训练好的烟火检测模型把它和人员检测模型并列执行检测到烟火时走独立的告警通道告警级别高于普通人员入侵。6.3 ONNX Runtime加速推理Ultralytics库本身会自适应使用GPU但在生产环境中将YOLO模型导出为ONNX格式再使用ONNX Runtime推理往往可以获得更低的推理延迟和更可控的资源占用。导出ONNX模型from ultralytics import YOLO model YOLO(yolov8n.pt) model.export(formatonnx, dynamicTrue)然后在代码里使用YOLO(yolov8n.onnx)加载。ONNX Runtime如果配置了CUDA Execution Provider推理速度会进一步提高。7. 常见问题与排查思路根据实际搭建过程中的经验整理几个高频问题问题现象常见原因解决思路程序启动后无法打开摄像头RTSP地址错误、密码包含特殊字符未转义、网络不通先用VLC测试RTSP地址检查摄像机IP是否可达密码中有、:等特殊字符时做URL编码画面卡顿或延迟高摄像头码流太大、解码性能不够、缓冲区排队改用子码流降低摄像头帧率调大CAP_PROP_BUFFERSIZE缩小画面尺寸GPU利用率很低CPU占用高OpenCV解码占CPU、模型未使用GPU推理检查PyTorch的CUDA是否可用确认模型权重加载到了GPU上用nvidia-smi观察进程频繁误报置信度阈值太低、检测目标类别不精确、布防区域太宽提高conf_threshold限定target_classes缩小布防区域开启布防时段漏报严重置信度阈值太高、摄像头角度不理想、目标太小降低阈值到0.35-0.4调整摄像头角度增加模型输入分辨率告警重复推送未正确配置冷却时间检查cooldown_seconds配置检查should_alert逻辑里key的设计推理速度慢模型太大、显卡性能不足、输入分辨率太高换yolov8n小模型降低推理画幅用TensorRT加速长时间运行后程序卡死内存泄漏、RTSP流断开重连异常给主循环加try-except定期重启服务查看systemd日志服务器重启后服务没有自动拉起systemd服务未enabled执行sudo systemctl enable ai-monitor7.1 RTSP断流重连的健壮性处理RTSP流在弱网、摄像头重启、路由器抖动时可能断开。代码里的重连逻辑是基础版本实际生产建议加上指数退避重连reconnect_interval 5 while True: cap cv2.VideoCapture(rtsp_url) if cap.isOpened(): reconnect_interval 5 # 成功连接后重置间隔 break print(f连接失败{reconnect_interval}秒后重试...) time.sleep(reconnect_interval) reconnect_interval min(reconnect_interval * 2, 60)这样可以避免在摄像头离线期间程序疯狂尝试连接消耗CPU和带宽。7.2 夜间低照度画面识别率低夜间环境光不足时摄像头画面噪点多、对比度低YOLO模型识别率会明显下降。几个常用的改善手段确认摄像头开启了红外夜视模式画面尽量清晰。在AI推理前对图像做预处理直方图均衡化、降噪。选择带补光功能的摄像头或者在重点区域增加照明。使用专门针对低照度场景微调的检测模型。8. 最佳实践与工程建议8.1 安全与合规边界本地AI监控虽然技术门槛不高但使用时要特别注意合规问题。监控区域如果是公共区域要确保有合法的监控布设授权并按规定做好提示标识。涉及员工、访客等个人信息采集时要遵循相关法律法规要求。告警截图和视频片段属于敏感数据本地存储要注意访问权限控制。不要把包含人脸、车牌等信息的视频流直接推到公网或第三方服务除非确认链路合规且加密。8.2 配置管理配置集中放在config.yaml里是工程化第一步。更进一步可以按摄像头建独立配置文件或者使用配置中心统一管理多台推理服务器的配置。摄像头密码不要明文存在仓库里。建议从环境变量或密钥管理服务读取import os rtsp_url os.getenv(CAMERA_RTSP_URL, rtsp://...)这样代码入库时不会泄露密码。8.3 告警升级机制单层告警在白天可行但夜间值班时可能没人盯手机。建议设计告警升级机制检测到事件后第一优先级推送到值班人员微信。2分钟内未确认未点击“已处理”升级推送到项目经理或保安队长。重大事件如检测到烟火直接电话语音告警。这个机制可以依托于企业微信机器人、钉钉机器人或第三方告警平台实现。初期可以先用最简单的方式Server酱推送到一个群多人同时接收。8.4 数据存储与清理策略截图和录像文件会持续增长需要设计定期清理策略。简单做法是用定时任务清掉30天前的文件find /data/screenshots -type f -name *.jpg -mtime 30 -delete find /data/videos -type f -name *.mp4 -mtime 30 -delete生产环境建议把告警截图和录像接入对象存储并按摄像头和时间建立目录结构data/ ├── camera_001/ │ ├── 2025-01-15/ │ │ ├── alert_10_30_00.jpg │ │ └── alert_10_30_00.mp4 ├── camera_002/8.5 标准化的告警事件记录除了截图和录像建议把每次告警的元数据写入SQLite或MySQL方便事后查询和统计。事件表可以设计成CREATE TABLE alert_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, camera_id TEXT NOT NULL, event_type TEXT NOT NULL, confidence REAL, zone TEXT, screenshot_path TEXT, video_path TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );有了事件记录后续可以按天、周、月统计数据比如“本周一共发生了多少次人员入侵告警”“主要集中几点”等。这对优化布防策略很有价值。8.6 定期评估识别效果AI模型不是部署完就一劳永逸的。随着季节变化、光照变化、摄像头角度微调识别效果会漂移。建议每月抽看一批告警截图统计误报率。定期统计漏报事件分析原因。如果需要更高准确率收集现场数据做模型微调训练。9. 总结与下一步学习方向整套方案从硬件选型、环境搭建到代码实现核心思路是摄像头负责采集本地AI负责理解画面告警服务负责通知人。摄像头、录像机不用换只是在现有监控系统旁边加了一台运行推理服务的计算设备就能把“被动录像”升级成“主动值守”。目前这套方案已经可以实现24小时不间断画面分析人形、车辆检测按布防区域、布防时段精准告警告警截图、短视频自动留存手机微信实时接收告警推送。如果你在搭建过程中踩了坑先检查三个最容易出问题的地方RTSP地址是否可用、GPU环境是否配置正确、布防区域和置信度阈值是否合理。这三点解决了整套系统就稳定了一大半。下一步想深入的话有几个方向可以参考多路摄像头的智能调度与负载均衡、基于现场数据微调专属检测模型、把AI检测结果接入到现有弱电管理平台中或者增加人脸识别、车牌识别等更细分的能力。
返回列表