あきらぼ

テック系ブログ

スマホでガレージを制御しよう

こんにちは。

 

今回はガレージのIoTでシャッターの開閉をスマホから操作できるようにしようということがやりたいことになります。

 

構成としてはFlaskウェブサーバーとESP32のSocket通信で制御しています。

あまり同じ構成を見なかったので参考になればと思います。

 

 

 

 

背景

今回のIoTの背景としては友人と共同で契約しているガレージがリモコンはあるのですが、反応が悪いし忘れることもあるので、いつも必ず持っているスマホで開閉できるようにしたいからです。

 

全体システム

全体システムは以下のようになっています。

ユーザー操作順に説明すると

  1. スマホからFlaskサーバーのサイトへアクセス
  2. シャッター開閉ボタンを押してHTTPリクエストをサーバーへ送信
  3. Flaskサーバーが別スレッドのSocket通信サーバーへQueueで受け取った制御指令を送信
  4. SocketサーバーがESP32に制御信号をSocketで送信
  5. ESP32がGPIOでシャッターを制御

といった流れになります。

 

良いところは、ESP32側で面倒な固定IPやポート変換等の心配をしなくていいことになります。

サーバーを持っていることが前提になりますが、私はKagoya VPSを契約しているので、その固定IPサーバーを有効活用します。

 

実装

早速実装ですが、まずサーバー側のPythonの実装です。

Flaskサーバーの機能とThreadingでSocket通信のサーバーを起動しています。

 

Socket通信サーバー側の実装はこちら。

単純にQueueで受け取った指令を繋いでいるESP32に送っています。

 

 

最後にESP32側の実装です。

基本的には受け取ったSocketメッセージに応じて制御します。

 

その他の細かいhtml等は以下のレポジトリをご参照。

(だいぶアップデートしてしまっているかもしれませんが。)

GitHub - Aki-R/shutter_control_server

GitHub - Aki-R/garage_shutter: シャッター開閉するやつ

 

テスト

スマホからの操作画面はこんな感じです。

一旦、直接ガレージのシャッターではなくLEDライト点灯でテストします。

youtu.be

 

デプロイ(ガレージ実装)

あとはガレージのシャッター制御回路に実装するだけです。

GPIOからのリレー制御にはこちらのボードを使いました。

サインスマート(SainSmart) 5V リレーモジュール for Arduino DSP AVR PIC (4チャンネル)

 

 

実装完了です。

 

課題

最後に少し課題がありまして、長時間接続しているとESP32がClinet.Connected()がTrueのまま、実際は接続が切れているという不具合があります。

なのでESP32側を再起動すると復活するのですが、詳細は解析中。

対策としては制御信号がなくても定期通信するようにするとかですかね。

 

以上、ガレージIoT化でした。

ESP32のClientでのSocket受信でデッドロックした件(解決済み)

こんにちは。

先日、ESP32とサーバーでのWeb Socket通信でハマったのでそのことを書こうと思います。

 

 

 

ベースのリポジトリはこちらです。

(Documentationは全然できていません。)

サーバー側

github.com

クライアント側(ESP32)

github.com

システム構成

システム構成としては

サーバー

サーバーはUbuntuサーバー・PythonでサーバーWebSocketで制御信号をESP32に送る。

クライアント

クライアントはESP32でサーバーに接続してSocketの制御信号を受信できるように待機して、信号を受信する。

ハマったデッドロック事象

ESP32がデッドロックしたコードはこんな感じ。

サーバー側実装例

サーバー側はクラインアントからの接続を待機して、接続があったら今度は、制御シグナルが来るまで待つようなタイムアウトのない実装となっています。

クライアント側実装

クライアント側はサーバーに接続後、サーバーからのSocketでの制御信号が来るまで待機(Loop)するような実装となっています。

問題事象

発生したデッドロックはクライアント側です。

簡単に言うと、client.connect()で接続した後に長時間(30分程度)なんのメッセージがないままになるとclient.connected()はTrue(接続済み)を示すのですが、実際は接続されていません。

