メインコンテンツへスキップ

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

WebSocketチャネルを介してストリーミング送信することで set_transform 命令、リアルタイムナビゲーション機器の作業スペースの位置。このチュートリアルでは、HTTP を使用してセッションとデバイスを検出します( チュートリアル 08 (完全に同一)、その後WebSocketを開き、各ティックごとに位置情報の更新をプッシュします。

以下の表は、2つの変換操作の違いを示しています:

操作チュートリアル代表的な用法
configure.mount — マウント変換の永続化08一度設定(またはごくまれに変更)し、デバイスの物理的な設置方法を記述するために使用します
set_transform — ワークスペース変換の永続化09(このチュートリアル)これも永続化機能ですが、各ティックごとにストリーム送信が可能で、リアルタイムのワークスペースナビゲーションに使用されます。

どちらのコマンドも、以下の方法で実行できます。 HTTP と WebSocket 使用されており、いずれも永続化された——サービスは、最後に送信した値を記憶します。両者の違いは、用途と更新頻度にあります:configure.mount 低頻度の更新(物理設定)に対して最適化が行われ、一方で set_transform 高頻度のストリーム送信(シーンナビゲーション)向けに最適化を行いました。

このワークスペースの原点を、実行時に変化するオブジェクトに合わせて移動させる必要がある場合は、以下を使用してください。 set_transform――例えば、手の動きに追従したり、プログラムによる位置ずれをアニメーション化したり、オペレーターが仮想作業空間をリアルタイムで微調整したりすることなどです。

マウントとワークスペースの間には親子関係が存在する

個別にみると、configure.mount 和 set_transform 一見すると同じ効果が得られるように見えますが、両者には父子の関係:座標パイプラインにおいて、作業空間変換がマウントに適用されるその上(device → basis → mount → workspace → application)。パイプラインの全容については、以下を参照してください。 マウントとワークスペース。

利用シーン​

  • ワークスペースのリアルタイムナビゲーション。触覚シーンを停止させることなく、デバイスのカーソルワークスペース(すなわち、エンドエフェクタが到達可能なエンベロープ領域)を移動させる。
  • オペレーター主導の位置合わせ。別の端末にいる担当者に方向キーを押し続けさせ、メインの触覚アプリケーションの実行中にワークスペースを平行移動させます。これは、リアルタイムのセッション中に各ユーザーのデスクトップをキャリブレーションする場合に適しています。
  • ワークスペースの運動をプログラムで制御します。スクリプト(オーディオ駆動、物理駆動など)によってワークスペースの位置を制御し、各ティックごとに位置情報のみを含むトランスフォームパッチを送信します。

前提条件​

チュートリアル 09 では、実行中の WebSocket セッションが必要です。つまり、実行中の「Haptic Loop」内に、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つのターミナルでそれを起動し、次に別のターミナルでチュートリアル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 テキストが完全に一致――チュートリアル 00 または 08 のセレクタは、修正することなくここで使用できます。

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

このチュートリアルでは、次のURLを開きます。 ws://localhost:10001 の WebSocket。最初のメッセージを受信したときのみ,セッション構成の登録情報を送信します:

{
"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つのスレッドで1つの位置アキュムレータを共有しています:

スレッド職務内容
メインスレッド読み込み 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 とマウント