DockerとGoogle CoralでFrigate NVRを構築:自宅ラボ向けリアルタイムAIカメラ検知

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

背景と導入理由:クラウド依存の監視システムからの脱却

半年前、自宅の監視カメラ構成は限界を迎えていました。6台のReolink 4KカメラからSynology NASへ直接RTSPストリーミングを行っていたものの、カメラ内蔵のピクセル動体検知に完全に依存していたため、結果は目に見える混乱となりました。風に揺れるオークの枝、ブラインドを横切る車のヘッドライト、赤外線レンズの前を横切る蛾など、あらゆる些細な動きで即座に通知が発報され、夜間だけでスマートフォンに40〜60件もの通知が届く有様でした。あっという間にアラート疲れに陥り、万が一本当に空き巣が入ったとしても完全に埋もれて見逃してしまうような危険な状態でした。

RingやNestといった一般消費者向けのクラウドエコシステムはスマートな検知機能をアピールしていますが、月額10〜20ドルのサブスクリプション契約に縛られます。さらに厄介なのは、4K映像6本分をアップロードすると約30〜50Mbpsもの上り帯域を消費し、プライベートな映像をサードパーティのサーバーへ送信し続けることになる点です。ローカルでのデータ保持とゼロ遅延の自動化を重視するなら、カメラの映像フィードを外部に任せるのはナンセンスと言わざるを得ません。

そこで従来のワークフローを刷新し、Docker上で動作するFrigate NVRとGoogle Coral USB Edge TPUを中心とした監視基盤をゼロから再構築しました。Frigateのアーキテクチャが秀逸なのは、4K/30fpsの全ストリームに対して無理やりAI推論を実行する力技を避けている点にあります。代わりに、720p/5fpsといった低解像度のサブストリームを監視してピクセルの変化のみを検知し、動きを感知したエリアの境界ボックス(バウンディングボックス)だけを切り取ってCoral TPUに渡して推論を行います。

そのパフォーマンス改善効果は絶大でした。Intel Core i5-10400サーバーにおけるホストCPU使用率は、OpenCVのソフトウェア検知時の85%から12%未満へと急落しました。オブジェクトの推論時間も、x86 CPUコアでの160msからCoralでのわずか8.5msへと劇的に短縮されました。24時間365日の連続録画を6か月間運用した結果、本物のイベントをひとつも見逃すことなく、数千件にも及ぶ誤報を完全に排除できています。

インストール:ホストの準備とDockerデプロイ

DockerでFrigateを起動する前に、Coralアクセラレータと通信できるようにホストカーネルを準備し、プロセス間でフレームを共有するための十分な共有メモリを割り当てておきます。

1. ホストの準備とUSB権限の設定

DebianまたはUbuntuホストのネイティブなUSB 3.0ポートにGoogle Coral USBを接続します。lsusbを実行して、カーネルが物理デバイスを正しく認識しているか確認してください。

lsusb | grep -i "Global Unichip\|Google"
# 初期化前のデバイスは ID 1a6e:089a Global Unichip Corp. と表示されます
# Edge TPU ランタイムによって初期化されると、ID は 18d1:9302 Google Inc. に切り替わります

ベンダーIDの変化に注目してください。ハードウェアがファームウェアをロードする際にデバイス識別情報が変わるため、コンテナが読み書き権限を維持できるように両方の状態をカバーするudevルールを追加する必要があります。

sudo bash -c 'cat <<EOF > /etc/udev/rules.d/99-edgetpu-accelerator.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", MODE="0666"
EOF'

sudo udevadm control --reload-rules && sudo udevadm trigger

2. ストレージ構成と共有メモリの割り当て

Frigateは、デコードされた生のビデオフレームをやり取りするゼロコピーパイプラインとして共有メモリ(/dev/shm)を使用します。Dockerのデフォルトサイズはわずか64MBしかなく、高ビットレートのカメラを複数接続すると数秒でクラッシュしてしまいます。目安として、カメラ1台につき30MBを割り当てるのが適切です。6台構成の場合、256mbを割り当てておけば十分な余裕を確保できます。