つまり、接続したまま長時間待機で切断されるがその結果がClientのクラス・インスタンスに反映されません。

なのでデッドロック(切断も再接続もしない)に陥ってしまいます。

対策

問題事象はクライアント側で発生しましたが、サーバー側に対策を行いました。

要は長時間接続したまま制御信号を送らないという状態を作らなければいいわけです。

サーバー側の対策はこんな感じです。

クライアントからの接続が完了して5分経っても制御メッセージが来ない場合にタイムアウト処理をして一旦接続を切断します。

するとクライアント側は切断され再接続を行うので、デッドロックに陥りません。

 

こちらで対策完了です。

iPhoneの写真をheicファイルからjpegファイルにpython で変換する

またやってしまいました。

 

今までWindowsユーザーの私はiphone のデフォルト設定であるheicフォーマットの写真を扱いづらいのでjpeg保存にしていたのですが、iPhoneを買い替えた際に元の設定に戻ってしまっていました。

 

heicは圧縮率が高いので便利な面もありますが、こうやってブログを書いたりする際にWindowsPCで読み込んだり加工しづらく困ってしまいます。

すでに写真はheicのままAmazonPhotoにアップロードされてしまっているし。

 

 

 

そこで今回はpythonでheicからjpegへ変換するスクリプトを書いてみました。

 

変換で使用するパッケージはpillow_heifです。

以下のコマンドでインストールします。

sudo pip install pillow_heif

windowsなら

python -m pip install pillow_heif

 

あとはChatGPTさんに協力してもらいながらスクリプトを書きます。

GPTさん優秀なプロンプトエンジニアなので、適切に指示すれば綺麗に作ってくれます。

 

 

あとは入力にheicファイルが保存されているディレクトリと変換したファイルを保存したいディレクトリを指定して使用します。

python heic_jpeg_converter.py -i input_directory -o output_directory

実際の例はこんな感じ。

python heic_jpeg_converter.py -i path/to/heic_files -o path/to/output_directory

 

とりあえず、これで変換してブログ用の画像等を変換して使いました。

 

今回作成したスクリプトはこちら。

github.com

Streamlitで部屋の温度・湿度モニターの可視化ボード作成

こんにちは。

 

今回はStreamlitを使って以前作った部屋の温湿度モニターの可視化ボードを作ってみました。

以前はChart.jsを使ってグラフ化していたのですが、Streamlitを使うと簡単にもっとInteractiveな表示ができそうなので試してみた次第です。

 

前回Chat.jsを使った際の記事はこちら。

 

aki-lab.hatenadiary.com

 

Streamlitとは?

Streamlitとはpythonのwebフレームワークで、データ解析に特化したフレームワークになります。

Pandasなどと相性がよく、データサイエンスやAIのフィールドでよく利用されているようです。

実際、私も仕事で解析のスクリプト組んだとしても結果の共有に悩む場面が多いですが、こういったWEBで表示できると共有先の環境に合わせる必要がなく便利というわけです。

 

streamlit.io

 

実際、使用してみた感じでもWEB上で細かくデータも見ることができるのでとても便利です。

一時解析ぐらいできてしまうぐらいインタラクティブに動かせるのも魅力です。

実際のサンプルサイト

今回使った実際のサンプルサイトはこちらになります。

JSON形式のデータを読み込んで表示しています。

https://aki-r-streamlit-sample-app-gkayym.streamlit.app/

 

まずはLineグラフに関して。

SreamlitのAPIにも線グラフのAPIはありますが、Plotlyと組み合わせられるのでグラフはPlotlyで表示しています。

Plotlyの方がカスタマイズ性が高いです。

実際見てもらうとドラッグ、ズームも簡単でPNGで出力もできます。

 

以前使っていたChart.jsよりもグリグリと動かせるので使い勝手がとても良いです。

 

次にPandasのデータフレーム表示に関して。

データフレームを簡単に表示できるだけでなく、変数名のところをクリックすることで簡単にソートできてしまいます。

