跳转至主要内容

WebSocket 协议

模拟通道是端口 10001 上的一个 WebSocket 连接。它负责在您的应用程序与该服务之间传输 实时的控制和状态循环。

基本规则:每条消息仅限一个回复​

该服务会针对从您的客户端收到的每条消息 准确地发送一条状态更新:

这里既没有轮询(polling),也没有订阅,也没有主动的带外发送。发送频率 完全由您的发送频率决定。


步骤 1 — 初始盘点(服务的第一条消息)​

连接时,该服务会发送一个 完整快照 装有 config, state et 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": []
}

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

Bloc内容修改
config固件、预设、基准、安装方式、滤镜的相关信息很少发生——仅在明确更改配置时才会发生
state位置、速度、力、方向、变换每个周期(高频)
status就绪、已校准、通电、正在使用中偶尔(频率较低)

步骤 2 — 提交您的第一笔订单​

分析初始清单以查找您的设备标识符,然后发送一条 包含您的会话配置文件、所有初始配置和/或控制命令的消息:

{
"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 (不含 config),除非配置变更触发了完整快照的发送:

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

第 4 步 — 重复​

发送一条指令 → 接收状态更新。该循环将根据您的发送频率持续进行。


configure ou commands​

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

词典角色坚持
configure特定设置:预设、基底、安装、滤镜、模块配置保存至下次修改
commands每个循环的检测:力、位置、扭矩贴上一次,然后就被遗忘了

查看 会话配置 有关 完整密钥列表的 configure,以及 控制命令 适用于 以下条目: commands.

备注
set_transform 是个例外

set_transform 位于……之下 commands,但它是 persistante — 该服务 会保留上一个值,直到收到新值为止。这是因为它的功能是 在场景中导航:在摄像机移动过程中,您会持续传输变换数据,但希望在两次按键之间,上一个位置保持不变。


《指标》 execute​

所有条目 configure ou commands 可能包括 "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 et probe_orientation 是专用于监控的连接保持(keepalive)消息,适用于 未发送控制命令的会话(例如,Haply Hub)。如果您的会话已经发送了 set_cursor_force, set_cursor_position等, 请不要再发送探测器了 — 设备的状态已在每条回复中注明。 将两者结合会浪费带宽,并可能触发某些事件 session-probe-dropped.

未知密钥将被静默忽略

如果某条请求似乎没有生效,可能是字段名称有误。该 服务目前会忽略未识别的 JSON 键,且不会触发错误。请查看服务日志,并在 API 参考文档中核对字段名称。此行为将在 后续版本中进行调整。