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
操作方法
- Python
- C++
キーを押し続けていると、連続して移動します。キーから指を離すと止まります。
| キー | 動作 |
|---|---|
→ / ← | +X / −X |
↑ / ↓ | +Y / −Y |
Page Up / Page Down | +Z / −Z |
= / - | 探索速度の増加/減少 |
0 | ワークスペースを初期状態にリセット |
H | ヘルプを表示 |
Esc | 終了 (Ctrl+C(動作も) |
行単位での入力方式です。コマンドを入力した後、Enterキーを押してください。
| コマンド | 動作 |
|---|---|
x+[N] / x-[N] | X軸の速度を±N mm/フレームに設定 (x+(入力するだけで、現在のデフォルト速度が使用されます) |
y+[N] / y-[N] | Y速度を±N mm/フレームに設定 |
z+[N] / z-[N] | Z速度を±N mm/フレームに設定 |
s+[N] / s-[N] | 基本速度を±N mm/フレーム分調整する |
stop | すべての速度を0に設定 |
reset | 速度を0に設定し、原点に戻る |
h | ヘルプを表示 |
終了するには、Ctrl+C(またはCtrl+D/EOF)を押してください。
動作原理
ステップ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
};
ソースコード
チュートリアル 09 は、SDK とともにローカルにインストールされます。サービスのインストールディレクトリ内の tutorials/09-haply-inverse-ws-remote-control/こちらでご確認ください。
関連文書: set_transform コマンド ・ セッション — 最初のメッセージによるハンドシェイク ・ マウントとワークスペース ・ セレクター ・ チュートリアル 08 — HTTP リモート設定 ・ チュートリアル 07 — Basis およびマウント