記録帳

クラウド、データ分析、ウイスキーなど。

Arduino+ラズパイ3+Azureで生物検出システムを作る

コロナ真っ最中のGW、いかがお過ごしでしょうか。
最近、仕事でエッジ関連の話題が出ているため、積読だったこの本を読んで、生物検出システムを作ってみました。

どんなシステム?

何か生き物が近づいたら、カメラが起動し写真を撮って、「何の生物が近づいたか?」をLINEに通知するシステムです。
こんな通知が届きます。

f:id:supa25:20210504183726p:plain
LINEのイメージ

鳥とか獣でも試したかったですが、テストできそうになかったので諦めました。
こんな時、ペットを飼っていれば…。

アーキテクチャ

物理的なマシンは灰色、クラウド上は青色で示しています。
ほぼ本に書いてあった通りの構成です。ただ、本では機械学習部分を「自分で教師データをスクレイピングしてきて、アノテーションして、学習させてね!」と書いてあったので、
そこだけはAzure Cognitive Servicesの中のComputer Visionを使っています。
今この本が出たら、絶対同じように既存のAPI使うだろうな。このあたりの技術の進歩はすごいですね。(この本は2017年5月発売)

f:id:supa25:20210504184206p:plain
アーキ図

ちなみに、実際のデバイス類はこのような感じです。
ラズパイ、名刺サイズで普通にOSが動くんですね。すごい。

f:id:supa25:20210508234534j:plain
バイス

処理の流れ

①センサーが動くものを検知する
②検知したことをブレッドボードからBluetoothでラズパイに通知する
③通知が来たら写真を撮って、wifi通してAzureに送る
④IoT Hubで一時的に保持して、StreamAnalyticsに送る
⑤StreamAnalyticsで処理(今回は全データ取得するだけ)して、ServiceBusQueueに送る
⑥ServiceBusQueueで一時的に保持して、AzureVMに送る
⑦AzureVM上のpythonで、届いた画像を引数にComputerVisionのAPIを呼び出す
⑧そのレスポンスと画像をLINEに通知する
⑨LINEにメッセージが届く

①ー③の物理的な部分はわかりやすいですが、④以降のAzureサービス部分の役割が少しわかりにくいですね。
具体的には、なぜStreamAnalyticsやServiceBusQueueを挟むのか?IoT Hubから直接AzureVMでもよいのでは?と思いましたが、
以下のブログにこのような記載がありました。(この方の場合は、ServiceBusの後がAzureVMではなくLogicAppsですが)

クラウドでは、この"イベント(プログラムの区切り)"を意識して、各々の処理を得意とするサービスを使うと、楽にサービスを組み立てることができます。
今回のシナリオではこんな感じです。

  • IoT Hubがテレメトリデータの収集と保持に集中
  • Stream Analytics が時間軸を意識した処理に集中
  • Service Bus が発生イベントの保持に集中
  • Logic Apps がメール配信処理に集中

これらは要件は1サーバー内に構築することも可能です。しかし、それでは部分的なスケールアウトやサーバー停止に対して脆弱な状態となってしまいます。
そこで、"処理"と"保持"をイベントの切り替えタイミングでうまく利用してそれぞれの作業を得意とするサービスを使います。
こうすることで、例えばメールサーバーの停止に伴うエラーがたくさん出たとしても、少なくともテレメトリデータの保持や時間軸を意識した警告には影響が無い仕組みを簡単に作ることができます。

qiita.com

つまり、1つのサービスでもできるが、一部分の性能UPや障害の対応がよりやりやすいように、「保持」と「処理」を分けているということでした。
機械学習では「前処理」「学習」「推論」を分けて実装して、問題の特定が素早くできるようにしますが、それのストリーミング処理版というところですかね。

大変だったこと

プログラムは書籍についていたので、これをそのまま流せば楽勝…と思っていましたが、ところどころエラーが発生して、その処理に時間がかかりました。
逆に、慣れていないハード面の扱い(基盤に抵抗刺したり、Bluetoothを飛ばしてラズパイで検知したり)のところは書いてある通りやればいけました。

