跳到主要内容

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 m)。
  3. 每个周期,向当前目标发送一条 set_cursor_position 命令。光标会平滑地跟随目标——服务端会对速率进行限制并做插值处理。
  4. 当用户输入 n 并按 ENTER 时,输入线程会生成一个新的随机目标。+ / - 调整速度;q 退出。

Python(按住移动模型)​

  1. 打开 WebSocket。在第一帧状态数据到达时,检查 status.calibrated——如果设备尚未校准,则提示用户。
  2. 读取 config.type,以选择工作空间半径(minverse 为 0.04 m,其他情况为 0.10 m)。
  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(组合)