跳转至正文

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 米,其余 = 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 的有效载荷是相同的。在第一个 tick 中包含会话配置文件和 configure.preset其中包含,后续的Tick中则包含 set_cursor_position仅包含这些。

这是一个单线程异步循环。键盘轮询(handle_keys) 该操作会在每个 tick 周期内以内联方式执行,不使用单独的线程。 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) · Mount & Workspace(预设) · 类型 (vec3) · 教程 06(综合)