中でも一番時間がかかったのが、保存したカメラの写真をAzureに送るために開こうとすると発生するUnauthorizedAccessException。
これは画像を開いてそれをBase64エンコードする処理ですが、そのReadAllBytesで発生していました。

byte[] bufPhoto = await Task.Run(() => File.ReadAllBytes(photoFile.Path));
string b64Photo = CryptographicBuffer.EncodeToBase64String(bufPhoto.AsBuffer());

しかし、なぜか以下のOpenReadAsyncで開くと正常に動作します。

using (var photoStream = await photoFile.OpenReadAsync()){
    // 撮った写真を画面に表示する
    BitmapImage bitmap = new BitmapImage();
    bitmap.SetSource(photoStream);
    capturedImage.Stretch = Stretch.None;
    capturedImage.Source = bitmap;

原因は結局分からなかったので、後者のやり方で画像を開いてBase64エンコードする処理をgoogle検索し続けました。
(2つの処理でそれぞれ返り値の型が異なるので、そのままは使えない。このあたりのC#の文法の理解も時間がかかりました)

結果、以下の神Stackoverflowを見つけ、何とか解決。
how to convert an image to base64string in c# - Stack Overflow

ほぼ流用させてもらい、以下のように修正したら通りました。
正直、このDataReaderが何をしているのかさっぱりわかりません。わかる方いたら教えてください。

byte[] fileBytes = null;
using (var photoStream = await photoFile.OpenReadAsync())
{
    fileBytes = new byte[photoStream.Size];
    using (var reader = new DataReader(photoStream))
    {
        await reader.LoadAsync((uint)photoStream.Size);
        reader.ReadBytes(fileBytes);
    }

    string b64Photo = Convert.ToBase64String(fileBytes);

気づき

API化は正義

マイコン、Azure領域は、かなりAPI化が進んでいたのでそこまで句ではありませんでした。
例えばマイコンだと、Bluetoothに送るときは
Serial.write(1);
で終わりです。
また、AzureもAI関連のAPIが多いので画像さえ取得できれば学習もさせずにいろいろなことができます。
ラズパイの上での実装は、C#そのまま書くのではなくなにかフレームワークが欲しいところです。

統合開発環境は正義

一般的には、IDE使えば画面もDBもロジックも開発できると思います。
ただ、今回のIoTシステムではArduino IDEとVisualStudio2019とVSCodeの3つを使いました。
マイコン、ラズパイ、Azure上で開発するためにはそうする必要があったからですが、ここも統一できるようにして欲しいところです。

物理的な動作はオモロイ

基本的にソフトウェアの開発をずっとしていたので、「実際のセンサが動く」「カメラで写真が撮られる」のようなこととは無縁でした。
今回実際やってみて、このあたりの物理的なデバイスの動作を見るのがかなり面白かったです。
ブレッドボードに素子ぶっさすのも楽しかったので、IoTシステムは他にも作りたいと思いました。

チェックポイントで動作させるとモチベが続く

今回、ところどころで動作確認をするようにしていました。
マイコンのランプをチカらせる
Bluetoothに値が乗っているかアプリでチェックする
・カメラがちゃんと撮れてるか確認する
・Azureに画像がちゃんと飛んでるか確認する
・ちゃんと画像分類するか確認する
など。
最初からこれらのチェックポイントを意識したわけではなかったですが、今回4日間にわたって自主的に作れたのはこのおかげだと思ってます。

ToDo

気づきを受けて、次にやりたいこと。
①最近のIoTシステム開発事情の調査
今回感じた不便さは、世の中みんな感じるはずなので、今は解決しているはず。
AWSとかがサービス出してるのではないか?ということで調査したいと思います。

②別のIoTシステム作る
かなり楽しかったので、また作りたいと思っています。
今度は、アームとか動かしてみたい。そしてAWSで構築してみたい。


久々に書いたらかなり長くなってしまいました。
残りのGW、楽しんでいきましょう!