皆様、こんにちは。ソリューションアーキテクトの松永(ニックネーム:taketo)です。
今回は先日AWS Signature V4へ対応したデータ転送サービス「SORACOM Beam」(以下、Beam)を使ったAmazon SageMakerへの連携についてご紹介致します。
これにより、デバイスに認証情報と SigV4 生成ロジックを持たずともAmazon SageMakerのAPIを呼び出すことが可能になります。例えば、M5Stackなどのマイコンから、Amazon SageMakerにデプロイされたAIのモデルで推論することができます。
本ブログでは、M5Stackに温湿度が取得できるセンサーを取り付け、Amazon SageMakerで作成した電力需要予測モデルを呼び出す例をご紹介したいと思います。その中で、SORACOM Beamを利用するメリットも解説していきます。
アーキテクチャ
全体のシステム構成をみていきましょう。まず、デバイスからSORACOM BeamへHTTPリクエストを送信し、SORACOM Beam経由でAmazon SageMakerのAPIを呼び出しています。ここで重要なのが、先日ご紹介したSORACOM BeamのAWS 署名バージョン 4 (SigV4) 対応を利用して、AWS Signature V4署名ヘッダーを付加してAPIを呼び出している点です。これにより、M5StackではAWSの認証情報を持たずともAmazon SageMakerの推論を実行することができています。後述しておりますが、M5Stackで動かすコードにも一般的なHTTPライブラリを使っており、ArduinoにAWS SDKが対応していなくても簡易に実装ができます。
続いて、ハードウェアの構成を解説していきます。
マイコンで動くM5Stackに3G回線で通信できる構成で3Gに対応しているplan-D(ドコモ回線)を利用しています。環境センサはM5スタック用のもので、温度・湿度・気圧が取得できます。
構築方法
それでは早速AIモデルを開発し、M5Stackから予測してみましょう。
電力需要予測モデルの開発
東京の温湿度情報からその時間の東京の電力需要を予測するモデルを開発します。具体的なAIモデルの作成とAmazon SageMakerへのモデルのデプロイ方法については、「ユーザードキュメント:IAM 認証を利用して Amazon SageMaker で推論する」を参照してください。
設定後、Webサイトエントリポイントの通りに、デバイスから「http://beam.soracom.io:18080/」にリクエストされたHTTPリクエストが、Amazon SageMakerのオレゴンのエンドポイント「https://runtime.sagemaker.us-west-2.amazonaws.com/」に転送されてるようになります。
ガイドでは2022年12月の東京の気象データと電力需要実績を元にモデルを開発します。お客様がテストされる時期と環境によりモデルの精度がでない可能性がございます。
Arduino開発環境準備
「ソラコムガイド:M5Stack 開発環境セットアップ (Windows / macOS 共通)」に従い、Arduino開発環境を準備し、必要なソフトウェアのインストールを行ってください。
Arduinoスケッチの開発
電力需要予測を実行するには、「時刻(24時間表記)」「温度(℃)」「湿度(%)」の情報が必要となります。この値を取得し、SageMakerのモデルで需要予測をした結果を表示するプログラムを開発していきましょう。
新しいスケッチを開き、下記のコードをコピーし、M5Stackにインストールしましょう。
#include <M5Stack.h>
//Libraries for cellular connectivity
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
#include <HTTPClient.h>
#include <ArduinoHttpClient.h>
//Libraries for sensor
#include "UNIT_ENV.h"
TinyGsm modem(Serial2);
TinyGsmClient ctx(modem);
const char* beamServerAddress = "beam.soracom.io";
const int beamPort = 18080;
const String powerModelName = "sagemaker-xgboost-2023-02-28-00-54-23-394";
const char* clockServerAddress = "worldtimeapi.org";
const int clockServerPort = 80;
const String datetime_prefix = "datetime: ";
SHT3X sht30;
QMP6988 qmp6988;
void setup() {
//Initialization
M5.begin();
M5.lcd.setTextSize(2);
M5.Lcd.clear(BLACK);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.println("M5Stack Now Powered On");
//Initializing the modem and configure APN
M5.Lcd.print("Restarting Modem...");
Serial2.begin(115200, SERIAL_8N1, 16, 17);
modem.restart();
M5.Lcd.println("Done.");
M5.Lcd.print("Modem Info:");
String modemInfo = modem.getModemInfo();
M5.Lcd.println(modemInfo);
M5.Lcd.print("Waiting to be registered..");
while (!modem.waitForNetwork()) M5.Lcd.print(".");
M5.Lcd.println("done.");
M5.Lcd.print("Creating PDP context with APN soracom.io..");
modem.gprsConnect("soracom.io", "sora", "sora");
M5.Lcd.println("done.");
M5.Lcd.print("Connecting to the network..");
while (!modem.isNetworkConnected()) M5.Lcd.print(".");
M5.Lcd.println("done.");
M5.Lcd.print("My IP addr:");
IPAddress ipaddr = modem.localIP();
M5.Lcd.print(ipaddr);
//Initializing the sensor
Wire.begin();
qmp6988.init();
delay(2000);
}
void loop() {
//1.Get hour
M5.Lcd.setCursor(0, 0);
M5.Lcd.clear(BLACK);
M5.Lcd.setTextColor(WHITE);
String datetime = get_datetime();
int currentHour = datetime.substring(11,13).toInt();
//2.Get temperature and humidity
float tmp = 0.0; // temperature
float hum = 0.0; // humidity
if(sht30.get()==0){
tmp = sht30.cTemp;
hum = sht30.humidity;
}
M5.Lcd.printf("Hour: %d Temp: %2.1f Humi: %2.0f%% \r\n", currentHour, tmp, hum);
M5.Lcd.printf("Predicting power demand.. ");
HttpClient client = HttpClient(ctx, beamServerAddress, beamPort);
//3.Inference power demand for the next hour based
String contentType = "text/csv";
String body = String(currentHour) + "," + String(tmp) + "," + String(hum);
int err = client.post("/endpoints/" + powerModelName + "/invocations", contentType, body);
//Check if the request is succeeded
if (err != 0) {
M5.Lcd.println("failed.");
return;
}
M5.Lcd.println("done.");
// Read the status code and body of the response
int statusCode = client.responseStatusCode();
String response = client.responseBody();
M5.Lcd.printf("Power Demand: ");
M5.Lcd.println(response);
delay(5000);
M5.Lcd.setCursor(0, 0);
M5.Lcd.clear(BLACK);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.println("Retrying now...");
}
//Get time info
String get_datetime()
{
M5.Lcd.printf("Get current time..");
HttpClient client = HttpClient(ctx, clockServerAddress, clockServerPort);
int err = client.get("/api/timezone/Asia/Tokyo.txt");
//Check if the request is succeeded
if (err != 0) {
M5.Lcd.println("failed.");
return "";
}
String response = client.responseBody();
int start = response.indexOf(datetime_prefix);
int end = response.indexOf('\n', start);
String datetime = response.substring(start + datetime_prefix.length(), end);
M5.Lcd.println("done.");
return datetime;
}
プログラムのloop()処理の内容を解説致します。プログラムのコメントしている部分ごとに内容をみていきましょう。
- 1.Get hour:WorldTimeAPIより日本の現時刻を取得し、「時刻(24時間表記)」情報を抽出します。
- 2.Get temperature and humidity:M5Stack用温湿度気圧センサユニット Ver.3(ENV Ⅲ)から「温度(℃)」「湿度(%)」を取得しています。
- 3.Inference power demand for the next hour based:SORACOM Beam経由でAmazon SageMakerのエンドポイントを呼び出し、電力需要予測を実行しています。取得した結果をM5Stackに表示しています。このとき、Amazon SageMakerを実行するための認証情報を付加していないことにご注目ください。実際の認証情報はSORACOM Beamで付加されているためデバイス側では不要になっていることがポイントです。また、AWS SDKなどを利用せず、ArduinoHttpClientを利用して実行できていることも重要です。マイコンではAWS SDKが対応していない場合がございますが、HTTPクライアントがあればSORACOM BeamでAWSを呼び出すことができています。
稼働確認
実際に、稼働確認をしてみます。
こちらのように温湿度情報から電力需要予測を実行し、テスト実施時点は2月28日の15時時点で温度24.2度 湿度37%でした。この時に約2873.53万kWが東京で電力需要があると予測ができていますね!
さいごに
いかがだったでしょうか?IoTプロジェクトの中でAIの活用事例も出てきている中でSORACOM Beamを利用することでいかに簡単にAWS連携ができるのか感じて頂けたでしょうか?是非、色々なAIモデルを開発して頂き、是非お客様のプロジェクトでご活用ください!
― ソラコム松永 (taketo)