跳转至主要内容

WebSocket 协议

模拟通道是一个监听端口 10001 的 WebSocket 连接。它负责在您的应用程序与该服务之间传输由命令和状态组成的实时循环。

基本规则:每条消息只回复一次​

该服务会针对从您的客户端接收到的每条消息,发送且仅发送一次状态更新:

没有轮询、没有订阅,也没有带外推送。时钟频率完全由您的发送频率决定。


步骤 1 — 初步情况评估(服务端发来的第一条消息)​

在建立连接时,该服务会发送一个 完整的快照 mit config, state und status 对于每个已识别的设备:

{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"config": {
"type": "inverse3",
"port": "COM12",
"device_info": { "major_version": 7, "minor_version": 4, "id": "049D", "device_type": 4, "uuid": "…" },
"extended_device_id": "…",
"extended_firmware_version": "…",
"gravity_compensation": { "enabled": true, "scaling_factor": 1.0 },
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": { "enabled": true },
"home_return": { "enabled": false },
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 1, "y": 1, "z": 1 } },
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": 0.0 }
}
},
"state": {
"cursor_position": { "x": 0.0, "y": -0.12, "z": 0.15 },
"cursor_velocity": { "x": 0.0, "y": 0.0, "z": 0.0 },
"angular_position": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"angular_velocity": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"body_orientation": { "w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0 },
"current_cursor_force": { "x": 0.0, "y": 0.0, "z": 0.0 },
"current_cursor_position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"current_angular_torques": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"current_angular_position": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"control_domain": "cartesian",
"control_mode": "idle",
"transform": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 1, "y": 1, "z": 1 } },
"transform_velocity": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 0, "y": 0, "z": 0 } }
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [],
"wireless_verse_grip": []
}

这三个区块的刷新频率各不相同:

块包含变更频率
config固件信息、预设、基础、混音、滤波器很少见,仅在显式更改配置时才会发生
state位置、速度、力、方向、变换每次时钟脉冲(高频)
status就绪、已校准、供电、正在使用中偶尔(低频)

步骤 2 — 发送第一个命令​

解析初始状态检查结果,以查找您的设备 ID。然后发送一条包含您的会话配置文件、任何初始配置和/或控制命令的消息:

{
"session": {
"configure": {
"profile": { "name": "co.haply.inverse.tutorials:hello-floor" }
}
},
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
},
"commands": {
"set_cursor_force": { "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }
}
}
]
}

步骤 3 — 后续状态更新​

首次交换后,该服务仅发送 state + status (ohne config),除非配置变更触发了完整的快照推送:

{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}

第 4 步 — 重复​

发送一条命令 → 您将收到状态更新。循环将按照您的发送频率继续运行。


configure vs. commands​

您发出的消息中的每个设备条目都可以包含两个地图:

地图Zweck持久性
configure一次性设置:预设、基础、安装、滤波器、模块配置将保存至修改为止
commands每帧控制:力、位置、扭矩用过一次,之后就忘了

完整的列表如下: configure-您可以在以下位置找到密钥: 会话配置,以及该 commands-条目位于 控制命令.

提示
set_transform 是一个特例

set_transform 属于 commands,但 持久的 — 该服务会保留上一次的值,直到您发送新的值为止。这是因为它的用途是场景导航:您在摄像机移动过程中实时传输变换数据,但希望在两次输入之间保留上一次的位置。


《Das Flag》 execute​

每个条目 configure oder简体中文(大陆) commands kann "execute": false 包含,以便该服务处理有效载荷 解析并验证,却不加以运用。

{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}

这对于……很有用 基于反射的序列化器 (例如: JsonUtility (在 Unity 中),这些函数总是会输出所有字段:请将未使用的条目设置为 execute: false,以免覆盖实际的配置。默认值为 true — 如果省略该标志,则表示“按常规方式应用”。


警告​

如果您已经发送了命令,请不要发送测试数据

probe_position und probe_orientation 这些纯粹是用于会话的监视保持活动连接机制,不会发送任何控制命令(例如Haply Hub)。如果您的会话已经 set_cursor_force, set_cursor_position 等等,发送, 请勿发送额外的样品 — 设备状态无论如何都包含在每个响应中。两者同时存在既会浪费带宽,又可能 session-probe-dropped-触发事件。

未知密钥将被默认忽略

如果某条命令似乎没有生效,可能是字段名称有误。该服务目前会忽略未识别的 JSON 键,且不会显示错误信息。请检查服务日志,并将字段名称与API 参考文档进行核对。此行为将在未来版本中进行修改。