これだけで簡単な一時解析できるぐらいの便利さです。

 

こんな形で、データ表示としては強力なGUIを作ることができます。

 

実装コード

今回上記のサンプルを作成したのは以下のコードです。

GUIアプリですが、この程度であれば非同期やコールバックを考えずにそこそこ実装できます。

 

 

公開はサーバーであればstreamlitをpipでインストールして実行するだけ。

今回添付したサンプルはstreamlitの機能でgithub連携で公開していてデプロイも楽ちんです。

 

今回のコードはこちらです。

github.com

 

以上、Streamlitでの可視化ダッシュボード作成でした。

 

【Tensorflow】【Python】PythonとSpleeterでYoutubeから動画をダウンロードして音楽とボーカル音声を分離して保存する。

こんにちは。

 

今回は音声と演奏を分けるSpleeterを使用してYoutubeから動画をダウンロードして音声と演奏を分けてみようと思います。

 

こんなことができるようになります。

www.youtube.com

 

 

 

 

Spleeter

Spleeterとは音楽ストリーミングサービス「dezzer」が公開しているオープンソースのTensorflowをつかったAIの演奏分離ライブラリになります。

 

Deezer Research - Spleeter

 

GitHub - deezer/spleeter: Deezer source separation library including pretrained models.

 

最大でボーカル / ドラム / ベース / ピアノ / その他 の5つの音に分解することができます(5 stems)

 

今回はボーカルと演奏の二つに分けていきます。

 

インストール

今回はYouTubeからPythonで動画をダウンロードするのにpytubeのパッケージを使います。

ダウンロードした動画から音声mp3の抽出はffmpeg、最後に音声分離のspleeterを使用します。

それぞれpipでインストールできるのでインストールします。

 

コード

コードはとりあえず以下のようにしました。

 

 

流れとしては

  1. pytubeでmp4として動画をYoutubeからダウンロード
  2. ffmpegでmp4から音声だけのmp3を抽出
  3. 抽出したmp3からspleeterでボーカル・演奏分離

みたいな感じです。

 

ちなみにSpleeterによる分離する音声の時間をdurationで決められるのですが、デフォルトで10分、伸ばしても1000秒程度までで、それ以上だとエラーを吐いてしまいます。

 

暇なときに10分毎に処理できるようにアップデートしようと思います。

 

あとは、出力されたmp3をうまく使えば冒頭の動画のようにできます。

 

コードのレポジトリはこちら。

GitHub - Aki-R/YouTube_Downloader

 

これでVtuber歌枠を編集しやすくなりました。

 

 

【Keras】【Tensorflow】VGG16ベースのモデルをSequentialモデルとFunctionalモデルで作る

こんにちは。

 

今回は以前作ったVGG16ベースで作った画像分類器をSequentialからFunctionalでモデルを作り直そうと思います。

 

以前作った画像分類器の記事はこちら。

aki-lab.hatenadiary.com

 

 

 

 

Sequential

前回はSequentialモデルでVGG16をベースとして前段にデータ拡張、後段に全結合層を追加していました。

 

具体的なコードはこんな感じ。

実際にモデルを確認してみます。

上記コードで作ったモデルに対して以下のように詳細を確認します。

model.summary()

以下のように表示されます。

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 rescaling (Rescaling)       (None, 256, 256, 3)       0         
                                                                 
 random_rotation (RandomRota  (None, 256, 256, 3)      0         
 tion)                                                           
                                                                 
 random_zoom (RandomZoom)    (None, 256, 256, 3)       0         
                                                                 
 random_flip (RandomFlip)    (None, 256, 256, 3)       0         
                                                                 
 random_translation (RandomT  (None, 256, 256, 3)      0         
 ranslation)                                                     
                                                                 
 vgg16 (Functional)          (None, None, None, 512)   14714688  
                                                                 
 flatten (Flatten)           (None, 32768)             0         
                                                                 
 dense (Dense)               (None, 512)               16777728  
                                                                 
 dropout (Dropout)           (None, 512)               0         
                                                                 
 dense_1 (Dense)             (None, 58)                29754     
                                                                 
