Chaîne de simulation
Aperçu
Le canal de simulation est un canal WebSocket bidirectionnel à haute fréquence qui sert à échanger des informations sur l'état de l'appareil ainsi qu'à envoyer des commandes de session ou des commandes relatives à l'appareil.
URL standard : ws://localhost:10001
Le port peut être modifié dans la configuration.
Comportement de base (important) :
- Dès qu'un client se connecte, le service envoie un message contenant l'inventaire initial, qui comprend la liste complète des appareils.
- Ensuite, le serveur envoie exactement un message de mise à jour d'état pour chaque message reçu du client.
- Chaque mise à jour d'état contient l'état (et le statut) de tous les appareils.
Cette page décrit le protocole de base du canal de simulation et ses commandes.
Des modules ou fonctions supplémentaires peuvent étendre le système en y ajoutant des commandes et/ou des champs d'état personnalisés, puis les enregistrer. Ceux-ci sont documentés séparément dans la section « Modules ».
Règles de protocole
Une réponse par message client
Pour chaque message reçu du client, le service envoie un message de mise à jour d'état indiquant l'état de tous les appareils.
Cela signifie :
- Si vous avez besoin d'un instantané à jour de l'état de l'appareil, vous devez envoyer une commande (une commande de session, une commande d'interrogation ou une commande de l'appareil).
- Le canal fonctionne comme une boucle « tick » pilotée par les messages du client.
Interrogation sans application de forces (commandes d'interrogation)
Si vous souhaitez observer des changements d'état sans appliquer de forces ni modifier les paramètres de simulation, utilisez les commandes d'interrogation :
probe_positionpourinverse3probe_orientationpour les poignées Verse
Les commandes d'interrogation ne contiennent aucune donnée de commande et servent uniquement à interroger les informations relatives à l'appareil, afin que les données de position et d'orientation soient à jour lors du prochain message de mise à jour d'état.
Sessions de simulation qui envoient déjà des commandes de contrôle (set_cursor_force, set_cursor_position etc.), ne doivent pas Envoyer des commandes d'interrogation : l'état de l'appareil est toujours indiqué dans la trame de réponse. Les commandes d'interrogation servent à séances consacrées exclusivement à la surveillance conçus (par exemple, Haply Hub) qui doivent interroger l'état de l'appareil sans le commander.
Configuration vs. État
- Mourir Message contenant l'inventaire initial contient
config,stateetstatusdes appareils. - Régulière Mise à jour sur la situation- Contient des messages
stateetstatusdes appareils.
Si vous avez à nouveau besoin d'un instantané incluant la configuration, utilisez session.force_render_full_state.
configure contre commands
Les messages relatifs aux appareils prennent en charge deux affectations distinctes : configure et commands. Ils ont des finalités différentes :
| Affectation | Objet | Fréquence |
|---|---|---|
configure | Configuration unique et permanente : préréglage, paramètres de base, montage, filtres, paramètres des modules | Envoyer une seule fois ou en cas de modifications |
commands | Cochez « non persistant » : force, position, couples | Doit être envoyé à chaque tick |
Entrées dans commands (set_cursor_force, set_cursor_position, set_angular_torques, set_angular_position) seront appliqué une fois par tick et non enregistré – si vous cessez de les envoyer, l'effet prend fin immédiatement et l'appareil repasse en mode veille. Utilisez configure pour tout ce qui doit rester en mémoire pendant plusieurs ticks, sans être renvoyé.
set_transform est un cas particulierset_transform figure sous commands, mais c'est persistant – Le service conserve
la dernière transformation envoyée jusqu’à ce que vous en envoyiez une nouvelle. Vous doivent elle ne doit pas
être envoyée à chaque tick. Cependant, comme son objectif est la navigation dans la caméra ou dans la scène,
la diffusion en continu à la fréquence des ticks (par exemple, une transformation par image lorsque l'utilisateur effectue un
panoramique dans la scène) correspond au modèle d'utilisation attendu pour une navigation continue dans le mouvement.
Systèmes de coordonnées
Haply utilise par défaut un système de coordonnées droitier avec l'axe Z vers le haut.
Deux paramètres de configuration influencent la manière dont les coordonnées sont interprétées et renvoyées :
session.configure.basis: modifie la correspondance entre les coordonnées de Haply et le système de coordonnées de votre application (voir Base (voir plus bas).inverse3[*].configure.preset: sélectionne une configuration d'usine nommée qui définit l'origine de l'espace de travail (par exemple, définitarm_front_centeredce point(0, 0, 0)au centre de la zone de travail). Reportez-vous à la section « Configurer » spécifique à l'appareil.
Formats d'actualités
Cette section décrit l'enveloppe de niveau supérieur et les types de messages. Des exemples complets seront présentés plus loin dans ce document.
Groupes d'appareils
Les messages sont regroupés au niveau supérieur par type d'appareil (par exemple, inverse3, verse_grip, wireless_verse_grip). Chaque clé de type d'appareil est associée à un tableau contenant des objets spécifiques à cet appareil.
Initiales Inventar (Serveur → Client)
Est envoyé une seule fois, immédiatement après l'établissement d'une connexion WebSocket.
Chaque entrée contient :
device_idconfigstatestatus
Voir : Exemple : charge utile de l'inventaire initial
Mise à jour de l'état (serveur → client)
Est envoyé une seule fois pour chaque message client.
Chaque entrée contient :
device_idstatestatus
Voir : Exemple : charge utile de mise à jour d'état
Enveloppe pour les commandes de session (client → serveur)
Les commandes de session sont des actions qui s'appliquent à la connexion ou à la session en cours et qui ne sont pas spécifiques à un appareil.
{
"session": {
"<command_name>": {
"...": "..."
}
}
}
Enveloppe pour les commandes de périphériques (client → serveur)
Les commandes destinées aux appareils sont envoyées sous la clé « type d'appareil » sous forme de tableau, ce qui permet de transmettre des commandes à un ou plusieurs appareils dans un seul message. Chaque entrée d'appareil prend en charge à la fois une configure-l'affectation ainsi qu'une commands-Affectation.
{
"<device_type>": [
{
"device_id": "<id>",
"configure": {
"<config_key>": { "...": "..." }
},
"commands": {
"<command_name>": { "...": "..." }
}
}
]
}
Vous pouvez indiquer plusieurs entrées afin de commander plusieurs appareils du même type dans un seul message.
Veuillez noter que commands Un dictionnaire peut contenir plusieurs commandes pour un appareil donné, mais une seule par type de commande.
Le champ execute
Chaque commande ou option de configuration accepte une valeur booléenne facultative "execute" (Norme : true). Mettez "execute": false, afin que l'entrée soit analysée, mais non appliquée. Cela s'avère utile pour les sérialiseurs basés sur la réflexion (par exemple, Unity JsonUtility), qui affichent systématiquement chaque champ.
"set_cursor_force": { "execute": false, "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }
Référence des commandes
Session
Forcer le rendu complet de l'état
Demandez un aperçu de l'état et des configurations de tous les appareils.
{
"session": {
"force_render_full_state": {}
}
}
Configurer la session
La configuration persistante, liée à la session, est définie via session.configure envoyé. Cela correspond au modèle de la configure- Carte au niveau de l'appareil.
Profil
Définit le nom du profil de session utilisé par le hub « Haply » pour identifier la simulation et enregistrer les paramétrages spécifiques à chaque appareil pour chaque application.
{
"session": {
"configure": {
"profile": {
"name": "my_profile"
}
}
}
}
Base (pour toute la séance)
Définit le système de référence de base pour l'ensemble de la session. Le système de référence de base décrit la manière dont les coordonnées «Haply » sont converties dans le système de coordonnées de votre application ; une fois défini, tous les états des périphériques sont renvoyés dans ce système de référence et toutes les valeurs envoyées y sont interprétées.
La chaîne de permutation est définie par rapport au système de coordonnées de type « main droite » (axe Z vers le haut) par Haply — une permutation issue de X, Y, Z, éventuellement précédé de + ou -. Exemples :
XYZ,ZYX,+Y-Z+X,X-ZYYZXcela signifie que vousYqui correspond à l'orientation juridique de Haply , votreZdans le sens de la marche de Haply et vousXdans le sens ascendant de Haply.
Exemple de système pour gauchers avec l'axe Z orienté vers le haut (Unreal, X-YZ) :
{
"session": {
"configure": {
"basis": { "permutation": "X-YZ" }
}
}
}
session.set_basisLa convention relative au signe des axes a évolué entre l'ancienne session.set_basis et
session.configure.basis modifiée. Une permutation qui fonctionnait avec l'ancienne commande peut produire une transformation inversée avec la nouvelle. Si nécessaire, inversez les signes des axes (par exemple, X-ZY zu XZ-Y).
Commandes pour tous les appareils
Mesure (tous les appareils)
Utilisez les commandes de sondage pour obtenir des informations actualisées sur la position et l'orientation, sans appliquer de forces ni d'autres modifications à la simulation.
- inverse3:
probe_position - Poignées Verse (
verse_grip,wireless_verse_grip) :probe_orientation
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"probe_position": {}
}
}
],
"verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
],
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
]
}
Appliquer la transformation (tous les appareils)
Définit la transformation de l'espace de travail (de l'espace de l'appareil à l'espace de l'application) pour un appareil.
Contrairement aux autres commands- les entrées sont set_transform durable – Le service conserve la dernière valeur envoyée jusqu’à ce que vous en envoyiez une nouvelle. Vous n’avez pas besoin de la transmettre à chaque tick. Cependant, comme son objectif est la navigation dans la caméra ou la scène, en cas de déplacement continu, la diffusion à la fréquence des ticks correspond au modèle d’utilisation attendu (par exemple, une transformation par image lorsque l’utilisateur déplace la scène).
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_transform": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
Sur les appareils « Verse grip » et « Wireless Verse grip », seul le composant rotation de la transformation – le capteur de préhension ne signale que l'orientation, il n'y a donc rien qui position ou scale pourraient être redimensionnés ou déplacés. Les champs position et scale sont acceptées (et renvoyées dans le snapshot) uniquement pour des raisons de cohérence du schéma avec Inverse3 . Il est d'usage de les laisser sur les valeurs d'identité (position = {0,0,0}, scale = {1,1,1}).
Configuration des appareils « Inverse3 »
Les paramètres de configuration sont enregistrés dans le fichier configure- Carte envoyée. Vous êtes tenace : envoyez-la une seule fois ou en cas de modifications.
Préréglage
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
}
}
]
}
Valeurs disponibles : device_defaults, arm_front, arm_front_centered, led_front, led_front_centered, custom.
Base (au niveau de l'appareil)
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"basis": { "permutation": "ZXY" }
}
}
]
}
Mont
Définit la transformation de montage de l'appareil. Veuillez tenir compte de la transform-Wrapper au sein de mount.
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"mount": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
Mourir Page des commandes (Client vers serveur) englobe la transformation : "mount": { "transform": { ... } }.
Die Page « Aperçu » (serveur-client) présente une architecture plate : "mount": { "position": {...}, "rotation": {...}, "scale": {...} }.
C'est voulu : les commandes utilisent un wrapper uniforme, tandis que les instantanés sérialisent directement la transformation. Veillez à ne pas copier la structure de l'instantané dans la charge utile d'une commande.
Amortissement
Contrôle l'amortissement uniforme et/ou directionnel. Au moins un champ doit être renseigné.
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"damping": { "scalar": 0.5 }
}
}
]
}
Vous pouvez également définir un amortissement en fonction du sens de circulation, ou les deux variantes simultanément :
"damping": { "scalar": 0.5, "vector": { "x": 0.0, "y": 1.0, "z": 0.0 } }
Force Gate
Définit le gain d'atténuation du Force Gate (de 0,0 à 1,0).
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"force_gate": { "gain": 0.3 }
}
}
]
}
Inverse3-Commandes
Pour envoyer des commandes à un inverse3- Pour envoyer l'appareil, ajoutez une entrée avec une device_id sous la clé inverse3 hinzu. Les commandes s'appliquent à chaque tick et doivent être renvoyées à chaque tick pour rester actives.
Commander un seul appareil :
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
Contrôler plusieurs appareils à partir d'un seul message :
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
},
{
"device_id": "049E",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
Définir la position du curseur
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_position": {
"position": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
Définir Cursor-Kraft
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
Définir la position angulaire
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_position": {
"angles": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
Définir les couples angulaires
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_torques": {
"torques": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
Configuration pour Verse Grip
Les appareils Verse Grip et Wireless Verse Grip prennent en charge les mêmes configure-Des clés telles que Inverse3 pour preset, basis et mount.
Protocole étendu pour Verse Grip
La commande set_extension_data Fait partie du protocole étendu pour les Verse Grips et s'utilise avec les versions de Grip qui implémentent le protocole d'extensions de carte.
Définir les données d'extension de la poignée
Longueurs de données prises en charge :
- Jusqu'à 20 octets dans le sens montant (client → appareil).
- Jusqu’à 12 octets dans le sens descendant (appareil → client), renvoyés dans le message « State Update » sous la forme
state.extension_data.
Spécifications techniques :
- Longueur du tableau : 20 octets
- Plage de valeurs : chaque valeur est comprise entre 0 et 255
{
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"set_extension_data": {
"extension_data": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
}
}
}
]
}
Exemples
Exemple : liste initiale des appareils (charge utile)
Le service envoie un message contenant la liste complète des appareils dès qu'une connexion WebSocket est établie. Le message initial se présente comme suit : JSON-Format :
{
"inverse3": [
{
"device_id": "04BA",
"config": {
"type": "inverse3",
"port": "COM13",
"device_info": {
"major_version": 7,
"minor_version": 1,
"id": "04BA",
"device_type": 4,
"uuid": "2D35F80DD9005F599B68F49944CB04BA"
},
"extended_device_id": "2D35F80DD9005F599B68F49944CB04BA",
"extended_firmware_version": "8C20FDC8010AA1E15AA133CDA2534874",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 1
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"home_return": {
"enabled": false
},
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": 0 }
},
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"config": {
"type": "verse_grip",
"port": "COM3",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"config": {
"type": "wireless_verse_grip",
"port": "COM6",
"major_version": 1,
"minor_version": 4,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}
Exemple : charge utile « State-Update »
Pour chaque message reçu, le service envoie un message de mise à jour d'état indiquant l'état de tous les appareils.
Si vous souhaitez connaître l'état du système, vous devez d'abord lui envoyer un message (par exemple, une commande de test ou une valeur de force, même si ces valeurs sont nulles). Ceci est particulièrement important lorsque des appareils sont utilisés comme sources d'entrée (par exemple, pour le suivi de position) sans exercer de forces.
Le message de mise à jour de l'état contient les informations suivantes : JSON-Format :
{
"inverse3": [
{
"device_id": "04BA",
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
],
"custom_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"extension_data": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"ready": true
}
}
]
}