03. Daten des Wireless VerseGrip anzeigen
Das gleiche Prinzip wie im Tutorial 02, jedoch für einen Wireless VerseGrip – fügt die Tasten (A/B/C) und den Akkustand zur Live-Anzeige hinzu.
Was Sie lernen werden:
- Die für die drahtlose Version spezifischen Statusfelder lesen:
buttons.{a,b,c},battery_level,hall - Verwenden
probe_orientationals Keepalive-Nachricht für einen autonomen Beobachter - Das gleiche Handshake-Schema, das wie im Tutorial 02 auf die erste Nachricht beschränkt ist
Ablauf
- Stellen Sie eine WebSocket-Verbindung zu
ws://localhost:10001und warten Sie auf den ersten Status-Frame. - Laden Sie die
device_iddes ersten Wireless VerseGrip aus der Tabellewireless_verse_grip. - Erstellen Sie eine Abfrage mit dem Sitzungsprofil und ein Keepalive
probe_orientationpro Gerät. - Senden Sie die Anfrage ab und entfernen Sie anschließend das Feld.
session— Das ist ein einmaliger Handschlag. - Wandeln Sie bei jedem folgenden Frame das Quaternion in Euler-Winkel um und zeigen Sie die zeitlich begrenzte Telemetrie an, einschließlich des Tastenstatus und des Akkustands. Senden Sie bei jeder Iteration das Keepalive zurück.
Einstellungen
| Nom | Standardwert | Rolle |
|---|---|---|
URI | ws://localhost:10001 | URL des WebSocket-Simulationskanals |
PRINT_EVERY_MS | 100 | Begrenzung der Bildwiederholfrequenz der Konsole |
| Name des Sitzungsprofils | co.haply.inverse.tutorials:print-wireless-verse-grip | Identifiziere diese Simulation im „ Haply “-Hub |
Die Umwandlung erfolgt intrinsisch von Z-X-Y (Querlage → Nicklage → Rolllage) im Koordinatensystem der Anwendung. +X right, +Y forward, +Z up. Nicht verwenden glm::eulerAngles — Diese Funktion folgt einer anderen Konvention und liefert hier falsche Werte. 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 Ausrichtung des gekoppelten „VerseGrip“ in jedem Status-Frame – es ist kein Sensor erforderlich. Verwenden Sie probe_orientation Gilt ausschließlich für eigenständige Tools zur Überwachung des Griffs, wie in dieser Anleitung beschrieben.
Meister der Les-États
Ab data.wireless_verse_grip[0].state :
orientation—quaternion(w, x, y, z)hall— Vollständige Beschreibung des Hall-Sensorsbuttons.a,buttons.b,buttons.c— Boolesche Wertebattery_level— Gleitkomma (0,0 – 1,0)transform.rotation— Drehung des Arbeitsbereichs (quaternion); Position und Maßstab gelten nicht für ein Gerät, das lediglich die Ausrichtung angibt, 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 beim Einlesen einen Standardwert an. Aktivieren Sie serialization/explicit_fields um ihn weiterhin zu erhalten.
Versand / Empfang
Gleiche Struktur wie der Tutorial 02, lediglich mit der Gerätetabelle wireless_verse_grip. Die WebSocket-Schleife empfängt einen Status-Frame und sendet den Handshake sowie den Keepalive zurück probe_orientation; Die erste ausgehende Nachricht enthält das Sitzungsprofil, und jeder nachfolgende Frame enthält nur das Keepalive.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Nur eine asynchrone Schleife.
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["wireless_verse_grip"][0]["device_id"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-wireless-verse-grip"}}},
"wireless_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
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["wireless_verse_grip"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-wireless-verse-grip"}}}}}}},
{"wireless_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
Typisierte Strukturen. Beachten Sie, dass button_state wird ausdrücklich genannt (und nicht buttons), um zu vermeiden, dass das Feld verdeckt wird buttons de wvg_state — Glaze ordnet Felder anhand ihrer Namen zu, daher spielt der Name des Strukturtyps keine Rolle.
// Struct models
struct quat { float w{1.0f}, x{}, y{}, z{}; };
struct button_state { bool a{}, b{}, c{}; };
struct wvg_state {
quat orientation{};
uint8_t hall{};
button_state buttons{};
float battery_level{};
};
struct wvg_device { std::string device_id; wvg_state state; };
struct devices_message { std::vector<wvg_device> wireless_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> wireless_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;
request_msg.session = session_cmd{ /* profile = print-wireless-verse-grip */ };
device_commands dc{ .device_id = data.wireless_verse_grip[0].device_id };
dc.commands.probe_orientation = probe_orientation_cmd{};
request_msg.wireless_verse_grip.push_back(std::move(dc));
}
std::string out_json;
(void)glz::write_json(request_msg, out_json);
ws.send(out_json);
request_msg.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 aufbereitete JSON-Bruttolast anstelle der einzeiligen Zusammenfassung an.--query-config— wieder einspeisensession.force_render_full_statebei jedem ausgehenden Tick, sodass jeder Rahmen den Block enthältconfiganstelle des reinen Zustandsdeltas.
--query-config ist besonders nützlich für den kabellosen VerseGrip, da der Block config enthält das Feld sub_type gemäß dem WVG (stylus / prototype / ruko / kingfisher, hinzugefügt in Version 3.5), zusätzlich zu den üblichen Konfigurationsfeldern. Die Ausführung von python 03-haply-inverse-print-wireless-verse-grip.py --full --query-config ermöglicht es, zu sehen config.sub_type sich in Echtzeit aktualisieren, gleichzeitig mit den anderen Konfigurationswerten.
Die C++-Varianten bieten diese Optionen nicht.
Das Tutorial 03 wird ebenfalls lokal mit dem SDK installiert – siehe tutorials/03-haply-inverse-print-wireless-verse-grip/ im Installationsverzeichnis des Dienstes.
Quelle: Python · C++ · C++ Glaze
Verwandte Links: Tutorial 02 (VG mit Kabel) · Typen (Quaternionen) · Steuerbefehle (probe_orientation) · WebSocket-Protokoll