家裡有兩隻虎斑貓,aki 和 kuro。養了貓之後,Furbo 寵物攝影機的廣告就一直出現在我眼前:遠端丟零食、AI 寵物保母、每天自動剪好的貓咪日記、嘔吐偵測。看久了真的心動,有一陣子差點就下單了。後來認真去查,才發現我想要的功能幾乎都綁在訂閱裡,而且它的錄影只存 AI 判斷有寵物的片段。

剛好客廳本來就有一台 Tapo C210 一直開著,只是它沒有 SD 卡,只能看即時畫面、沒有回放。所以最後我沒買 Furbo,改拿它的功能清單當目標,用 Frigate 加 Home Assistant 自己做:錄影可以回看、貓有動靜會通知、每天自動整理一份「今天貓做了什麼」。做了大概兩週,結果是:錄影、通知、本機 AI 寫的貓咪日記和每日精華影片都能用,唯一沒做成的是「分辨是哪一隻貓」。我訓練了一個分類模型,用同框的畫面驗證,正確率 38%,比亂猜的 50% 還低。

這篇照順序記錄:我查到的 Furbo 缺點、從零開始的架設步驟(compose 和 config 都附上,換成自己的帳密就能用)、選哪個本機視覺模型、分貓辨識是怎麼驗出來不能用的、日記和精華影片怎麼做,最後是整套跑起來吃多少 VRAM 和硬碟。

差點買 Furbo,查完才發現的事#

以下是 2026 年 10 月在 Furbo 台灣官網查到的:

  • 攝影機:貓咪攝影機 360 度版頁面寫「最低享 1,699 元」,但那是要同時綁寵物保姆訂閱的價格(至少綁 3 個月)。不綁約的單機,官方蝦皮賣場是 5,399 元。盒子裡不附電源供應器,另購 299 元;Wi-Fi 只支援 2.4GHz。
  • 不訂閱能用的:即時畫面、雙向通話、丟零食、逗貓棒、喵喵通知。任何錄影都不能回看。
  • 寵物保姆訂閱:標準方案年繳首年 1,840 元(月繳原價每月 219 元),錄影保留 3 天;高級方案每月 188 元起,保留 7 天。台灣的兩個方案只差在保存天數,貓咪日記、嘔吐偵測、多寵辨識兩邊都有。多一台攝影機,每台每月再加 40 元。

真正讓我打消念頭的是錄影方式。Furbo 的回看是 AI 偵測到寵物才存下來的片段,不是連續錄影。App Store 台灣區的負評很多都繞著這點,例如「想看的畫面常常都沒錄到」、「整天傳喵喵通知給我,但是嘔吐的部分卻偵測不到」,另外斷線也是很常見的抱怨。Furbo 自己的說明文件也寫了「寵物緊急狀況」警報的門檻:一天內偵測到兩次過度嘔吐,或 5 分鐘內偵測到兩次抽搐;可能的誤判來源則列了在花紋地毯上低頭、光影變化、把玩具放下。看起來就是低頭姿勢的判斷,再加上重複次數的門檻。我客廳本來就有一台 C210,買 Furbo 等於再多一台只吃 2.4GHz 的攝影機,然後每年付一筆訂閱費,換 3 天的 AI 片段。

廣告推得這麼兇也有原因。數位時代 10 月的專訪提到,Furbo 的訂閱收入在 2023 年之後超過硬體,現在占整體營收一半以上,策略就是壓低攝影機的入手價,再用錄影保存天數把訂閱分級。

不過查評價的過程也看得出大家最喜歡哪個功能:每天一支 5 秒到 1 分鐘、配好音樂的縮時「貓咪日記」影片。

對照我原本的 HA 儀表板,差別很清楚:我做的是「現在畫面裡有沒有貓」,Furbo 賣的是「貓剛剛做了什麼」。所以目標就定成三件事:錄影回看、事件摘要、每日精華。

從零開始架設#

整套的組成是這樣:

  • 鏡頭:Tapo C210,主串流 2304×1296、子串流 1280×720,只支援 2.4GHz,沒有 SD 卡,所以 Frigate 是它唯一的錄影方案。
  • 主機:一台 24 小時開著的 Linux 伺服器,顯卡是 RTX 5060 Ti 16GB,錄影放在 16TB 的硬碟上。
  • 0.17.2:錄影、物件偵測、事件管理,跑在 Docker 裡。
  • Ollama:在主機上跑視覺語言模型,幫每個事件寫摘要。
  • broker:Frigate 和 Home Assistant 之間傳狀態用。
  • Home Assistant:通知、儀表板、日記頁。

下面照我實際架的順序分成七步,每一步都收起來了,要照著做再點開。設定檔都拿掉了帳密和 IP,改成你自己的就能用。

第 1 步:鏡頭打開 RTSP

Tapo 的鏡頭要先在 App 裡的鏡頭設定 → 進階設定,建立一組「攝影機帳戶」,這組帳密跟你登入 Tapo 的帳號是分開的。建好之後串流網址是:

text
rtsp://攝影機帳戶:密碼@鏡頭IP:554/stream1   # 主串流
rtsp://攝影機帳戶:密碼@鏡頭IP:554/stream2   # 子串流

接上去之前先用 ffprobe 量一次實際的解析度和位元率,不要照規格表或網路上的說法填。我的 C210 實測主串流是 2304×1296、25fps,位元率約 1.15 Mbps,換算一天大約 11.6GB;子串流 1280×720、約 0.15 Mbps。Frigate 0.17 的解析度自動偵測會出錯,官方建議在 config 裡把 detect 的寬高寫死,用的就是這裡量到的值。另外 Tapo 官方 FAQ 有寫,雲端儲存、SD 卡、NVR 錄影三種同時最多開兩種,三種都開的話 NVR 會被自動關掉。我沒裝 SD 卡也沒訂雲端,所以沒遇到。

第 2 步:主機裝顯卡驅動和 Ollama

Docker 要能用到 NVIDIA 顯卡,需要裝驅動和 NVIDIA Container Toolkit(我是驅動 580、Toolkit 1.19)。裝完先確認容器看得到顯卡:

bash
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker run --rm --gpus all ubuntu nvidia-smi

Ollama 直接裝在主機上,再拉視覺模型:

bash
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen3-vl:8b-instruct
sudo systemctl edit ollama

systemctl edit 打開的檔案裡加這兩行,第一行讓容器連得到,第二行讓模型常駐在 VRAM(原因在後面資源那節):

