在TwinMaker中,展示登录用户当前位置在3D上的方法。(Note: This sentence is already in Chinese, so there is no need for paraphrasing.)
这篇文章是关于什么的?
在这篇文章中,我们将解释两个问题。
-
- TwinMaker上で、ログインユーザーごとに違うコンテンツを表示する方法を解説します
- TwinMaker上で、リアルタイムな位置情報を、3Dコンテンツの位置に反映させる方法を解説します
在这篇文章中要制作的东西
在这篇文章中,我们将创建以下类似TwinMaker的场景。
-
- Aさんがログインすると、Aさんの部屋のルンバの位置に、ルンバの3Dモデルが表示されます
-
- Bさんがログインすると、Bさんの部屋のルンバの位置に、ルンバの3Dモデルが表示されます
- また、注釈に表示される画像と文言が変わります

在这篇文章中,我们不会涉及到获取位置信息的问题,只是简单地说明一下在3D空间中Roomba的位置会发生变化。
怎么实现呢?
-
- Pythonの定期実行で、動的にTwinMakerのシーンをコピーします
- ログインユーザーの情報が必要になるので、iot-app-kitでWebアプリ化します
你为什么开心?
- ユーザーAさんにはAさん向けの3Dを、BさんにはBさん向けの3Dだけを見せることができます
所需之物
-
- AWSアカウント
-
- Python3.10
-
- VSCode
- Node.js(記事はv14.15.1で検証していますが、新しいほうがいいです)
首先是准备工作:引入IoT应用开发工具包。
由于TwinMaker只能被拥有AWS账户的开发者在AWS管理控制台上查看,因此它不适合根据登录用户的不同动态变化。我们将使用iot-app-kit来解决这个问题。
物联网应用套件是由AWS发布的一个库。可以将TwinMaker的3D场景、图表等作为React的Web应用程序进行构建。可以创建专门供用户浏览的Web页面。
由于物联网应用套件库仍然是一个相对新的库,因此构建过程会有一些困难。即使按照README中的指示操作,有时仍无法运行。请参考以下步骤开始构建。
步骤1:创建一个React项目
# プロジェクトを作りたいフォルダで、reactのプロジェクトを作成します
npx create-react-app app --template typescript
# プロジェクトができたら、作成したフォルダに移動します
cd app
# 必要な依存ライブラリをインストールします
npm install
步骤2:安装IoT应用套件。
# app-kitをインストールします(※READMEに出ている手順はここまでです)
npm install @iot-app-kit/components
npm install @iot-app-kit/react-components
npm install @iot-app-kit/source-iottwinmaker
npm install @iot-app-kit/scene-composer
# とりあえずエラーが出るので、babelのプラグインをインストールします
npm install @babel/plugin-proposal-private-property-in-object
# sassがないので、sassをインストールします
npm install sass
# ポリフィルが必要なので、react-app-rewiredとポリフィルをインストールします
npm install react-app-rewired --save-dev
npm install node-polyfill-webpack-plugin
第三步:在项目的根目录下,放置适用于react-app-wired的配置文件。
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(
new NodePolyfillPlugin({
excludeAliases: ["console"],
})
);
config.resolve.alias = {
"react/jsx-runtime.js": "react/jsx-runtime",
"react/jsx-dev-runtime.js": "react/jsx-dev-runtime",
};
return config;
};
第四步:修改package.json文件中的scripts,以便能够使用react-app-rewired。
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
步骤5:由于three.js版本不兼容,您需要获取最新版本。
npm install three
npm install @react-three/fiber
npm install @react-three/drei
npm install three-stdlib
第六步:准备就绪后,启动React。
npm run start
目前来看,多半这样就可以了(※截至2023年6月11日)。
{
“name”: “app”,
“version”: “0.1.0”,
“private”: true,
“dependencies”: {
“@babel/plugin-proposal-private-property-in-object”: “^7.21.11”,
“@iot-app-kit/components”: “^6.2.0”,
“@iot-app-kit/react-components”: “^6.2.0”,
“@iot-app-kit/scene-composer”: “^3.3.0”,
“@iot-app-kit/source-iottwinmaker”: “^6.2.0”,
“@react-three/drei”: “^9.74.8”,
“@react-three/fiber”: “^8.13.1”,
“@testing-library/jest-dom”: “^5.16.5”,
“@testing-library/react”: “^13.4.0”,
“@testing-library/user-event”: “^13.5.0”,
“@types/jest”: “^27.5.2”,
“@types/node”: “^16.18.35”,
“@types/react”: “^18.2.11”,
“@types/react-dom”: “^18.2.4”,
“jsx-runtime”: “^1.2.0”,
“node-polyfill-webpack-plugin”: “^2.0.1”,
“path-browserify”: “^1.0.1”,
“react”: “^18.2.0”,
“react-dom”: “^18.2.0”,
“react-scripts”: “5.0.1”,
“sass”: “^1.63.3”,
“three”: “^0.153.0”,
“three-stdlib”: “^2.23.9”,
“typescript”: “^4.9.5”,
“web-vitals”: “^2.1.4”
},
“scripts”: {
“start”: “react-app-rewired start”,
“build”: “react-app-rewired build”,
“test”: “react-app-rewired test”,
“eject”: “react-app-rewired eject”
},
“eslintConfig”: {
“extends”: [
“react-app”,
“react-app/jest”
]
},
“browserslist”: {
“production”: [
“last 1 chrome version”
],
“development”: [
“last 1 chrome version”
]
},
“devDependencies”: {
“react-app-rewired”: “^2.2.1”
}
}
建设任务1:创建成为模板的TwinMaker场景。
进行工作
首先,使用模板的名称创建一个 TwinMaker 场景
具体操作如下两点:
-
- 部屋の3Dモデルを中央に配置する
- カメラをCamera1の名前で作成する

