Frigate NVR自建NVR偵測系統

感念論壇分享,在這邊也學習很多!小弟也來分享一個最近弄好的一個專案,是利用一個名叫Frigate NVR的開源NVR錄影系統,主要優勢在於它的AI物件偵測能力,可以搭配上同樣開源的模型物件,來判斷畫面裡面的東西,不管是車、動物、還是物件都可以。

志在分享!資料都是網路上找來的,如果有講得不準確的地方還請告訴小弟,非常感謝!

在用這套系統之前,小弟是用簡單的門窗感應器搭配上紅軍大大的NR呼叫相機截圖通知流程,來達到開關門會拍照記錄通知,並且在開門達到一段時間後會自動傳送即時連結。

Line Notify Token + 攝影機截圖 Line訊息傳送 NR應用方法
分享 透過 Line 傳送攝影機即時影像連結

沒什麼特別需求的話,其實這樣就相當好用了!但是小弟強迫症發作,看到那每天開關門那樣的無用訊息轟炸,到最後會變成根本沒在看那訊息。另外在HA裡面利用相機截圖或是錄影都會有延遲輸出,都會需要額外微調才有辦法真的拍照重點,不然都只會有開門關門的照片卻沒拍到人…

但利用這套NVR所內建的通知API所產出的縮圖或視錄影幾乎可以達到即時,就算有延遲也差不多1~2秒(視硬體規格而定),而且還可以真的是偵測到人的畫面才會產生縮圖,更甚至還能放大畫面加偵測框以及時間(就像上面第一張圖一樣)

image

先講硬體,小弟使用的攝影機就是在群組裡大家推崇的TP-LINK Tapo C210,其他相機設定可以參考Frigate說明文件,主要是要能支援RTSP,直接輸出H.264視訊和AAC音源最好,主機部分是有能支援硬體解碼處理器,一般工控機不要等級太弱的都行,如果有AI加速器(Coral)更好,我自己是直接使用群暉的DS720搭配Coral USB Accelerator,依實際使用上來看NAS的處理器資源占用平均都只有10~20%。

https://docs.frigate.video/frigate/hardware

安裝環境部分虛擬機、Docker都可以使用(官方推薦是Docker),HA內也有附加元件可以直接安裝,如果像小弟一樣會使用到AI加速器的話則不太建議用虛擬機(效能會被打折)。那為了將系統區分以及方便維護,小弟是選擇用docker compose安裝方式,使用的群暉DSM則為最新的7.2版本

安裝步驟

小弟是先在資料加內新增config以及storagey資料夾後面備用,然後開啟Container Manager新增一個專案將下面的yaml程式碼按照自己需求貼上後按下一步、下一步(對內服務使用,網頁入口設定不用設定),建立容器。

version: "3.9"
services:
  frigate:
    container_name: frigate
    privileged: true  #權限最大化建議開啟增加
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:stable
#    environment:
#      - LIBVA_DRIVER_NAME=i965 #硬體解碼J4125處理器建議增加
    shm_size: "64mb" #兩個720p鏡頭差不多64mb,如果更多的話就再額外增加
#    devices:
#      - /dev/bus/usb:/dev/bus/usb #有裝AI加速器要增加
#      - /dev/dri/renderD128 #INTEL硬體解碼驅動
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /volume1/docker/frigate/config:/config #設定檔位置
      - /volume1/docker/frigate/storage:/media/frigate #截圖錄影存檔位置
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "5000:5000" #必要,需要給HA串接使用
#      - "8554:8554" #非必要,如果有同步用go2rtc解碼才需要
#      - "8555:8555/tcp" #非必要,如果有同步用go2rtc解碼才需要
#      - "8555:8555/udp" #非必要,如果有同步用go2rtc解碼才需要

完成後會自動建立一個名為frigate容器,docker安裝動作就這樣結束了!
image

如果你的compose跟我的一樣的話,輸入NAS區域網路網址:5000,就可以進入compose設定畫面。再來就是進入config先來簡單配置一下相機設定,細部設定我們後面再來討論,先完成HA和frigate連結!設定檔使用的格式都是yaml,如果會用HA設定基本上不會太難。

mqtt:
  enabled: False

cameras:
  name_of_your_camera: #攝影機名稱
    ffmpeg:
      inputs:
        - path: rtsp://10.0.10.10:554/rtsp #攝影機RTSP連結
          roles: #需要的模式,有detect 檢測、record錄影
            - detect 
    detect:
      enabled: False #還沒配置好檢測模式和區域先不要開,會額外消耗主機效能
      width: 1280 #攝影機的寬度,視畫面情況而定,解析度不是越高越好
      height: 720 #攝影機的高度,視畫面情況而定,解析度不是越高越好

正常來說,設定好攝影機名稱和RTSP連結後,存檔重開就可以看到攝影機畫面了!

