Digital TwinsとMixed Realityの組み合わせを解説する続編
先日のBuildで紹介されていたDigital TwinsとMixed Realityに関するサービスや技術についての解説の続編として今回はAzure Digital Twinsについての紹介をしたいと思います。
今回も解説につかうものはMicrosoft Learn 「Azure Digital Twins と Unity を使用して Mixed Reality デジタル ツインを構築する」になります。
必要に応じて以下の記事も参照してください。
とりあえずラーニングパスの概要を掴みたい方は以下のqiita記事の中でもラーニングパス「Azure Digital Twins と Unity を使用して Mixed Reality デジタル ツインを構築する」とはを参照してください。
ラーニングパスのアーキテクチャについて(再掲)
この節は以前の記事の内容と同じですが図の解説範囲だけ今回ように変更しています。
このラーニングパスについては以下のようなアーキテクチャ構造を取っています。このラーニングパスはサンプル風力発電プラントのデジタルツインを想定した作りになっています。
物理環境には数台の風車が稼働している想定になっており、以下の流れで処理が行われます。デモアプリでは異常系としてAlertの発生もできるようになっています。これらの情報はプログラムでエミュレートされています。
- 風車はAzure IoT Hubで管理。各風車はリアルタイムで風車の周辺温度、風速、回転数をAzure IoT Hubのテレメトリ情報として送信
- Azure IoT Hubは風車からのテレメトリー情報をEvent Gridへ転送
- Event Grid(システムトピック)ではAzure IoT Hubを監視しテレメトリー情報の受信を検知するとAzure Functionsを呼出
- Azure Functions内では受信したテレメトリー情報を、Azure Digital Twinsであらかじめモデル化された風車の情報を更新
- Azure Digital Twinsは更新されたモデル情報をEvent Gridへ転送
- Event Gridでは登録されたAzure Functionsを呼出
- Azure Functions内ではDigital Twinsから取得した情報をSignalRを通じて同報発信を実施
- HoloLensはアプリケーション内でSignalRに接続し通知された情報で風車の各情報を更新
- 風車に問題が発生した場合、Azure Digital Twinsに対してAlertを送信。Alert情報はテレメトリー情報と同じ経路で処理される
- Alertの連続送信を防止するためにAzure Digital Twinsの情報を取得
- HoloLensからAlert情報のリセットを行う。Azure Digital Twinsに対してAlert解除を実施
ラーニングパスで活用されている技術(Azure Digital Twins編)
ではラーニングパスを参考にAzure Digital Twinsがラーニングパス上でどのように活用されているか確認していきます。
Azure Digital Twinsとは(概要)
現実世界の環境全体のデジタル モデルに基づいたツイン グラフの作成を可能にする、サービスとしてのプラットフォームです。
物理環境をデジタル環境側で構築する際の情報を管理するためのモデル定義とデジタルツインの情報を管理します。一種のJSON型のRDMSが一番近いイメージになると思います。以下はAzure Digital Twinsで扱う情報を模式的に表現したものです。
Azure Digital Twinsではまず、物理環境のデジタルツインを作成するにあたりモデルを構築します。このモデルは物理環境のある要素とそのパラメータで構成されており、図のような建物をモデル化すると、以下のようなモデル構成になります。なお、これはあくまで一例で実際の物理環境に合わせてこの情報は変わります。
- Building:建物勢体を表すモデル。名称、住所をパラメータに持ち、建物内部は複数のフロアで構成される。
- Floor:フロアを表すモデル。階数をパラメータに持ち、フロアには複数の部屋、入り口が存在する。
- Room:部屋を表すモデル。室名、温度、湿度、人数をパラメータに持つ。
- Entrance:入り口を表すモデル。開錠時間、施錠時間をパラメータに持つ。
このように物理環境を構成する要素をモデルで定義し実際の物理環境と対になるデジタル環境の構造物を作成します。
図の例は、4階建てで2階以上は3部屋で構成、1階には1つ入口がある建物に対応したデジタル上の構造物となります。
そして、この構築されたデジタル環境に、物理環境でのセンシングデータをリアルタイムで反映することでデジタルツインが実現します。
DTDL(Digital Twin Definition Language)
Azure Digital Twinsで扱う情報は上記の様なモデルを取りますが、その記述様式としてDTDL(Digital Twin Definition Language)を用います。これは簡単なJsonで定義された文法になっており、OSSで公開されています(この記事を記載時点2021/06/24ではV2)。DTDLはJsonで記述はするのですが、ちょうどオブジェクト指向でいうところのクラスとインスタンスの関係に似ています。構造的にも継承関係などを持ったりもできます。
参考までにラーニングパスの風車のモデルは以下の様に定義されています。風車のID(TurbineID),風車の状態(Alert)がプロパティで定義されていて、センシングデータはテレメトリ情報として記述されています。それぞれのタイプはモデル上での情報分類に使用しており、プロパティは文字通りで対象の物理環境に対するモデルの特徴を表す情報になります。風車のID、建物名前、住所といったものです。テレメトリ情報はIoT HubでもあったAzure Digital Twinsから送信されるデータとして定義します。
これらの定義はあくまでは分類上の話ですので、テレメトリ情報をプロパティで表現したからといって正しく動作しないなどはないです。
{
"@id": "dtmi:digitaltwins:Basic:Turbine1;1",
"@type": "Interface",
"@context": [
"dtmi:dtdl:context;2"
],
"displayName": "Wind Turbine -v2",
"description": "Wind Turbine data",
"contents": [
{
"@type": "Property",
"name": "TurbineID",
"displayName": "TurbineID",
"description": "The unique id of the turbine",
"writable": true,
"schema": "string"
},
{
"@type": "Property",
"name": "Alert",
"displayName": "Alert",
"description": "whether or not this turbine needs maintenance",
"writable": true,
"schema": "boolean"
},
{
"@type": "Telemetry",
"name": "TimeInterval",
"description": "The time interval of this data",
"schema": "string"
},
{
"@type": "Telemetry",
"name": "Description",
"schema": "string"
},
{
"@type": "Telemetry",
"name": "Code",
"schema": "integer"
},
{
"@type": "Telemetry",
"name": "WindSpeed",
"schema": "double"
},
{
"@type": "Telemetry",
"name": "Ambient",
"schema": "double"
},
{
"@type": "Telemetry",
"name": "Rotor",
"schema": "double"
},
{
"@type": "Telemetry",
"name": "Power",
"schema": "double"
}
]
}
Digital Twinsの可視化
Azure Digital Twinsはある種のJsonで記述するRDBMSのようなものなので、その情報を引出すためにはクエリ言語等を利用してAPI経由で呼出します。クエリの結果はJsonで返却されて情報を見ることができます。
一方で、Azure Digital Twinsで構成した情報はモデル全体の関係性を持っている場合が多くその関係性を俯瞰視点で見たい場合もあると思います。
このためのツールや手段が提供されています。
「クイック スタート - Azure Digital Twins Explorer を使用して、Azure Digital Twins のサンプル シナリオを精査する」でも紹介されているAzure Digital Twins Explorerを利用する方法です。このツールはNode.jsで作られた可視化ツールでAzure Digital Twinsのモデル定義とそのデジタルツインの情報をGraphで視覚的に確認することが可能です。詳細な使い方は上記のクイックスタートのリンク先を参照してください。
ラーニングパスの情報をAzure Digital Twins Explorerで確認すると以下のようなUIで可視化することが可能です。
環境構築
Azure Digital Twinsを含めてこのラーニングパスではARM-Templateを活用しています。
ARM-Templateについて
ARM(Azure Resource Managerの略)テンプレートは文字通りAzureのリソースを管理するためのテンプレートになります。ラーニングパスではazuredeploy.bicepファイルがそれです。中身を見るときはVisual Studio Codeが便利です。合わせて以下のExtensionも追加しておくと自分で作るときに役立つと思います。
Azure Digital Twins関連のサービス
Azure サービス名 | ラーニングパスでの利用方法 |
---|---|
Azure Digital Twins | 風車の物理環境に対するデジタルツインの情報を管理する |
Azure Functions | Azure IoT Hubからのテレメトリ情報をAzure Digital Twinsに登録する関数(telemetryfunction)と、届いたテレメトリ情報をSignalRを利用して同報発信する関数(broadcast) |
Identity & role | Azure Digital Twinsを利用する為にはAzure Digital Twinsのロールが必要になるためそのためのIdentitiyを定義します。 |
Event Grid | システムトピックとしてAzure IoT Hubに紐づけられたEvent Gridです。 |
PostDeploymentscript | これ自体はAzureのサービスではないのですが、ARM-Templateの後処理として実行します |
Azure Digital Twins
Azure Digital Twinsの作成部分です。依存関係としてAzureのマネージドIDが入っています。
// create ADT instance
resource adt 'Microsoft.DigitalTwins/digitalTwinsInstances@2020-03-01-preview' = {
name: adtName
location: location
tags: {}
properties: {}
dependsOn: [
identity
]
}
Azure Functions
Identity & role
権限周りはまずAzure マネージドIDを作成します。ラーニングパスの実行に必要な各種権限を付与していきます。
// Azure マネージドIDの作成
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: identityName
location: location
}
//リソースグループの所有権を作成したAzure マネージドIDに割当て
// add RBAC role to resource group -
resource rgroledef 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: rgRoleDefinitionName
properties: {
roleDefinitionId: rgRoleDefinitionId
principalId: reference(identityName).principalId
principalType: 'ServicePrincipal'
}
}
// Azure Digital Twins データ所有者のロールを作成したAzure マネージドIDに割当て
// add "Digital Twins Data Owner" role to ADT instance for our deployment
resource adtroledef 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: ADTroleDefinitionName
properties: {
roleDefinitionId: ADTroleDefinitionId
principalId: reference(identityName).principalId
principalType: 'ServicePrincipal'
}
}
// ストレージ BLOB データ共同作成者のロールを作成したAzure マネージドIDに割当て
// add "Storage Blob Data Contributor" role to RG for our deployment
resource storageroledef 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: storageRoleDefinitionName
properties: {
roleDefinitionId: storageRoleDefinitionId
principalId: reference(identityName).principalId
principalType: 'ServicePrincipal'
}
}
// Azure Functionsのシステム割り当て済みIDに対してAzure Digital Twins データ所有者のロールに割当て
// add "Digital Twins Data Owner" permissions to teh system identity of the Azure Functions
resource adtroledefapp 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: ADTroleDefinitionAppName
properties: {
roleDefinitionId: ADTroleDefinitionId
principalId: reference(funcApp.id, '2019-08-01', 'Full').identity.principalId
principalType: 'ServicePrincipal'
}
dependsOn: [
funcApp
]
}
// オーナーにAzure Digital Twins データ所有者のロールを割り当てる
// assign ADT data role owner permissions to Application
resource ADTRoleDefinitionUser 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: ADTRoleDefinitionAppRegName
properties: {
roleDefinitionId: ADTroleDefinitionId
principalId: userId
principalType: 'User'
}
}
// assign ADT data role owner permissions to App Registration
resource ADTRoleDefinitionAppReg 'Microsoft.Authorization/roleAssignments@2018-09-01-preview' = {
name: ADTRoleDefinitionUserName
properties: {
roleDefinitionId: ADTroleDefinitionId
principalId: appRegObjectId
principalType: 'ServicePrincipal'
}
}
Event Grid
Azure Digital Twinsに登録して利用するためのEvent Gridを作成します。Azure IoT Hubの時とは異なりシステムトピックではない通常のEvent Gridを作成します。最終的にこのEvent GridをAzure Digital Twinsに紐づけて処理ルートを構築します。
resource eventGridChangeLogTopic 'Microsoft.EventGrid/topics@2020-10-15-preview' = {
name: eventGridCLTopicName
location: location
sku: {
name: 'Basic'
}
kind: 'Azure'
identity: {
type: 'None'
}
properties: {
inputSchema: 'EventGridSchema'
publicNetworkAccess: 'Enabled'
}
}
PostDeploymentscript
Azure Digital Twinsについてはサービスを構築しただけでは利用できません。先の説明をした通り物理環境に対応するモデル定義の登録と、デジタル環境上に物理環境に対応する構造を構築する必要があります。この処理についてはAzure CLI等コマンドを実行する必要があります。このため、ARM-Template上で一番最後にBashを実行するように定義されています。Bashの中ではAzure CLIによってモデルデータの登録、Event Gridの登録を実施します。
resource PostDeploymentscript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'PostDeploymentscript'
location: resourceGroup().location
kind: 'AzureCLI'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${identity.id}': {}
}
}
properties: {
forceUpdateTag: utcValue
azCliVersion: '2.15.0'
arguments: '${iot.name} ${adt.name} ${resourceGroup().name} ${location} ${eventGridChangeLogTopic.name} ${eventGridChangeLogTopic.id} ${funcApp.id} ${storage.name} ${fileContainerName}'
primaryScriptUri: 'https://raw.githubusercontent.com/MicrosoftDocs/mslearn-mr-adt-in-unity/main/ARM-Template/postdeploy.sh'
supportingScriptUris: []
timeout: 'PT30M'
cleanupPreference: 'OnExpiration'
retentionInterval: 'P1D'
}
dependsOn: [
rgroledef
iot
]
}
a.モデルの登録
Bashスクリプトの最初はAzure Digital Twinsに風車のモデルと登録します。モデルデータはGitHub上に格納されているモデルデータ(turbine.json)を登録しています。
# echo 'input model'
turbineid=$(az dt model create -n $adtname --models ./blade-infra/models/turbine.json --query [].id -o tsv)
b.デジタルツインの生成
モデルの登録後ラーニングパスではT98~T107までの風車をデジタルツインとして構成します。
# echo 'instantiate ADT Instances'
for i in {98..107}
do
echo "Create Turbine T$i"
az dt twin create -n $adtname --dtmi $turbineid --twin-id "T$i"
az dt twin update -n $adtname --twin-id "T$i" --json-patch '[{"op":"add", "path":"/TurbineID", "value": "'"T$i"'"},{"op":"add", "path":"/Alert", "value": false}]'
done
c.Azure Digital TwinsへのEvent Gridの割り当てとEvent GridへのFunctionsの設定
最後にAzure Digital Twinsのテレメトリ情報を使ってSignalRを同報発信するためにEvent GridとFunctionsの定義を行います。
Azure Digital TwinsにEvent Gridを登録するためにはAzure Digital TwinsのエンドポイントとしてEvent Gridを登録します。
次に、このエンドポイントをイベントルートに登録します。
この2段階の設定を行うことで、Azure Digital Twinsのテレメトリ情報が変更されるとイベントルートで定義された内容に従って変更情報をチェックし、通知の必要性がある情報であれば、エンドポイントに渡します。エンドポイントは紐づけされたEvent Gridに通知しそこに登録されている関数が呼び出されるという形になります。
# az eventgrid topic create -g $rgname --name $egname -l $location
az dt endpoint create eventgrid --dt-name $adtname --eventgrid-resource-group $rgname --eventgrid-topic $egname --endpoint-name "$egname-ep"
az dt route create --dt-name $adtname --endpoint-name "$egname-ep" --route-name "$egname-rt"
# Create Subscriptions
az eventgrid event-subscription create --name "$egname-broadcast-sub" --source-resource-id $egid --endpoint "$funcappid/functions/broadcast" --endpoint-type azurefunction
実装部分
次に実装部分でどういうことをしているかを見ていきましょう。今回はAzure IoT Hubからの情報でAzure Digital Twinsの上方を更新する関数(telemetryfunction)と、Azure Digital Twinsの情報が更新されるとSignalRに同報発信する関数(broadcast)の実装と処理の流れを紹介します。
関数の定義(Azure IoT Hub -> Azure Digital Twins)
まずはAzure IoT HubからAzure Digital Twinsにテレメトリ情報を送る関数部分の解説を行いたいと思います。
Azure Funtionsの引数部分でAzure IoT Hubの情報を取得するようになっています。引数の属性として定義されるEventGridTriggerAttributeによってこの関数はEvent Gridの内で定義できるタイプになります。
引数のeventGridEventには、Event Gridが受け取った情報が含まれています。今回のサンプルではテレメトリー情報としてJsonデータが入っています。
public async void Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log)
Jsonデータは以下の形式で取得が可能です。
// データの取得
JObject alertMessage = (JObject)JsonConvert.DeserializeObject(eventGridEvent.Data.ToString());
このJsonデータをAzure Digital Twinsにテレメトリ情報として更新していきます。
その処理を抜粋すると以下のような形になります。一般的なRDBMSの様に接続を開始し、データを準備し、更新するといった部分がSDKで提供されています。こ
今回の場合、風車のデジタルツインとしての情報は「テレメトリ情報」、「プロパティ情報」の2系統あるため、処理が分かれています。
プロパティ情報については初めに説明したこのラーニングパスのアーキテクチャでのAlert情報を更新する際の処理として実装されています。
一方、IoT Hubからセンサー情報として受け取る部分はテレメトリ情報として更新するように実装されています。
// Httpリクエスト送信の準備
HttpClient httpClient = new HttpClient();
// Azureの権限情報を取得
var credentials = new DefaultAzureCredential();
// Azure Digital Twinsへの接続
DigitalTwinsClient client = new DigitalTwinsClient(
new Uri(adtServiceUrl), credentials, new DigitalTwinsClientOptions
{ Transport = new HttpClientTransport(httpClient) });
// 1. テレメトリ情報の送信 -----------------------------------------------
//テレメトリ用更新データの準備
var turbineTelemetry = new Dictionary<string, Object>()
{
["TurbineID"] = ID,
["TimeInterval"] = TimeInterval,
["Description"] = Description,
["Code"] = Code,
["WindSpeed"] = WindSpeed,
["Ambient"] = Ambient,
["Rotor"] = Rotor,
["Power"] = Power
};
// テレメトリ情報の送信
await client.PublishTelemetryAsync(deviceId, Guid.NewGuid().ToString(), JsonConvert.SerializeObject(turbineTelemetry));
// 1. プロパティ情報の更新 -----------------------------------------------
// プロパティ用更新データの準備
var updateProperty = new JsonPatchDocument();
updateProperty.AppendReplace("/Alert", alert.Value<bool>());
updateProperty.AppendReplace("/TurbineID", ID.Value<string>());
// プロパティ更新処理
await client.UpdateDigitalTwinAsync(deviceId, updateProperty);
関数の定義(Azure Digital Twins -> SignalR)
次にAzure Digital TiwnsからSignalRへ同報発信する関数の処理を見ていきましょう。
SignalRを利用したFunctionsはいくつかセオリーを守ると呼出しが簡単になります。ポイントは2つほどあり、1.SignalRへの接続先を固定のキー名でAzure Functionsのアプリ設定に登録し、2.negotiateという名前の関数を用意することで、クライアント側からSignalRの接続を簡略化できるようになっています。
詳しくは以下のしばやんさんの情報などがわかりやすいと思います。
SignalRへの接続情報の取得
キー名は AzureSignalRConnectionString で固定で、値に接続文字列を入れる。そして以下の通りnegotiate関数を用意しておくとOKです。これでクライアント側(Hololens)での実装が楽になります。
[FunctionName("negotiate")]
public static SignalRConnectionInfo GetSignalRInfo(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
[SignalRConnectionInfo(HubName = "dttelemetry")] SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}
メッセージの送信側のメソッドは以下の通り。signalRMessagesを通してメッセージを投げると同報発信することが可能になります。Azure Digital Twinsからの情報はEvent Gridを介して関数が呼び出されるようになっています。Azure IoT HubからAzure Digital Twinsへの関数(telemetryfunction)と同様で引数のeventGridEventからAzure Digital Twinsの更新された情報を取得できるのでこの情報を加工してSignalRに伝えます。
[FunctionName("broadcast")]
public static Task SendMessage(
[EventGridTrigger] EventGridEvent eventGridEvent,
[SignalR(HubName = "dttelemetry")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
// Event Gridからの情報を取得
var data = eventGridData.SelectToken("data");
//SignalRで送信するデータの準備
var telemetryMessage = new Dictionary<object, object>();
foreach (JProperty property in data.Children())
{
log.LogInformation(property.Name + " - " + property.Value);
telemetryMessage.Add(property.Name, property.Value);
}
// SignalR でテレメトリ情報を同報発信する
await signalRMessages.AddAsync(
new SignalRMessage
{
Target = "TelemetryMessage",
Arguments = new[] { telemetryMessage }
});
処理の流れは?
Azure IoT HubからAzure Digital Twinsに情報が届き、そこからHoloLensに向かって情報を発信するための関数を呼ぶまでの処理の流れは以下のようになっています。Azure Digital Twins内にはデータを外部に出力するために「Event Route」, 「End Point」の2つを経由して実現しています。フローは以下の通りです。
- Azure IoT Hubから呼び出されたAzure Functionsを通してAzure Digital Twins内の情報を更新します。
- Azure Digital Twinsの情報が更新されるとEvent Routeに定義したルートに対して情報を通知します。
- Event Routeは情報の通知を受けると、フィルター条件に合致する場合後段のEnd Pointのその情報を通知します。
- End PointにはあらかじめEvent Gridとリンクされており、End PointからEvent Gridへ情報を転送します。
- Event GridはAzure Functionsを呼出して処理を実施する。
まとめ
今回は、「Microsoft Learn 「Azure Digital Twins と Unity を使用して Mixed Reality デジタルツインを構築する」解説(Azure Digital Twins編)」としてAzureで構成するDigital TwinsのベースになるAzure Digital Twinsの動作についてラーニングパスのサンプルで紹介しました。実際のプロジェクトでの活用時の参考になると幸いです。