我从Sketchfab借用了这个模拟房间的GLB模型(其授权为CC4.0)。
链接:https://sketchfab.com/3d-models/studio-apartment-vray-baked-textures-included-cae2d96ede1d4112b1fd391099a43f77
完成这个任务后,就会变成这样
使用模板的名称创建场景时,将在与S3中对应的存储桶(存储桶结尾为-iad)中创建template.json的JSON文件。

TwinMaker的场景将通过与场景名称相同的JSON文件在S3上进行统一管理。
构建作业2:创建与用户数量相同的空场景。
进行
由于本次计划是让两个用户同时登录,所以我创建了两个空场景。
-
- shotaoki_mail_com
- other_user_mail_com
由于命名规则的限制,无法使用@等符号,因此我们使用下划线替代电子邮件地址的符号部分。

根据配额规定,您可以在一个工作区创建100个场景,所以即使再增加97个人也没有问题。
构建任务3:使用Python编写脚本,实现复制模板的处理。

进行
-
- 用Python脚本从S3中获取一个模板的JSON文件,
-
- 更改对象的配置信息,覆盖现有的针对用户A的JSON文件,
- 更改对象的配置信息,覆盖现有的针对用户B的JSON文件。
这样做可以创建适合用户A和用户B的场景。
我也从Sketchfab借用了Roomba的数据,该模型也是CC 4.0许可证。链接在这里:
源代码
用以下内容的Python文件,在Python应用程序下使用–bucketname ${S3的存储桶名称}命令来执行。
pip install pydantic boto3
from argparse import ArgumentParser
from pydantic import BaseModel
import boto3
import io
import json
from typing import List
class Argments(BaseModel):
"""
app.pyの引数を定義する
実行方法:
python app.py --bucketname ${バケット名}
"""
bucketname: str
@classmethod
def parse_args(cls):
# pythonファイルの実行引数をpydanticにパースする
parser = ArgumentParser()
for k in cls.schema()["properties"].keys():
parser.add_argument(f"-{k[0:1]}", f"--{k}")
return cls.parse_obj(parser.parse_args().__dict__)
def add_roomba(
template: dict, args: Argments, x: float, y: float, z: float, message: str
):
"""
ルンバを部屋に召喚する
message: アノテーションに表示するメッセージ
x, y, z: 表示する座標
"""
current: List[dict] = template["nodes"]
# ルンバを配置する(glbがややズレているので、固定値でオフセットを入れる)
current.append(
{
"name": "low-poly_roomba",
"transform": {
"position": [-3.056 + x, -0.4 + y, -2.78 + z],
"rotation": [0, 0, 0],
"scale": [0.5, 0.5, 0.5],
},
"transformConstraint": {},
"components": [
{
"type": "ModelRef",
"uri": f"s3://{args.bucketname}/low-poly_roomba.glb",
"modelType": "GLB",
"unitOfMeasure": "meters",
}
],
"properties": {},
}
)
# ルンバの上のアノテーションを配置する
current.append(
{
"name": "Annotation",
"transform": {
"position": [-1.2 + x, 0.1671 + y, 1.4 + z],
"rotation": [0, 0, 0],
"scale": [0.5, 0.5, 0.5],
},
"transformConstraint": {},
"components": [
{
"type": "DataOverlay",
"subType": "TextAnnotation",
"valueDataBindings": [],
"dataRows": [{"rowType": "Markdown", "content": message}],
}
],
"properties": {},
}
)
# オブジェクトの配置情報を設定する
template["nodes"] = current
# 全てのオブジェクトをルートノードに設定する
template["rootNodeIndexes"] = [r for r in range(len(current))]
def main(args: Argments):
"""
ユーザーAのシーンを作成する
"""
s3 = boto3.resource("s3")
bucket = s3.Bucket(args.bucketname)
# テンプレートシーンをコピーする
template = {}
with io.BytesIO() as f:
bucket.download_fileobj("template.json", f)
template = json.loads(f.getvalue())
# ルンバ召喚
add_roomba(
template,
args,
x=0,
y=0,
z=0,
message="""
## Aさんのルンバ
""",
)
# ルンバを召還したシーンを、ユーザーAのシーンとして保存する
with io.BytesIO(json.dumps(template).encode("utf-8")) as f:
bucket.upload_fileobj(f, "shotaoki_mail_com.json")
def main2(args: Argments):
"""
ユーザーBのシーンを作成する
"""
s3 = boto3.resource("s3")
bucket = s3.Bucket(args.bucketname)
# テンプレートシーンをコピーする
template = {}
with io.BytesIO() as f:
bucket.download_fileobj("template.json", f)
template = json.loads(f.getvalue())
# ルンバ召喚(※X方向に1.6メートル、Z方向に2.2メートル移動した場所)
add_roomba(
template,
args,
x=1.6,
y=0,
z=-2.2,
message="""
## Bさんのルンバ
""",
)
# ルンバを召還したシーンを、ユーザーBのシーンとして保存する
with io.BytesIO(json.dumps(template).encode("utf-8")) as f:
bucket.upload_fileobj(f, "other_user_mail_com.json")
# 処理を実行する
main(Argments.parse_args())
main2(Argments.parse_args())
環境構築4:從iot-app-kit打開場景。
进行
我打开了之前准备好的React项目,并更新了src/App.tsx文件。
import "./App.css";
import { initialize } from "@iot-app-kit/source-iottwinmaker";
import { SceneViewer } from "@iot-app-kit/scene-composer";
function App() {
const sceneLoader = initialize("${workspace-name}", {
awsCredentials: {
accessKeyId: "XXXXXXXXXXXXXXX",
secretAccessKey: "XXXXXXXXXXXXXXX",
},
awsRegion: "${region-name}",
}).s3SceneLoader("${scene-name}");
return (
<div className="App">
<SceneViewer sceneLoader={sceneLoader} activeCamera="Camera1" />
</div>
);
}
export default App;
请将变量替换为您自己的环境如下:
移动了一下
在终端中运行 npm run start,然后在浏览器中打开 http://localhost:3000,即可显示 TwinMaker 的界面。