ini
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_KEEP_ALIVE=-1"

這裡有個坑:有開 ufw 的話,Frigate 容器還是連不到 Ollama。Ollama 是主機上的行程,容器連它的流量會被 ufw 預設的 deny 擋掉;反過來 MQTT 跑在容器裡、埠是 Docker 發布的,會繞過 ufw,所以會出現「MQTT 通、Ollama 逾時」的狀況。解法是只放行 Frigate 的 Docker 網段(網段在下面的 compose 裡釘死):

bash
sudo ufw allow from 172.28.0.0/24 to any port 11434 proto tcp
第 3 步:架 MQTT broker

我用 Mosquitto。一開始是借用之前給 ESP32 開的那個 broker,只多加兩個帳號;後來有一次主機重開,那個專案的其他服務都回來了,只有 broker 沒起來,Frigate 的實體在 HA 上「不可用」了 15 個小時才發現。所以現在獨立成一個 compose,不跟任何專案綁在一起:

yaml
# mosquitto/compose.yml
services:
  mosquitto:
    image: eclipse-mosquitto:2
    container_name: mosquitto
    restart: unless-stopped
    ports:
      - "1883:1883"
    volumes:
      - ./config:/mosquitto/config
      - ./data:/mosquitto/data
text
# mosquitto/config/mosquitto.conf
listener 1883
allow_anonymous false
password_file /mosquitto/config/passwd
persistence true
persistence_location /mosquitto/data/

建兩個帳號,一個給 Frigate、一個給 HA(第一個指令的 -c 會建立新檔,第二個不要加):

bash
docker run --rm -it -v ./config:/mosquitto/config eclipse-mosquitto:2 \
  mosquitto_passwd -c /mosquitto/config/passwd frigate
docker run --rm -it -v ./config:/mosquitto/config eclipse-mosquitto:2 \
  mosquitto_passwd /mosquitto/config/passwd hass

帳號是在 broker 已經跑起來之後才加的話,要 docker compose restart mosquitto,新帳號才會生效。

第 4 步:匯出偵測模型(RF-DETR)

Frigate 不內附 NVIDIA 用的模型,要自己匯出。0.17 在 amd64 上已經拿掉 TensorRT 偵測器,照舊文件設 type: tensorrt 會直接報錯要你改用 ONNX;映像選 -tensorrt 版,ONNX 就會自動用顯卡跑。

模型我選 RF-DETR Nano,因為它支援 0.17 對 NVIDIA 的 CUDA Graphs。官方文件給的匯出指令如下,唯一改的地方是先裝 CPU 版的 PyTorch:官方那行會拉 CUDA 版,下載好幾 GB,但匯出 ONNX 根本用不到顯卡。

bash
docker build . --build-arg MODEL_SIZE=Nano --rm --output . -f- <<'EOF'
FROM python:3.12 AS build
RUN apt-get update && apt-get install --no-install-recommends -y libgl1 && rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:0.10.4 /uv /bin/
WORKDIR /rfdetr
# 先裝 CPU 版 torch,下載量小很多
RUN uv pip install --system torch==2.8.0 --index-url https://download.pytorch.org/whl/cpu
RUN uv pip install --system rfdetr[onnxexport] onnx==1.19.1 transformers==4.57.6 onnxscript
ARG MODEL_SIZE
RUN python3 -c "from rfdetr import RFDETR${MODEL_SIZE}; x = RFDETR${MODEL_SIZE}(resolution=320); x.export(simplify=True)"
FROM scratch
ARG MODEL_SIZE
COPY --from=build /rfdetr/output/inference_model.onnx /rfdetr-${MODEL_SIZE}.onnx
EOF

跑完會得到約 108MB 的 rfdetr-Nano.onnx,改名成 rfdetr.onnx 放進 config/model_cache/。這個資料夾如果是 Frigate 第一次啟動時建的,擁有者會是 root,要用 sudo 複製。我實測這顆模型在 RTX 5060 Ti 上用 CUDA Graphs 推論 5.5ms,用 CPU 跑是 216ms。

第 5 步:Frigate 的 compose.yml
yaml
services:
  frigate:
    image: ghcr.io/blakeblackshear/frigate:0.17.2-tensorrt
    container_name: frigate
    restart: unless-stopped
    shm_size: 1g                 # 偵測用的影格放這裡,太小會直接 crash
    ports:
      - "127.0.0.1:5000:5000"    # 沒有認證的 API,只給同一台的 HA 用
      - "8971:8971"              # 有登入頁的網頁介面
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config:/config
      - /mnt/hdd/frigate/media:/media/frigate
    tmpfs:
      - /tmp/cache:size=1g       # 錄影暫存放記憶體,不要一直寫 SSD
    env_file: .env
    environment:
      - TZ=Asia/Taipei
    mem_limit: 10g
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    devices:                     # 明列裝置檔,見下方說明
      - /dev/nvidia0:/dev/nvidia0
      - /dev/nvidiactl:/dev/nvidiactl
      - /dev/nvidia-uvm:/dev/nvidia-uvm
      - /dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools
      - /dev/nvidia-modeset:/dev/nvidia-modeset

networks:
  default:
    ipam:
      config:
        - subnet: 172.28.0.0/24  # 釘死網段,ufw 規則才不會失效

.env 放帳密,Frigate 的設定檔可以用 {FRIGATE_變數名} 引用以 FRIGATE_ 開頭的環境變數,config 裡就不會出現明碼:

bash
FRIGATE_RTSP_USER=攝影機帳戶
FRIGATE_RTSP_PASSWORD=攝影機帳戶密碼
FRIGATE_MQTT_USER=frigate
FRIGATE_MQTT_PASSWORD=剛剛設的密碼

幾個數字是踩過才調的:

  • shm_size:偵測改跑主串流之後,Frigate 自己回報最少要 204MB,預設的 256m 太緊,我直接給 1g 留餘裕。改完要 docker compose up -d --force-recreate,只 restart 不會生效。
  • mem_limit:一開始給太少(4g、後來 6g),錄影寫入的檔案快取一直把容器推到上限,串流被限流斷線,拉到 10g 才穩。
  • tmpfs:Frigate 會先把 10 秒一段的片段寫進 /tmp/cache,再決定要搬到硬碟還是丟掉。這個暫存如果留在容器的檔案系統裡,等於 24 小時對系統碟寫入再刪除,所以放記憶體。這是我當初跟保留天數一起調的,目的就是不想「瘋狂寫入」。
  • 8971 是 Docker 發布的埠,一樣會繞過 ufw,區網裡的裝置都連得到。Frigate 0.17.2 修了 6 個安全公告,還有 3 個沒修,前提都是存在 viewer 角色的帳號(viewer 讀得到 log,而 log 裡有自動產生的 admin 密碼),所以我只留一個 admin,不建 viewer。另外 -tensorrt 映像有 12.9GB,第一次拉要等一下。
  • devices:只靠 deploy 要顯卡的話,主機哪天執行 systemctl daemon-reload(裝套件時常會自動觸發),容器會悄悄失去 GPU,等 ffmpeg 重開時才爆。明列裝置檔就不會被拿掉。