Home assistant設定步驟

再來跳到HA設定裡面,由於小弟沒有要控制攝影機方位,所以沒有安裝HACS裡的Tapo: Cameras Control,但是HA裡還是需要camera實體,所以小弟的替代方案就用frigate推出的add-on來替代

安裝完重新HA後,新增附加元件。

如果你是跟我一樣用docker安裝的話,那網址就是剛剛上面的NAS區域網路網址:5000

完成加入後,攝影機實體跳出來就完成了。

如果有需要直接在HA控制的話可以另外在Supervisor add-on裡面安裝Frigate Proxy,

frigate設定步驟

在官方文件裡面有完整說明全部的設定,小弟也是英文苦手,全部的都是靠著google翻譯慢慢研究測試,如果有更好的偵測設定歡迎分享給小弟

以下附上我的配置,偵測環境主要為客廳玄關門,如果是透天使用可能需要多配置

mqtt:
  enabled: true #打開才會用MQTT去做通知
  host: #MQTT網址
  port: 1883
  user: #MQTT帳號
  password: #MQTT網址密碼
  
detectors: #有使用AI加速器要加上這段,不然就砍掉,預設是處理器解碼
  coral:
    type: edgetpu
    device: usb 

ffmpeg:
  hwaccel_args: preset-vaapi #使用INTEL硬體解碼

cameras:
 camera: #攝影機名稱
    ffmpeg:
      inputs:
        - path: #攝影機RTSP連結
          roles:
            - record #錄影
            - detect #偵測

    record: #錄影設定
      enabled: true 
      retain: #全時錄影保留3天
        days: 3 
        mode: all
      events:
        pre_capture: 1 #偵測錄影前提前1秒
        post_capture: 3 #偵測錄影後剪輯往後3秒
        retain: #偵測錄影保留10天
          default: 10

    detect: #偵測設定
      enabled: true
      fps: 15 
      width: 1280
      height: 720
      stationary:
        interval: 30 #靜態偵測持續偵測影格
        threshold: 150 #靜態偵測影格

    snapshots: #快照
      enabled: true
      retain: 
        default: 10

    mqtt: #MQTT通知
      enabled: true
      timestamp: false
      bounding_box: true
      crop: false
      height: 480
      quality: 100

    objects: #偵測物件
      track:
        - person
      filters: #不須偵測範圍遮罩
        person:
          mask:
            - 453,0,466,276,617,354,610,0,1280,0,1280,720,0,720,0,0

按照我的配置,只要有"人"出現在玄關位置,就會自動拍照並且錄影


image

可以先觀察幾天,確認偵測的設定都有準確偵測的話就可以進行通知的環節了

Node red設定步驟

通知的部分官方有直接提供縮圖、快照以及錄影的api可以直接使用,讓你的NVR不須對外門戶大開(HA本身還是要對外)

其中的event-id部分就是由每次偵測後frigate會自動產生,小弟一樣是利用NR來流程配置,程式碼就貼在下面讓大家匯入參考囉!