在环境构建5中,将图像放入注释。
在标注中可以显示图像。
在源代码中的部分写有“A先生的Roomba”(注释、覆盖),可以使用Markdown格式,在的格式下,可以显示喜欢的图片。
要在文本下方显示图像,需要留出两行空白。必须有文字内容。由于注释显示区域的大小取决于文字内容,所以无法仅显示图像。
## Aさんのルンバ

请注意:在管理控制台上,注释中的图片链接不会工作。
※由于AWS管理控制台中的TwinMaker受到连接域的限制,因此图片链接无法正常使用。需要使用iot-app-kit。
总结:将登录用户的位置信息以3D方式显示。
需要获取位置信息并将其与SiteWise进行协作,然后通过Lambda定期执行来进行场景复制,就可以让登录用户将其拥有的物联网设备的位置信息显示在3D上。
使用TwinMaker,我们可以通过数字双胞胎将类似于Roomba被虚拟墙困住无法移动的情况可视化出来。您不需要进行复杂的编码,也不需要学习Blender等3D软件。这是一个很大的优点。
另外,如果将注释图像的获取源设置为ApiGateway或S3对象Lambda,则还可以在打开TwinMaker页面的瞬间显示图表。
填補
场景的更新只在重新加载时进行,TwinMaker上不能播放3D模型的动画(※通常的方法)。因此,保持仪表盘打开并实时跟踪是很困难的。
在Lambda中动态创建并传送在TwinMaker中显示的3D模型是可能的。但这种情况下,需要将Lambda定期执行产生的文件放置在S3中。通过Object Lambda的端点直接从Lambda传送3D模型供TwinMaker引用是不可行的。(*您可以使用别名通过S3端点进行传送,但由于Object Lambda的端点不能严格作为S3端点处理,所以会受到域限制的约束。)
请参考此处关于如何从图像动态创建GLTF的方法