第 6 步:Frigate 的 config.yml

這是我現在客廳鏡頭的設定,精簡過但可以直接跑。motion 和 review 那幾行的原因在下一節,prompt 的寫法在「選本機視覺模型」那節。

yaml
mqtt:
  enabled: true
  host: 172.28.0.1               # 從容器連主機上的 broker(Docker 網段的 gateway)
  port: 1883
  user: '{FRIGATE_MQTT_USER}'
  password: '{FRIGATE_MQTT_PASSWORD}'

detectors:
  onnx:
    type: onnx

model:
  model_type: rfdetr
  width: 320
  height: 320
  input_tensor: nchw
  input_dtype: float
  path: /config/model_cache/rfdetr.onnx
  # rfdetr 用內建的 COCO 91 類標籤,不要設 labelmap_path

ffmpeg:
  hwaccel_args: preset-nvidia    # 用顯卡解碼

go2rtc:                          # Frigate 內建的串流轉發,鏡頭只被拉一次
  streams:
    keting:
      - rtsp://{FRIGATE_RTSP_USER}:{FRIGATE_RTSP_PASSWORD}@鏡頭IP:554/stream1
    keting_sub:
      - rtsp://{FRIGATE_RTSP_USER}:{FRIGATE_RTSP_PASSWORD}@鏡頭IP:554/stream2

genai:
  provider: ollama
  base_url: http://172.28.0.1:11434
  model: qwen3-vl:8b-instruct

semantic_search:                 # 讓 Explore 頁可以用文字搜事件,只索引開啟後的新事件
  enabled: true
  model: jinav1
  model_size: large

review:
  alerts:
    labels: [person]             # 只有人算 alert,貓留在 detections
  genai:
    enabled: true
    preferred_language: Traditional Chinese
    detections: true
    image_source: recordings
    activity_context_prompt: |
      這是台灣一般住家的客廳監視器,不是防盜攝影機。
      (完整內容見下面「選本機視覺模型」那節)

audio:
  enabled: true
  listen: [meow, hiss, caterwaul, speech, scream, yell, fire_alarm]

cameras:
  keting:
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/keting
          input_args: preset-rtsp-restream
          roles: [record, detect]
        - path: rtsp://127.0.0.1:8554/keting_sub
          input_args: preset-rtsp-restream
          roles: [audio]         # 音訊另開一條連線,掛子串流省頻寬
    detect:
      width: 2304                # 0.17 要寫死;跑主串流的原因見「分辨兩隻貓」
      height: 1296
      fps: 5
    motion:
      mask:
        - 0,0,0.38,0,0.38,0.075,0,0.075
      contour_area: 25
    objects:
      track: [cat, person]       # 沒養狗,dog 拿掉(偵測器會把貓認成狗)
      genai:                     # 物件描述用英文,給語意搜尋
        enabled: true
        objects: [cat, person]
        object_prompts:
          cat: >-
            Frames from an indoor living-room camera.
            In ONE short sentence, state what the cat is doing.
            If two cats are present, say so.
            Do NOT describe coat colour, pattern or markings.
            Start directly with "A cat" or "Two cats". No preamble.
            If no cat is visible, reply exactly: nothing notable.
    record:
      enabled: true
      continuous:
        days: 3
      motion:
        days: 14
      alerts:
        retain:
          days: 30
      detections:
        retain:
          days: 14
    snapshots:
      enabled: true
      bounding_box: true
      retain:
        default: 14

docker compose up -d 之後,網頁介面在 https://主機IP:8971(自簽憑證,瀏覽器會警告)。之後只改 config.yml 的話,docker compose up -d 什麼都不會做(compose.yml 沒變),要用 docker compose restart frigate。

admin 的初始密碼只會在第一次啟動時印在 log 裡一次,一啟動就馬上記下來:

bash
docker logs frigate 2>&1 | grep -i password

log 被輪替洗掉(多重啟幾次就會)或容器被重建(前面改 shm_size 要 --force-recreate)之後,這組密碼就找不回來了。真的找不到的話,在 config 加 auth: { reset_admin_password: true } 重啟一次拿新密碼,記得改回 false,不然每次重啟都會換一組新的。第一次啟動時 log 可能會出現一次 Detection appears to be stuck,那是 CUDA Graph 第一次捕捉超過 watchdog 的 10 秒門檻,自己會恢復。

第 7 步:接進 Home Assistant

我的 HA 是 Docker 版、用 host 網路,跟 Frigate 在同一台;下面用到 HACS,沒裝過的話要先照 HACS 官方文件裝好。

  1. 先加 MQTT 整合:設定 → 裝置與服務 → 新增整合 → MQTT,填 broker 的位址和 hass 帳號。我一開始漏了這步,結果 Frigate 的實體全部是「不可用」,只有走 HTTP API 的幾個 sensor 有值,看起來很像 Frigate 壞掉。
  2. 裝 Frigate 整合:從 HACS 搜尋 Frigate 安裝,重啟 HA 後新增整合,網址填 http://127.0.0.1:5000。我的 HA 是 host 網路、跟 Frigate 在同一台,所以直接走 loopback 上那個沒認證的 API。裝好會多出幾十個實體,像是 sensor.keting_cat_count(畫面裡幾隻貓)、binary_sensor.keting_hiss_sound(偵測到哈氣)。
  3. 打開 GenAI 開關:Frigate 整合會建立兩個 GenAI 的開關(物件描述、review 摘要),預設是停用的,要手動啟用再重啟 HA。另外整合提供的開關和數值實體(像是 number.keting_contour_area)是執行時的覆寫,會蓋掉 config 裡的值,調了 config 沒效果的話先看這裡。
  4. 回看介面:同樣從 HACS 裝 Advanced Camera Card,可以在 HA 裡看即時畫面、事件縮圖和時間軸。

