[アドカレ2024] IoT気圧チェッカーで安心してクリスマスを過ごそう!

はじめに

ハッピーホリデー! こんにちは、omkです。

いよいよ待ちに待ったクリスマスですね。
このアドベントカレンダーもついに最後の記事となり、完走できたことを嬉しく思うとともに終わってしまうことへの寂しさを覚えております。

さて、季節はクリスマス、冬真っ只中ですが「冬」といえば何でしょう。



そう。西高東低の冬型の気圧配置ですね。ここテストに出ます。

気圧が低いと頭が痛くなってしまうよーっという方も多いと思います。
僕も例に漏れずそうなのですが、人より気圧に敏感なのでちょっとした低気圧でも調子が悪くなってしまうのが辛いところです。
それでも冬の間は大阪では比較的気圧が高いのでありがたいです。

というところで、やっぱり気圧の影響度も人それぞれなので個人の頭痛の状況でパーソナライズして危険度を評価してくれる頭痛チェッカーを作成して安心してクリスマスを過ごせるようにします。

では早速作っていきます。

用意したもの

◯ATOMS3(マイコン)

ATOMS3はM5から販売されている小型のマイコンで、コントローラーにESP32-S3を採用しているのが特徴です。
Arduinoのフレームワークで開発できるから嬉しいですね。

家に転がってたので無料でした。

◯DPS310(気圧センサー)

高性能な気圧センサーです。
ATOMS3がGROVE(HY2.0-4P)かqiコネ(正式名称知らない)に対応しているのでGROVEで繋げられるこれを採用しました。
専用のライブラリが公開されているので扱いやすいです。

これは700円くらいで売ってました。

作ってみた

文量の関係で細かく記載していくのが難しいので予め全体の構成を記載しておきます。
大体こんな感じです。

ATOMS3開発環境準備

まずはATOMS3の開発環境の準備です。
ArduinoIDEやらUIFlowもありますが断然PlatformIO派です。

VSCodeでPlatformIOを開いてプロジェクトを作ります。

あとはコードを書くだけ。超簡単。

ちなみに環境設定ファイルは最終的にこんな感じになりました。

[env:m5stack-atoms3]
platform = espressif32
board = m5stack-atoms3
framework = arduino
lib_deps =
    m5stack/M5Unified @ ^0.2.1
    m5stack/M5AtomS3 @ ^1.0.1
    fastled/FastLED@^3.9.4
    knolleary/PubSubClient@^2.8
    bblanchon/ArduinoJson@^7.2.1

気圧の取得

センサーとATOMS3をGROVEでつなぎます。

ライブラリをGithubからzipでダウンロードしてプロジェクトにインポートしていい感じにします。
https://github.com/Infineon/DPS310-Pressure-Sensor

気圧取得部のコードはサンプルほぼそのまま。

float getPressure() {
  float pressure;
  uint8_t oversampling = 7;
  int16_t ret;
  ret = Dps310PressureSensor.measurePressureOnce(pressure, oversampling);
  if (ret != 0)
  {
    M5.Display.print("FAIL! ret = ");
    M5.Display.println(ret);
    exit(1);
  }
  return pressure / 100;
}

よし!

学習用データの作成

これで気圧を取得するところまでが実装できました。
さっきGoogleで検索したら一般的には6~10hPa程度気圧が下がると頭が痛くなるらしいです。
1気圧1013hPaなので1003~1007あたりが危険域って感じですかね。

実際どうなのか知らないので僕が頭痛を感じる気圧のデータを集めて調べましょう。

AWS IoT Coreに特定時点の気圧とその時点で頭が痛かったかを送信してS3にデータを溜め込んで学習用データとします。

ATOMS3はモニター部がボタンになっているので痛かったらボタンを押して、ボタン押下の有無を頭痛の有無とします。

AWS側でモノやら証明書やらを作成して、繋がるようにしたらAWS IoT CoreにJSON形式でパブリッシュします。

void pushTrainData(float pressure, bool isPressed){
  JsonDocument doc;
  doc["pressure"] = pressure;
  doc["have_headache"] = isPressed;
  char message[100];
  serializeJson(doc, message);
  mqtt_client.publish(PUBLISH_PUSH_TRAINDATA_TOPIC, message);
  isButtonPushed = false;
}

IoTルールで対象トピックに来たデータをFirehoseストリームに渡してS3に貯めるようにします。

こんな感じでデータが入ってきます。

中身はこんな感じのJSON行です。

