跳至主要内容
版本:3.5.x

09. WebSocket 工作区导航器

浏览设备的 工作区位置(实时) 通过流媒体 set_transform 通过 WebSocket 通道发送命令。本教程使用 HTTP 来发现会话和设备(与 教程 08),然后在每个时间步长内打开一个 WebSocket 来推送位置更新。

这说明了两种变换操作之间的区别:

操作教程典型用法
configure.mount — 持久挂载转换08只需设置一次(或极少设置),用于描述设备的物理安装方式
set_transform — 持久化工作区变换09(本期)同样是持久性的,但可以每帧流式传输,以实现实时工作区导航

这两个命令均可通过 HTTP 和 WebSocket,而且两者都是 持久的 — 该服务会记住您上次发送的值。两者的区别在于用途和更新频率: configure.mount 适用于更新频率较低的情况(物理配置),而 set_transform 已针对高频流式传输(场景导航)进行了优化。

使用 set_transform 当工作区原点需要追踪运行时发生变化的对象时——例如跟随手部动作、动画化过程偏移,或是让操作员实时微调虚拟工作区。

“Mount” 和“workspace” 已建立父子关系

就个人而言, configure.mountset_transform 看似产生了相同的效果,但它们是 由……抚养长大: 已应用工作区变换 除此之外 坐标管道中的挂载点(device → basis → mount → workspace → application). 参见 安装与工作区 完整流程。

用例

  • 实时工作空间导航。在不暂停触觉场景的情况下,移动设备的游标工作空间(即末端执行器能够触及的边界区域)。
  • 由操作员控制的对齐。在主触觉应用运行时,让另一台终端的用户按住方向键来移动工作区——这在实时会话中进行用户专属的桌面校准时非常有用。
  • 基于流程的工作区运动。通过在每个时间步长发送仅包含位置的变换补丁,利用脚本(如音频响应式、物理驱动式等)控制工作区的位置。

先决条件

教程 09 需要一个活跃的 WebSocket 会话——即一个客户端,该客户端的Inverse3 已处于运行Inverse3 触觉循环Inverse3 。随后,教程 09 会连接到该会话,并实时切换其工作空间。

启动会话的最快方法

打开Haply 并启动Orb演示程序。这将打开一个持久的触觉会话,您可以立即开始操作:

./09-haply-inverse-ws-remote-control --session co.haply.hub::demo-orb
python 09-haply-inverse-ws-remote-control.py --session "co.haply.hub::demo-orb"

“Orb”场景会在工作空间原点处渲染一个球体——通过“Tutorial 09”移动工作空间会实时移动整个场景,从而为您提供即时视觉反馈。

任何其他运行触觉循环的教程(01–07)也适用。先在一个终端中启动它,然后在另一个终端中启动教程 09,并让其以交互方式发现该会话。

使用方法

# Discover session interactively, use first detected device
./09-haply-inverse-ws-remote-control
python 09-haply-inverse-ws-remote-control.py

# Target the Haply Hub Orb demo directly
./09-haply-inverse-ws-remote-control --session co.haply.hub::demo-orb
python 09-haply-inverse-ws-remote-control.py --session "co.haply.hub::demo-orb"

# Target a session directly, specify device
./09-haply-inverse-ws-remote-control --session :my_profile:0 --device A14
python 09-haply-inverse-ws-remote-control.py --session "#42" --device A14

控制

按住按键可连续移动。松开按键即可停止。

关键行动
/ +X / −X
/ +Y / −Y
Page Up / Page Down+Z / −Z
= / -加快/减慢导航速度
0将工作区重置为原点
H显示帮助
Esc退出 (Ctrl+C (也适用)

工作原理

步骤 1 — HTTP 会话与设备发现

本教程复用了相同的 discover_session() / discover_device() 辅助程序作为 教程 08: GET /sessions (或 GET /sessions/<selector>--session),然后 GET /devices (或使用 --device (直接)。这两个辅助程序都支持相同的 CLI 选项,且 SESSION_HELP 文本 — 来自教程 00 或 08 的选择器在此处无需修改即可使用。

步骤 2 — WebSocket 会话,首次消息握手

该教程会打开一个 WebSocket 连接至 ws://localhost:10001. 在 仅接收第一条消息,它会发送会话配置文件注册:

{
"session": {"configure": {"profile": {"name": "co.haply.inverse.tutorials:ws-remote-control"}}},
"inverse3": [{"device_id": "...", "commands": {"set_transform": {"transform": {"position": {"x": 0, "y": 0, "z": 0}}}}}]
}

后续的刻度省略 session — 只有 inverse3 发送命令数组。

第 3 步 — 每时钟周期 set_transform

set_transform 接受一个 部分变换 — 仅修改您想要更改的字段。本教程仅发送位置信息;旋转和缩放仍保持其会话默认值(由 configure.mount(如有)。

{
"inverse3": [{"device_id": "...", "commands": {"set_transform": {"transform": {"position": {"x": 0.02, "y": 0.0, "z": 0.01}}}}}]
}

该位置以逐帧方式累积。当频率约为 1 kHz 时,服务会重新应用 set_transform 在每次更新时(零阶保持),因此工作区会保持在您上次推送的位置。

set_transform vs configure.mount

这两个命令都会为每个会话存储一个持久化变换,但工作区变换位于 除此之外 管道中的挂载点(参见 安装与工作区). 教程 09 正在播放 set_transform 在每个时钟周期内,每个新的 WebSocket 帧都会替换之前流式传输的工作区变换——因此,任何其他正在流式传输的客户端 set_transform 对同一设备的设置将在下一个时钟周期被覆盖。 configure.mount 是独立的,并保持其值。在实际操作中,请为每个关注点选择一个通道:mount 用于物理配置, set_transform 用于实时导航。

线程模型(C++)

该 C++ 教程使用了两个线程,它们共享一个位置累加器:

主题角色
主线程阅读量 std::getline(std::cin, ...),解析令牌,更新 nav_vel[]default_step
WebSocket 回调线程进展 nav_pos[] 使用 nav_vel[] * dt, 序列化 set_transform, 发送

std::mutex nav_mutex 保护 nav_pos[], nav_vel[]default_step. 该 last_tick 时间戳和 first_ws_msg 这些标志仅在 WS 回调范围内有效,无需互斥锁。

ws.onmessage = [&](const std::string &) {
float pos_snap[3];
{
std::lock_guard<std::mutex> lk(nav_mutex);
for (int i = 0; i < 3; ++i) nav_pos[i] += nav_vel[i] * 0.001f; // mm/frame → m
for (int i = 0; i < 3; ++i) pos_snap[i] = nav_pos[i];
}
// build and send set_transform with pos_snap
};

来源

随 SDK 安装程序一起发布

教程 09 已随 SDK 一起本地安装——请查看 tutorials/09-haply-inverse-ws-remote-control/ 位于服务安装目录下。

相关: set_transform 命令 · 会话 — 首次消息握手 · 安装与工作区 · 选择器 · 教程 08 — HTTP 远程配置 · 教程 07 — 基座与安装