02. VerseGrip-Anzeige
Zeigt kontinuierlich die Ausrichtung (Quaternion + Euler-Winkel Z-X-Y), den Pegel des Hall-Sensors und den Status der Taste des ersten kabelgebundenen VerseGrip an.
Was Sie lernen werden:
- Die Anleitung lesen
quaternionauf der Grundlage des Zustandsmodells - Ein Quaternion in Euler-Winkel Z-X-Y in Grad umrechnen (+X nach rechts, +Y nach vorne, +Z nach oben)
- Verwenden
probe_orientationals Signal zur Aufrechterhaltung eines autonomen Beobachters - Das Beispiel für den Händedruck, das nur in der ersten Nachricht gesendet wurde (identisch mit dem aus Tutorial 01)
Ablauf
- Stellen Sie eine WebSocket-Verbindung zu
ws://localhost:10001und warten Sie auf den ersten Status-Frame. - Wählen Sie die
device_iddes ersten VerseGrip mit Kabel in der Tabelleverse_grip. - Erstellen Sie eine Abfrage mit dem Sitzungsprofil und ein Halte-Signal
probe_orientationpro Gerät (ein Befehl mit leerem Objekt, der den Ausrichtungsfluss des Griffs in den Zustandsrahmen beibehält). - Senden Sie die Anfrage ab und entfernen Sie anschließend das Feld.
session: Es handelt sich um einen einmaligen Händedruck. - Wandeln Sie bei jedem folgenden Frame das Quaternion in Euler-Winkel um und zeigen Sie die Telemetriedaten mit begrenzter Bildrate an. Senden Sie das Halte-Signal bei jedem Zyklus zurück.
Einstellungen
| Nom | Standardwert | Rolle |
|---|---|---|
URI | ws://localhost:10001 | URL des WebSocket-Simulationskanals |
PRINT_EVERY_MS | 100 | Begrenzung der Bildwiederholrate der Konsolenanzeige |
| Name des Sitzungsprofils | co.haply.inverse.tutorials:print-verse-grip | Identifiziere diese Simulation im „ Haply “-Hub |
Die Konversion ist eine intrinsische Z-X-Y-Drehung (Gieren → Nicken → Rollen) im Koordinatensystem der Anwendung. +X right, +Y forward, +Z up. Verwenden Sie nicht glm::eulerAngles : Diese Funktion folgt einer anderen Konvention und würde hier falsche Werte liefern. Alle drei Sprachvarianten führen dieselben Berechnungen durch; die Formel finden Sie im Quellcode.
probe_orientation ist wirklich notwendigprobe_orientation ist nur dann sinnvoll, wenn Ihre Sitzung sendet keine Befehl an einen „ Inverse3 “. Sobald Sie einen „ Inverse3 “ (Kraft, Position, Drehmoment ...) befehlen, übermittelt der Dienst automatisch die zugehörige Ausrichtung des „VerseGrip“ in jedem Status-Frame: Ein Halte-Signal ist nicht erforderlich. Verwenden Sie probe_orientation Gilt ausschließlich für eigenständige Griff-Tracking-Tools, wie in dieser Anleitung beschrieben.
Meister der Les-États
Seit data.verse_grip[0].state :
orientation—quaternion(w, x, y, z)hall— Ganzzahlwert des Hall-Sensorsbutton— Booleschetransform.rotation— Drehung des Arbeitsbereichs (quaternion); Position und Maßstab gelten nicht für ein Gerät, das lediglich die Ausrichtung liefert, und werden niemals ausgegeben
transform.rotation wird weggelassen, wenn es gleich dem Identitätsquaternion ist {w:1,x:0,y:0,z:0}. Geben Sie einen Standardwert für die Wiedergabe an. Aktivieren Sie serialization/explicit_fields um ihn regelmäßig zu erhalten.
Versand / Empfang
Die WebSocket-Schleife: Empfangen Sie einen Status-Frame, erstellen Sie anschließend den Handshake und das Keep-Alive-Signal und senden Sie diese zurück. probe_orientation. Die erste ausgehende Nachricht enthält das Sitzungsprofil; jeder nachfolgende Rahmen enthält nur noch das Halte-Signal.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Einzigartige asynchrone Schleife: recv() → Aufbau der Steuerung → send() → Probe.
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 steuert WebSocket über seinen E/A-Thread – die Verarbeitung jedes Frames erfolgt in ws.onmessage. Der Hauptthread bleibt blockiert, bis die Eingabetaste gedrückt wird.
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
Dasselbe libhv-Rückrufmodell. Typisierte Strukturen ersetzen nlohmann::json — probe_orientation_cmd ist eine leere Struktur (Glaze schreibt dies in der Form {}). std::optional<session_cmd> regelt den gelegentlichen Händedruck: .reset() Nach der ersten Übermittlung werden diese aus den folgenden JSON-Ausgaben ausgeschlossen.
// 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
Befehlszeilenoptionen (Python)
Die Python-Variante akzeptiert dieselben beiden Optionen wie das Tutorial 01:
--full— zeigt die rohe JSON-Nutzlast in lesbarer Form an, anstatt nur eine einzeilige Zusammenfassung.--query-config— wieder einspeisensession.force_render_full_statebei jedem ausgehenden Zyklus, damit jeder Rahmen den Block enthältconfig(Gerätetyp, Firmware, Konfigurationsänderung, …) anstelle der bloßen Zustandsänderung. Nützlich zum Debuggen von Parameteränderungen in Echtzeit.
Beide Optionen lassen sich kombinieren. Die C++-Varianten bieten diese Optionen nicht an.
Das Tutorial 02 wird ebenfalls lokal zusammen mit dem SDK installiert – siehe tutorials/02-haply-inverse-print-verse-grip/ im Installationsverzeichnis des Dienstes.
Quelle: Python · C++ · C++ Glaze
Siehe auch: Typen (Quaternionen) · Steuerbefehle (probe_orientation) · WebSocket-Protokoll · Tutorial 03 (kabellose Videospiele)