Raspberrypi zero WとABEJA Platformを活用した侵入者通知アプリを作る
やりたいこと
家庭で簡単に防犯カメラを作りたいと思い、下記のアイテムを利用して作ってみました。 僕の家はマンションの1階にあり、5〜6畳分の庭が付いています。庭内には植物・野菜を栽培したり洗濯物を干しているので、1階に住む住人としては不審者が入ってこないかどうかが気になったりします。そこで、Raspberrypi zero W, camera module, ABEJA Platform, LINE Messaging APIを用いて、不審者が庭内で検出された場合にLINEに通知が来る仕組みを作りたいと思います。LINEに通知するのは極力リアルタイムで検知したいというのと、写真で証拠を記録し通報に利用できるというメリットがあります。家庭のセキュリティサービスを展開しているものもありますが、初期工事費用や月額でそれなりにするので、もっと簡易な防災通知ができたらと思って試してみました。今回のRaspberrypi zero Wとcamera moduleだけだと5000円ほどで実現が可能です。
必要なもの
camera moduleの撮影条件と処理の流れ
家の中からRaspberrypi zero Wとcamera moduleで庭の写真撮影を行います。撮影された写真を下記の手順で記録し、もし人間が検出された場合にのみLINE Messaging APIにてLINEに通知します。
- 家の中から庭に向けてRaspberryapi zero Wとcamera moduleを設定
- camera moduleで撮影した画像をABEJA PlatformのWebAPIに定期的にupload。cronにより5秒間隔で撮影とWebAPIにuploadを実行。
- ABEJA PlatformのWebAPIにてSSDによる物体検出を行う
- 物体検出の結果、人間が判定された場合はその画像をLINE Messaging APIを利用してPush通知する
最近はRaspberrypi側で物体検出を行うことも試されていますが、ABEJA PlatformはAIのModelのアップデートが簡単なWebUIを用いてCloud側で実現できるので、Raspberrypi側はcameraの撮影処理とWebAPIのClientを一度書いてしまえば良いようにしておきます。ただし、外部との通信回数は当然増えます。
Raspi zero Wとcamera moduleを家の中から庭に向けて設置する
家の中から外に向けてcamera moduleと接続されたRaspi zero Wを設置します。庭に向けて侵入者をチェックする向きにします。上の写真は相当適当にRaspi zero Wを設置していますが、固定できるものがあると良いです。今回はRaspiを横にして、ウルトラマンフーマでRaspiを支えています。写真は前後から撮影した様子です。
Raspi zero Wの設定手順
Raspi zero Wにsshで接続する
黄色で囲った部分の設定を進めます。Raspi zero w, camera moduleを購入します。microsdのリーダーがあれば、OSの書き込みができるので、それを活用します。初期設定についてはここでは詳しく説明を記載しません。右記のドキュメントを参照してください。 Raspberry Pi Zero(W, WH)のセットアップ - Qiita Raspi側にsshで接続して、パッケージやOSのアップデートを行います。ちなみにRaspiのpythonはv2.7.13がdefaultだったので、pyenvを使ってv3.6.8に切り替えます。また必要なpython moduleのpicameraをinstallします。
$ ssh pi@raspberrypi.local $ sudo apt-get update $ sudo apt-get upgrade $ uname -a Linux raspberrypi 4.14.71+ #1145 Fri Sep 21 15:06:38 BST 2018 armv6l GNU/Linux $ python -V Python 2.7.13 $ sudo apt-get install -y git openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev $ mkdir .pyenv $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ pyenv install 3.6.8 $ pyenv global 3.6.8 $ python -V Python 3.6.8 $ pip install picamera
Raspi zeroのWifiの有効化
- Raspi zeroのWifiを有効化します。
- 有効なESSIDを抽出し、/etc/wpa_supplicant/wpa_supplicant.conf に設定を下記設定を記載
$ sudo iwlist wlan0 scan | grep ESSID $ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf country=JP ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="xxxxxxx" #抽出したSSID psk="yyyyyy" #SSIDに紐づくパスワード }
wconfigコマンドでwlan0が有効になっていることを確認します。
wlan0 IEEE 802.11 ESSID:"xxxxxx" Mode:Managed Frequency:2.412 GHz Access Point: yyyyy Bit Rate=52 Mb/s Tx-Power=31 dBm Retry short limit:7 RTS thr:off Fragment thr:off Power Management:on Link Quality=37/70 Signal level=-73 dBm Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0 Tx excessive retries:1 Invalid misc:0 Missed beacon:0
camera moduleの有効化
Raspi側でraspi-configコマンドにて有効化。5 Interfacing Options Configure connections to peripherals
を選択して、camera moduleをenableにします。vcgencmd get_cameraコマンドでsupported=1, detected=1となればcamera moduleの利用がOKな状態です。
$ sudo raspi-config 5 Interfacing Options Configure connections to peripherals P1 Camera Enable/Disable connection to the Raspberry Pi Camera [enable]を選択する $ vcgencmd get_camera supported=1 detected=1
テストコマンドで写真撮影をしてみる
raspistillコマンドで撮影と保存をしてみます。問題なくRaspi上で撮影・保存することができました。
$ raspistill -w 1280 -h 800 -o image.jpg $ ls -la image.jpg -rw-r--r-- 1 pi pi 651162 Aug 25 04:34 image.jpg
撮影処理の設定
以下のPython Scriptをcronなどに仕込んでおいて定期的に写真を撮影し、それをABEJA PlatformのAPIにrequestします。ABEJA PlatformのAPI側では受け取った画像をSSDの物体検出にかけます。ABEJA Platformは学習済みモデルを簡単にWebAPIとして作成することができるので、次章で作成したendpointを下記処理に加える必要があります。
import requests import picamera import time # endpoint付け足し url = 'https://abeja-internal.api.abeja.io/deployments/xxxxxxxx' user = 'user-xxxxxxxx' access_token = 'yyyyyy' headers = {'Content-Type': 'application/octet-stream'} with picamera.PiCamera() as camera: camera.resolution = (1280, 800) camera.rotation = 270 camera.start_preview() time.sleep(2) camera.capture('person_detect.jpeg') camera.stop_preview() files = {'upload_file' : open('./person_detect.jpeg', 'rb') } r = requests.post(url, files=files, auth=(user, access_token), headers=headers) print(r.content)
ABEJA Platformを使ったSSDによる人物検出
黄色で囲った部分の設定を進めます。 ABEJA Platformとは? ABEJAが開発するcloudを用いたAIの開発・運用基盤を意味します。詳しく知りたい方は以前にABEJA Platformに関するpostをしたので、そちらをご確認ください。ABEJA Platform上でSSDを使った物体検出・人物検出を行い、人物が検出された場合はLINE Messaging APIを使った通知をするという流れを作ります。SSDの物体検出モデルのソースコードと学習済みモデルは下記に存在します。
- https://github.com/rykov8/ssd_keras
- https://mega.nz/#F!7RowVLCL!q3cEVRK9jyOSB9el3SssIA
- weights_SSD300.hdf5
- weights_300x300_old.hdf5
ABEJA Platform上でSSDを動かす
ABEJA Platform上でjupyter notebookを操作することが可能なので、ssd_kerasや上で取得したweightファイルをuploadします。ssd_keras内にある SSD.ipynb
というファイルをjupyter上で開けば実行することができます。このSSD.ipynbを拡張して、人を検出したときのみ次の処理に回すような設定をしました。サンプルは次のipynbになります。security-notification/SSD.ipynb at master · yutakikuchi/security-notification · GitHub
上で動作確認をし、SSD.ipynbの内容をABEJA Platform上のWebAPIとして実行するにはipynbの内容をモデルハンドラーとして実装、実行環境にモデルと実行ファイルをDeployする必要があります。Deploy後はABEJA PlatformのWebAPIのEndpointから画像ファイル取得、SSDのモデルに適用する事ができるようになります。モデルハンドラーの実装については下記に記載されています。
モデルハンドラー関数 for 19.04 :: ABEJA Platform Developers
LINEにpush通知を送る
Push通知先のChannel初期設定
LINEにpush通知を送るためには、https://developers.line.biz/console/ にてMessaging API用のchannel登録をする必要があります。 channel登録完了後に以下の情報をconsoleから抽出し、下記のAPIに対してrequestを送付するためのheaderやbody情報に加えます。
- Access Token :
アクセストークン(ロングターム)
と記載されている項目 - To(送付先) :
Your user ID
またchannelの基本情報画面にBotのQRコードが掲載されるので、必ず友達として追加する必要があります。Push通知のメッセージは追加されたBot側のアカウントにメッセージと画像が表示されます。
LINE Messaging APIにRequestする
黄色で囲った部分の設定を進めます。 LINE Messaging APIを利用してLINEに対してPush通知をします。LINEへのMessaging pushを行うにはbinaryのpostではなく、画像をURLを必要とします。よってABEJA Platform Datalakeに人物検出をされた場合は画像をアップロードし、画像のURLを発行し、そのURLを基にLINEのAPIに対してpushします。ここではABEJA PlatformのDatalakeについては特に記載をしません。
- Endpoint :
https://api.line.me/v2/bot/message/push
- Content-Type :
application/json
- Authorization :
Bearer <Access Token>
- Request Body : JSON format
下記にLINEへのpush通知のサンプルコードを記載します。このサンプルコードをABEJA PlatformのWebAPIのロジックの中に加えます。
import requests import json headers = { "Content-Type": "application/json", "Authorization": "Bearer <Access Token>" } body = { "to" : "xxxxxxx", "messages":[ { "text": "【緊急通知】怪しいやーつがご自宅の庭に侵入しています", "type": "text" }, { "type": "image", "originalContentUrl" : "https://xxxxxxx", "previewImageUrl" : "https://xxxxxxx" } ] } response = requests.post( 'https://api.line.me/v2/bot/message/push', json.dumps(body), headers=headers )
LINEへのpush通知の結果
人が検出された場合のみLINEにpush通知が来るようになりました。(注)smileのアイコンは自動的に付与されません。SSDで検出されたbounding boxも表示されてpush通知されています。侵入者が庭にいたという証拠も写真として押させることができたので、これで目的が達成です。ただし、夜など暗い撮像条件下ではほとんど反応しない、本当の住人を顔判定で通知の除外対象としたかったのですが、現時点での課題は多くあります。