設定ファイルと録画データを永続化するためのディレクトリをホスト上に作成します。

mkdir -p ~/frigate/config ~/frigate/storage

3. Docker Composeの定義

以下は、Coral USBのパススルー、Intel QuickSyncによるハードウェアデコード、RAMベースのキャッシュを設定した実戦仕様のdocker-compose.ymlです。

services:
  frigate:
    container_name: frigate
    privileged: true # TPUおよびGPUノード向けのハードウェアパススルーを簡素化
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:stable
    shm_size: "256mb"
    devices:
      - /dev/bus/usb:/dev/bus/usb # Coral USBバスをパススルー
      - /dev/dri/renderD128:/dev/dri/renderD128 # Intel QuickSync / VA-API ノード
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ~/frigate/config:/config
      - ~/frigate/storage:/media/frigate
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000 # SSDの書き込み寿命の摩耗を抑える1GBのRAMキャッシュ
    ports:
      - "5000:5000"
      - "8554:8554" # RTSP再配信
      - "8555:8555/tcp" # WebRTC再生
      - "8555:8555/udp" # WebRTC再生
    environment:
      FRIGATE_RTSP_PASSWORD: "SecureRtspPassword123"

バックグラウンドでコンテナを起動します。

docker compose up -d

設定:ストリーム、ディテクター、マスクの最適化

パイプラインの全設定は~/frigate/config/config.ymlに記述します。初心者が陥りがちな失敗は、4Kメインフィードに対して直接オブジェクト検知を実行してしまうことです。これは絶対に避けましょう。カメラのストリームは、検知ループ専用の720pサブストリームと、録画保存用のフル解像度メインストリームの2系統に分割して設定します。

1. 本番向け完全設定例

mqtt:
  enabled: true
  host: 192.168.1.10
  user: mqtt_user
  password: mqtt_password

detectors:
  coral:
    type: edgetpu
    device: usb

ffmpeg:
  hwaccel_args: preset-vaapi

record:
  enabled: true
  retain:
    days: 3
    mode: all
  events:
    retain:
      default: 14
      mode: active_objects

snapshots:
  enabled: true
  retain:
    default: 14

cameras:
  driveway:
    ffmpeg:
      inputs:
        # 軽量な動体検知およびTPU推論用のサブストリーム(720p @ 5fps)
        - path: rtsp://viewer:[email protected]:554/h264Preview_01_sub
          roles:
            - detect
        # 24時間常時録画およびスナップショット用のメインストリーム(4K @ 25fps)
        - path: rtsp://viewer:[email protected]:554/h264Preview_01_main
          roles:
            - record
    detect:
      enabled: true
      width: 1280
      height: 720
      fps: 5
    objects:
      track:
        - person
        - car
      filters:
        person:
          min_score: 0.65
          threshold: 0.75
        car:
          min_score: 0.60
          threshold: 0.70
    motion:
      mask:
        # 空、遠くの樹冠、タイムスタンプをマスク(検知解像度の座標)
        - 0,0,1280,0,1280,180,0,180

2. 重要なチューニングパラメータ

  • 検知フレームレート(5 fps): 検知fpsは5に固定してください。歩行者や走行中の車がフレーム境界を通過する速度は比較的緩やかなため、これを15や30に引き上げてもトラッキング精度は向上せず、CPU消費量だけが3倍に跳ね上がります。
  • tmpfs RAMキャッシュ: Frigateは入力フィードを10秒ごとのセグメント単位で取り込みます。これらの一時スライスをディスクに書き込み続けると、1日あたり数百GBもの書き込みが発生してしまいます。/tmp/cacheを1GBのtmpfsマウントに向けることでこのパイプラインをメモリ上で完結させ、NVMeドライブの早期寿命消耗を防ぐことができます。
  • モーションマスク: CoralはFrigateがピクセルの変化を検出するまで完全にアイドル状態を維持します。Frigate内蔵のWeb UIマスクエディタを活用して、画面上のタイムスタンプ表示、風で揺れる木々、遠くの道路などをマスクしてください。ピクセルが動体検知に引っかからなければ、TPUが無駄にリソースを消費することもありません。

