以前の記事でインストールしたZoneMinderのマシンが不調でしたので、これを機に、Frigate に乗り換えました。Frigateは、AIで人や車を検知できる NVR(ネットワークビデオレコーダー)です。古い Intel NUC に Ubuntu Server を入れただけの状態から、Docker と Frigate をインストールして、最初の1台のカメラの映像が web UI に映るまでの手順をまとめておきます。
Frigateについて
以前の記事で、ZoneMinderをインストールしました。

特に問題なく動作していたのですが、マシン(古い Intel NUC)が不調だったのかしばらくして動かなくなってました。とりあえずUbuntu Serverだけを入れ直して、放置してました。放置中の2年間、マシンは安定してました。そこで、再度、ZoneMinderを入れ直そうかと思ったのですが、他の選択肢も調べたくなりました。Claudeに、無料で使えるオープンソースのおすすめを聞いてみたところ、以下を挙げてくれました。
- Frigate:今いちばん勢いがあります。AIで人・車・動物などを検出するので、木の揺れや光の変化による誤検知がZoneMinderの動体検知よりかなり少なくなります。Docker で動かし、Google Coral(1万円前後のUSB TPU)や Intel の内蔵GPU を使えば、CPU負荷が低いまま検出できます。Home Assistant との連携も強いです。ZoneMinderからの乗り換え先として最有力だと思います。
- Shinobi:Node.js 製のNVRで、ZoneMinderより画面が今風です。たくさんのカメラを録画することに向いています。
- motionEye:設定がとても簡単です。Raspberry Pi で1〜2台を動かすような小規模な使い方なら十分です。
- Agent DVR(iSpy の後継):Windows・Mac・Linux で動き、ローカルで使う分には無料です。外出先からのリモートアクセスは有料のサブスクになります。
とのことでした。Intel内蔵GPUが使えるなら、NUCにぴったりと思い、Frigateを試すことにしました。サイトはこちらです。
マシン環境
インストールするマシンとカメラの仕様は以下です。

