跳至主要内容

05. 位置控制

通过 set_cursor_position 将Inverse3 的光标移动到目标位置。不同语言的交互模型各不相同——C++ 使用一次性随机目标,Python 使用连续的键盘驱动移动。

学习内容:

  • 使用 set_cursor_position 实现位置模式控制
  • 同一底层命令对应的两种不同交互模型
  • 将目标限制在工作空间球体内——Minverse 使用的半径比Inverse3 更小
  • 设置工作空间预设,使原点位于工作空间中心

工作流程​

C++(随机目标模型)​

  1. 启动一个后台输入线程,从标准输入(stdin)读取按行缓冲的按键(n、+、-、q)。
  2. 打开 WebSocket。当第一帧状态数据到达时,进行注册 会话配置文件,并设置 configure.preset: arm_front_centered。在球体内生成第一个随机目标(拒绝采样,半径 0.08 米)。
  3. 每个周期,向当前目标发送一条 set_cursor_position 命令。光标会平滑地跟随目标——服务器会进行速率限制并进行插值处理。
  4. 当用户输入 n 按下 ENTER 键时,输入线程会生成一个新的随机目标。+ / - 调整速度;q 退出。

Python(按住移动模型)​

  1. 打开 WebSocket。当第一帧状态数据到达时,检查 status.calibrated——如果设备尚未校准,则向用户发出提示。
  2. 读取 config.type,以选择工作空间半径(minverse 为 0.04 米,其他情况为 0.10 米)。
  3. 注册 会话配置文件,并设置 configure.preset: arm_front_centered。
  4. 每个周期:轮询键盘状态(W/A/S/D/Q/E),沿每个被按下的轴的方向按 SPEED 更新目标位置,将其限制在工作空间球体内,并发送 set_cursor_position。按 R 可将目标重置为原点。

参数​

名称默认值(C++)默认值(Python)用途
workspace_radius / RADIUS_INVERSE30.08 m0.10 m(Inverse3)/ 0.04 m(Minverse)目标球体的半径
speed_step / SPEED0.01 / 每次按键0.00005 m / 每个周期每次交互的步长
PRINT_EVERY_MS—100遥测节流(Python)
会话配置文件co.haply.inverse.tutorials:position-control相同用于在Haply Hub中进行标识
校准检查(Python)

Python 变体会从第一帧的状态数据开始检查 status.calibrated,如果设备未经过校准,则会提示用户。C++ 版本则假定校准已完成。

读取的状态字段​

  • data.inverse3[0].device_id ——用于构建命令
  • data.inverse3[0].state.cursor_position ——遥测数据
  • (Python,仅第一帧)data.inverse3[0].config.type ——用于在Inverse3 与Minverse 之间的半径中进行选择
  • (Python,仅第一帧)data.inverse3[0].status.calibrated ——如果为 false,则提示用户

发送 / 接收​

通信流程​

  • C++ 运行一个后台 stdin 线程,写入 std::atomic<float> 类型的目标值;WebSocket 线程会在每个周期读取这些值。输入 n 按下 ENTER 键时,输入线程会生成一个新的随机目标;输入 q 此时,两个线程都会退出。
  • Python 作为单线程异步实现——WebSocket 在每个周期轮询键盘状态,并直接更新 position。

Inverse-API 的负载内容相同:第一个周期携带会话配置文件和 configure.preset,后续周期仅包含 set_cursor_position。

单一异步循环。键盘轮询(handle_keys)在每个周期内以联机方式运行,不使用线程。config.type 和 status.calibrated 仅在第一帧的状态数据中读取一次。

async with websockets.connect(URI) as websocket:
while True:
msg = await websocket.recv()
data = json.loads(msg)

if first_message:
first_message = False
device_id = data["inverse3"][0]["device_id"]
radius = get_workspace_radius(data["inverse3"][0].get("config", {}))
# Handshake: profile + preset (one-shot)
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": {"preset": {"preset": "arm_front_centered"}},
}],
}
else:
# Per tick: update position from keyboard (classic polling, not shown), send command
position = handle_keys(position, radius)
request_msg = {
"inverse3": [{
"device_id": device_id,
"commands": {"set_cursor_position": {"position": position}},
}],
}

await websocket.send(json.dumps(request_msg))
随 SDK 安装程序一并提供

教程 05 同样随 SDK 一起安装在本地——请在服务安装目录下的 tutorials/05-haply-inverse-position-control/ 中查找。

源代码: Python·C++·C++ Glaze

相关内容: 控制命令(set_cursor_position) · 挂载与工作区(预设) · 类型(vec3) · 教程 06(组合)