[{"id":"f69513513bf98c8c","type":"group","z":"a45d65a9191f182c","name":"","style":{"label":true},"nodes":["67c9dc67ed4d2c6f","e2f49ed002d17626","f1f98d37d2b244fa","5516e61faa9f2d8e","2ecb3636ef2ae317"],"x":174,"y":679,"w":792,"h":82},{"id":"67c9dc67ed4d2c6f","type":"mqtt in","z":"a45d65a9191f182c","g":"f69513513bf98c8c","name":"","topic":"frigate/events","qos":"2","datatype":"auto-detect","broker":"","nl":false,"rap":true,"rh":0,"inputs":0,"x":270,"y":720,"wires":[["f1f98d37d2b244fa"]]},{"id":"e2f49ed002d17626","type":"change","z":"a45d65a9191f182c","g":"f69513513bf98c8c","name":"","rules":[{"t":"set","p":"tts","pt":"msg","to":"偵測動態","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":720,"wires":[["5516e61faa9f2d8e"]]},{"id":"f1f98d37d2b244fa","type":"rbe","z":"a45d65a9191f182c","g":"f69513513bf98c8c","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload.after.id","topi":"topic","x":410,"y":720,"wires":[["e2f49ed002d17626"]]},{"id":"5516e61faa9f2d8e","type":"function","z":"a45d65a9191f182c","g":"f69513513bf98c8c","name":"偵測錄影截圖","func":"var TimeNow = new Date();\nvar yyyy = TimeNow.getFullYear()\nvar MM = (TimeNow.getMonth() + 1 < 10 ? '0' : '') + (TimeNow.getMonth() + 1);\nvar dd = (TimeNow.getDate() < 10 ? '0' : '') + TimeNow.getDate();\nvar h = (TimeNow.getHours() < 10 ? '0' : '') + TimeNow.getHours();\nvar m = (TimeNow.getMinutes() < 10 ? '0' : '') + TimeNow.getMinutes();\nvar s = (TimeNow.getSeconds() < 10 ? '0' : '') + TimeNow.getSeconds();\nvar filedate = '_' + yyyy + MM + dd + '_' + h + m + '_' + s;\nvar date = yyyy + '/' + MM + '/' + dd + ' ' + h + ':' + m + ':' + s;\n\nlet evenid = msg.payload.after.id;\nlet clip = \"https://HA網址/api/frigate/notifications/\" + evenid + \"/clip.mp4\"\n\nmsg.payload_link = { \n   \"data\": { \"url\": \"https://HA網址/api/frigate/notifications/\" + evenid + \"/snapshot.jpg\" },\n   \"message\": date + \"\\n\" + msg.tts + \"\\n\" + clip\n};\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":720,"y":720,"wires":[["2ecb3636ef2ae317"]]},{"id":"2ecb3636ef2ae317","type":"api-call-service","z":"a45d65a9191f182c","g":"f69513513bf98c8c","name":"公開LINE","server":"f3d3d353.161b2","version":5,"debugenabled":false,"domain":"notify","service":"line_notify","areaId":[],"deviceId":[],"entityId":[],"data":"msg.payload_link","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":720,"wires":[[]]},{"id":"f3d3d353.161b2","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

小弟只用其中雞毛蒜皮的功能,這套系統還有很多應用,就再麻煩大大們互相分享啦!

2/5更新,不會用NR也可以用HA的自動化流程

alias: frigate event
description: ""
trigger:
  - platform: mqtt
    topic: frigate/events
    id: frigate-event
    value_template: "{{ value_json['after']['camera'] }}"
    variables:
      after_zones: "{{ trigger.payload_json['after']['entered_zones'] }}"
      before_zones: "{{ trigger.payload_json['before']['entered_zones'] }}"
      camera: "{{ trigger.payload_json['after']['camera'] }}"
      id: "{{ trigger.payload_json['after']['id'] }}"
      label: "{{ trigger.payload_json['after']['label'] }}"
      score: "{{ trigger.payload_json['after']['score'] }}"
      time_clip_start: "{{ trigger.payload_json['after']['start_time'] - 10.0 }}"
condition:
  - condition: or
    conditions:
      - condition: template
        value_template: "{{ trigger.payload_json['type'] == 'new' }}"
      - condition: template
        value_template: "{{ before_zones | length == 0 }}"
  - condition: template
    value_template: "{{ trigger.payload_json[\"after\"][\"entered_zones\"]|length > 0 }}"
  - condition: template
    value_template: "{{ [\"entrance\"] | select(\"in\", after_zones) | list | length > 0 }}"  #要改這段,你的zone
action:
  - choose:
      - conditions:
          - condition: trigger
            id: frigate-event
        sequence:
          - service: notify.line_notif_private
            data_template:
              message: >-
                {{ label }} detected in the {{ after_zones[0] | replace("_", "
                ") | title }}
              data:
                url: >-
                  https://your.domain.com/api/frigate/notifications/{{ id
                  }}/snapshot.jpg #要改這段
          - repeat:
              until:
                - condition: template
                  value_template: "{{ wait.trigger.payload_json[\"type\"] == \"end\" }}"
              sequence:
                - wait_for_trigger:
                    - platform: mqtt
                      topic: frigate/events
                      payload: "{{ trigger.payload_json[\"after\"][\"id\"] }}"
                      value_template: "{{ value_json[\"after\"][\"id\"] }}"
                  continue_on_timeout: false
                  timeout: "00:02:00"
                - condition: template
                  value_template: "{{ wait.trigger.payload_json['type'] == 'end' }}"
    default: []
mode: parallel
max: 10

3個讚

版大請問一下,因為nas也是5000,跟frigate會重複,我該要如果改,謝謝

在Container Manager新增專案的docker-compose.yml裡,把5000改成其他你需要的port

版大您好,我有試過改其他的port但還是連不上去,而且我的docker在執行的時候會一下運作良好,一下無專案在執行,這樣是有成功嗎?謝謝

先按照位置在NAS裡建立資料夾
/volume1/docker/frigate/config
/volume1/docker/frigate/storage

然後把下面的設定檔,存成yml丟到config資料夾裡再試試看

好,我先這樣試試看

我的DOCKER還是一樣一下有執行一下沒有,不知道是不是NAS的效能不太夠

要看一下log,看是因為什麼問題沒有辦法啟動

大大請問一下,有用過自訂模型嗎,現在在碰這塊想自己用自己訓練的模型,有的話請指點一下小弟我,非常感謝

不好意思我沒有試過,我還是都用預設的模型