{"pressure":1016,"have_headache":false}
{"pressure":1017,"have_headache":false}
{"pressure":1016,"have_headache":false}
{"pressure":1017,"have_headache":false}
{"pressure":1016,"have_headache":false}
(略)

このままじゃ扱いづらいのでCSVに変換します。

対象S3パスをGlue DataBrewで単一のCSVファイルとして出力します。

いい感じになりました。こういうときDataBrewが便利なんですよねー。

have_headache,pressure
0,1020
0,1020
0,1020
0,1020
0,1020
0,1020
0,1020
(略)

学習

ではこのデータを用いて学習をしていきたいのですが、 今月からやり始めたので低気圧のデータが全くありません。
おのれ、シベリア気団め。

仕方ないので比較的頭痛めのダミーデータを作成して学習していきます。

SageMaker StudioでJupyter Notebookインスタンスを起動します。
学習にはscikit-learnのランダムフォレストを利用します。

データをダウンロードしてPandasでDataFrameとして読み込みます。

データを分割してランダムフォレストで学習します。

テストデータで評価。

温度感の確認。うんうん、そうだよねーって感じの値がでました。

モデルをS3にアウトプット。

もうNotebookは利用しないので停止します。

頭痛チェックシステム開発

学習モデルが出来たのであとはこれに問い合わせを行う機構を作ります。
IoT Coreと親和性を考えてLambdaで実装していきます。

まずモデルとpandasやらscikit-learnやらの必要なライブラリをLambdaレイヤーに入れます。

……scikit-learnのサイズが大きすぎて入りませんでした。

代わりにEFSに入れてLambdaにマウントします。

EFSのファイルをWEBから操作するのには「Simple File Manager for Amazon EFS」が便利ですよね。
まぁちょっとディレクトリ周りは面倒なところもありますが……

ここから起動出来ます。
https://aws.amazon.com/jp/solutions/implementations/simple-file-manager-for-amazon-efs/

こんな感じでライブラリを入れていきます。

eventで気圧を受け取ってモデルから推論した危険度を特定トピックにパブリッシュするLambda関数を作成しました。
Initでタイムアウトしないように全部ハンドラーに突っ込む力技です。

import sys
sys.path.append("/mnt/efs/python")

import json
import boto3
import os
import joblib
import sklearn
import pandas as pd

def lambda_handler(event, context):
    print(event)

    # モデルの取得
    model = joblib.load('/mnt/efs/model.joblib')

    # 推論
    data = pd.DataFrame({"Pressure": [event['pressure']]})
    proba = model.predict_proba(data)
    proba_df = pd.DataFrame(proba, columns=["No Pain", "Pain"])
    print(proba_df)
    headache_risk = float(proba_df.loc[0, 'Pain'])
    print(headache_risk)

    # トピックにパブリッシュ
    iot = boto3.client('iot-data')
    response = iot.publish(
        topic='AtomS3/get-risk',
        qos=1,
        payload=json.dumps({"headache_risk": headache_risk})
    )
    print(response)

エラーハンドリングを忘れていますがこれでLambdaは完成です。
作成した関数をIoTルールの宛先に設定してATOMS3からの気圧のパブリッシュを受けるようにします。

最後にATOMS3側で気圧をパブリッシュするのと、Lambdaからの危険度を受けて画面の色を変えるように修正したらすべての実装が完了です!!

void alartRisk(float risk){
  if (risk > 0.7){
    nowColor = RED;
  }else if(risk > 0.4){
    nowColor = YELLOW;
  }else{
    nowColor = GREEN;
  }
}

これが

こうなる

オールグリーンです。

グリーンじゃないパターンも取りたくて低気圧発生装置を作ってみましたが、 肺活量が全然足りなくて諦めました。
1018→1006hPaくらいまではいけたんですがキープが難しい……

おわりに

これで気圧が低くなり始めたら気付けるので対策も簡単で安心してクリスマスを過ごせますね。
本当は継続的にモデルにフィードバックするようにしたいんですがそこまでやるのは大変なのでこれで完成とします。

さてさて、楽しかったDENET ADVENT CALENDAR 2024もこれにて終了です。
お気に入りの記事は見つかりましたか?
チキンの焼き時間の間にどんな記事があったか改めて振り返ってみるのもいいでしょう。

どなたさまにとっても今日のこのクリスマスの日が良き1日となることを心よりお祈りいたします。

それではまた、雪の降る季節でお会いしましょう。

さよなら、さよなら、さよなら……

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA