ラズパイで見守りシステム開発その2：動体検知と通知機能の実装

こんにちは。

[前回はディスプレイとキーボードがない環境でのラズパイ初期設定](https://qiita.com/quotto/items/73eb17f24c47469a1986)を行ったので、今回はいよいよ動体検知とそれを通知する機能を実装します。

[3回目のリモート接続設定はこちらです。](https://qiita.com/quotto/items/1dc8f16063bbe98c4ca8)

## システム概要
### 環境
ラズパイ：Raspberry Pi4 Model B
OS: Raspberry Pi OS(32bit)
動体検知: [motion](https://motion-project.github.io/)
通知システム: LINE Notify

動体検知やリアルタイムでのストリーム配信には有名ツールであるmotionを使います。コーディング無しで簡単に実装可能な素晴らしいツールです。

### 構成
今回実装したシステムの概要図です。動体検知は広く使われているmotionを使い、通知はLINEのAPIにリクエストするだけなのでほぼノーコードです。
![無題のプレゼンテーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/86d877c8-9632-3755-c845-a108c25ed0ee.png)

## カメラの接続と有効化
では作成済みのラズパイにカメラを接続します。

私が今回購入したのは[こちらのカメラ](https://www.amazon.co.jp/gp/product/B07T5DZY73/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/152db48f-e14d-7560-ee77-92eab325c002.png)

基本的に子どもが寝ている状態で使いたいので赤外線LED付きのものにしました。

### カメラの有効化
……カメラをラズパイに接続した写真を撮り忘れたのですが難しいことではないので省略します。

カメラを接続したらラズパイから有効化します。VNCでラズパイに接続し`Preferences`→`Raspberry Pi Configuration`→`Interfaces`と進み、`Camera`を`Enabled`に変更します。その後リブートすれば、カメラの接続は完了です。

![2020-11-20_23h07_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/b00d5c6a-4e17-19c7-1711-781f33814e19.png)

![2020-11-20_23h08_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/74290a0e-e079-68d0-0250-04abd5c0646a.png)

![2020-11-20_23h08_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/9dbf838a-2c80-b242-6205-1c5aafef253f.png)

### カメラの動作確認
ラズパイのコンソールを立ち上げ`vcgencmd get_camera`を実行します。supported,detectedが1となればOKです。

```shell
pi@raspberrypi:~$ vcgencmd get_camera
supported=1 detected=1
```

それではカメラを使って写真を撮ってみます。`raspistill`コマンドで写真を撮影して画像ファイルに保存できます。

```shell
pi@raspberrypi:~$ sudo raspistill -o image.jpg
```
`-o`で指定した名前で画像ファイルが作成されます。
![2020-11-20_23h13_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/3b84651b-a2f1-bb87-7c56-fc855d1e3764.png)

## motionの導入
### インストール
見守りシステムの要、動体検知を行うmotionを導入します。

aptやyumリポジトリからインストールできます。（今回はapt）

```shell
pi@raspberrypi:~$ sudo apt-get install motion
```

### motion.confの編集
見守りシステムの要です。`/etc/motion/motion.conf`の設定内容に従って動体検知や通知を行います。

私がデフォルトから変更した部分をセクションごとに示します。

#### 起動設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| daemon  | on  | motionプロセスをバックグラウンドで起動する  |

#### 撮影画像に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| width  | 640  | 保存およびストリーム配信画像の横サイズ  |
| height  | 480  | 保存およびストリーム配信画像の縦サイズ  |
| framerate  | 2  | 1秒あたりの撮影フレーム数  |

#### 動体検知に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| threshold  | 4500  | 動体検知する際のしきい値。<br/>単位はpixelで、フレーム間でこれ以上のpixelが変化した場合に動体検知する。  |
| event_gap  | 3  | 動体検知イベントの間隔。動体検知すると指定した秒数を一つのイベントとして扱うため、動体検知時に保存される動画の秒数になる。-1の場合はすべてのイベントをひとつの動画に保存する。0の場合には動体検知のたびに動画が保存される。  |

`event_gap`をデフォルト60から3にした理由ですが、後述のとおり画像で保存するフレーム指定（`output_pictures`）をfirstまたはbestとすると、`event_gap`で指定した秒数だけ画像保存が遅延するためです。

#### 画像保存に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| output_pictures  | best  | 動体検知時にどの画像を保存するかを設定する。<br/>off: 保存しない<br/>on: 全フレーム<br/>first: 最初のフレーム<br/>best: 最も変化が大きかったフレーム|
| quality | 100 | 保存画像の画質（圧縮率）。 |

`output_pictures`がonだとフレームの数だけ通知が来てしまうので私はbestに設定しました。

ただしfirstまたはbestに設定した場合は前述の`event_gap`で設定した秒数が経過しないと画像が保存されない＝通知が行われないため、注意が必要です。

#### 動画保存に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| ffmpeg_output_movies  | off  | 動体検知時の動画保存設定。動画保存する場合はonにする。 |
| quality | 100 | 保存画像の画質（圧縮率）。 |

`ffmpeg_output_movies`について、今回は`event_gap`を3にしている関係で、動体検知のたびに3秒単位の動画大量に保存されてしまうためoffとしました。

#### ストリーム配信に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| stream_localhost | off | onの場合にはローカルホストからのみストリーム配信が確認できる |
| quality | 100 | ストリーム配信画像の画質（圧縮率）。 |


#### イベント通知に関する設定
| 項目 | 設定値 | 概要 |
|:-:|:-:|:-:|
| on_picture_save | /home/pi/motion_notify.sh %f & | 動体検知で画像が保存された際に実行されるコマンド。%付きの変数が利用可能。<br/>ちなみに%fは保存ファイルの絶対パスで、その他にいくつか動的パラメータが用意されている。|

コマンドの中身は後述します。

### 起動時のデーモン設定
motion.conf内の`daemon`との関連性がよくわかりませんが、`/etc/default/motion`を以下のとおり修正して起動時のデーモン設定を有効化します。

```properties:/etc/default/motion
# set to 'yes' to enable the motion daemon
start_motion_daemon=yes
```

### 起動してみる
ここまでできたらmotionを起動して動作確認してみます。

```shell
pi@raspberrypi:~$ sudo motion
```

**http://ラズパイIP:8081**にアクセスすると、ストリーム配信で1秒ごとの撮影画像を見ることができます。
![2020-11-26_01h19_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/27cd0bac-c895-aa7e-7cc4-dfdf193cfd8a.png)


また動体検知すると指定のフォルダ（デフォルトでは`/var/lib/motion`）に検知時の画像ファイルが保存されています。
![2020-11-26_09h51_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/4ca77123-fa54-d1db-776f-eab4f017dec6.png)



### （おまけ）自動起動設定
ラズパイの再起動時などイチイチ手動でmotionを起動するのも面倒なのでsystemdで自動起動するように設定します。

```properties:/etc/systemd/system/motion.service
[Unit]
Description=motion
After=network.service

[Service]
Type=forking
Restart=on-success
ExecStart=motion
ExecStop=pkill motion

[Install]
WantedBy=multi-user.target
```

```shell
sudo systemctl enable motion
```

## 通知の実装
最後に動体検知が行われた場合にLINEに通知を行う仕組みを実装します。

### LINE Notifyの登録
プログラム等からLINEに通知する簡単な方法がLINE Notifyを利用することです。LINE Notifyからの送信先を選択して登録することでAPIトークンが発行されるので、それを使ってHTTPリクエストでメッセージを飛ばせます。

LINE Notifyの登録には[LINE Notify](https://notify-bot.line.me/ja/)からログインします。

ログインしたらトップページ右上から**「登録サービス管理」**を選択します。

![2020-11-24_00h31_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/00100272-bf4d-40ce-7eba-a7a0c421b1d3.png)

表示されたページの**「トークンを発行する」**をクリックします。
![2020-11-21_00h33_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/840e45be-d6ac-8e0a-5ba5-a70068ad311a.png)

するとLINE Notifyでの配信先選択になるので一番上**「1:1でLINE Notifyから通知を受ける」**を選択して**「発行する」**をクリックします。これで現在ログインしているLINEアカウントにLINE Notifyからメッセージが送られます。
![2020-11-21_00h33_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/69d273ba-7034-da22-5704-3645b71a79fb.png)

APIトークンが表示されるのでコピーしておきます。
![2020-11-21_00h34_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/fed9ffcb-964c-3144-8973-65144a30636b.png)

HTTPのPOSTリクエストのAuthorizationヘッダにこのAPIトークンを設定すればLINEで通知を受け取れます。

### motionの通知処理実装
motionにおける動体検知時の実行処理は前述のとおり`motion.conf`内の`on_pictures_save`で定義します。

ここに直接curlコマンドを書いてもいいのですが、今回やりたかったこととして**「1分間に1回だけ通知する」**というのがあります。

motion.confのトピックで書いたとおり`event_gap`を3にしているため、何も考えずにLINE Notifyにリクエストを投げると**最短で3秒に1回通知されてしまう**ためです。

これを回避するために以下のShellScriptを作りました。

```shell:/home/pi/motion_notify.sh
#!/bin/bash

ENABLE_FILE="/home/pi/send_enable"
if [ -e $ENABLE_FILE ]; then
        rm $ENABLE_FILE
        curl -X POST -H "Authorization: Bearer APIトークン" -F "message=ラズパイで動体検知したよ!http://192.168.10.21:8081" -F "imageFile=@$1" https://notify-api.line.me/api/notify
        sleep 60
        touch $ENABLE_FILE
fi
```

通知の本体は以下のcurlコマンドです。LINE Notifyの仕様に乗っ取り、Authorizationヘッダに先程のAPIトークンを設定、メッセージ本文には動体検知時の画像（`motion.conf`内の第1引数で画像ファイルの絶対パスを渡す）と、すぐにストリーム配信が確認できるようにURLを書いて送信します。

```shell
curl -X POST -H "Authorization: Bearer APIトークン" -F "message=ラズパイで動体検知したよ!http://192.168.10.21:8081" -F "imageFile=@$1" https://notify-api.line.me/api/notify
```

この通知を1分間に1回に抑えるように`/home/pi/send_enable`というファイルが存在した時だけcurlを実行するようにしています。curl実行直後に当該ファイルを削除しそのまま60秒間待機します。

### 動体検知のテスト
実際にmotionが動体検知すると、その時の画像を付けてLINEに通知が来るようになりました！
![Screenshot_20201126-100136886.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183744/c7d8d7ef-f705-75ab-fc44-470ed99b2ec4.jpeg)

URLも付いているので通知が来て「おやっ？」と思ったら即座にストリーム配信も確認できます。

なお今回はご覧のとおりローカルIPのため家にいる時限定ですが、例えば外からも確認したい場合には別途対応が必要です。それは次回にしたいと思います。


## ひととおりできました
ということで2回目にして見守りシステムは完成しました。ラズパイは初めてだったのですが初心者にも優しい作り、かつインターネット上にも情報がたくさんあるので問題なく作ることができました。

[次回](https://qiita.com/quotto/items/1dc8f16063bbe98c4ca8)はおまけになりますが、外からも見守りシステムを使えるような設定をしてみたいと思います。

## 参考URL
 https://www7390uo.sakura.ne.jp/wordpress/archives/250
https://www.itmedia.co.jp/news/articles/1912/01/news009.html