- マシン:Intel NUC(Core i5-4250U、第4世代 Haswell、内蔵 GPU は Intel HD 5000)、メモリ 16GB
- ストレージ:mSATA SSD 500GB(OS 用)と2.5インチHDD 2TB(録画用)をNUCに内蔵。
- OS:Ubuntu Server 24.04 LTS(インストール直後の状態)。ディスプレイもキーボードも付けず、Mac から SSH で操作
- カメラ:AliExpress で買った3,000円未満の安価な PoE 方式 IP カメラ(中身は Xiongmai 系)。H.264 で配信
Intel NUCは、小型で省電力のPCでした。10年近く前の非力な CPU ですが、映像のデコードを内蔵 GPU に任せれば、Coral などの AI アクセラレータを追加しなくても、数台のカメラを CPU 使用率3割程度で動かせます。ただ、Haswellは古いのでH.265には未対応で、H.264 にしか対応していません。安価なカメラも、たいていは設定で H.264 にできるので問題ありません。
以下、Claudeの指示に従って、現状を確認し、環境を整え、インストールを進めます。
/dev/driにrenderD128があれば、内蔵 GPU を映像のデコードに使えるそうです。大丈夫でした。
$ ls -l /dev/dri
total 0
drwxr-xr-x 2 root root 80 Oct 17 23:35 by-path
crw-rw---- 1 root video 226, 1 Oct 17 23:35 card1
crw-rw---- 1 root render 226, 128 Oct 17 23:35 renderD128
HDD をフォーマットして、/media/frigate に自動でマウントされるようにします。デバイス名(/dev/sda1)は自分の環境に合わせて読み替えてください。
sudo mkfs.ext4 -L frigate-rec /dev/sda1
sudo mkdir -p /media/frigate
echo 'LABEL=frigate-rec /media/frigate ext4 defaults,noatime,nofail 0 2' | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount -a
df -h /media/frigate
デバイス名ではなくラベル(frigate-rec)でマウントするので、ディスクの認識順が変わっても大丈夫です。nofail を付けておくと、HDD が壊れても NUC 自体は起動します。
録画の時刻が正しくないと困るので、タイムゾーンを日本時間にします。
sudo timedatectl set-timezone Asia/Tokyo
timedatectl
続けて、内蔵 GPU でどの動画形式をハードウェアデコードできるかを確認します。
sudo apt update
sudo apt install -y vainfo i965-va-driver
sudo LIBVA_DRIVER_NAME=i965 vainfo --display drm --device /dev/dri/renderD128
出力に VAProfileH264Main : VAEntrypointVLD があったので、H.264 のハードウェアデコードが使えるとのことです。ただ、この世代の GPU は H.265(HEVC)のハードウェアデコードに対応していません。カメラの設定で、配信の形式を H.264 にしておきます。また、新しい Intel GPU 用のドライバではなく、古い i965 ドライバを使います。
Frigateをインストールする
Claudeにお願いして、Frigateをインストールして、最初のカメラ1台を登録して、動作を確認しました。長々とした手順は鬱陶しいかと思い、この記事の末尾に「付録:Frigateインストール手順」として書いておきます。実際にインストールを試みる方は、参考にしてください。
ただ、記事を読みながらインストールを進めていくよりも、AIに相談するのがずっと楽で間違いないと思います。今回の場合ならば、
Ubuntu Serverだけをインストールして、有線LANに固定アドレスを設定したLinuxマシンに、Frigateをインストールしたいです。このLinuxマシンは、古いIntel NUCで、CPUは、Intel(R) Core(TM) i5-4250U CPU @ 1.30GHzです。ストレージは、500GB SSDと 2TB HDDで、SSDにシステムがインストールしてあり、HDDは録画用にする予定です。メモリは16GBです。ディスプレイやキーボードは付いていません。Macから、sshで接続していますので、Frigateインストールのためのコマンドを指示してください。
というようなプロンプトでClaudeにお願いしたところ、付録に示したような手順を示してくれました。コマンドに対する応答が正しいかどうかも確認してくれます。また、必要に応じて、環境についての質問(接続しようとしているIPカメラの情報など)もしてくれます。
1時間くらいの作業で、最初のカメラ映像を、FrigateのWebページに表示することができました。

AI検知の設定
AIによる検知は、カメラ単位で指定できます。人の形のアイコンをクリックすると、人、車、動物などの検知が有効になります。(下の状態では、検知は動作していません)

検知対象の種類は、左側の設定アイコンから指定できます。検知対象は、カメラ全体と、個々のカメラのそれぞれに指定できます。普通は、「人」を選び、必要に応じて「車、自転車」などを選ぶことになると思います。

