Raspberrypi zero WとEdge TPUを活用した侵入者通知アプリを作る
やること
以前のBlog PostでRaspberrypi zeroとABEJA Platform(Cloud環境)を活用した侵入者通知アプリを作りました。解決したい課題としては、1階のマンションに住んでいる場合、庭・バルコニーへの不審者の立ち入りが気になります。そこで簡易的なRaspberrypiとLINE Botを利用し、簡易的な写真付きの侵入者通知アプリを作りたいと思いました。
前回はRaspberrypi側にcronで5秒間隔にて画像を撮影し、撮影された画像ををそのままABEJA PlatformのAPIへpostし、SSDにて人物検出を行いました。ただし、これには5秒以内に人がカメラの中に収まることを前提にしたものだったので、侵入者を見落とす可能性がありました。今回は人物検出をEdge側にてSSDで行い、リアルタイムで不審者を検出します。これはTPUがあるからこそ為せる技です。不審者を検出した場合のみLINE Botに通知し、常にAPIのcallのために外部通信を発生させる事無く、より効率的な構成にしようと思います。人物の検出はraspiのcamera moduleで行うため、事前に設定がonになっていること(vcgencmd get_camera
コマンド)で確認をします。
RaspberrypiとEdge TPUの接続
今回使用しているのはRaspberrypi zero WとEdge TPUのUSB Acceleratorとなります。
- Raspberrypi zero Wはスターターキット : 4104円
- USB Accelerator : 9450円
- Raspiのcamera module : 1400円
だったので今回のハードの設定での 合計15000円
で構築している環境になります。上の写真のようにmicroB-USBの変換を用いて、USBのAcceleratorをRaspiと接続をしています。またraspberrypiのケースの中には既にcamera moduleが接続されており、ケースの中央の円からcameraが外向きに出ている状態です。
Raspberry Pi Zero W スターターキット--販売終了 - スイッチサイエンス Raspberry Pi Shop by KSY https://www.amazon.co.jp/raspberry-カメラモジュール-Raspberry-とケース500W画素-感光チップOV5647センサー/dp/B07NSS1QRW
実装する上で必要なもの
- Raspberrypi zero W
- Edge TPU(USB Accelerator)
- ABEJA Platform アカウント
- bitly アカウント
- LINE Developers アカウント
Raspberrypi側の環境設定
Raspberrypi zero Wにpyenvでpython 3.5.7を入れています。Defaultのsystemでのpythonは2.7.13になります。
pi@raspberrypi:~$ uname -a Linux raspberrypi 4.19.66+ #1253 Thu Aug 15 11:37:30 BST 2019 armv6l GNU/Linux pi@raspberrypi:~$ pyenv versions system * 3.5.7 (set by /home/pi/.python-version) 3.6.8 pi@raspberrypi:~$ echo $SHELL /usr/bin/zsh pi@raspberrypi:~$ vcgencmd get_camera supported=1 detected=1
Raspi側のpyenv設定とnumpy等のinstall
Raspiのdefaultのpythonは2.7.13なので、これを3.5.7に設定します。またnumpy, pillow, picamera, opencv-python, opencv-contrib-pythonm, opencv-contrib-python-headlessを必要とするのでpipでinstallしておきます。また最新のRaspiのOSディストリビューションなどを最新のものにします。これを怠るとedgetpu側でerrorを起こす場合があります。opencv関係のpackageを入れないと後ほどerrorが出るので、先に入れておきます。
pi@raspberrypi:~$ sudo apt-get update pi@raspberrypi:~$ sudo apt-get upgrade pi@raspberrypi:~$ sudo apt-get dist-upgrade pi@raspberrypi:~$ sudo apt install libhdf5-100 libharfbuzz0b libwebp6 libjasper1 libilmbase12 libopenexr22 libgstreamer1.0-0 libavcodec-extra57 libavformat57 libswscale4 libgtk-3 libgtk-3-0 libqtgui4 libqt4-test pi@raspberrypi:~$ reboot pi@raspberrypi:~$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv // 下記を追加 pi@raspberrypi:~$ vim .zshrc export PYENV_ROOT=$HOME/.pyenv export PATH=$PYENV_ROOT/bin:$PATH eval "$(pyenv init -)" pi@raspberrypi:~$ source .zshrc pi@raspberrypi:~$ pyenv install 3.5.7 pi@raspberrypi:~$ pyenv local 3.5.7 pi@raspberrypi:~$ pyenv global 3.5.7 // pipで必要なmoduleを追加 pi@raspberrypi:~$ pip install numpy pillow picamera opencv-python opencv-contrib-python opencv-contrib-python-headless requests pi@raspberrypi:~$ pip freeze certifi==2019.9.11 chardet==3.0.4 edgetpu==1.9.2 gi==1.2 idna==2.8 numpy==1.17.2 opencv-contrib-python==3.4.4.19 opencv-contrib-python-headless==3.4.4.19 opencv-python==3.4.4.19 picamera==1.13 Pillow==6.1.0 pygame==1.9.6 pyparsing==2.4.2 requests==2.22.0 scipy==1.3.1 svgwrite==1.3.1 urllib3==1.25.5
RaspiへのTPU Moduleのinstall
Edge TPUのModuleを上記リンクの内容に従ってinstallします。僕のRaspiはzero Wというタイプなので、EdgeTPU 1.9.2というModuleをinstallします。またinstall先が /usr/local/lib/python3.5/dist-packages/
配下になるため、こちらをpythonpathに追加します。
pi@raspberrypi:~$ wget https://github.com/google-coral/edgetpu-platforms/releases/download/v1.9.2/edgetpu_api_1.9.2.tar.gz pi@raspberrypi:~$ tar xzf edgetpu_api_1.9.2.tar.gz pi@raspberrypi:~$ cd edgetpu_api pi@raspberrypi:~$ bash ./install.sh pi@raspberrypi:~$ vi ~/.zshrc //追記 export PYTHONPATH='$PYTHONPATH:/usr/local/lib/python3.5/dist-packages' pi@raspberrypi:~$ source ~/.zshrc
Google Colraのgitにあるexampeを実行してみる
github.com
上記githubに存在するexamples-cameraを実行してみます。git clone後にmodelファイルをdownloadする必要があるので、download_models.shを実行します。download後にclassify_capture.pyを実行するとraspiのcamera moduleで撮影されたstreaming動画をclassificationを行いますが、一定時間の動画を解析した後に Deadline exceeded
のerrorで処理が途中で止まってしまいます。おそらく原因はUSBの転送処理の限界、もしくはTPUの電源の限界かと思います。(まだ正しい解決策が分かっていません)
pi@raspberrypi:~$ git clone https://github.com/google-coral/examples-camera.git pi@raspberrypi:~$ cd examples-camera pi@raspberrypi:~$ bash ./download_models.sh // 出力結果 ./mobilenet_v1_1.0_224_quant_embedding_extractor.tflite ./mobilenet_v2_1.0_224_inat_plant_quant_edgetpu.tflite pi@raspberrypi:~$ cd raspicam pi@raspberrypi:~$ python classify_capture.py // 出力結果 Inference: 19.25ms FPS: 12.2 61% jersey, T-shirt, tee shirt 5% sweatshirt 3% abaya Inference: 20.39ms FPS: 12.0 52% jersey, T-shirt, tee shirt 7% abaya 3% sweatshirt F /home/pi/edgetpu-ml-cpp-release-rpi0-1.9.2/darwinn/third_party/darwinn/driver/usb/usb_driver.cc:834] transfer on tag 1 failed. Abort. Deadline exceeded: USB transfer error 2 [LibUsbDataOutCallback]
不審者を検出し、写真をLINEに通知する
SSD modelファイルの指定
今回も簡易的なテスト通知を行う仕組みとします。上の画像のように家の中から外に向けてraspberrypiを固定し、常に監視している状態を作ります。
上述の ./download_models.sh
を実行したことにより様々な学習済みmodelファイルがdownloadされています。今回人物検出に使うモデルはSSDを指定します。raspberrypi側のcamera module + SSDで検知した不審者の写真を都度LINEに通知し、証拠写真としても記録する事を試みます。 学習済みmodelのファイルは mobilenet_ssd_v1_coco_quant_postprocess_edgetpu.tflite
、検出ラベルのファイルとして coco_labels.txt
を利用します。 coco_labelsにはpersonも含まれているので、今回はこれを利用します。
pi@raspberrypi:~$ ls ../all_models | grep ssd mobilenet_ssd_v1_coco_quant_postprocess.tflite* mobilenet_ssd_v1_coco_quant_postprocess_edgetpu.tflite* mobilenet_ssd_v2_coco_quant_postprocess.tflite* mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite* mobilenet_ssd_v2_face_quant_postprocess.tflite* mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite* pi@raspberrypi:~$ ls ../all_models/coco_labels.txt ../all_models/coco_labels.txt pi@raspberrypi:~$ grep "person" ../all_models/coco_labels.txt 0 person
classify_capture.pyの修正と処理手順
https://github.com/google-coral/examples-camera.git
こちらの examples-camera
ディレクトリにある classify_capture.py
を下記のように修正し、LINEへの通知を行います。※下記はテストコードであり、一度人物を検出した場合はexitさせています。
ちなみに処理手順は以下のものです。
- raspberrypiのcamera moduleでstreamingから画像をcapture。その際にはpythonのpicamera moduleを使用。 - Edge TPU上でSSDを実行し、人物判定確率が60%以上のものに関しては画像をアップロード。 - LINE Botに通知するためには参照可能な画像URLを必要とするため、ABEJA Platform Datalakeに人物判定された画像をアップロード。URLを取得。 - URLの短縮化。bitlyのAPIから取得。 - LINE Messaging APIに画像の短縮URLを含めたJsonのRequest BodyをPOST。 - LINEに不審者をリアルタイムで検出し、証拠画像付きの通知が流れてくる。
ABEJA Platform Datalakeへの画像upload
LINE Botに通知する画像は参照可能なURLである表現される必要があります。今回はABEJA Platform Datalakeを使用することにします。ABEJA Platform Datalakeに画像をpostするために、SDKをinstallします。Datalakeに画像をuploadするとdownload_urlを取得する事ができるので、LINEへの画像URL postのためにdownload_urlという属性を利用します。ソースコード上にABEJAのcredential情報を入力すれば下記の内容で画像がupload、download_urlが取得可能です。
pi@raspberrypi:~$ curl -s https://packagecloud.io/install/repositories/abeja/platform-public/script.python.sh | bash pi@raspberrypi:~$ pip install abeja-sdk pi@raspberrypi:~$ pip freeze abeja-sdk==0.2.12 ...
def upload_image_datalake(image_path): abeja_credential = { 'user_id': abeja_user_id, 'personal_access_token': abeja_personal_access_token } datalake_client = DatalakeClient(organization_id=abeja_organization_id, credential=abeja_credential) channel = datalake_client.get_channel(abeja_channel_id) res = channel.upload_file(image_path) datalake_file = channel.get_file(file_id=res.file_id) content = datalake_file.get_file_info() return content['download_url']
Datalake.download_urlの短縮化
LINE Botに通知する上で、LINE Messaging APIを利用する必要がありますが、Request Bodyに必要な originalContentUrl
という参照可能な画像URLを指定するURLの制限として最大文字数があるため、bitlyを用いて短縮化を図っています。
def get_bitly_url(download_url): headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(bitly_auth_key) } body = { 'long_url': download_url } res = requests.post(bitly_post_url, json.dumps(body), headers=headers ) return 'https://{}'.format(res.json()['id'])
LINEへの通知結果
上のように人が検出された場合のみLINEにpush通知が来るようになりました。(注)smileのアイコンは自動的に付与されません。SSDで検出されたbounding boxも表示されてpush通知されています。侵入者が庭にいたという証拠も写真として押させることができた、また前回課題になっていた5秒間隔でCloudにある人物検出modelをAPIでcallすることなく、Edge側でその判定を持つことで見落とし無くリアルタイムに検知する仕組みを構築できました。次回は本当の住人を顔判定から見極め、そうではない不審者のみを通知することに挑戦したいと思います。