跳过正文
  1. 文章/

Frigate NVR 配置全记录:从 Docker 到 HA 推送通知

作者
Yang Hu

在 Debian 服务器(Intel N97)上部署 Frigate NVR,替代传统 NVR 的检测功能。 涵盖 Docker Compose、go2rtc 流配置、硬件加速、HA 集成、推送通知及基于区域的告警。 VLAN 间流量经由主路由器(UCG Ultra)转发。

硬件与背景
#

  • 服务器: Intel N97 迷你主机(debian.lan10.0.10.11),Debian 13
  • 摄像头: 8 台 Reolink PoE 摄像头(摄像头 VLAN,10.0.40.0/24),2 台 Nanit 婴儿监控器(IoT VLAN,10.0.20.0/24
  • 现有 NVR: 保留运行,负责持续录像;Frigate 专注于检测和事件片段
  • Home Assistant: 位于 IoT VLAN(10.0.20.10),已运行 MQTT Broker

存储设计
#

录像存储在 NAS 专用共享目录,避免占用本地 NVMe。

在 Synology DSM 中:创建共享文件夹 surveillance,NFS 导出给 10.0.10.11, 权限选择 Map all users to admin(将所有 UID 映射为 admin), 避免 Frigate 容器 UID(1001)在 NAS 上找不到对应用户的问题。

在 debian 上挂载:

1
2
sudo mkdir -p /mnt/nas-surveillance
sudo mount -t nfs 10.0.10.10:/volume1/surveillance /mnt/nas-surveillance

添加到 /etc/fstab

1
10.0.10.10:/volume1/surveillance /mnt/nas-surveillance nfs vers=3,rw,_netdev,nofail,soft,rsize=65536,wsize=65536,timeo=300 0 0

Docker Compose
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# /home/yang/docker/frigate/docker-compose.yml
services:
  frigate:
    container_name: frigate
    image: ghcr.io/blakeblackshear/frigate:stable
    restart: unless-stopped
    shm_size: "256mb"
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config:/config
      - /mnt/nas-surveillance/frigate:/media/frigate
    ports:
      - "5000:5000"
      - "8554:8554"
      - "8555:8555/tcp"
      - "8555:8555/udp"
    env_file: docker-compose.env
1
2
3
4
5
# docker-compose.env
FRIGATE_RTSP_PASSWORD=...
FRIGATE_DOORBELL_PASSWORD=...   # 门铃密码不同,单独设置
FRIGATE_MQTT_USER=...
FRIGATE_MQTT_PASSWORD=...

密码通过环境变量注入,在 config.yml 中以 {FRIGATE_VARIABLE_NAME} 引用, 避免明文写入配置文件。

shm_size: 256mb — 帧缓冲的共享内存。8 路摄像头 5fps 检测流约需 20–30 MB, 256 MB 留有充足余量。

/dev/dri/renderD128 — Intel 核显,用于 VAAPI 硬件解码。 无需 privileged: true,直接传递设备即可。


config.yml
#

MQTT 与硬件加速
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
mqtt:
  enabled: true
  host: 10.0.20.10       # HA 的 IP(IoT VLAN)
  port: 1883
  user: "{FRIGATE_MQTT_USER}"
  password: "{FRIGATE_MQTT_PASSWORD}"

ffmpeg:
  hwaccel_args: preset-vaapi

detectors:
  ov:
    type: openvino
    device: GPU

model:
  path: /openvino-model/ssdlite_mobilenet_v2.xml
  labelmap_path: /openvino-model/coco_91cl_bkgr.txt
  width: 300
  height: 300

ffmpeg 与检测器加速的区别: preset-vaapi 负责视频解码(H.264/H.265 → 原始帧),使用 Intel 核显的媒体引擎。OpenVINO 检测器负责推理(运行目标检测模型),使用核显的 EU 计算单元。两者共用同一块 N97 核显,但使用不同的硬件模块,互不干扰。

为什么选 OpenVINO 而非 CPU? N97 原生支持 OpenVINO,GPU 推理比 CPU 快 3–5 倍,无需额外硬件。模型(ssdlite_mobilenet_v2)完全相同,只是换了执行后端。

model: 必须放在顶层。 这是全局配置块,不能嵌套在 detectors 下面。如果把 path 写在 detectors.ov.model 内,Frigate 会读到 None 路径并在启动时崩溃。

MQTT host 填 HA 的 IP(10.0.20.10),流量经 UCG Ultra 路由转发。

录像策略
#

1
2
3
4
5
6
7
8
record:
  enabled: true
  alerts:
    retain:
      days: 7
  detections:
    retain:
      days: 7

顶层没有 retain.days,意味着不做持续录像,只保存事件片段。 现有 NVR 负责 24/7 全程录像,Frigate 保存带标签的短片段,两者互补。

go2rtc 流配置——常见错误
#

Frigate 内置 go2rtc 管理视频流。每台摄像头需要两个命名流: 主流(高分辨率,用于录像)和子流(低分辨率,用于检测)。 关键错误:

错误写法 — 主流和子流写在同一个流名下:

1
2
3
4
5
go2rtc:
  streams:
    front:
      - rtsp://admin:pass@10.0.40.124:554/h264Preview_01_main
      - rtsp://admin:pass@10.0.40.124:554/h264Preview_01_sub

go2rtc 将多个源视为备用关系,只选其中一个。 后续引用 front_sub 会返回 404。

正确写法 — 分别命名:

1
2
3
4
5
6
go2rtc:
  streams:
    front:
      - "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@10.0.40.124:554/h264Preview_01_main"
    front_sub:
      - "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@10.0.40.124:554/h264Preview_01_sub"

摄像头配置中分别引用:

1
2
3
4
5
6
7
8
cameras:
  front:
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/front_sub
          roles: [detect]
        - path: rtsp://127.0.0.1:8554/front
          roles: [record]

Reolink RTSP 地址格式#

标准 Reolink 摄像头:

  • 主流:rtsp://admin:pass@IP:554/h264Preview_01_main
  • 子流:rtsp://admin:pass@IP:554/h264Preview_01_sub

较新型号(如 CX810)使用 H.265:

  • rtsp://admin:pass@IP:554/h265Preview_01_main

Reolink Duo 有两个镜头,路径为 Preview_01Preview_02。 实际使用中只有一个镜头有效,当作单摄像头处理即可。

摄像头配置
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
cameras:
  front:
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/front_sub
          roles: [detect]
        - path: rtsp://127.0.0.1:8554/front
          roles: [record]
    detect:
      enabled: true      # 必须显式设置——0.17 默认为 false
      width: 640
      height: 360        # 与子流实际分辨率匹配
      fps: 5

detect.enabled: true 必须显式写明。 Frigate 0.17 中默认为 false。 启动时如果视频流连接失败,Frigate 会自动禁用检测,且流恢复后不会自动重启。 务必在每台摄像头配置中显式设置。

width/height 必须与子流实际分辨率匹配。 Frigate 在送入检测模型前会将帧缩放到该分辨率。如果不匹配,画面会被拉伸,导致目标形状变形,检测准确度下降——对远距离或小目标尤其明显。用 ffprobe 探测子流实际分辨率:

1
2
3
4
docker exec frigate /usr/lib/ffmpeg/7.0/bin/ffprobe \
  -v error -select_streams v:0 \
  -show_entries stream=width,height -of csv=p=0 \
  rtsp://127.0.0.1:8554/front_sub

我的摄像头子流分辨率:

摄像头子流分辨率备注
Reolink 标准型(front、backyard、side_a/b、cx810)640×36016:9
Reolink E1640×360PTZ,不设 zone
Reolink 门铃480×640竖屏——宽高互换
Reolink Duo(duo_a)1536×576超宽 8:3;检测配置为 1280×480

检测参数调优
#

默认阈值:min_score: 0.5threshold: 0.7threshold 是跟踪窗口内的置信度滑动平均值——间歇性低置信度检测可能达不到该阈值。

对于广角或远距摄像头,降低阈值、提高检测分辨率有明显效果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  duo_a:
    detect:
      enabled: true
      width: 1280    # 默认 640——更多像素,远距目标识别更准
      height: 720
      fps: 5
    objects:
      filters:
        person:
          min_score: 0.45
          threshold: 0.55

区域与告警
#

Frigate 0.17 将事件分为 alerts(告警)detections(检测) 两类。 默认情况下,画面任何位置的 person 和 car 均触发告警。区域配置可限制这一行为。

工作原理
#

  • 区域多边形在 Frigate UI 中绘制(可视化编辑器),自动写入 config.yml
  • 区域的 objects 列表限定哪些类别可激活该区域
  • loitering_time 要求目标在区域内停留 N 秒后才触发
  • required_zones 写在 review.alerts 下,控制哪些事件升级为告警

required_zones 的正确位置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cameras:
  duo_a:
    review:
      alerts:
        required_zones:
          - frontyard-parking-zone
          - person-driveway-loister-zone
    zones:
      frontyard-parking-zone:
        coordinates: "..."
        loitering_time: 180   # 3 分钟
        objects:
          - car
      person-driveway-loister-zone:
        coordinates: "..."
        loitering_time: 30    # 30 秒
        objects:
          - person

required_zones 放在 cameras.<name>.review.alerts 下—— 不是 objects.filters 下(该位置不存在此键,会导致配置报错)。

效果
#

事件结果
车辆路过仅检测,不告警
车辆在区域内停留 3 分钟以上触发告警
行人路过仅检测,不告警
行人在区域内停留 30 秒以上触发告警

Home Assistant 集成
#

Frigate 集成
#

在 HA 中:设置 → 集成 → 添加 → Frigate

填写 Frigate 的主 LAN 地址:http://10.0.10.11:5000

推送通知
#

通过 HA 自动化实现带截图的推送通知:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
alias: Frigate person notification
triggers:
  - trigger: mqtt
    topic: frigate/events
conditions:
  - condition: template
    value_template: >
      {{ trigger.payload_json.type == 'new' and
         trigger.payload_json.after.label == 'person' and
         trigger.payload_json.after.camera in ['doorbell', 'e1'] }}
actions:
  - delay: "00:00:02"
  - action: notify.mobile_app_yangs_iphone
    data:
      title: "{{ trigger.payload_json.after.camera | title }}: 检测到人"
      message: "{{ now().strftime('%H:%M') }}"
      data:
        image: "http://10.0.10.11:5000/api/events/{{ trigger.payload_json.after.id }}/snapshot.jpg"
        url: "http://frigate.tailnet:5000/review?camera={{ trigger.payload_json.after.camera }}"
        push:
          sound:
            name: default

为什么用 type == 'new' 而不是 type == 'end' end 触发时可以提供完整视频片段,但通知会延迟—— 且如果人一直停留在画面中,end 永远不会触发。 new 立即触发;延迟 2 秒等待 Frigate 生成首帧截图。

截图 URL 使用 Frigate 的 LAN 地址(10.0.10.11),流量经主路由转发。 点击跳转 URL 使用 Tailscale 域名,支持远程访问。


踩坑总结
#

  • go2rtc 流名称必须按分辨率分别命名。 主流和子流写在同一个名称下, 子流通过 go2rtc RTSP 服务器访问时返回 404。

  • Frigate 0.17 中 detect.enabled 默认为 false。 必须在每台摄像头配置 中显式声明。启动时流连接失败会导致检测被自动禁用,且不会自动恢复。

  • required_zones 放在 review.alerts 下,不是 objects.filters 放错位置会导致配置校验报错。

  • 部分摄像头默认关闭 RTSP。 较新的 Reolink 型号(如 CX810)需要在 摄像头 Web 界面手动开启 RTSP,端口 554 connection refused 即为此症状。

  • H.265 摄像头使用不同的 RTSP 路径。 需将 h264Preview_01_main 改为 h265Preview_01_main

  • 实时画面和检测是相互独立的。 浏览器通过 WebRTC 直接从 go2rtc 获取视频, 即使检测功能异常,实时画面仍然正常显示。能看到直播流不代表检测在运行。

  • HA↔Frigate 流量经主路由转发。 需确保 UCG Ultra 防火墙允许 IoT VLAN → 主 LAN 的 5000 和 1935 端口通行。