跳过至主要内容

07. Basis 和 Mount 的游乐场

设备的 坐标系变换 以交互方式操作,configure.basis、configure.preset、configure.mount 展示了它们在钢丝上如何协同工作。为了将交互操作的重点放在 configure 命令上,我们固定了水平方向的地板。

本教程将教你:

  • 底牌的排列的设置("XZY" →(Y 轴在上的应用程序框架)
  • 预设(arm_front)的选择,以及对其设置内容的理解
  • 使用旋转四元数在运行时覆盖挂载(键盘操作)
  • 排他规则: mount 和 preset 是,一样 设备配置 无法在同一块内共存
  • 单次拍摄的 configure 工作原理:每次按下按键一次,仅发送一条 configure 消息
  • (C++ Glaze)std::optional 使用……对排他性字段进行建模

工作流​

  1. 在第一条消息中,会话配置文件、configure.basis: "XZY"、configure.preset: arm_front 将发送。由于地板是固定的,因此 set_cursor_force 开始发送。
  2. 在每个 tick 中,读取光标的 Y 坐标,force_y = max(0, (floor_pos - y) * stiffness) 计算后发送。
  3. 当用户按下马鞍旋转键时,pending_configure = true 设置该标志。
  4. 在下一个 tick 时,configure.mount 构建块。该变换的 rotation 是一个单位四元数(由当前俯仰角和偏航角依次合成 Z 轴方向,随后是 X 轴方向)。preset 此处省略。因为二者在线路上是互斥的。
  5. 重置键(R)来取消覆盖。在接下来的 configure 步骤中,再次 preset 会被使用。

参数​

姓名预设值用途
BASIS"XZY"轴的排列— Y 轴位于上方的应用程序框架
DEVICE_PRESET / DEVICE_CONFIG_PRESET"arm_front"命名预设— 原点为设备底座
FLOOR_POS_Y0.0 m固定的地板平面(应用程序的 Y 轴)
STIFFNESS1000 不适用床的弹簧刚度系数
MOUNT_STEP_DEG10°每次按键的旋转量
PRINT_EVERY_MS200遥测采样间隔

操作​

键动作
W / S将支架围绕设备的 +X 轴旋转 ±10°(俯仰角)
A / D将支架围绕设备的 +Z 轴旋转 ±10°(偏航)
R重置安装 — 恢复为预设状态
H显示操作方法
Q结束
mount 和 preset 不能同时指定

该服务包含这两项 设备配置 拒绝该块。如果用户覆盖了挂载设置,在本教程中,此后所有 configure 操作都将 preset 省略。R 按下后,在接下来的 configure 中 preset 再次生效,mount 将被删除。

输入模型的差异

C++ 的变体在后台的 stdin 线程中按行读取输入(每输入一个字符就按一次回车键)。Python 则通过主异步循环实时获取键盘输入,因此,keyboard 使用该软件包。无需按 ENTER 键。键位和命令都一样。

读取状态字段​

data.inverse3[i].state 从以下内容中读取:

  • cursor_position.y — vec3。用于计算向地基的贯入量
  • current_cursor_force — 将作为遥测数据上报

发送/接收​

所有变体的有效载荷格式都相同。值得注意的区别在于,各实现具有排他性的 mount / preset 如何构建该分支,以及输入线程如何通知 WebSocket 线程。

keyboard 这是一个通过包来轮询实时键盘输入的单一异步循环。pending_configure 是一个由键处理程序设置的全局标志,configure 每次发送区块时都会被清空。

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"]
# Handshake: profile + basis + preset
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": build_configure_block(first_handshake=True),
# -> {"basis": {"permutation": "XZY"},
# "preset": {"preset": "arm_front"}}
}],
}
else:
handle_key_inputs() # may set pending_configure = True (classic, not shown)

y = data["inverse3"][0]["state"]["cursor_position"]["y"]
force_y = 0.0 if y > FLOOR_POS_Y else (FLOOR_POS_Y - y) * STIFFNESS

entry = {
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": force_y, "z": 0.0}}},
}
if pending_configure:
entry["configure"] = build_configure_block(first_handshake=False)
# -> {"mount": {...}} OR {"preset": {...}} (never both)
pending_configure = False

request_msg = {"inverse3": [entry]}

await websocket.send(json.dumps(request_msg))
包含在 SDK 安装程序中

教程 07 已随 SDK 一起安装在本地。位于服务安装目录下的 tutorials/07-haply-inverse-basis-and-mount/ 请确认。

来源: Python·C++·C++ Glaze

相关条目: 底牌的排列 · 安装与工作区 · 设备设置 · 控制命令(set_cursor_force) · 型(transform) · 教程 04(Hello Floor)