web UI のボタンでの切り替えは、Frigate を再起動しても保持されます。ただし、設定ファイル(config.yml)の値は書き換わらないので、設定ファイルと実際の状態が食い違うことがあります。現在状態は web UI で確認するのが確実です。
RTSPの再配信
今まではIPカメラに、Homebridgeが接続していました。他に、Amazon Fire TVのアプリで、IPカメラを閲覧することもありました。それに加えて、今回、Frigateがカメラに接続することになります。貧弱なハードウェアのIPカメラに、複数のアプリが接続すると、負担がかかり、不安定になる可能性もあります。
そこで、カメラへアクセスするアプリをFrigateだけにして、他のアプリは、Frigateが配信する画像を見ることにします。IPカメラのコンピュータに比べたら、頑丈なNUCですので、合理的です。
Frigate に内蔵されている go2rtc は、各カメラの映像を NUC のポート 8554 で RTSP として再配信しています。docker-compose.yml でポート 8554 を開けてあるので、例えば以下のURLで接続できます。
rtsp://<NUCのIP>:8554/front
URL の最後の名前は、config.yml の go2rtc: streams: に書いた名前です。Frigate以外は、これを指定すれば、IPカメラへの負担を軽減できます。例えば、HomebridgeのCamera FFmpegプラグインの設定に、
{
"name": "Camera FFmpeg",
"cameras": [
{
"name": "Front",
"unbridge": true,
"videoConfig": {
"source": "-i rtsp://<NUCのIP>:8554/front"
}
},
と書いておけば、NUCが配信する2次的なストリームを利用できます。
なお、Frigateが再配信しているストリームは、パスワード保護されていません。LANに接続した人なら、パスワード無しで見られますので、URLの扱いに注意が必要です。
以下は、試していないので未確認ですが、パスワード保護が気になる場合は、config.ymlのgo2rtc:に認証を追加することもできるそうです。これは、.env に FRIGATE_RTSP_USER と FRIGATE_RTSP_PASS を追加しておいた場合の例です。
go2rtc:
rtsp:
username: "{FRIGATE_RTSP_USER}"
password: "{FRIGATE_RTSP_PASS}"
streams: ...
そして、2次ストリームを利用するアプリでは rtsp://ユーザー:パスワード@<NUCのIP>:8554/front の形で接続します。
ただし、go2rtc の RTSP に認証を付けると、Frigate 自身もgo2rtcに接続しているので、録画や検知ができなくなる可能性もあるとのことです。認証を付けたら、Frigate の映像や録画が止まっていないか確認し、止まった場合は cameras の path にも認証情報を入れる対応が必要らしいです。
まとめ
古いIntel NUCに、Frigateをインストールしました。カメラ1台の映像が web UI に映り、録画と AI 検知が動きました。カメラを増やすときは、go2rtc の streams と cameras に同じ形で追加していきます。
その後、Frigateの検出結果を、Homebridgeで受け取り、Apple Home の在室センサーとして利用できるように設定しました。次の記事にまとめる予定です。
さらにしばらく使用していたら、録画用の2.5インチHDDが故障しました。元々、限界状態にあったようで、ZoneMinderが動作しなくなったのも、おそらくはHDDが原因だったと思われます。(そのおかげで、AI検知できるFrigateに移行できたのですが)現在は、SSDの空き領域に録画する設定で凌いでますが(これもClaudeが設定してくれました)、将来は3.5インチHDDの追加を検討してます。
古い NUC でも、内蔵 GPU にデコードを任せる構成にすれば、AI 検知付きの NVR として十分に使えます。眠っている小型 PC があれば、ぜひ試してみてください。
付録:Frigateインストール手順
Ubuntu ServerをインストールしたLinuxマシンに、Frigateをインストールして、1台のカメラを設定するまでの手順です。ヘッドレスのLinuxマシンに、Macからssh接続し、シェルコマンドを使ってインストールします。Macには、ffmpegがインストールされているものとします。
Dockerをインストールする
Docker 公式のインストールスクリプトを使います。
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
exit
SSH でログインし直してから、動作を確認します。
docker run --rm hello-world
docker compose version
Frigateをインストールする
次に、Frigate 用のフォルダを作って、docker-compose.yml を用意します。cat から最後の EOF までをまとめてターミナルに貼り付けると、ファイルが作られます。Claudeが、エディタ要らずにファイルを作れるよう配慮してくれたおかげです。実際には、該当部分をコピーして、docker-compose.ymlというファイルをviなどのエディタで開き、ペーストしても良いです。
mkdir -p ~/frigate/config
cd ~/frigate
cat > docker-compose.yml << 'EOF'
services:
frigate:
container_name: frigate
image: ghcr.io/blakeblackshear/frigate:stable
restart: unless-stopped
stop_grace_period: 30s
shm_size: "512mb"
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
volumes:
- /etc/localtime:/etc/localtime:ro
- ./config:/config
- /media/frigate:/media/frigate
- type: tmpfs
target: /tmp/cache
tmpfs:
size: 1000000000
ports:
- "8971:8971"
- "8554:8554"
- "8555:8555/tcp"
- "8555:8555/udp"
env_file: .env
environment:
LIBVA_DRIVER_NAME: i965
EOF
docker compose pull
ここで行なっていることは以下です。
- frigateディレクトリを作り、そこに以下の内容のdocker-compose.ymlファイルを作ります。
- devices:内蔵 GPU をコンテナに渡します。
- LIBVA_DRIVER_NAME: i965:Haswell 用の古いドライバを使う指定です。
- /media/frigate:先の手順でマウントした HDD を録画の保存先にします。
- tmpfs:録画の一時ファイルをメモリ上に置き、HDD への書き込みを減らします。
- env_file: この先の手順で、カメラのパスワードを.envに保存し、Frigateの設定ファイルから読み込みます。
- ポート 8971 が web UI、8554 は RTSP の再配信用です。
- 最後に、docker compose pull により Frigate のイメージ(数GB)をダウンロードします。数分かかります。
YAML は行頭のスペースの数に意味があります。貼り付けた後に cat docker-compose.yml で、インデントがずれていないか確認しておくと安心です。
カメラの RTSP URL を調べる
Frigate に設定する前に、カメラの映像を取り出せる URL を確認します。Mac に ffmpeg が入っていれば、ffprobe で調べられます。
AliExpress などの安価なカメラは、中身が Xiongmai(XMEye)製のことが多く、ONVIF のポートが 8899 になっているのが目印です。その場合、URL は次の形です。
ffprobe -v error -rtsp_transport tcp \
-show_entries stream=codec_name,width,height,r_frame_rate -of compact \
"rtsp://192.168.xxx.xxx/user=ユーザー名&password=パスワード&channel=0&stream=0"
stream=0 がメインストリーム(高解像度)、stream=1 がサブストリーム(低解像度)です。結果は stream|codec_name=h264|width=1920|height=1080|… のように表示されます。両方調べて、サブストリームの解像度を控えておきます(検知の設定に使います)。ほかのメーカーは URL の形が違います。たとえば Wansview Q5 は rtsp://ユーザー名:パスワード@IP:554/live/ch0(サブは ch1)でした。スマホアプリに URL が表示されていることもあります。
パスワードと設定ファイルを用意する
カメラのユーザー名とパスワードを、設定ファイルとは別の隠しファイル .env に保存します。
cd ~/frigate
cat > .env << 'EOF'
FRIGATE_CAM_USER=ユーザー名
FRIGATE_CAM_PASS=パスワード
EOF
chmod 600 .env
続けて、Frigate の設定ファイル config.yml を作ります。まずはカメラ1台(frontという名前にしました)だけです。IPアドレスの一部は、仮にxxxとしてありますが、実際のカメラに合わせます。
cat > config/config.yml << 'EOF'
mqtt:
enabled: false
detectors:
ov:
type: openvino
device: CPU
model:
width: 300
height: 300
input_tensor: nhwc
input_pixel_format: bgr
path: /openvino-model/ssdlite_mobilenet_v2.xml
labelmap_path: /openvino-model/coco_91cl_bkgr.txt
ffmpeg:
hwaccel_args: preset-vaapi
detect:
enabled: true
go2rtc:
streams:
front:
- "rtsp://192.168.xxx.xxx:554/user={FRIGATE_CAM_USER}&password={FRIGATE_CAM_PASS}&channel=0&stream=0"
front_sub:
- "rtsp://192.168.xxx.xxx:554/user={FRIGATE_CAM_USER}&password={FRIGATE_CAM_PASS}&channel=0&stream=1"
objects:
track:
- person
- car
record:
enabled: true
continuous:
days: 3
motion:
days: 7
alerts:
retain:
days: 30
detections:
retain:
days: 30
cameras:
front:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/front
input_args: preset-rtsp-restream
roles: [record]
- path: rtsp://127.0.0.1:8554/front_sub
input_args: preset-rtsp-restream
roles: [detect]
detect: {width: 704, height: 576, fps: 5}
EOF
設定内容は以下です。
- AI 検知は、CPU 上の OpenVINO で行います。第4世代の内蔵 GPU は OpenVINO の GPU 推論に対応していないので、CPU を使います。それでも推論は1回あたり約10ms と十分に速いです。
- 映像のデコードは内蔵 GPU(VAAPI)に任せます。
- go2rtc がカメラに1本だけ接続し、Frigate の内部や、ほかの視聴アプリに映像を再配信します。カメラへの接続数が増えないので、安価なカメラでも安定します。
- 録画はメインストリーム、検知はサブストリームを使います。録画はそのまま保存(再エンコードなし)、検知は低解像度・5fps にして CPU の負荷を抑えます。detect の width と height は、前の『カメラの RTSP URL を調べる』で調べたサブストリームの解像度に合わせます。
- パスワードは {FRIGATE_CAM_USER} のように .env の値を参照するので、設定ファイルに直接書かずに済みます。
- 録画の保持期間:常時録画3日、動きのあった部分7日、人や車を検知した場面30日。
これらの設定の一部は、FrigateのWeb UIからも変更できます。
起動して、web UI で映像を見る
Frigateを起動します。
docker compose up -d
sleep 60
docker logs frigate 2>&1 | grep -i -A2 password
docker logs --tail 30 frigate
ここで、管理者(admin)のパスワードがログに一度だけ表示されます。Web UIへのアクセスに必要ですので、必ず控えておきます。後でWeb UIから変更できます。
ブラウザで https://<NUCのIPアドレス>:8971 を開き、admin と控えたパスワードでログインします。自己署名の証明書なので警告が出ますが、自分の NUC なので先に進んで問題ありません。カメラの映像が映れば成功です。ログイン後に、Settings → Users でパスワードを覚えやすいものに変えておくと楽です。
次の2点は、ログに出ますが、気にしなくてよいです。
- using uninitialized “go2rtc_stream_proxy” variable: Frigate 内部の Web サーバーの警告で、動作には影響しません。
- Unable to collect Intel GPU stats … GPUs older than Broadwell (5th gen) are not supported: Haswell では GPU の使用率の表示ができないという意味です。デコードにはちゃんと GPU が使われています。
なお、config.ymlの冒頭に、
tls:
enabled: false
と追加すると、httpsではなくて、httpでアクセスできるようになり、警告も出なくなります。ただ、Frigate のブラウザ通知(人を検知したときのプッシュ通知)は、https でないと使えません。通知を設定する時には、 https に戻す必要があります。
動作と負荷を確認する
web UI の System 画面で、推論速度(Inference Speed)やカメラごとの fps を確認できます。NUC 側では次のコマンドで負荷がわかります。
docker stats --no-stream frigate
uptime
docker stats の CPU% は、100% が1スレッド分です (この NUC は4スレッドなので最大 400%)。私の環境では、カメラ1台で約20%、6台で約120%でした。
カメラごとに、検知用の映像が届いているかを確認するには、次のようにします。
docker exec frigate curl -s http://127.0.0.1:5000/api/stats > /tmp/stats.json
python3 -c 'import json;d=json.load(open("/tmp/stats.json"));[print(k,v["camera_fps"],v["process_fps"],v["detection_fps"],v["skipped_fps"]) for k,v in d["cameras"].items()]'
camera_fps と process_fps が 0 より大きく、skipped_fps が 0 なら正常です。detection_fps は、画面に動きがないときは 0 で正常です(Frigate は動きがあったときだけ AI 検知を動かします)。
最後に、一度再起動して、HDD のマウントと Frigate が自動で戻るかを確認しておきます。
sudo reboot
# 再接続後
df -h /media/frigate
docker ps



コメント