本文へスキップ

09. WebSocket ワークスペース ナビゲーター

set_transform WebSocketチャネルを介してコマンドをストリーミングすることで、デバイスの ワークスペースの位置をリアルタイムで 探索します。このチュートリアルでは、HTTP に基づいてセッションとデバイスを探索し(チュートリアル 08(これとまったく同じ方式で)、その後、毎ティックごとに位置情報の更新を送信するためにWebSocket接続を開きます。

この表は、2つの変換演算の違いを示しています:

演算チュートリアル一般的な用途
configure.mount — 永久マウントの変換08デバイスの物理的な取り付け方法を説明するために、一度(あるいはごくまれに)設定する
set_transform — ワークスペースの永続的な変換09(本チュートリアル)やはり永続的ですが、ワークスペースをリアルタイムで探索するために、各ティックごとにストリーミングすることが可能です。

両方のコマンドとも HTTP および WebSocket 環境で使用可能であり、どちらも 恒久的なです。サービスは最後に送信された値を記憶しています。2つのコマンドの違いは、目的と更新の頻度にあります。 configure.mountは、まれな更新(物理的な設定)に最適化されている一方で、 set_transformは、高頻度ストリーミング(シーン検索)に最適化されています。

実行時にワークスペースの原点が変化する対象を追跡する必要がある場合 set_transformを使用してください。例えば、手の動きを追跡したり、手続き型オフセットをアニメーションで実装したり、オペレーターが仮想ワークスペースをリアルタイムで微調整できるようにする場合などに使用します。

マウントとワークスペースは親子関係にあります

個別にみると configure.mountわあ set_transformは同じような効果をもたらしているように見えますが、この2つの値は 親子関係です。座標パイプライン(device → basis → mount → workspace → application)におけるワークスペースの変換は、マウント 上へ 適用されます。パイプライン全体は マウントとワークスペースを参照してください。

ユースケース​

  • ワークスペースのリアルタイム探索。ハプティックシーンを中断することなく、デバイスのカーソルワークスペース(エンドエフェクタが到達可能な境界領域)を移動します。
  • オペレーター主導の配置。メインのハプティックアプリが実行されている間、別の端末で別のユーザーが方向キーを押してワークスペースを移動できるようにします。ライブセッション中のユーザーごとのワークベンチキャリブレーションに役立ちます。
  • 手続き型ワークスペースモーション。各ティックごとに位置専用の変換パッチを送信し、スクリプト(オーディオ反応型、物理ベースなど)によってワークスペースの位置を制御します。

前提条件​

チュートリアル 09 では、アクティブな WebSocket セッションが必要です。つまり、すでに実行中のハプティックループで Inverse3 を使用しているクライアントである必要があります。チュートリアル 09 では、そのセッションに接続した後、ワークスペースをリアルタイムで移動させます。

セッションを最も高速に実行する方法

Haply Hubを開き、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」シーンでは、ワークスペースの原点に球体がレンダリングされます。チュートリアル09にワークスペースを移動すると、シーン全体がリアルタイムで移動し、即座に視覚的なフィードバックが得られます。

ハプティック・ループを実行する他のチュートリアル(01~07)もすべて正常に動作します。1つのターミナルでそれらのチュートリアルを起動し、2つ目のターミナルでチュートリアル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セッションおよびデバイスの調査​

このチュートリアルは チュートリアル 08…と同じ discover_session() / discover_device() ヘルパーを再利用します。 GET /sessions(または --sessionと組み合わせて使う GET /sessions/<selector>)を呼び出した後、 GET /devicesを呼び出すか、 --deviceを直接使用します。2つのヘルパーは同じ SESSION_HELP テキストとともに同じ CLI フラグを受け取り、チュートリアル 00 または 08 のセレクタは、ここでも変更なしで動作します。

ステップ2 — WebSocketセッション、最初のメッセージによるハンドシェイク​

このチュートリアルは ws://localhost:10001WebSocket接続を開きます。 最初に受信したメッセージについてのみ セッションプロファイルの登録情報を送信します:

{
"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と configure.mountの違い

どちらのコマンドもセッションごとに永続的な変換を保存しますが、パイプラインではワークスペースの変換はマウント 上へ 置かれます(マウントとワークスペース (参照)。チュートリアル09では、各ティックごとに set_transformのストリーミング中は、新しいWebSocketフレームが到着するたびに、以前にストリーミングされたワークスペースの変換が上書きされます。したがって、同じデバイス上で set_transformをストリーミングしている他のクライアントは、次のティックで上書きされます。 configure.mountは独立しており、独自の値を維持します。実務では、関心事ごとに1つずつチャネルを選択してください。物理的な設定にはマウントを、リアルタイムの探索には set_transformを使用します。

スレッドモデル(C++)​

この C++ チュートリアルでは、位置カウンタを共有する 2 つのスレッドを使用しています:

スレッド役割
メインスレッド入力の読み取り(std::getline(std::cin, ...)), トークンの解析、 nav_vel[] および default_step 更新
WebSocket コールバックスレッドnav_vel[] * dt 値を使用して nav_pos[]を前進させ、 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 — Basis およびマウント