実車からAWS IoT FleetWiseにデータ収集してみた
CX事業本部製造ビジネステクノロジー部の新澤です。
AWS IoT FleetWiseは、いわゆるコネクティッドカーと呼ばれるネットワークに接続された車両で取得されたデータを効率よく収集することに特化したマネージドサービスです。
前回の「マイコン基盤のセンサーデータをAWS IoT FleetWiseで収集してみた」では、ArduinoとRaspberry Piを使ってIoT FleetWiseへのデータ収集を試してみましたが、実は、Raspberry PiにCANモジュールをセットアップしたあたりから「これ、実車に接続したら、簡単にデータ収集できるんじゃ?」という気がしていたので、実際に試してみました。
概要
実車のデータは、OBDコネクタから取得することができます。OBDコネクタは、車両故障診断を行うために使われるもので、コネクタ形状や通信プロトコル、データのコードなどが規格として定められています。日本国内では、2008年10月以降に型式認定を受け生産されている車両については搭載が義務付けられています。
車両側の準備
OBDコネクタの確認
まずは、データを取得する車両のOBDコネクタの位置を確認します。
今回の対象車両の場合は、助手席の足元にありました。
ここに前回記事で使用したRaspberry Pi+CANモジュールを接続するわけですが、接続するためにOBDケーブルを作成しました。 今回は、CAN HI/LOWの2つのピンのみ結線したものにしています。 (線処理が雑なのは気にしないでください……)
Raspberry Pi + CANモジュールを接続した様子です。写真にはありませんが、実際にはRaspberry Piはモバイルバッテリーに接続して動作させました。ネットワークはモバイルWiFiルーターを使用しています。
Raspberry Piの準備
前回は、CANの通信速度を1Mbpsで設定しましたが、実際の車両では、250kbpsもしくは500kbpsとなりますので、インターフェースcan0の通信速度の設定を変更しておきます。ちなみに今回の車両の場合は500kbpsでした。
pi@raspberrypi:~ $ sudo ip link set can0 down pi@raspberrypi:~ $ sudo ip link set can0 type can bitrate 500000 pi@raspberrypi:~ $ sudo ip link set can0 up pi@raspberrypi:~ $ sudo systemctl restart fwe@0
設定変更した後、candumpコマンドでデータが流れているのが確認できればOKです。
pi@raspberrypi:~ $ candump can0 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00 can0 12345678 [8] 00 00 00 00 00 00 00 00
IoT FleetWiseの準備
データ収集のためには、OBDのシグナルを含むシグナルカタログを作成する必要がありますが、OBDの場合はAWSから提供されているテンプレートを使うことができます。
AWS提供テンプレートのシグナルモデルの作成は、管理コンソールから車両モデルを作成する際に選択することができます。
AWSリソースのデプロイ
- 管理コンソールの車両モデルで、「提供されたテンプレートを追加」をクリックします。
-
「OBD_II」をチェックし、「追加」をクリックします。インターフェイス名はcan0のままでOKです。
「OBD_II」という車両モデルが作成されました。
ここまでの手順で、OBDデータを取得するためのシグナルカタログ、車両モデル、デコーダーマニフェストまでが作成済みの状態になるので、次は車両とキャンペーンを作成します。
残りのリソースは、前回同様にCDKで実装してみます。 全体のソースはこちらに置いています。以下で紹介していないTimestreamなどのリソースの実装に関してはこちらを参照ください。
今回は速度・エンジン回転数・冷却水温度の3つのシグナルを収集するキャンペーンを作成してみます。 -
車両の定義 車両には、車両モデルとデコーダーマニフェストを関連付ける必要があるので、先ほど作成した車両モデルの画面に表示されたデコーダーマニフェストのARNを取得します。 この時、キャンペーン作成時に必要になるシグナルカタログのARNも同時に取得できますので、こちらも控えておきます。
# 車両モデルのARN $ aws iotfleetwise get-model-manifest --name OBD_II --region us-east-1 | jq -r .arn,.signalCatalogArn arn:aws:iotfleetwise:us-east-1:123456789012:model-manifest/OBD_II arn:aws:iotfleetwise:us-east-1:123456789012:signal-catalog/DefaultSignalCatalog # デコーダーマニフェストのARN $ aws iotfleetwise get-decoder-manifest --name DefaultDecoderManifest --region us-east-1 | jq -r .arn arn:aws:iotfleetwise:us-east-1:123456789012:decoder-manifest/DefaultDecoderManifest
車両のCDK実装は以下になります。
const vehicleName = "fwdemo-rpi"; // コンソールで作成した車両モデルOBD_II const modelManifestArn = "<車両モデルARN>"; // コンソールで作成したデコーダーマニフェスト const decoderManifestArn = "<デコーダーマニフェストARN>"; const vehicle = new fleetwise.CfnVehicle(this, "Vehicle", { name: vehicleName, decoderManifestArn: decoderManifestArn, modelManifestArn: modelManifestArn, associationBehavior: "ValidateIotThingExists", });
-
キャンペーン
キャンペーンのCDK実装は以下になります。const signalCatalogArn = "<シグナルカタログARN>"; const campaign = new fleetwise.CfnCampaign(this, "Campaign", { name: "TimeBasedCampaign001", action: "APPROVE", priority: 0, targetArn: vehicle.attrArn, collectionScheme: { timeBasedCollectionScheme: { periodMs: 10000, }, }, signalCatalogArn: signalCatalogArn, signalsToCollect: [ { name: "OBD.Speed", // 速度 }, { name: "OBD.EngineSpeed", // エンジン回転数 }, { name: "OBD.CoolantTemperature", // 冷却水温度 }, ], spoolingMode: "TO_DISK", diagnosticsMode: "SEND_ACTIVE_DTCS", dataDestinationConfigs: [ { timestreamConfig: { timestreamTableArn: `arn:aws:timestream:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:database/${timestreamDb.databaseName}/table/${timestreamTable.tableName}`, executionRoleArn: role.roleArn, }, }, ], });
キャンペーンのデプロイ
CDKデプロイ後に、IoT FleetWiseのマネージドコンソールでキャンペーンをデプロイし、データ収集を開始します。
コンソールでの操作手順はこちらを参照ください。
データを確認してみる
FleetWise Edge Agent(FWE)のログを確認
Raspberry Piにて以下のコマンドでFWEのログを確認します。
sudo journalctl -fu fwe@0 --output=cat
以下のようなログが出ていれば、データ収集と送信が成功しています。
pi@raspberrypi:~ $ sudo journalctl -fu fwe@0 --output=cat [Thread: 790] [2024-02-25T09:20:54.681Z] [INFO ] [DataSenderManagerWorkerThread.cpp:219] [operator()()]: [FWE data ready to send with eventID 289380637 from arn:aws:iotfleetwise:us-east-1:123456789012:campaign/TimeBasedCampaign001 Signals:6 [1002:0.000000,1002:0.000000,1001:850.000000,1001:862.500000,994:70.000000,994:70.000000,] first signal timestamp: 1708852854646 trigger timestamp: 1708852854681 raw CAN frames:0 DTCs:0 Geohash:] [Thread: 790] [2024-02-25T09:20:54.681Z] [INFO ] [DataSenderManager.cpp:295] [send()]: [A Payload of size: 203 bytes has been uploaded]
Timestreamのデータを確認
Timestreamの管理コンソールでクエリを実行して、収集されたデータを確認してみます。
- Timestreamのテーブルリストからテーブルを選択します。
-
クエリエディタを開き、テーブル"campaign"のメニューから「データをプレビュー」をクリックします。
-
クエリ結果を確認します。
OBDのデータが収集されていることが確認できました!
Grafanaでグラフ表示してみる
Timestreamに収集されたデータをGrafanaでグラフ表示してみます。
今回は、GrafanaはローカルPC上のDockerで実行することにします。
- 以下のDockerfileでGrafanaのDockerイメージにTimestreamのプラグインをインストールしたイメージを作成します。
FROM grafana/grafana-enterprise RUN grafana-cli plugins install grafana-timestream-datasource
-
docker-compose.yamlは以下のようにして、localhost:3011でアクセスできるようにします。
version: "3.8" services: grafana: build: context: . container_name: grafana restart: unless-stopped ports: - '3011:3000'
-
Grafanaのコンテナを起動します。
$ docker-compose up --build
-
Grafanaのログイン画面にアクセスします。
初期ID/パスワードともに"admin"でログインできます。 -
ログイン後、GrafanaからTimestreamにアクセスするための設定を行いますが、その際、Timestreamにアクセスする権限を持つIAMロールまたはIAMユーザーが必要になります。 IAMユーザーを作成する場合は、IAM管理コンソールでAWS管理ポリシー"AmazonTimestreamReadOnlyAccess"を付与したIAMユーザーを作成してください。
CDKで作成する場合は、以下のようなコードになります。
下記コードで作成した場合はシークレットマネージャーにアクセスキーIDとシークレットアクセスキーを格納していますので、シークレットマネージャーの管理コンソールから取得してください。const grafanaUser = new iam.User(this, "TimestreamReadOnlyUser", { userName: "grafana-user", managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonTimestreamReadOnlyAccess")], }); const accessKey = new iam.AccessKey(this, "grafanaUserAccessKey", { user: grafanaUser, }); const accessKeySecret = new secretsmanager.Secret(this, "GrafanaUserCredential", { secretName: "grafana-user-credential", secretObjectValue: { accessKeyId: cdk.SecretValue.unsafePlainText(accessKey.accessKeyId), secretAccessKey: accessKey.secretAccessKey, }, });
-
メニューからHome -> Connections -> Data sourcesと移動し、"Add new data source"をクリックします。
次に"Time series databases"のリストから"Amazon Timestream"を選択します。 -
Timestreamデータソースの設定で、認証情報、Timestreamデータベース、テーブルを指定したら"Test & Save"をクリックします。
-
続いて、ダッシュボードを作成します。
メニューからHome -> Dashboardsと移動し、New -> New dashboardをクリックします。
通常、"Add visualization"を選んで、グラフのパネルを設定することになりますが、ここでは簡略のため"Import dashboard"で作成済みのダッシュボードのJSONファイルをインポートします。JSONファイルはこちらを利用可能です。
※ Timestreamデータソースの名前が"grafana-timestream-datasource"と異なる場合はJSONファイルを環境に合わせて修正してからインポートしてください。 -
"Import"をクリックします。
-
3つのシグナルを可視化できました!
ちなみに、収集されたデータは自宅近くのスーパーまで買い物に出かけた際のもので、信号でのスタート&ストップのために速度が頻繁に0になっているのがわかるかと思います。
エンジン回転数が一瞬0に落ちているのは、アイドリングストップが発生したためです。
冷却水温が一度落ちているのは…なぜでしょう?わかりません(^^;;
最後に
今回は、車両でOBDデータを取得してIoT FleetWiseに収集から可視化まで行なってみました。
事前に感じていた以上に簡単に車両からデータを取得できたので、とても感動しました!
今回作成したOBDのシグナルカタログには、他にも多数のシグナルが定義されていますので、それらを眺めて、いろいろ活用アイディアを考えてみるのも楽しそうです。