02. ヴェルセグリップの表示
初代有線VerseGripからの姿勢(クォータニオン+オイラー角 Z-X-Y)、ホールセンサーのレベル、およびボタンの状態を継続的に表示します。
このコースで学べる内容:
- ガイダンスを読む
quaternion状態トレースに基づいて - クォータニオンを、度単位のZ-X-Yオイラー角に変換する(+Xが右、+Yが前方、+Zが上方)
- 利用
probe_orientation自律型観測者の維持を示すシグナルとして - 最初のメッセージでのみ送信される握手モデル(チュートリアル01のものと同じ)
進行
- WebSocket接続を以下に向けて確立してください
ws://localhost:10001そして、最初のステータスフレームを待ちます。 - [選択]
device_id表内の最初の有線VerseGripverse_grip. - 以下のを使用してクエリを作成してください セッションプロファイル およびホールド信号
probe_orientationpar appareil(状態フレーム内でハンドルの向きフローを維持する、空のオブジェクトに対するコマンド)。 - リクエストを送信してから、そのフィールドを削除してください
session: これはその場限りの握手です。 - 次のフレームごとに、クォータニオンをオイラー角に変換し、テレメトリを制限されたレートで表示してください。各サイクルごとにホールド信号を返してください。
パラメータ
| ノム | デフォルト値 | 役割 |
|---|---|---|
URI | ws://localhost:10001 | WebSocket シミュレーションチャネルの URL |
PRINT_EVERY_MS | 100 | コンソール表示の更新レートの上限 |
| セッションプロファイル名 | co.haply.inverse.tutorials:print-verse-grip | Haply Hub でこのシミュレーションを特定してください |
変換とは、写像座標系におけるZ-X-Y方向の内回転(横揺れ→ピッチ→ロール)である。 +X right, +Y forward, +Z up. 使用しないでください glm::eulerAngles : この関数は別の規約に従っているため、ここでは誤った値を返すことになります。3つの言語バリエーションはいずれも同じ計算を実装しています。計算式についてはソースコードを参照してください。
probe_orientation 本当に必要なのかprobe_orientation セッションが有効な場合にのみ有効です 一切送信しない Inverse3 へのコマンド。Inverse3 (力、位置、トルクなど)をコマンドすると、サービスは関連するVerseGripの向きを各ステータスフレームで自動的に配信します。保持信号は必要ありません。使用方法: probe_orientation このチュートリアルのように、スタンドアロンのハンドル追跡ツールにのみ適用されます。
よく読まれている記事
以来 data.verse_grip[0].state :
orientation—quaternion(w, x, y, z)hall— ホール効果センサーの整数値button— booléentransform.rotation— ワークスペースの回転 (quaternion) ; 位置とスケールは、方位情報のみを提供するデバイスには適用されず、決して送信されることはありません
transform.rotation 単位四元数と等しい場合は省略される {w:1,x:0,y:0,z:0}. 読み取りのデフォルト値を指定してください。有効にしてください serialization/explicit_fields これを確実に受け取るために。
送信/受信
WebSocketループ:ステータスフレームを受信し、ハンドシェイクとキープシグナルを構築して返信する probe_orientation. 最初の送信メッセージにはセッションプロファイルが含まれており、それ以降の各フレームには維持信号のみが含まれる。
- 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["verse_grip"][0]["device_id"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-verse-grip"}}},
"verse_grip": [{
"device_id": device_id,
"commands": {"probe_orientation": {}} # empty — keepalive
}]
}
await websocket.send(json.dumps(request_msg))
request_msg.pop("session", None) # one-shot handshake
libhvは、そのI/Oストリーム上でWebSocketを処理します。各フレームの処理は ws.onmessage. メインスレッドは、ENTERキーが押されるまでブロックされたままになります。
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["verse_grip"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-versegrip"}}}}}}},
{"verse_grip", json::array({
{{"device_id", device_id},
{"commands", {{"probe_orientation", json::object()}}}},
})},
};
}
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
libhv と同じリコールモデルです。型付き構造体が nlohmann::json — probe_orientation_cmd は空の構造体である(グレイズはこれを次のように記述している {}). std::optional<session_cmd> その場限りの握手に対応します: .reset() 最初の送信後、それ以降のJSON出力からそれを除外します。
// Struct models
struct quat { float w{1.0f}, x{}, y{}, z{}; };
struct grip_state { quat orientation{}; bool button{}; uint8_t hall{}; };
struct grip_device { std::string device_id; grip_state state; };
struct devices_message { std::vector<grip_device> verse_grip; };
struct probe_orientation_cmd {}; // empty object on the wire
struct commands_message {
std::optional<session_cmd> session; // one-shot — omitted when unset
std::vector<device_commands> verse_grip;
};
// Send / receive
ws.onmessage = [&](const std::string &msg) {
devices_message data{};
if (glz::read<glz_settings>(data, msg)) return;
if (first_message) {
first_message = false;
out_cmds.session = session_cmd{ /* profile = print-versegrip */ };
device_commands dc{ .device_id = data.verse_grip[0].device_id };
dc.commands.probe_orientation = probe_orientation_cmd{};
out_cmds.verse_grip.push_back(std::move(dc));
}
std::string out_json;
(void)glz::write_json(out_cmds, out_json);
ws.send(out_json);
out_cmds.session.reset(); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
コマンドラインオプション(Python)
Python版では、チュートリアル01と同じ2つのオプションが使用可能です:
--full— 1行の要約ではなく、生のJSONペイロードを読みやすい形式で表示します。--query-config— réinjectesession.force_render_full_state各送信サイクルごとに、各フレームにそのブロックが含まれるようにするためconfig(デバイス種別、ファームウェア、マウント変換など)を、単なる状態の差分だけでなく表示します。パラメータの変更をリアルタイムでデバッグするのに役立ちます。
この2つのオプションは組み合わせて使用できます。C++のバリエーションでは、これらのオプションは提供されていません。
チュートリアル 02 も SDK とともにローカルにインストールされています。詳しくは tutorials/02-haply-inverse-print-verse-grip/ サービスのインストールディレクトリ内。
関連項目: 型(クォータニオン) ・ 制御コマンド (probe_orientation) ・ WebSocketプロトコル ・ チュートリアル 03(ワイヤレスVG)