如果你是先裝 Frigate 整合、後來才補 MQTT,要回去重新載入一次 Frigate 整合,它才會吃到 MQTT。

Advanced Camera Card 我調了幾個地方:

  • 選單預設是收起來的,進了時間軸會找不到路回即時畫面,我第一次只能重新整理頁面。改成 menu.position: outside,再設 view.default_reset 讓它閒置 120 秒自動回到即時畫面。
  • triggers.reviews 預設只有 high,在我的設定裡等於只有人;加上 medium,貓的事件才會自動跳出來。
  • 如果 go2rtc 的 API 只綁在 loopback,瀏覽器連不到,即時畫面要改用 live_provider: webrtc-card(搭配 AlexxIT 的 WebRTC 卡片)。
  • 在 sections 版面裡給它 column_span: 2,畫面才不會太小。

通知走 ntfy 還是 HA App

我自己用 Android,通知原本走自架的 ,可以附影片、設優先級、加按鈕。但家人用 iPhone 就收不到:iOS 不允許 App 在背景保持長連線,自架的 ntfy 沒有設定轉發到官方伺服器(upstream)的話,通知不會即時送達,官方文件寫的是可能延遲好幾個小時。家人手機本來就有裝 HA 的 App,它走的是蘋果的 APNs,所以 iPhone 的通知全部改走 HA App。如果你家只有一種手機,選一種就好,HA App 最省事。

用 ntfy 的話有兩個小坑:新開的主題手機 App 要另外訂閱,不然測試通知怎麼發都沒反應;附件大小上限是 server 設定檔自己設的,影片太大會被擋。

通知附上「剛才的畫面」

光寫「客廳偵測到哈氣」其實看不出發生什麼事,所以我讓通知附上一句 GenAI 摘要。麻煩的是摘要要等那段 review 結束後才會產生,警報觸發的當下還沒有;而火災警報、尖叫這種通知又不能為了等文案延遲。最後的做法是寫一支小腳本,抓最近 10 分鐘內已經有摘要的那一則,沒有就留空:

python
# /config/diary/recent_summary.py:印出最近一則有摘要的片段,沒有就印空字串
import json, sys, time, urllib.request

after = int(time.time()) - int(sys.argv[1]) * 60
revs = json.load(urllib.request.urlopen(
    f"http://127.0.0.1:5000/api/review?after={after}&cameras=keting&limit=100"))
done = [r for r in revs if (r["data"].get("metadata") or {}).get("title")]
if done:
    r = max(done, key=lambda r: r["start_time"])
    m = r["data"]["metadata"]
    t = time.strftime("%H:%M", time.localtime(r["start_time"]))
    print(f"{t} {m.get('shortSummary') or m['title']}"[:90])
yaml
# configuration.yaml
shell_command:
  frigate_recent_summary: "python3 /config/diary/recent_summary.py 10"

哈氣、嚎叫通常會持續一陣子,這樣抓到的多半就是同一起事件。下面是「偵測到貓哈氣」那條自動化的簡化版:

yaml
- id: cat_hiss_alert
  alias: 客廳偵測到貓哈氣
  mode: single
  triggers:
    - trigger: state
      entity_id: binary_sensor.keting_hiss_sound
      from: "off"
      to: "on"
  actions:
    - action: shell_command.frigate_recent_summary
      response_variable: ctx
    - variables:
        scene: "{{ (ctx.stdout | default('', true)) | trim }}"
    - action: notify.mobile_app_你的手機
      data:
        title: 客廳偵測到哈氣
        message: >-
          {{ now().strftime('%H:%M') }},畫面裡有
          {{ states('sensor.keting_cat_count') }} 隻貓。
          {%- if scene %} 剛才的畫面:{{ scene }}{%- endif %}
        data:
          image: /api/camera_proxy/camera.keting   # 附上當下的畫面
          url: /lovelace/living                    # 點通知打開客廳頁
          push:
            interruption-level: time-sensitive     # 穿透 iOS 專注模式

腳本輸出的是一行文字,例如我寫這段時實際跑出來的:「15:41 兩隻貓分別在沙發和貓塔貓窩內休息,環境安靜無異常活動。」這行會直接接在通知內文的「剛才的畫面:」後面。

跑起來之後要調的兩個地方#

上面的 config 已經包含這兩個修正,這裡說明為什麼要改。

第一個是 motion 幾乎一直在觸發。我統計了一下,2412 個 10 秒錄影片段裡有 2348 個被判定為有動作(97.3%),但真的有物件的只有 6.3%。原因有兩個:畫面左上角燒錄的時間戳每秒都在變,佔了靜止畫面八成以上的變動像素;另外每 2 到 4 秒會有一次全畫面的壓縮雜訊。這不只浪費 GPU,record.motion.days: 14 等於連續錄了 14 天。

yaml
motion:
  mask:
    - 0,0,0.38,0,0.38,0.075,0,0.075   # 遮掉左上角的時間戳
  contour_area: 25                    # 濾掉壓縮雜訊,貓的面積大約 500

改完之後 motion 比例降到 31%,畫面沒動靜時偵測器的 fps 是 0。

第二個是 GenAI 摘要預設不會幫貓寫。Frigate 的 review 分成 alerts 和 detections,貓屬於 detections,而 review.genai.detections 預設是 false:

yaml
review:
  genai:
    enabled: true
    detections: true          # 預設 false,貓的事件不會有摘要
    image_source: recordings  # 預設用 180p 的預覽圖,會漏掉人和貓

image_source 也改成用錄影(480p)。用預設的預覽圖時,有一段明明偵測到人(信心 0.96),摘要卻寫「客廳內完全靜止」。

選本機視覺模型#

我用同一批畫面比了三個模型,畫面裡實際是兩隻虎斑貓,另外放了幾張空畫面當幻覺對照組:

模型貓的數量花色人/貓中文耗時
gemma3:4b兩隻 ✓✗常搞錯繁體,但會編東西1.6s
gemma3:12b漏一隻✗把人寫成貓繁體,內容空泛2.9s
qwen3-vl:8b兩隻 ✓虎斑 ✓全對繁體,JSON 乾淨4.4s