=================================================================
Total params: 31,522,170
Trainable params: 23,886,906
Non-trainable params: 7,635,264
_________________________________________________________________

 

Functional

次は同じモデルをFunctionalモデルで作成します。

作成コードはこちらです。

 

先ほどと同じようにモデルを確認します。

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_1 (InputLayer)        [(None, 256, 256, 3)]     0

 rescaling (Rescaling)       (None, 256, 256, 3)       0

 random_rotation (RandomRota  (None, 256, 256, 3)      0
 tion)

 random_zoom (RandomZoom)    (None, 256, 256, 3)       0

 random_flip (RandomFlip)    (None, 256, 256, 3)       0

 random_translation (RandomT  (None, 256, 256, 3)      0
 ranslation)

 block1_conv1 (Conv2D)       (None, 256, 256, 64)      1792

 block1_conv2 (Conv2D)       (None, 256, 256, 64)      36928

 block1_pool (MaxPooling2D)  (None, 128, 128, 64)      0

 block2_conv1 (Conv2D)       (None, 128, 128, 128)     73856

 block2_conv2 (Conv2D)       (None, 128, 128, 128)     147584

 block2_pool (MaxPooling2D)  (None, 64, 64, 128)       0

 block3_conv1 (Conv2D)       (None, 64, 64, 256)       295168

 block3_conv2 (Conv2D)       (None, 64, 64, 256)       590080

 block3_conv3 (Conv2D)       (None, 64, 64, 256)       590080

 block3_pool (MaxPooling2D)  (None, 32, 32, 256)       0

 block4_conv1 (Conv2D)       (None, 32, 32, 512)       1180160

 block4_conv2 (Conv2D)       (None, 32, 32, 512)       2359808

 block4_conv3 (Conv2D)       (None, 32, 32, 512)       2359808

 block4_pool (MaxPooling2D)  (None, 16, 16, 512)       0

 block5_conv1 (Conv2D)       (None, 16, 16, 512)       2359808

 block5_conv2 (Conv2D)       (None, 16, 16, 512)       2359808

 block5_conv3 (Conv2D)       (None, 16, 16, 512)       2359808

 block5_pool (MaxPooling2D)  (None, 8, 8, 512)         0

 flatten (Flatten)           (None, 32768)             0

 dense (Dense)               (None, 512)               16777728

 dropout (Dropout)           (None, 512)               0

 dense_1 (Dense)             (None, 58)                29754

=================================================================
Total params: 31,522,170
Trainable params: 23,886,906
Non-trainable params: 7,635,264
_________________________________________________________________

VGG16モデルが展開されてしまっていますが、意図通り、Functionalモデルで作成することができました。

 

次回はこちらで学習させて量子化した計量モデルを作っていこうと思います。

 

 

物体検出モデルYOLOv5を用いて家の前の交通量調査をしてみた

こんにちは。

 

先日、YOLOv5の使い方を確認しました。

aki-lab.hatenadiary.com

 

今度はこのモデルを使って、当初の目的である交通量調査を行いたいと思います。

こんな感じになります。

 

 

 

将来的にはリアルタイムで交通量をモニタしたいのですが、まずはコンセプト検証ということで録画したビデオ映像に対して交通量調査機能を実装していこうと思います。

 

そこで、まずはカメラの出番です。

結構昔のモデルにはなってしまいますがGoPro Hero3になります。

 

バッテリーは死んでいたのですが、互換品購入してバッテリー入れ替えたら時間的には十分な時間録画できそうです。

Amazon | Smatree GoPro Hero3 用1290mAhバッテリー2個 充電器とUSBコード付き CHDHX-301-JP | カメラ用アクセサリキット 通販

 

ベランダのへりに固定したら動画を撮影します。

 

約1時間の動画が撮影できました。

以下がその一部になります。

 

youtu.be

 

この動画に対してYOLOv5の物体検出モデルを使って交通量をカウントしていきます。

 

