4K Views
August 31, 24
スライド概要
Meta Questを使い現実世界と仮想世界を融合させる。
現実世界のデバイスの操作や情報の表示を、MRデバイスを使い仮想のインターフェース上で実現する方法を解説します。
・現実世界の扇風機をMRのインターフェースから操作(コンセントのオンオフ)
・現実世界の温度/湿度センサーの情報をMRで表示
MRで現実と仮想の境界をなくす - デバイス連動編 - 2024.8.31
大久保 聡 Mail [email protected] Twitter @followapp
それほど遠くない未来
インターフェース がMRデバイスに統合される 1 Human Interface 2 Sensor Device Life Log 新しいデータが蓄積され 3 Spatial Map +Data データが現実の位置情報と 連動できる
必要な情報が 必要な時に 必要な場所で利用でき 現実のものに 仮想のIFを通じて働きかけることができる
仮想というIFを通じて現実を操作 現実 IoT機器 家電 スマホ 車 etc. デジタル の 情報 仮想 という IF 現実の操作 情報の利用
仮想を通じて現実を操作する。
META QUESTを用い、現実の操作・可視化を行う Meta Questから、直接現実にアクセスさせることも可能。今回はPythonのライブラリが充実してい ることもあり、現実とのIF部分はPythonを用い、Meta QuestからはWeb APIを経由してアクセスさ えることとする。
共通の環境構築 Python仮想環境の作成し、仮想環境に切替ます。 python3 -m venv .venv source .venv/bin/activate Webサーバーをインストールします。 pip install --upgrade fastapi Pythonのプログラム開発用に、Visual Studio Codeをインストールします。 sudo apt update sudo apt install code
VS CODEの設定 Pythonの実行環境のエクステンションをインストールします。
仮想を介して現実のデバイスにアクセスする 現実 IoT機器 家電 スマホ 車 etc. デジタル の 情報 仮想 という IF 現実の操作 情報の利用
スマートコンセント https://www.tp-link.com/jp/smart-home/tapo/tapo-p105/ ネットワークからオン・オフできるスマートコンセント「tp-lin Tapo P105」を使って家電を操作す る。 1個1000-1500円くらい。 スマホのアプリ、音声アシスタント、IFTTTからコンセントのオン・オフができる。 Wifi(ローカルエリアネットワーク)で直接操作もできる。。 Tapo P105
システム構成 コンセントオンオフで制御できる家電ということで、扇風機をP105 経由で操作します。複雑な操作 が必要な場合は、IFTTTや赤外線リモコンを経由して操作すれば良いと思います。 Webサーバー 兼 家電とのIFはRaspberryPiを利用します。 Web Server (fastapi) Client API (tapo) Python 3 Tapo P105 Quest3 RaspberryPI 5
準備、TAPOのIPアドレスの確認 TAPOのスマホアプリを起動し、デバイス一覧からTapo P105を選択します。 詳細ボタンを押し、割り当てられているIPアドレスを確認します。
環境構築 Tp-Link のスマートプラグ操作用のライブラリインストールします。 pip install --upgrade tapo
TAPOへのアクセスとWEB API
onplug/{name}とoffplug/{name}というIFを作成し実装します。
Tapoの利用時に作成したユーザーのIDとPWが接続時に必要となります。
調べたIPアドレスとnameを対応させ、それぞれにアクセスできるようなIFにします。
Import uvicorn
From fastapi import FastAPI, HTTPException, Request
From starlette.exceptions import HTTPException as StarletteHTTPException
Import asyncio
Import os
From tapo import ApiClient
From fastapi.responses import JSONResponse
Import struct
@app.get("/offplug/{name}")
async def off(
name: str
):
ip = plugs[name]
device = await client.p105(ip)
await device.off()
return {"Result": "OK"}
app = FastAPI()
Client = ApiClient(“id", “password")
Plugs = {'Plug1':'192.168.11.10', 'Plug2':'192.168.11.11’}
if __name__ == "__main__":
uvicorn.run(app, host="192.168.11.9", port=8000)
@app.get("/onplug/{name}")
async def on(
name: str
):
ip = plugs[name]
device = await client.p105(ip)
await device.on()
return {"Result": "OK"}
QUESTからWEB APIを叩く
トグルボタンのオン/オフでコンセントと、仮想空間内の風のオン/オフを切り替えます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Threading;
public class DeviceControll : MonoBehaviour
{
private Toggle toggle;
[SerializeField]
WindZone windZone;
public void OnToggleChanged()
{
if(toggle.isOn)
{
SendWebRequest("onplug", this.GetCancellationTokenOnDestroy()).Forget();
windZone.windTurbulence = 0.5f;
}
else
{
SendWebRequest("offplug", this.GetCancellationTokenOnDestroy()).Forget();
windZone.windTurbulence = 0;
}
}
[SerializeField]
string url = "http://192.168.11.9:8000";
async UniTask SendWebRequest(string command, CancellationToken ct = default)
{
var uriBuilder = new System.UriBuilder(url + "/" + command + "/" + deviceName);
var request = UnityWebRequest.Get(uriBuilder.Uri);
[SerializeField]
string deviceName = "Plug1";
await request.SendWebRequest().ToUniTask(cancellationToken: ct);
// Start is called before the first frame update
void Start()
{
toggle = GetComponent<Toggle>();
}
}
}
動作イメージ Youtube動画をご覧ください。 https://youtu.be/kfgXEEcWHXQ
仮想を通じてみえない情報を見る
みえないデジタル情報を仮想のIFを介して見る 現実 IoT機器 家電 スマホ 車 etc. デジタル の 情報 仮想 という IF 現実の操作 情報の利用
BLE温度・湿度計 https://inkbird.com/collections/thermometers-and-hygrometers Bluetooth Low Energy(無線)で温度・湿度が取得できる「INK BIRD IBS-TH1 MINI 」を使って、 環境情報を可視化する。 IBS-TH2が2000円くらい。 スマホアプリ、Bluetooth接続で値の取得ができる。 IBS-TH2 IBS-TH1 MINI
システム構成 Webサーバー 兼 家電とのIFはRaspberryPiを利用します。 Web Server (fastapi) BLE Library (bluepy3) Python 3 Quest3 IBS-TH1 MINI RaspberryPI 5
準備、IBS-TH1 MINI のMACアドレス確認 MACアドレスを調べる Bluetoothデバイスの一覧を表示し、デバイス名がspsのものを確認する。 $ sudo hcitool lescan LE Scan ... DE:FF:EF:47:C8:C8 (unknown) B0:7E:11:EE:71:51 sps 67:AA:3D:83:1D:34 (unknown) 67:AA:3D:83:1D:34 (unknown) A8:10:87:50:F7:96 (unknown) A8:10:87:50:F7:96 sps 45:11:89:9C:19:89 (unknown) CF:9B:A7:81:69:62 (unknown)
環境構築 BLEアクセス用のライブラリをインストール pip install bs4 lxml requests sudo apt-get install libglib2.0-dev libbluetooth-dev pip install --upgrade bluepy3 bluepy3-helperに権限付与 sudo setcap cap_net_raw,cap_net_admin+ep .venv/lib/python3.11/sitepackages/bluepy3/bluepy3-helper
BLEへのアクセスとWEB API
gettemp/{name}というIFを作成し実装します。
調べたMACアドレスとnameを対応させ、それぞれにアクセスできるようなIFにします。
import uvicorn
from fastapi import FastAPI, HTTPException, Request
from starlette.exceptions import HTTPException as StarletteHTTPException
import asyncio
import os
from fastapi.responses import JSONResponse
from bluepy3 import btle
import struct
app = FastAPI()
Thermometers
= {'Thermometer1':'B0:7E:11:EE:71:51', 'Thermometer2':'A8:10:87:50:F7:96'}
@app.get("/gettemp/{name}")
async def off(
name: str
):
mac = Thermometers[name]
sensorValue = await get_ibsth1_mini_data(mac)
return {
"result": "OK",
"temp": sensorValue['Temperature’],
"humidity": sensorValue['Humidity’]
}
if __name__ == "__main__":
async def get_ibsth1_mini_data(macaddr):
uvicorn.run(app, host="192.168.11.9", port=8000)
peripheral = btle.Peripheral(macaddr)
characteristic = peripheral.readCharacteristic(0x002d)
(temp, humid, unknown1, unknown2, unknown3) = struct.unpack('<hhBBB', characteristic)
sensorValue = {
'Temperature': temp / 100,
'Humidity': humid / 100,
'unknown1': unknown1,
'unknown2': unknown2,
'unknown3': unknown3,
}
peripheral.disconnect()
return sensorValue
QUESTからWEB APIを叩く
一定間隔ごとに、温度と湿度を取得しTextの文字を更新します。
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Threading;
using TMPro;
[System.Serializable]
public class Result
{
public string result;
public string temp;
public string humidity;
}
public class GetTemp : MonoBehaviour
{
[SerializeField]
string url = "http://192.168.11.9:8000";
[SerializeField]
string deviceName = "Thermometer1";
[SerializeField]
TextMeshProUGUI temperatureText;
[SerializeField]
TextMeshProUGUI humidityText;
[SerializeField]
Image image;
private async void Start()
{
while (true)
{
// n秒ごとに処理を行う
await UniTask.Delay(5000);
SendWebRequest("gettemp", this.GetCancellationTokenOnDestroy()).Forget();
}
}
QUESTからWEB APIを叩く
async UniTask SendWebRequest(string command, CancellationToken ct = default)
{
var uriBuilder = new System.UriBuilder(url + "/" + command + "/" + deviceName);
var request = UnityWebRequest.Get(uriBuilder.Uri);
await request.SendWebRequest().ToUniTask(cancellationToken: ct);
// Result:{"Result":"OK","Temp":27.25,"Humidity":62.66}
Result result = JsonUtility.FromJson<Result>(request.downloadHandler.text);
if (double.TryParse(result.temp, out double temp))
{
temperatureText.text = temp.ToString("F1") + "℃";
}
else
{
temperatureText.text = "- ℃";
}
if (double.TryParse(result.humidity, out double humidity))
{
humidityText.text = humidity.ToString("F1") + "%";
}
else
{
humidityText.text = "- %";
}
}
}
動作イメージ Youtube動画をご覧ください。https://youtu.be/XnPf5r7HTsM