最後用 qwen3-vl:8b。測的過程中也發現幾個 Frigate 預設值的問題:

  • 預設的物件 prompt 是「分析這組包含 {label} 的圖片」,等於先告訴模型畫面裡一定有那個東西,空畫面也會被描述出一隻貓。
  • 只設 preferred_language: Traditional Chinese 擋不住簡體。預設的 review 摘要寫出了「花盆从沙发搬运到凳子上」這種簡體句子,還自己編了一個人名「Joe」。一開始我以為是 gemma3:4b 不會寫繁體,後來發現是 Frigate 內建 prompt 的語言指令太弱,模型本身寫得出繁體。要在自己的 prompt 裡明寫「一律使用繁體中文」和「禁止編造人名」。
  • 預設 prompt 是給室外防盜用的,會提車道、包裹、深夜的陌生人,跟客廳完全不搭。
  • 物件描述預設是英文,而且每則都用「Here's an analysis of the cat's behavior based on the images...」這種廢話開頭,所以 prompt 裡才要寫 No preamble。

我把 prompt 改成符合我家的情境:這是台灣住家的客廳,不是防盜;固定物件只有沙發、茶几、貓塔、貓砂盆、紙箱、電風扇和空氣清淨機;貓塔上是封閉式的圓形貓窩;家裡沒有養狗;不要描述毛色、不要編人名、不要說持續了幾秒。

最後那幾條都是被幻覺逼出來的,而且是我自己翻日記時看出來的:我家根本沒有籠子。第一版跑了幾天之後,摘要裡出現過一天 14 次「貓在籠子裡」(其實是貓塔上的圓形貓窩)、「狗」,還有一則「貓低頭乾嘔,持續約 136 秒」,136 秒其實是那段錄影的總長度,模型只看了其中幾張影格。「狗」不全是視覺模型的錯:Frigate 的偵測器本身一天會把貓認成狗 8 次左右(信心 0.77 到 0.95),所以我直接把 dog 從 objects.track 拿掉。

我現在用的完整 prompt(activity_context_prompt)
yaml
review:
  genai:
    activity_context_prompt: |
      這是台灣一般住家的客廳監視器,不是防盜攝影機。
      - 住戶(成人)與兩隻貓在此活動,兩者進出都是完全正常的事。
      - 沒有車道、門廊、包裹等室外元素,不要提到這些。
      - 畫面裡固定會出現的東西只有這些:沙發、茶几、矮凳、貓塔、
        貓砂盆、紙箱、立式電風扇、空氣清淨機。
      - 貓塔上有一個封閉式的圓形貓窩。那是貓窩,不是籠子,家裡沒有任何籠子。
      - 畫面裡只拍得到其中一個貓砂盆(家裡有兩個)。
        「進入貓砂盆內、蹲踞或撥砂」才寫成使用貓砂盆;
        只是走過旁邊、在旁邊休息,就寫「在貓砂盆旁」。
      - 家裡沒有養狗。偵測結果若標成 dog 那是誤判,不要在描述裡提到狗。
      - 一律使用繁體中文(台灣用語)。
      - 禁止臆測或編造人名。未知人物一律寫「一名成人」或「住戶」。
      - 只描述畫面中確實看得到的東西,看不清楚就說看不清楚。
      - 不要描述貓的毛色或花紋,統一寫「一隻貓」。
      - 禁止聲稱任何行為「持續了幾秒/幾分鐘」。你看到的是抽樣出來的
        少數幾張影格,不是連續影片。
      - 不要用「全程」「整段時間」「持續」這類字眼。
      - Level 0(正常):住戶活動、貓的任何活動、日常家事。這是絕大多數情況的正確答案。
      - Level 1(可疑):不認識的人在屋內;貓明顯異常(持續嘔吐姿勢、抽搐、
        長時間倒地不動);物品翻倒。
      - Level 2(危險):破壞、打鬥、火煙、有人跌倒後無法起身。

Frigate 的 GenAI 其實有兩個地方會用到模型,我讓它們寫不同語言:

  • review 摘要(review.genai):一段時間的事件寫成標題和摘要,用繁體中文,給通知和日記用。
  • 物件描述(objects.genai):每個追蹤到的物件寫一句描述,存進語意搜尋的索引。語意搜尋用的 Jina CLIP v1 只支援英文(v2 才支援多語言),所以這邊的 prompt 用英文寫,要求一句話、不准描述毛色、沒看到貓就回 nothing notable。

這樣兩邊各用各的語言,不需要另外做翻譯。

改完 prompt 之後還有一個問題:舊的摘要已經寫進資料庫了。我先用同樣的影格比對新舊 prompt,確定新版真的不會再寫出籠子和假秒數,才去重寫舊的。例如 21:27 那段,舊版寫「貓咪疑似不適,持續低頭乾嘔」,新版是「一隻貓在立式電風扇旁的紙箱邊走動」。Frigate 只有重新產生「物件描述」的 API(PUT /api/events/<id>/description/regenerate),review 摘要沒有;網頁上的 Reindex 也只是拿現有的文字重算向量,不會重寫。所以我另外寫了一支腳本,從錄影抽影格、套用新的 prompt、用同一顆模型重寫,結果存在旁邊的檔案裡,日記讀的時候優先用它,Frigate 的資料庫不動。只挑有幻覺跡象的重寫,721 則裡挑出 72 則,每則約 3.6 秒。幾天後再抽查,幻覺從約 10% 降到 2.5%(1238 則裡 31 則),剩下的多半是「全程」「整段時間」這種用詞。

分辨兩隻貓#

這是唯一沒做成的部分。aki 和 kuro 是同一胎的兄妹,兩隻都是虎斑,一隻偏暖棕、一隻偏深灰,aki 比較深一點,連我自己看監視器畫面都常常認錯。

先查了別人怎麼做#

動手之前我先查了一輪,想知道「分辨長得一樣的貓」到底做不做得到。

最簡單的方法是戴不同顏色的項圈,這樣問題就從「認出是哪隻貓」變成「找畫面裡的紅色色塊」。可惜牠們不喜歡戴項圈,這條路直接放棄。家裡的寵物空氣清淨機(就是影片裡貓坐在上面那台)會量貓的體重,我也查過能不能拿來分:兩隻體重不一樣,誰坐上去就知道是誰。結果它的 API 不回傳體重,也是死路。

