01. 表示Inverse3
WebSocketシミュレーションに接続し、サービスから通知された最初のInverse3 から、カーソルの位置、速度、および力を配信します。
このコースで学べる内容:
- WebSocket接続を開き、完全な状態情報が含まれる初期メッセージを受信する
- 以下の方法で、ゼロフォースのキープアライブ信号を送信する
set_cursor_forceセッションをアクティブなままにするために - Haply Hubがシミュレーションを認識できるように、セッションプロファイルを登録する
- 最初のメッセージに限定された握手モデル:最初の送信後は session/configure を削除する
- 続きを読む
transformワークスペース(位置、回転、スケール)の、部分更新のセマンティクスを用いた操作 - コンソールでの表示速度を、読みやすいペースに制限する
進行
- WebSocket接続を次へ確立する
ws://localhost:10001. 当該サービスは直ちに 完全なステートトレース 接続されているデバイスの一覧を表示します。 - 最初のフレームで、以下のいずれかを選択してください。
device_id最初のInverse3 から、2つの部分で構成されるリクエストメッセージを作成します:session.configure.profile.name: シミュレーションを以下の場所に保存します Haply.- 注文
set_cursor_forceデバイスごとに、ベクトル値はゼロです。このサービスはこれを保持信号として使用し、コマンドが到着し続ける限り、ステータスフレームを送信し続けます。
- メッセージを転送する。 フィールドを削除する
session次のサイクルに入る前に:セッションプロファイルは1回限りのハンドシェイクであり、その後のサイクルではコマンドのみが送信されます。 - 次の各ステータスフレームについて:フィールドを表示する
vec3カーソルの情報(位置、速度、力)を、約10 Hzに制限されたサンプリングレートで取得し、その後、力をゼロに保持する信号を返す。
パラメータ
| ノム | デフォルト | 役割 |
|---|---|---|
URI | ws://localhost:10001 | WebSocket シミュレーションチャネルの URL |
PRINT_EVERY_MS | 100 | コンソールでの表示制限 |
| セッションプロファイル名 | co.haply.inverse.tutorials:print-inverse3 | Haply Hub でこのシミュレーションを特定してください |
よく読まれている記事
以来 data.inverse3[0].state :
cursor_position,cursor_velocity,current_cursor_force: タイプvec3chacuntransform: ワークスペースの変換;サブオブジェクトとしてposition(vec3),rotation(quaternion) およびscale(vec3)
同一値であるサブフィールド(position: {0,0,0}, rotation: {w:1,x:0,y:0,z:0}, scale: {1,1,1}) は omis 帯域幅を節約するために、ペイロードから除外します。読み取り時には常にデフォルト値を指定してください(例: .value("position", default_pos) C++では、 .get("position", default_pos) (Pythonでは)。有効にしてください serialization/explicit_fields すべてのフィールドを常に受信するように設定します。
送信と受信
WebSocketのループ:ステータスフレームを受信し、構築して返送する 制御フレーム. 最初の制御フレームには、セッションハンドシェイクと、強制値ゼロのホールド信号が格納されており、 set_cursor_force; それ以降の各フレームは、 que ホールド信号(セッションが解除されます)。
- Python
- C++ (nlohmann)
- C++ (Glaze)
一意な非同期ブークル: recv() → 注文を作成する → send() → 繰り返す。
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"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-inverse3"}}},
"inverse3": [{
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": 0.0, "z": 0.0}}},
}]
}
await websocket.send(json.dumps(request_msg))
request_msg.pop("session", None) # one-shot handshake
libhv は WebSocket を独自の I/O スレッドで処理します。フレームごとの処理は ws.onmessage. メインスレッドは単にENTERキーが押されるのを待っているだけです。
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["inverse3"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-inverse3"}}}}}}},
{"inverse3", json::array({
{{"device_id", device_id},
{"commands", {{"set_cursor_force",
{{"vector", {{"x", 0.0}, {"y", 0.0}, {"z", 0.0}}}}}}}},
})},
};
}
ws.send(request_msg.dump());
request_msg.erase("session"); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
nlohmann バージョンと同じ libhv コールバックモデルです。異なるのは本体部分のみです。Glaze はコンパイル時のリフレクションを利用します。JSON の形式を反映した構造体を宣言し、次に呼び出します glz::read / glz::write_json. std::optional<session_cmd> 使い捨ての手を握る動作を転送します。このフィールドが定義されていない場合、Glaze は JSON のシリアライズ時にこのフィールドを省略します。
// Struct models
struct vec3 { float x{}, y{}, z{}; };
struct inverse_state {
vec3 cursor_position{}, cursor_velocity{}, current_cursor_force{};
/* + body_orientation, angular_position, angular_velocity */
};
struct inverse_device { std::string device_id; inverse_state state; };
struct devices_message { std::vector<inverse_device> inverse3; };
struct set_cursor_force_cmd { vec3 vector; };
struct commands_message {
std::optional<session_cmd> session; // omitted from JSON when unset
std::vector<device_commands> inverse3;
};
// Send / receive
ws.onmessage = [&](const std::string &msg) {
devices_message data{};
if (glz::read<glz_settings>(data, msg)) return;
commands_message out_cmds{};
if (first_message) {
first_message = false;
out_cmds.session = session_cmd{ /* profile = print-inverse3 */ };
}
// ... populate out_cmds.inverse3 with zero-force keepalive ...
std::string out_json;
(void)glz::write_json(out_cmds, out_json);
ws.send(out_json);
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
コマンドラインオプション(Python)
Python版では、チュートリアルの表示内容を変更する2つのオプションが利用可能です:
| オプション | エフェクト |
|---|---|
--full | 各ステータスフレームのJSON形式の総ペイロードを、1行の要約ではなく、整形された形式で表示します。サービスから送信されるフィールドを確認するのに役立ちます。 |
--query-config | 再注入 session.force_render_full_state: {} 各送信サイクルごとに、サービスが完全なスナップショット(ブロックを含む)を再送信するようにするため config : 機種、ファームウェア、プリセット、マウント、フィルターなど)を各フレームごとに設定します。このオプションを指定しない場合、 config 最初のフレームにのみ送信され、それ以降のフレームには、継続的に配信される差分更新のみが含まれます。 |
この2つの選択肢は組み合わせて利用できます: python 01-haply-inverse-print-inverse3.py --full --query-config ブロックとともに完全なJSONペイロードを表示します config 各サイクルごとに表示されるため、Haply Hub や HTTP API からパラメータの変化をリアルタイムで観察するのに便利です。詳細については、以下を参照してください。 session.force_render_full_state pour la commande WebSocket sous-jacente.
C++版では、これらのオプションは利用できません。常に1行の要約が表示され、 config 最初のフレーム上で。
チュートリアル 01 も SDK とともにローカルにインストールされています。詳しくは以下をご覧ください。 tutorials/01-haply-inverse-print-inverse3/ サービスのインストールディレクトリ内。
関連項目: WebSocketプロトコル ・ 制御コマンド (set_cursor_force) ・ セッション ・ 型 (vec3)