WebSocket 协议
模拟通道是端口 10001上的 WebSocket 连接。它在应用程序和服务之间传输实时命令/状态循环。
基本规则:每条消息对应一个响应
对于从客户端接收到的每条消息,该服务都会发送恰好一次状态更新:
既没有轮询,也没有订阅,更没有带外(out-of-band)推送。更新间隔完全由应用程序的发送速率决定。
步骤 1 — 初始库存(来自服务的第一个消息)
连接时,该服务会对所有检测到的设备,config、state、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 | 固件信息、预设、basis、安装、滤镜 | 极少更改 — 仅在显式更改设置时 |
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 仅发送(config (不包括):
{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}
步骤 4 — 重复
发送命令后,将接收状态更新。循环将根据发送速率持续进行。
configure 和 commands
要发送的消息中的每个设备条目都可以包含两个映射。
| 地图 | 目的 | 持久性 |
|---|---|---|
configure | 一次性设置:预设、基准、安装、滤镜、模块设置 | 在发生变更前保持不变 |
commands | 逐脉冲控制:力、位置、扭矩 | 一旦适用即废止 |
configure 完整的键列表如下: 会话设置 ,commands 关于该条目 控制命令 请参阅。
set_transform 这是一种特殊情况set_transform 是 commands 虽然位于……下方,但,持久的。该服务会保留最后一个值,直到收到新值为止。这是因为其目的是用于场景导航。当需要在相机移动时实时传输变换数据,同时希望每次按下按钮时都能保持最后的位置时,可使用此功能。
execute 标志
configure 或者 commands 该条目中包含 "execute": false 可以包含。这样,服务无需应用有效载荷即可分析与验证仅执行此操作。
{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}
这会始终输出所有字段基于反射的序列化器(例如:Unity 的 JsonUtility)会很有帮助。对于未使用的条目, execute: false 请进行设置,以免覆盖实际设置。默认值为 true 。如果省略该标志,则表示“按常规应用”。
警告
probe_position 和 probe_orientation 是一种专用于监控的保持活动机制,适用于不发送控制命令的会话(例如:Haply Hub)。当会话已经 set_cursor_force、set_cursor_position 如果正在发送此类内容,则请勿额外发送探针。设备状态已包含在所有响应中。如果将两者混用,会浪费带宽,session-probe-dropped 可能会发生事件。
如果命令似乎没有生效,可能是字段名称有误。该服务目前会无提示地忽略未识别的 JSON 键。请检查服务日志,并验证字段名称是否与API 参考文档中的描述一致。此行为预计将在未来版本中进行更改。