學術上這叫個體重識別(re-identification),論文其實不少:

  • 一篇用中國「哈囉街貓」餵食站畫面做的研究,用 2,796 張照片、69 隻貓訓練 Siamese 網路,最好的組合(VGG16 加 contrastive loss)測試準確率達到 97%。
  • 另一篇比較了 ResNet、DenseNet、EfficientNet、ConvNeXt 和 Siamese 網路認個別的貓,結論是用遷移學習訓練的傳統 CNN 表現最好。
  • 我看的幾篇研究有一個共同點:毛色是最不可靠的特徵,有用的是臉和身側條紋的形狀。鼻紋雖然像指紋一樣獨一無二,但要微距才拍得到;Double Take、CompreFace 這類現成的工具只認人臉。

商用產品也沒有比較好。Furbo 的多寵辨識(Pet ID)要訂閱才能用,作法是幫每隻寵物上傳一張看得到臉和部分身體的照片;官方說明裡沒有任何準確率數字,也沒提到長得像的寵物怎麼辦。App Store 的評論倒是寫得很直接:有人說每隻都建了檔案、而且「長得一點都不像」,它還是分不清楚;也有人說它一直把公貓叫成母貓、跟另一隻貓或狗搞混,大概一半的時間不準。長得完全不像的都會認錯,同一胎、花色一樣的兩隻,我不覺得會比較好。

這些研究的準確率都很漂亮,但前提是畫面夠近、夠清楚,而且大多是在很多隻花色各異的貓裡挑出一隻。我的情況剛好相反:只有兩隻、同一胎、花色一樣,鏡頭還在 3 公尺外。所以最後決定先用 Frigate 內建的功能試,不要一開始就自己搭訓練流程。

先排除:讓視覺模型認花色#

qwen 的摘要裡有時寫「黑貓」、有時寫「虎斑貓」,看起來好像可以直接拿來分。但同一個事件,前一秒寫「兩隻都是虎斑」,下一秒寫「一隻虎斑、一隻黑貓」。我把 33 個事件依框的位置和亮度統計:被說成黑貓的 6 次,全部出現在畫面右側門口那一帶,也就是逆光的位置,亮度並沒有比較暗。模型讀到的是位置和光線,不是貓本身,所以後來把花色從 prompt 裡拿掉了。

Frigate 0.17 的自訂分類#

Frigate 0.17 有內建的物件分類功能,可以對偵測到的貓再做一次分類,結果寫進 sub label。官方文件的範例剛好就是分兩隻貓。

yaml
classification:
  custom:
    cats:
      enabled: true
      name: cats
      threshold: 0.8
      object_config:
        objects: [cat]
        classification_type: sub_label

建立的流程在 Frigate 網頁的 Classification 頁:取名、選物件 cat、類型 sub_label、類別填 aki 和 kuro,然後逐類挑訓練圖,沒選到的會自動歸到 none。類別要一個一個分開加,我第一次把「aki / kuro」打成同一個類別,挑圖畫面的標題就變成「Select all images with: aki / kuro」。

挑圖的時候我發現很多張連我自己都認不出是誰。訓練圖固定是 224×224,而偵測原本跑在子串流 1280×720 上,裁出來的貓短邊只有約 187px,是被放大到 224 的。把偵測改到主串流 2304×1296 之後,裁切變成約 241px,看得到臉的輪廓和身側的條紋:

改主串流的代價是 shm_size 要拉大(第 5 步那個 1g 就是這樣來的),偵測吃的像素量變成 3.24 倍,不過 motion 比例沒有回升(31% 到 34.5%)。

幾個用起來才知道的規則:

  • 訓練圖只能從 Frigate 自己裁的圖裡挑,不能上傳自己拍的照片。維護者的說法是裁切方式要跟實際偵測時一致。
  • 訓練跑在 CPU 上,通常 1 到 3 分鐘。按下訓練後網頁上的轉圈有時會一直轉,那是狀態沒更新,不是卡住。
  • 指派名字的規則:每次分類信心要超過 threshold(我設 0.8),而且至少 3 次判斷裡有 60% 是同一個名字,才會寫進 sub label。

WARNING

訓練完的模型不一定會自動載入。log 裡沒看到 Successfully loaded updated model 那行的話,新模型其實還沒上線,重啟 Frigate 才會載入。

前後訓練了幾次。第一版只有 24 張(aki 11、kuro 3、none 10),上線後模型標出來的 aki 和 kuro 比例是 3.7 比 1,剛好等於訓練集的 11 比 3,它學到的是比例而不是貓。最後訓練集是 aki 188 張、kuro 204 張、none 41 張。

怎麼知道模型準不準#

模型會幫每隻貓標上名字,但標得對不對,我自己肉眼也判斷不了。後來用的方法是同框:兩隻貓同時出現在畫面上時,模型給牠們的名字一定要不一樣。這是不需要人工判斷的標準答案。

實際算的時候有個坑:Frigate 有時會把同一隻貓追丟又重新抓到,變成兩個物件,看起來像同框。所以框重疊很多(IoU 大於 0.3)的配對要排除,幾乎不重疊的才算真的兩隻。

一開始扣掉追丟重抓的,真正的同框只剩 2 組,根本無法判斷。等到 9 月 5 日累積到 34 組真正的同框,被標成不同名字的只有 13 組,38%。兩隻貓被指派的比例大約一半一半,所以亂猜的期望值是 50%,模型比亂猜還差。

再拿模型去跑它自己的訓練集,正確率是 96.2%。訓練集 96%、沒看過的資料 38%,代表它是把訓練圖背起來了。原因在訓練資料本身:392 張圖其實只來自大約 30 個事件。我挑圖的方式是在網頁上點一個追蹤物件,把它的裁切圖整批收進去,所以同一個事件連續收了 5 到 20 張幾乎一樣的圖,有效的樣本數只有 30 左右。

訓練集還有另一個偏差:C210 晚上會切成紅外線黑白畫面,顏色線索整個消失。aki 的 188 張裡有 138 張是黑白的(73%),kuro 只有 26%。aki 本來就比較深,我在標的時候,暗暗的圖就都歸給了牠。我在 Recent 分頁看到的也是這樣:大部分都被標成 aki,只有明亮清楚的才認得出 kuro。不過模型在 kuro 的黑白圖上還是有 92.5% 標對,所以主因還是背答案,不只是「看到黑白就猜 aki」。Frigate 官方的建議也寫得很直接:多樣性比數量重要,一次選幾十張幾乎相同的圖,是讓模型變差最快的方式之一。

我也試了一個不用標註的方法:Frigate 為了語意搜尋,已經幫每隻偵測到的貓存了一份 CLIP 向量。拿 171 組同框配對去看這些向量能不能把兩隻分開,結果是 56.1%,只比亂猜好一點。

結論:瓶頸是鏡頭#

客廳的鏡頭離貓大約 3 公尺,貓臉只佔畫面寬度約 6%。兩隻花色一樣的貓,在這個距離下能分辨的特徵太少,換模型或多標幾百張圖也不太會改善。比較可行的方向是近距離的鏡頭,例如放在餵食區、離貓 50 公分的那種。

目前的處理是把 aki 和 kuro 的次數從日記和通知裡拿掉,頁面上掛一張警示卡。等餵食器那顆近拍鏡頭的資料累積夠了,再用同樣的同框方法重新驗證。

貓咪日記#

日記是這次最常用的功能。每個事件 Frigate 會產生一段摘要,我在 HA 上做了一個獨立頁面,每 30 分鐘重新整理一次,晚上 10 點推播當天的版本:

架構很單純:HA 每 30 分鐘透過 shell_command 執行一支 Python 腳本,它從 Frigate 的 /api/review 撈當天的事件和摘要,整理成一份 JSON 存在 HA 的 config 目錄;HA 再用一個 sensor 讀這份 JSON,頁面用 Markdown 卡片排版。晚上 10 點的推播讀的也是同一份,頁面和通知的數字才不會對不上。

做的過程中遇到幾個問題:

  • 一開始整份日記是在 HA 的 script 裡用模板算的,資料一多就撞到 HA 模板輸出 256KB 的上限(Template output exceeded maximum size of 262144 characters)。後來改成一支 Python 腳本產生 JSON,HA 只負責讀。
  • Frigate 0.17 會把通過自訂分類的物件標成 cat-verified,我原本用 "cat" in objects 篩選,結果靜默漏掉了 66% 的貓事件。
  • 頁面最上面要嵌當天的精華影片,結果換了新影片瀏覽器還是播舊的。原因是 HA 的 /local/ 回應帶了 31 天的快取標頭,強制重新整理也沒用。最後在影片網址後面加上檔案修改時間當版本號(?v= 加 mtime)才解決。影片直接用 Markdown 卡片裡的 video 標籤嵌,寬度要寫成 width="100%" 屬性,寫 style 會被 HA 的白名單剝掉。精華影片晚上 9 點 50 分才產生,一天有 22 小時「今天的影片」不存在,所以沒有今天的就退回顯示最近一支。
  • /local/ 底下的檔案不需要登入就能讀,所以放影片的資料夾我用一串隨機字元當名字,等於拿網址當密碼。
  • 推播的部分,家人的 iPhone 走 HA App(原因見第 7 步),日記設成被動通知,不會一直跳出來;異常聲音設成時效性通知。

除了日記,我也加了幾個 Furbo 沒有的小指標:跟過去 7 天同時段比較的活動量、客廳有人陪貓的時間、太久沒看到貓的提醒。最後這個一開始只設固定 4 小時,我想到的問題是:如果牠們跑去房間睡呢?我只有客廳這一顆鏡頭。所以門檻改成過去最長空窗的兩倍、至少 4 小時,每 30 分鐘檢查一次,通知文案也寫明「只有客廳這一顆鏡頭」。貓砂盆的部分只拍得到兩個之一,而且模型分不出是「經過」還是「使用」,所以只顯示「接近貓砂盆」的次數當趨勢參考。

每日精華影片#

這是照 Furbo 最受歡迎的功能做的:每天晚上 9 點 50 分,從當天的事件裡挑片段,剪成一支加速 1.6 倍的影片,放在日記頁最上面。Furbo 的日記只取早上 7 點到晚上 7 點,我沒有照做,因為我家的貓半夜最活躍,凌晨 2 點、3 點各有三十幾個事件。

第一版剪出來我看了之後覺得不對:晚上兩隻貓一起睡在沙發上、睡到翻肚的片段沒被剪進去,它到底是怎麼挑的?查下去有兩個問題。一是迴圈照小時遞增、選滿 10 段就停,所以永遠只會選到凌晨到早上 9 點;二是排序用的是偵測信心,而貓的信心幾乎全部在 0.98 到 0.99 之間,排序等於隨機。

修完之後先試了一版複合評分,兩隻貓同框加權、片段長度加權。但我覺得同框不是重點,精華應該是比較可愛、比較有事發生的片段,而真正知道「這段在幹嘛」的資訊,只存在 GenAI 寫的那句摘要裡。所以改成讓同一顆 qwen3-vl:8b 根據自己寫的摘要,替每個事件打 1 到 5 分:玩耍、追逐、理毛、伸展、兩隻互動、跟人互動是 5 分,靜止不動是 1 分。這裡也有幾個坑:

  • Ollama 的 format: "json" 只保證輸出是合法的 JSON,不保證格式。模型有一次自己吐了一份保全報告格式的 JSON。改用 structured outputs 指定 JSON Schema 才穩定。
  • 一次丟 95 則讓 8B 模型排序,它只會回前 10 個。改成每批 20 則、逐則打分。

整支腳本是 Python,每天 21:50 由 cron 執行,核心是三個 API 呼叫。下面是簡化過的版本:

python
import json, urllib.request

FRIGATE = "http://127.0.0.1:5000/api"
OLLAMA = "http://127.0.0.1:11434/api/generate"

# 1. 拿當天的 review 和 GenAI 摘要。參數是複數 cameras,
#    寫成單數 camera 會被靜默忽略,兩台鏡頭的片段混在一起
revs = json.load(urllib.request.urlopen(
    f"{FRIGATE}/review?after={start}&before={end}&cameras=keting&limit=2000"))

# 0.17 通過自訂分類的物件會變成 cat-verified,要把後綴拿掉再比
segs = [r for r in revs
        if any(o.removesuffix("-verified") == "cat" for o in r["data"]["objects"])
        and r["data"].get("metadata")]

# 2. 讓同一顆模型依摘要打分,format 一定要給 JSON Schema
SCHEMA = {"type": "object", "properties": {"scores": {"type": "array", "items": {
    "type": "object",
    "properties": {"i": {"type": "integer"}, "s": {"type": "integer"}},
    "required": ["i", "s"]}}}, "required": ["scores"]}

lines = [f"{i} {r['data']['metadata']['title']}" for i, r in enumerate(segs[:20])]
body = json.dumps({
    "model": "qwen3-vl:8b-instruct",
    "prompt": SCORE_PROMPT.format(items="\n".join(lines)),  # 1 到 5 分的評分標準
    "stream": False, "format": SCHEMA, "options": {"temperature": 0.1},
}).encode()
req = urllib.request.Request(OLLAMA, data=body, headers={"Content-Type": "application/json"})
scores = json.loads(json.load(urllib.request.urlopen(req))["response"])["scores"]

# 3. 依時間範圍直接切片段,不要抓整個 event 的 clip
#    (最長的事件有 2513 秒,伺服器要現場轉檔 42 分鐘)
clip = f"{FRIGATE}/{seg['camera']}/start/{t0:.3f}/end/{t1:.3f}/clip.mp4"

拿到片段之後用 ffmpeg 加速 1.6 倍(畫面 setpts、聲音 atempo),最後 concat 接成一支。接的時候所有片段的編碼參數要一致,所以只取客廳那顆鏡頭,有沒有音軌也要全部統一。

跑了一週,我還是覺得精華常常怪怪的。這次不是挑錯段,而是片段本身切錯:每段固定擷取 9.6 秒,但很多事件比 9.6 秒短,30 段裡有 9 段比擷取長度還短,有一段甚至是 0 秒。回頭追為什麼要固定 9.6 秒,答案是沒有任何技術理由,當初隨手訂的。改成依事件長度變動(輸出 2 到 10 秒),總長度控制在 180 秒。

最後一個問題是重複。其中一隻貓很常在貓塔的圓形貓窩裡睡覺,一天的精華裡有好幾段都是同一個貓窩:

一開始的想法是用摘要文字判斷重複,但我覺得標題一樣不是問題,重點是畫面內容,所以實際把縮圖抽出來看。結果文字相似度根本是反指標:同樣的描述畫面可能完全不同,不同的描述反而可能是同一個貓窩;拿整張畫面比相似度也不行,會被固定的背景主導。最後用的是偵測框的中心位置加面積:中心距離小於 0.1、面積差在兩倍內就算同一個位置,每個位置最多 3 段。選段分兩階段,前 70% 的時間純看分數,後 30% 補平常比較少被選到的時段。影片變長我不在意,只怕硬碟不夠,所以總長度抓 180 秒:實測每秒約 59KB,一支 10MB 左右,存 7 天不到 100MB。

現在每天產出大約 18 到 26 段、兩分多鐘、12MB 上下的影片,保留 7 天。下面是 10 月 12 日那支精華的其中 30 秒(原本有收音,放上來時拿掉了):凌晨一隻貓從門口走進來,清晨 5 點跳上貓塔,在上面打了個大呵欠。

0:00 / 0:00
每日精華影片節錄:片段由 qwen3-vl:8b 評分挑選,1.6 倍速

順帶一提,寫這篇的時候我拿影片裡打呵欠的那一格直接問 qwen3-vl(沒有套我的 prompt),它回的是「畫面中的黑貓正站在貓爬架的最上層……嘴巴張開,似乎在嘶叫」。虎斑被說成黑貓、呵欠被說成嘶叫,前面那段 prompt 要寫那麼細就是這個原因。

資源占用#

這套東西全部跑在家裡的伺服器上,下面是 10 月 13 日實際量到的數字(那時已經多接了一台餵食器的鏡頭,所以解碼有兩條)。

VRAM 的部分,nvidia-smi 看到的是:

行程VRAM
Ollama:qwen3-vl:8b(常駐)約 6.9 GB
Frigate 語意搜尋(CLIP 向量)約 0.8 GB
Frigate 偵測器(ONNX RF-DETR Nano)約 0.45 GB
ffmpeg 硬體解碼(每台鏡頭)約 0.3 GB
合計約 8.7 GB / 16 GB

大頭是視覺模型。我讓 Ollama 一直把模型留在 VRAM 裡(OLLAMA_KEEP_ALIVE=-1),因為每次重新載入都要等(一開始用 gemma3:4b 時,第一則摘要等了 36 秒)。照這個數字,8GB 的顯卡塞不下這個組合,12GB 以上會比較從容。精華影片的評分也是用同一顆 8B,如果改用 27B 的版本,就會把常駐的 8B 擠出 VRAM。

其他資源:

  • GPU 使用率:平常 2% 到 3%,整張卡待機約 31W。Frigate 的偵測只在畫面有動作時才跑,一次約 6ms,沒動靜時偵測 fps 是 0。真正會讓 GPU 跑滿的是寫摘要:我實測一則摘要約 3 秒,這段時間功耗衝到 160 到 170W。客廳每天大約 160 到 240 個事件,加起來 GPU 每天滿載的時間大約 10 分鐘。
  • CPU 和記憶體:docker stats 看 Frigate 整個容器是 17% 左右(100% 等於一個核心),記憶體約 5GB,容器上限我設 10GB。
  • 硬碟:客廳鏡頭連續錄影一天約 10GB。保留天數原本規劃的是連續錄影 14 天、有動作的 30 天、事件 60 天,我不想佔太多硬碟、也不想讓硬碟整天瘋狂寫入,所以縮成連續錄影 3 天、有動作的 14 天、事件 30 天,目前客廳的錄影總共約 69GB,另外快照和事件片段約 6.7GB。精華影片一天 12MB,保留 7 天可以忽略。換句話說,錄影能回看多久完全看硬碟有多大:以客廳一天 10GB 算,1TB 就能連續錄大約三個月,跟 Furbo 用 3 天或 7 天分訂閱等級是完全不同的思路。

跟 Furbo 比#

功能Furbo(訂閱)我的做法
錄影回看3 或 7 天,只有 AI 判斷的片段連續錄影,能存幾天只看硬碟多大(我設連續 3 天、事件 30 天)
事件摘要寵物保姆觀察日誌本機 qwen3-vl:8b,繁中
每日精華影片有有,LLM 評分挑片段
叫聲、異常聲音通知有有,附上前幾分鐘的畫面摘要
分辨每一隻寵物有(多寵辨識),但評論常抱怨認錯沒做成,同框驗證 38%
丟零食有沒有
費用攝影機 1,699 起(綁約),標準方案首年 1,840 元0,用現有的鏡頭、顯卡和硬碟

其實在驗出 38% 之前,我就已經在找有鏡頭的自動餵食器,原本是想補客廳鏡頭拍不到的地板角度,順便自動餵食。分貓失敗之後它多了一個任務:貓吃飯的時候離鏡頭只有幾十公分,剛好就是分貓需要的近拍。後來買的那台現在也接進 Frigate 了,怎麼接起來、分貓有沒有變準,會寫在另一篇。

參考連結