시뮬레이션 채널
개요
시뮬레이션 채널은기기의 상태를 교환하고 세션 또는기기 명령을 전송하는 데 사용되는 고주파 양방향 WebSocket 채널입니다.
기본 URL: ws://localhost:10001
설정에서 포트를 변경할 수 있습니다.
기본 계약서 (중요) :
- 클라이언트가 연결되면, 서비스는 전체 장치 목록이 포함된 초기 인벤토리 메시지를 전송합니다.
- 그 후, 서버는 클라이언트로부터 수신한 각 메시지에 대해 정확히 하나의 상태 업데이트 메시지를 전송합니다.
- 각 상태 업데이트에는 모든 기기의 상태(및 상태 정보)가 포함됩니다.
이 페이지에서는 시뮬레이션 채널의 프로토콜과 기본 명령어를 설명합니다.
추가 모듈이나 기능은 자체 명령어 및/또는 상태 필드를 통해 시스템을 확장하고 기능을 보강할 수 있습니다. 이에 대한 내용은 ‘모듈’ 섹션에서 별도로 설명되어 있습니다.
의전 규칙
고객의 메시지 응답
이 서비스는 클라이언트로부터 수신된 각 메시지에 대해, 모든 기기의 상태가 포함된 상태 업데이트 메시지를 전송합니다.
이는 다음과 같은 의미입니다:
- 장치의 최신 상태를 확인하려면 세션 명령, 폴링 명령 또는 장치 명령과 같은 명령을 전송해야 합니다.
- 이 채널은 클라이언트의 메시지에 의해 타이밍이 조절되는(“틱”) 루프 방식으로 작동합니다.
물리적 힘을 가하지 않는 심문 (탐색적 질문)
힘을 가하거나 시뮬레이션 매개변수를 변경하지 않고 상태 변화를 관찰하려면 다음 조사 명령을 사용하십시오:
probe_position붓다inverse3probe_orientationVerse 그립용
조회 명령에는 명령 데이터가 포함되어 있지 않으며, 단순히 장치 정보를 조회하도록 강제할 뿐이므로, 다음 상태 업데이트 시 위치 및 방향 데이터가 최신 상태로 반영됩니다.
이미 제어 명령을 전송하고 있는 시뮬레이션 세션(set_cursor_force, set_cursor_position, 등)은 pas 폴링 명령을 전송해야 합니다. — 응답 프레임에는 항상 장치 상태가 포함됩니다. 폴링 명령은 세션을 대상으로 합니다. 단순히 감시 목적으로만 (예: Haply Hub)와 같이, 장치를 제어하지 않고 상태만 조회해야 하는 경우입니다.
구성 및 상태
- 의 메시지초기 재고 조사 필드 포함
config,stateetstatus기기의. - 다음의 게시물 상태 업데이트 일반 필드에는 다음이 포함됩니다.
stateetstatus기기의.
설정이 포함된 스냅샷이 다시 필요하시면 다음을 사용하십시오. session.force_render_full_state.
configure et commands
기기 메시지는 두 가지 별개의 사전을 지원합니다: configure et commands. 이들의 역할은 각기 다릅니다:
| 사전 | 역할 | 주파수 |
|---|---|---|
configure | 일회성 영구 구성: 사전 설정, 기본값, 마운트, 필터, 모듈 설정 | 단일 발송 또는 변경 시 |
commands | 주기당, 비지속적: 힘, 위치, 토크 | 매 주기마다 전송되어야 합니다 |
다음 항목들 commands (set_cursor_force, set_cursor_position, set_angular_torques, set_angular_position)가 적용됩니다 사이클당 한 번씩 발생하며 저장되지 않습니다 -- 신호 전송을 중단하면 효과가 즉시 사라지고 기기는 대기 상태로 돌아갑니다. 사용하십시오 configure 한 주기에서 다음 주기로 넘어가면서 삭제되지 않고 유지되어야 하는 모든 항목에 대해.
set_transform 예외를 이루다set_transform 다음 위치에 있습니다 commands, 하지만 그는 지속적인 -- 이 서비스는 사용자가 새로운 변환을 전송할 때까지 마지막으로 전송된 변환을 유지합니다. 사용자는 꼭 그럴 필요는 없다 매 사이클마다 이를 전송해야 합니다. 하지만 이 기능의 목적은 카메라나 씬을 이동시키는 것이므로, 사이클 주기에 맞춰(예를 들어, 사용자가 씬을 이동시킬 때 프레임당 한 번의 변환) 이를 전송하는 것이 연속적인 이동을 위한 예상되는 사용 방식입니다.
좌표계
Haply 기본적으로 Z축이 위쪽을 향하는 오른손 좌표계를 사용합니다.
다음 두 가지 구성 항목은 좌표가 해석되고 반환되는 방식에 영향을 미칩니다:
session.configure.basis: Haply 의 좌표와 애플리케이션의 좌표계 간의 매핑을 변경합니다(자세한 내용은 기본 (아래 참조).inverse3[*].configure.preset: 지정된 공장 설정을 선택하여 작업 공간의 원점을 설정합니다(예:arm_front_centered장소(0, 0, 0)(작업 공간 중앙). 각 기기별 설정 섹션을 참조하십시오.
메시지 형식
이 섹션에서는 전반적인 구조와 상위 수준의 메시지 유형에 대해 설명합니다. 이 문서의 뒷부분에서 완전한 예제를 확인할 수 있습니다.
기기 그룹
메시지는 1단계에서 기기 유형별로 그룹화됩니다(예: inverse3, verse_grip, wireless_verse_grip). 각 기기 유형 키는 기기당 하나씩, 총 여러 개로 구성된 객체 배열에 해당합니다.
초기 인벤토리 (서버 → 클라이언트)
WebSocket 연결이 수립된 직후 한 번만 전송됩니다.
각 메뉴에는 다음이 포함됩니다:
device_idconfigstatestatus
참조: 예시: 초기 재고 유동자산
상태 업데이트 (서버 → 클라이언트)
클라이언트로부터 메시지를 수신할 때마다 한 번씩 호출됩니다.
각 메뉴에는 다음이 포함됩니다:
device_idstatestatus
세션 명령어 래퍼 (클라이언트 → 서버)
세션 명령어는 현재 연결 또는 세션에 적용되는 작업으로, 특정 기기에 국한되지 않습니다.
{
"session": {
"<command_name>": {
"...": "..."
}
}
}
장치 명령 래퍼 (클라이언트 → 서버)
기기 명령은 기기 유형 키 아래에 배열 형태로 전송되므로, 하나의 메시지로 하나 또는 여러 대의 기기에 명령을 보낼 수 있습니다. 각 기기 항목은 사전 하나를 지원합니다. configure 그리고 사전 한 권 commands.
{
"<device_type>": [
{
"device_id": "<id>",
"configure": {
"<config_key>": { "...": "..." }
},
"commands": {
"<command_name>": { "...": "..." }
}
}
]
}
하나의 메시지에 여러 항목을 포함하여 동일한 유형의 기기를 여러 대 주문할 수 있습니다.
다음 사항에 유의하시기 바랍니다. commands 이는 동일한 기기에 대해 여러 개의 명령을 포함할 수 있지만, 명령 유형당 하나만 포함할 수 있는 사전입니다.
Le champ execute
각 명령어 또는 구성 항목은 부울 값을 받아들입니다. "execute" 선택 사항 (기본값 true). 설정하십시오 "execute": false 입력 값이 적용되지 않은 상태로 분석되도록 하기 위함입니다. 이는 리플렉션을 기반으로 하는 직렬화기(예: Unity)에 유용합니다. JsonUtility), 이 신호들은 항상 모든 필드를 방출합니다.
"set_cursor_force": { "execute": false, "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }
주문 내역
세션
상태에 대한 전체 렌더링을 강제 적용
모든 기기의 상태 및 구성에 대한 스냅샷을 요청합니다.
{
"session": {
"force_render_full_state": {}
}
}
세션 구성
세션 수준의 영구 설정은 다음을 통해 전송됩니다. session.configure. 이 사전은 기존 사전과 동일한 원리를 따릅니다. configure 기기 차원에서.
프로필
Haply Hub가 시뮬레이션을 식별하고 각 애플리케이션별 기기 설정을 유지하기 위해 사용하는 세션 프로필의 이름을 지정합니다.
{
"session": {
"configure": {
"profile": {
"name": "my_profile"
}
}
}
}
기초 (세션 수준)
전체 세션에 대한 기본 좌표 변환을 정의합니다. 이 좌표 변환은Haply 의 좌표가 애플리케이션의 좌표계로 어떻게 변환되는지를 설명합니다. 일단 정의되면, 모든 기기 상태는 이 기본 좌표계를 기준으로 반환되며, 사용자가 전송하는 모든 값도 이 기준에 따라 해석됩니다.
순열 사슬은 Z축이 위쪽을 향하는 오른손 좌표계를 기준으로, Haply 에 의해 다음과 같이 표현됩니다: 의 순열은 X, Y, Z, 필요에 따라 그 앞에 + ou de -. 예시:
XYZ,ZYX,+Y-Z+X,X-ZYYZX이는 귀하의YHaply 의 직선에 해당하며, 귀하의ZHaply 의 앞쪽과 귀하의XHaply 상단.
Z축이 위쪽을 향하는 왼손 좌표계 예시 (언리얼, X-YZ) :
{
"session": {
"configure": {
"basis": { "permutation": "X-YZ" }
}
}
}
session.set_basis이전 명령어와 비교하여 축 표기 규칙이 변경되었습니다. session.set_basis et session.configure.basis. 이전 명령어에서는 제대로 작동하던 순열이 새로운 명령어에서는 역변환을 일으킬 수 있습니다. 이 경우 필요에 따라 축의 부호를 반전시키십시오 (예: X-ZY devient XZ-Y).
모든 기기의 제어 기능
설문조사 (모든 기기)
시뮬레이션에 힘이나 기타 변경 사항을 적용하지 않고도, 프로브 명령을 사용하여 최신 위치 및 방향 정보를 요청할 수 있습니다.
- inverse3 :
probe_position - 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": {}
}
}
]
}
변환 설정 (모든 기기)
특정 기기에 대한 작업 공간 변환(기기 공간에서 애플리케이션 공간으로의 변환)을 정의합니다.
다른 항목들과는 달리 commands, set_transform est 지속적인 -- 이 서비스는 사용자가 새로운 값을 전송할 때까지 마지막으로 전송된 값을 유지합니다. 매 사이클마다 값을 전송할 필요는 없습니다. 다만, 이 서비스의 기능이 카메라나 씬의 이동을 제어하는 것이므로, 지속적인 움직임을 구현하려면(예: 사용자가 씬을 이동하는 동안 프레임당 한 번씩 변환을 적용하는 경우) 사이클 주기에 맞춰 값을 전송하는 것이 권장되는 사용 방식입니다.
{
"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 }
}
}
}
}
]
}
Verse grip 및 Wireless Verse grip 기기의 경우, 해당 구성 요소만 rotation 변환에는 한 가지 효과가 있습니다. 손잡이는 방향만 전달하므로, 크기를 조정하거나 위치를 이동할 필요가 없습니다. position ou scale. 필드 position et scale Inverse3 와의 스키마 일관성을 보장하기 위한 목적으로만 허용되며(스냅샷으로 즉시 반환됨); 이를 정체성 값으로 그대로 두면 (position = {0,0,0}, scale = {1,1,1})가 표준 관행입니다.
기기 설정 Inverse3
구성 항목이 사전으로 전송됩니다. configure. 이 정보는 변경되지 않는 내용입니다. 한 번만 보내주시거나, 변경 사항이 있을 때만 보내주세요.
사전 설정
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
}
}
]
}
사용 가능한 값: device_defaults, arm_front, arm_front_centered, led_front, led_front_centered, custom.
베이스 (기기 레벨)
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"basis": { "permutation": "ZXY" }
}
}
]
}
몽타주
장치의 조립 변환을 정의합니다. 외곽선을 확인하십시오. transform ~안에서~ 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 }
}
}
}
}
]
}
Le côté 주문 (클라이언트-서버)는 변환 과정을 포괄합니다: "mount": { "transform": { ... } }.
Le côté 순간적인 (서버에서 클라이언트로) 연결이 끊어졌습니다: "mount": { "position": {...}, "rotation": {...}, "scale": {...} }.
이는 의도된 선택입니다. 명령어는 통합된 래퍼를 사용하는 반면, 스냅샷은 변환 결과를 직접 직렬화합니다. 명령어 페이로드에 스냅샷의 구조를 복사하지 않도록 주의하십시오.
상각
균일 감쇠 및/또는 방향성 감쇠를 제어합니다. 필드 중 적어도 하나는 입력해야 합니다.
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"damping": { "scalar": 0.5 }
}
}
]
}
방향별 감쇠를 설정하거나, 두 가지를 모두 설정할 수도 있습니다:
"damping": { "scalar": 0.5, "vector": { "x": 0.0, "y": 1.0, "z": 0.0 } }
포르트 드 포스
포스 게이트의 감쇠 이득을 설정합니다(0.0~1.0).
{
"inverse3": [
{
"device_id": "049D",
"configure": {
"force_gate": { "gain": 0.3 }
}
}
]
}
명령어 Inverse3
기기에 명령을 전송하려면 inverse3, 다음을 포함한 항목을 추가하십시오. device_id 키에 해당하는 inverse3. 명령은 매 사이클마다 전송되며, 활성 상태를 유지하려면 매 사이클마다 다시 전송해야 합니다.
단일 기체만 지휘:
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
하나의 메시지로 여러 기기를 제어하려면:
{
"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
}
}
}
}
]
}
커서 위치 설정하기
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_position": {
"position": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
커서의 강도 설정하기
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}
각도 위치 정의하기
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_position": {
"angles": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
각속도 모멘트 정의하기
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_torques": {
"torques": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}
Verse Grip : 설정
Verse Grip 및 Wireless Verse Grip 기기는 동일한 키를 지원합니다. configure Inverse3 를 다음으로: preset, basis et mount.
확장형 버스 그립
주문 set_extension_data 이는 카드 확장 통신 프로토콜을 구현하는 핸들 버전과 함께 사용되는 Verse Grip 확장 프로토콜의 일부입니다.
핸들의 확장 데이터 정의하기
지원되는 데이터 길이:
- 상류 방향(클라이언트 → 기기)으로 최대 20바이트.
- 최대 12바이트의 다운스트림 데이터(기기 → 클라이언트)가 State Update 메시지에 다음 형식으로 반환됩니다.
state.extension_data.
데이터 사양:
- 배열 길이: 20바이트
- 값의 범위: 각 값은 0에서 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
]
}
}
}
]
}
예시
예시: 초기 재고 순자산
이 서비스는 WebSocket이 연결되면 기기 전체 목록이 포함된 메시지를 전송합니다. 초기 메시지는 다음 형식을 따릅니다. JSON 다음:
{
"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
}
}
]
}
예시: 유효 부하 상태 업데이트
이 서비스는 수신된 각 메시지에 대해 모든 기기의 상태가 포함된 상태 업데이트 메시지를 전송합니다.
기계의 상태를 확인하려면, 먼저 기계에 메시지를 전송해야 합니다(예를 들어, 센서 조회 명령이나 힘 값 등, 값이 0인 경우에도 마찬가지입니다). 이는 힘을 가하지 않은 상태에서 기기를 입력 소스로 사용할 때(예: 위치 추적) 특히 중요합니다.
"State Update" 메시지는 형식을 준수합니다. JSON 다음:
{
"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
}
}
]
}