動作確認とモニタリング:6か月の運用メトリクス

予期せぬサービスの再起動を起こすことなく24時間365日体制で半年間稼働させた経験から、初期段階でハードウェアアクセラレータの動作を正しく検証しておくことが、後のトラブルシューティングに費やす膨大な時間を節約する鍵になると確信しています。

1. ハードウェアアクセラレーションとTPU速度の確認

ブラウザでhttp://<your-server-ip>:5000を開いてFrigateのWebインターフェースにアクセスし、Systemタブを開きます。Coralの推論時間(Inference Speed)を確認してください。

  • 目標推論時間: Google Coral USBの場合、7ms〜10msが正常な標準値です。この数値が50msを超えている場合はUSBバスのパススルー設定が失敗しており、低速なCPUエミュレーションにフォールバックしている可能性があります。
  • カメラプロセスのオーバーヘッド: Intel VA-APIがH.264/H.265のデコードを正しく処理していれば、最新のx86チップにおけるカメラプロセス1台あたりのCPU使用率はわずか1.5%〜3%程度に収まるはずです。

コンテナのシェルからVA-APIドライバのエンドポイントが正常にロードされていることを確認します。

docker exec -it frigate vainfo
# 出力に VAProfileH264Main、High、HEVCMain プロファイルが表示されることを確認

2. リアルタイムMQTTテレメトリの監視

FrigateはMQTTを介してホームオートメーションハブと直接通信します。ターミナルからmosquitto_subを使用して、アクティブなイベント状態やセンサーのペイロードを追跡できます。

mosquitto_sub -h 192.168.1.10 -u mqtt_user -P mqtt_password -t 'frigate/#' -v

監視すべき主要トピックは以下の通りです。

  • frigate/<camera_name>/person: 即時のHome Assistantトリガー用に1または0を発行します。
  • frigate/events: 正確なバウンディングボックスの座標、検知信頼度スコア、ゾーン進入情報を含むJSONペイロードを発行します。
  • frigate/stats: カメラごとのデコードfps、TPU温度、推論速度、ストレージ使用状況などの定期的な診断情報を生成します。

3. 運用から得られた知見と教訓

猛暑の夏や激しい嵐の中でこの構成を運用する中で、実用面における3つの重要なポイントが明らかになりました。

  1. USBバスの電力と発熱管理: Coral USBは継続的な推論スパイク時に最大900mAを消費し、温度も目に見えて上昇します(約52℃)。そのため、マザーボード背面のI/Oポートに直接接続してください。セルフパワー非対応のハブやフロントパネルの延長ケーブル経由で接続すると、USBバスのリセットやデバイスの切断が頻繁に発生します。
  2. 高速ストレージと低速ストレージの分離: すべてのデータを1つのディスクに混在させないでください。Dockerの設定、SQLiteデータベース(frigate.db)、Home Assistantの自動化関連データはNVMe SSDに配置して高速なクエリを確保します。一方で、24時間の常時録画ストリームは、シーケンシャル書き込み性能に優れたセカンダリZFSプールや大容量HDDアレイへ保存するのがベストです。
  3. ゾーンと閾値のチューニング: オブジェクトを追跡するだけでは半分しか解決しません。車道や玄関先などにポリゴンゾーンを明示的に定義しましょう。人物検知にはmin_score: 0.65とthreshold: 0.75を設定します。これにより、真夜中に車のヘッドライトが横切った際、駐車中の車両が誤って検知され続けるような誤報を防ぐことができます。

Frigate NVRとGoogle Coral TPUを組み合わせることで、手頃なRTSPカメラが高性能なエンタープライズ級の検知エンジンへと生まれ変わります。1秒未満の高速スマートアラート、サブスクリプション不要の運用、そして自宅のローカルハードウェア上で完結する完全なプライバシーを同時に手に入れることができます。

Share: