自托管、轻量、纯本地运行的 AI 监控 NVR 系统
Project description
solo-surveillance
自托管、轻量、纯本地运行的 AI 监控 NVR 系统。
通过 RTSP 或 ONVIF 连接 IP 摄像头,利用运动检测触发 AI 推理,记录事件——所有操作均在本地运行,零云依赖。
工作原理
solo-surveillance 持续监控摄像头,自动判断何时发生值得记录的事件。核心采用三阶段流水线:
- 流接入 — 通过 RTSP/ONVIF 连接每路摄像头,解码视频帧
- 运动门控 — 帧差检测过滤静态画面;只有真正出现运动的帧才会进入 AI
- AI 检测 — YOLOv8 人体检测和/或 LLM 场景理解在运动触发的帧上运行
MotionGate 是系统的性能基石。通过在 AI 推理前过滤掉静态帧,实际部署中通常可减少 90% 以上的 AI 调用次数。
每路摄像头运行在各自独立的 threading.Thread 中,拥有完全独立的 RTSPReader、MotionGate 和 AIPipeline 实例——摄像头工作线程间无共享可变状态。
功能特性
| 功能 | 描述 | 配置键 |
|---|---|---|
| 多路相机 | 每路独立线程,配置与运行时相互独立 | cameras[] |
| 双协议接入 | rtsp:// 直连或 onvif:// 自动发现 RTSP 地址 |
stream_url |
| 运动门控 | 帧差检测,预过滤静态帧 | motion.* |
| YOLOv8 人体检测 | 内置 YOLO,首次运行自动下载模型 | detectors.person |
| LLM 视觉场景 | 使用 Anthropic/OpenAI API 进行复杂场景理解 | detectors.llm_vision |
| 区域裁剪 | 仅在归一化 ROI 内检测,保留全分辨率录制 | region |
| AI 批量推理 | 多帧采样,按最大置信度合并结果 | ai.frames |
| 事件录制 | 按事件类型生成截图 (JPEG) 和视频片段 (MP4) | recordings.* |
| 时间线索引 | 基于 CSV 的事件索引,包含起止时间和文件路径 | 自动管理 |
| Web UI | 内置 HTTP 服务器,支持时间线导航、过滤与回放 | --http 参数 |
| Home Assistant | 检测到重大事件时推送 REST API 事件 | hass.* |
| Hook 脚本 | 事件触发时执行外部命令 | hooks.* |
| 自动重连 | RTSP 断流自动恢复,适合 7x24 运行 | 内置于 RTSPReader |
| 纯本地运行 | 所有推理、录制和回放均在设备端完成 | — |
快速开始
1. 安装
# 方式 A(推荐)——自动隔离环境,无需手动安装
uvx solo-surveillance
# 方式 B——全局安装
pip install solo-surveillance
2. 配置相机
curl -O https://raw.githubusercontent.com/tiancheng91/solo-surveillance/main/config.example.yaml
mv config.example.yaml config.yaml
编辑 config.yaml,填入相机地址:
cameras:
- id: door
enabled: true
stream_url: "rtsp://user:password@192.168.1.100:554/stream1"
也支持 ONVIF 自动发现:
- id: front_door
stream_url: "onvif://admin:password@192.168.1.100:80?profile=0"
完整配置参考
config.example.yaml(含 LLM 视觉、HA 集成、Hook 脚本等全部选项)。
3. 启动
solo-surveillance
首次启动会自动下载 YOLOv8 模型。看到如下日志即正常运行:
INFO [cam-door] 线程启动: door
INFO [cam-door] 已连接 RTSP
4. 打开 Web UI(可选)
solo-surveillance --http 0.0.0.0:8080
浏览器访问 http://<设备IP>:8080:
- 按相机、日期、时间段筛选事件
- 缩略图懒加载,点击放大查看截图
- 支持播放 MP4 视频片段
- 右侧时间轴:黄色时段表示有事件,拖拽可快速导航
- 排序切换:默认倒序(最新在前),点击翻转
Web UI 仅用于本地网络回放,不会将视频上传到云端。
命令行参考
solo-surveillance # 启动(默认读取当前目录 config.yaml)
solo-surveillance -v # 调试模式——查看 motion 触发、AI 冷却等详细日志
solo-surveillance -c /path/to/config.yaml # 指定配置路径
solo-surveillance --http :8080 # 启动 Web UI(默认端口 8080)
solo-surveillance --http 0.0.0.0:9090 # 指定监听地址和端口
Home Assistant 集成
支持将检测到的事件实时推送到 Home Assistant 事件总线,用于自动化联动(如灯光、报警、通知)。使用 Python 标准库实现,零额外依赖。
hass:
enabled: true
url: "http://homeassistant:8123"
token: "${HASS_TOKEN}"
配置后,每个事件(camera.motion、camera.person、camera.feeding 等)会自动 POST 到 HA 的 /api/events/{event_type}。
详细配置说明见 docs/homeassistant.md。
以下章节面向进阶用户,详细介绍配置选项与系统设计。
配置详解
YAML 格式,defaults 块设置全局默认值,cameras 列表中每路相机可选择性覆盖。配置值支持 ${ENV_VAR} 环境变量替换。
核心结构:
defaults:
motion: # 运动检测参数
ai: # AI 推理参数(帧数、冷却)
recordings: # 截图/视频录制
detectors: # YOLO / LLM 检测器
region: # 可选检测区域
cameras: # 相机列表,每路可覆盖 defaults
hass: # 可选:Home Assistant 集成
hooks: # 可选:外部脚本
llm: # 可选:LLM API 连接配置
完整配置见
config.example.yaml,涵盖所有选项及详细注释。 详细配置说明与最佳实践见 docs/configuration.md,场景配置示例见 docs/scenarios.md。
ONVIF URL 格式:
onvif://username:password@host:port?profile=N
profile:media profile 索引,默认 0- 支持
${ENV_VAR}避免明文密码:onvif://admin:${CAM_PASSWORD}@192.168.1.100
提示:
config.yaml建议加入.gitignore,避免泄露摄像头地址和凭据。
数据流
RTSP / ONVIF 流 ──> MotionGate (帧差门控)
│
min_change_ratio ≥ threshold?
│否└─ 跳过
│是
[AI cooldown 冷却检查]
│
collect_frames() 采集多帧
│
AIPipeline.run_batch() 批量推理
│
显著标签 ≥ threshold?
│否└─ 跳过
│是
录制截图/视频 → 追加 timeline.csv
│
Notifier 推送(HA / Hook 脚本)
录制与 Timeline
data/
{camera_id}/
{date}/
snapshots/
140530_person.jpg # 事件截图
clips/
140530_person.mp4 # 事件视频片段
timeline.csv # 本日事件索引
timeline.csv 格式:
start_time,end_time,event_type,snapshot_path,clip_path
2026-05-07T14:05:30,2026-05-07T14:05:35,person,snapshots/140530_person.jpg,clips/140530_person.mp4
同类型事件 3 秒内防重,避免连续重复录制。
Hook 脚本
Hook 脚本在 config.yaml 根级别配置(全局,所有事件类型均触发所有脚本):
# 可选:事件触发时执行的外部脚本
hooks:
- command: scripts/event_logger.sh
每个脚本接收命令行参数:
--camera-id xiaomi1
--event-type person
--start-time 2026-05-07T14:05:30
--end-time 2026-05-07T14:05:35
--snapshot-path snapshots/140530_person.jpg
--clip-path clips/140530_person.mp4
--labels '{"person": 0.85}'
检测器扩展
内置 PersonYoloDetector(YOLOv8 人体检测),可按以下步骤添加自定义检测器:
- 继承
VisionDetector或AudioDetector(surveillance/detectors/base.py) - 设置唯一
name类变量 - 实现
analyze_batch()返回VisionResult/AudioResult(接收多帧,自行决定如何使用) - 在
AIPipeline.from_camera_detectors()中注册
from surveillance.detectors.base import VisionDetector, VisionResult, VisionContext
class FireDetector(VisionDetector):
name = "fire_detector"
def analyze_batch(self, frames, ctx: VisionContext | None = None):
# 火焰检测逻辑...
return VisionResult(labels={"fire": 0.92})
项目结构
solo-surveillance/
├── config.example.yaml # 示例配置(含全部选项)
├── pyproject.toml # 包元数据、依赖、CLI 入口点
├── surveillance/ # 核心应用包
│ ├── __main__.py # `python -m surveillance` 入口
│ ├── main.py # CLI 解析、线程编排
│ ├── config_loader.py # YAML 加载、deep_merge、${ENV_VAR} 展开
│ ├── stream.py # RTSPReader — 帧捕获与自动重连
│ ├── motion.py # MotionGate — 帧差运动检测
│ ├── region.py # 帧裁剪至归一化 ROI
│ ├── vision_burst.py # 多帧采样与结果合并
│ ├── recordings.py # 截图/视频录制与 timeline CSV
│ ├── onvif.py # ONVIF 发现 → RTSP URL 解析
│ ├── http_server.py # 内置 Web UI 与 API 服务器
│ ├── static/index.html # 单页 Web UI
│ └── detectors/
│ ├── base.py # 抽象 VisionDetector / AudioDetector
│ ├── person_yolo.py # YOLOv8 人体检测
│ ├── llm_vision.py # LLM API 场景分析
│ └── pipeline.py # AIPipeline — 编排所有检测器
└── docs/ # 文档
├── configuration.md # 详细配置指南与最佳实践
├── scenarios.md # 场景配置示例
└── homeassistant.md # Home Assistant 集成详情
线程模型
- 每路已启用相机创建独立
threading.Thread threading.Event协调关闭(SIGINT / SIGTERM)- 每线程持有独立
RTSPReader、MotionGate、AIPipeline(无共享状态) - HTTP 服务器在守护线程中运行,不阻塞主流程
- Hook 脚本在独立守护线程中执行(30s 超时自动终止)
依赖
| 依赖 | 用途 | 使用模块 |
|---|---|---|
| opencv-python-headless | RTSP 捕获、图像处理、视频编码 | stream, motion, recordings, llm_vision |
| numpy | 帧数组运算 | 全局使用 |
| ultralytics | YOLOv8 推理 | detectors/person_yolo.py |
| PyYAML | 配置文件解析 | config_loader.py |
| onvif-zeep | ONVIF 设备发现与控制 | onvif.py(可选) |
| anthropic / openai | LLM 视觉 API | detectors/llm_vision.py(可选) |
要求 Python >= 3.11,支持 macOS 和 Linux。
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file solo_surveillance-0.3.1.tar.gz.
File metadata
- Download URL: solo_surveillance-0.3.1.tar.gz
- Upload date:
- Size: 929.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0aecbe21cd28e217f46c678d0324df1aefce2bec3bd2db539909a63acabdb160
|
|
| MD5 |
cd64c363e891332d3a298f88a73eea57
|
|
| BLAKE2b-256 |
6c6c7531fac071f57bc45e5576a996393c5d6b92220e44c1c28e5f16cba44b75
|
Provenance
The following attestation bundles were made for solo_surveillance-0.3.1.tar.gz:
Publisher:
publish.yml on tiancheng91/solo-surveillance
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
solo_surveillance-0.3.1.tar.gz -
Subject digest:
0aecbe21cd28e217f46c678d0324df1aefce2bec3bd2db539909a63acabdb160 - Sigstore transparency entry: 1583487481
- Sigstore integration time:
-
Permalink:
tiancheng91/solo-surveillance@7bbac0d66bdefefe650f61380c5d065294fbcdb0 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/tiancheng91
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7bbac0d66bdefefe650f61380c5d065294fbcdb0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file solo_surveillance-0.3.1-py3-none-any.whl.
File metadata
- Download URL: solo_surveillance-0.3.1-py3-none-any.whl
- Upload date:
- Size: 39.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63fa03e2ad26664483ce86d23125bfb1aa9206e619172c49a2caa21bfd4500e2
|
|
| MD5 |
9e405059fefd740b10b11b8eda24bc50
|
|
| BLAKE2b-256 |
e200a9bf88901cc5937c3eaae5bd265e6e68783cd5cb48e5700dc27a125004a3
|
Provenance
The following attestation bundles were made for solo_surveillance-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on tiancheng91/solo-surveillance
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
solo_surveillance-0.3.1-py3-none-any.whl -
Subject digest:
63fa03e2ad26664483ce86d23125bfb1aa9206e619172c49a2caa21bfd4500e2 - Sigstore transparency entry: 1583487737
- Sigstore integration time:
-
Permalink:
tiancheng91/solo-surveillance@7bbac0d66bdefefe650f61380c5d065294fbcdb0 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/tiancheng91
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7bbac0d66bdefefe650f61380c5d065294fbcdb0 -
Trigger Event:
push
-
Statement type: