Y's note

愛と夢と人生について書きます

Raspberrypi zero WとEdge TPUを活用した侵入者通知アプリを作る

やること

yut.hatenablog.com

f:id:yutakikuchi:20190921051804p:plain:w500

以前の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の接続

f:id:yutakikuchi:20190923151412j:plain:w300

今回使用しているのは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側の環境設定

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

GitHub - google-coral/edgetpu-platforms: EdgeTPU support for other platforms, specifically Raspberry Pi Zero

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ファイルの指定

f:id:yutakikuchi:20190926092938p:plain:w400 f:id:yutakikuchi:20190825121911j:plain:w400

今回も簡易的なテスト通知を行う仕組みとします。上の画像のように家の中から外に向けて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への通知結果

f:id:yutakikuchi:20190927081234p:plain:w250

上のように人が検出された場合のみLINEにpush通知が来るようになりました。(注)smileのアイコンは自動的に付与されません。SSDで検出されたbounding boxも表示されてpush通知されています。侵入者が庭にいたという証拠も写真として押させることができた、また前回課題になっていた5秒間隔でCloudにある人物検出modelをAPIでcallすることなく、Edge側でその判定を持つことで見落とし無くリアルタイムに検知する仕組みを構築できました。次回は本当の住人を顔判定から見極め、そうではない不審者のみを通知することに挑戦したいと思います。