まず手始めに動画に対して車として認識されたものを四角で囲ってみます。

確度(その物体の確からしさ)が高いものは青色、低いものは緑色で囲うようにしています。

 

youtu.be

 

白いガードレールを誤検出してしまっていますね。

確かに白いハイエースに見えないこともない?

 

次に物体をトラッキングしていきます。

交通量をカウントするためには検出した物体がどのように移動していくのかをトラッキングしていく必要があります。

カルマンフィルタを使って確率論的に処理したり、検出したオブジェクトの特徴量から同じ物体をトラッキングしたりと様々な手法があるようですが、とりあえず思い付きで作ってみました。

 

ラッキングに必要な工程としては以下になります。

1.オブジェクトの新規生成

2.オブジェクトの移動

3.オブジェクトの消失

となります。

 

今回はざっくりいうと、

1.オブジェクトの新規生成は今まで何も近傍にオブジェクトがなかったところにオブジェクトが生成された場合に新規オブジェクトとしてID付与。

2.検出中心の近傍にオブジェクトがあった場合はそのオブジェクトが移動したものとしてIDを継承。

3.オブジェクトの近傍に検出が無い場合はオブジェクトが消失したものとする

といった方式です。

 

このベースに加えて、新規生成場所や消失場所を制限させたり、近傍の定義を左右に長くする(道路的に上下の移動はほとんどない)等でトラッキングしやすいようにしています。

 

とりあえず、作ってみます。

オブジェクト一つ一つに固有の番号(ID)を付与して表示してみます。

youtu.be

 

オブジェクトの消失がうまくいかなかったり、逆に新規オブジェクトとして認識されてしまってIDが変化し続けてしまったりしていますね。

バグを修正していきます。

 

バグを修正したらコントロールライン(検査ライン)を決めて、そこを通過するオブジェクトの数をカウントしてきます。

 

とりあえず左方向の交通量をカウントします。

右下に交通量カウントを表示しています。

 

youtu.be

 

とりあえず私がテキトーに交通量カウントするのと同じぐらいの精度は出せているかなと思います。

 

やはりトラック等の大型車両で奥の車線が見えなくなったり、手前が見切れて上手く検出できなかったりと改善点は多々ありますが、作りたかったものはとりあえず完成です。

 

改善点としてはやはりオブジェクトトラッキングでカルマンフィルタ等の分散を用いた実装にしたいですね。

 

次にやるとしたら、これをリアルタイムで動かすことです。

全日交通量を監視するとなると常にリアルタイムで監視する必要があります。

するとPCはつけっぱなしできないので、低消費電力でギリギリYOLOv5がリアルタイムで動くハードウェアが必要になってきます。

JETSONとかですかね。(笑)

Amazon | NVIDIA Jetson Nano 2GB デベロッパーキット (945-13541-0000-000) | NVIDIA | グラフィックボード 通販

 

RaspberryPiの処理能力でもいけるのでしょうか。

Amazon | RasTech Raspberry Pi 4 Model B(RAM 2GB)ラズベリーパイ4B 技適マーク付き/ 32GBのMicroSDカード/ 5V 3A USB-Type-C ON/OFFスイッチ付き電源アダプター/2つのMicroHDMI-to-HDMIケーブルラインラズパイ専用ケース/冷却ファン/三つヒートシンク/ドライバー/カードリーダ 日本語取扱説明書 | R∞RasTech | ベアボーンPC 通販

 

とりあえずこんなところで、私としては自宅前が20分で片側二車線だけで100台以上通過する騒音ひどい環境だということが認識できたので満足です。

 

今回のコードはこちらになります。

github.com

 

以上、今回の開発でした。

 

追記

物体追跡(Object Tracking)のモデルとしてByteTrackというものをYOLOv5と合わせて使用している人が多いですね。

今度、使ってみようかなと思います。

【やってみた】YOLOv5+ByteTrackでオブジェクトトラッキング! - 神戸のデータ活用塾!KDL Data Blog