跳过至主要内容

05. 位置控制

set_cursor_position 通过该方法,将Inverse3 的游标移动到目标位置。交互模型因语言而异 — C++ 使用一次性随机目标,而 Python 则使用通过连续键盘操作实现的移动。

学习内容:

  • 用于位置模式控制的 set_cursor_position 的使用方法
  • 针对同一基础命令的两种不同交互模型
  • 将夹具夹紧在目标位置的工作空间球体上——Minverse 使用的半径小于Inverse3
  • 设置工作区预设,使原点位于工作区中心

工作流​

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

  1. 来自标准输入的按行缓冲的键盘输入(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. 在每个 tick 中,键盘的状态(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++ 是,std::atomic<float> 运行一个后台标准输入线程,用于写入目标值。WebSocket 线程会在每个时间步长读取该值。n 按下 ENTER 键后,输入线程将生成一个新的随机目标,q 这样,两个线程都将结束。
  • Python 这是一种单线程的异步处理。WebSocket 循环会在每个时间步长轮询键盘状态,position 直接更新。

Inverse-API 的有效载荷是相同的。在第一个时标中,会包含会话配置文件和 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) · 卡口与工作区(预设) · 类型 (vec3) · 教程 06(综合)