02. VerseGrip drucken
Gibt die Echtzeit-Ausgangsdaten des ersten kabelgebundenen VerseGrip aus (Quaternion + Eulerwinkel Z-X-Y), die Pegel der Hall-Sensoren sowie den Status der Tasten.
Lerninhalte:
- Aus dem Status-Frame lesen
quaternionHaltung - Umwandlung von Quaternionen in Eulerwinkel Z-X-Y in Grad (wobei +X rechts, +Y vorne und +Z oben bedeutet)
- wird
probe_orientationBefehl zum Aufrechterhalten des Betriebs als unabhängiger Beobachter - Ein Modell, bei dem nur die erste Nachricht einen Handshake erfordert (wie in Tutorial 01 )
Arbeitsablauf
- Öffnen Sie den Link
ws://localhost:10001WebSocket und warte auf den ersten Status-Frame. - von
verse_gripDas erste „VerseGrip“-Kabel im Array auswählendevice_id。 - Verwendung Sitzungskonfiguration sowie für jedes Gerät
probe_orientationAnforderung zur Erstellung eines Befehls zur Aufrechterhaltung der Aktivität (Dies ist ein Befehl für ein leeres Objekt, der sicherstellt, dass die Daten zur Haltehaltung kontinuierlich im Statusframe erscheinen). - Anfrage senden, dann entfernen
sessionFeld – Es wird ausschließlich für einen einmaligen Handshake verwendet. - In jedem folgenden Frame werden die Quaternionen in Euler-Winkel umgewandelt und die Telemetriedaten mit einer Drosselung ausgegeben. In jedem Zyklus wird der Keep-Alive-Befehl erneut gesendet.
Parameter
| Bezeichnung | Standardwert | Verwendungszweck |
|---|---|---|
URI | ws://localhost:10001 | URL des WebSocket-Simulationskanals |
PRINT_EVERY_MS | 100 | Intervall für die Drosselung der Konsolenausgabe |
| Name der Sitzungskonfiguration | co.haply.inverse.tutorials:print-verse-grip | Zur Identifizierung dieser Simulation im „ Haply Hub“ |
Die Umrechnung erfolgt im Anwendungskoordinatensystem +X right, +Y forward, +Z up Die Reihenfolge der Innenrotation Z-X-Y (Gieren → Nicken → Rollen).Bitte nichtVerwendung glm::eulerAngles——Da es sich an unterschiedliche Konventionen hält, werden hier falsche Ergebnisse angezeigt. Die mathematischen Berechnungen sind in allen drei Sprachversionen identisch; die konkreten Formeln finden Sie im Quellcode.
probe_orientationNur wenn die Sitzungnoch nichtWenn Sie einen Befehl an Inverse3 senden,probe_orientation Erst dann ist es sinnvoll. Sobald Sie Befehle (Kraft, Position, Drehmoment usw.) an Inverse3 senden, übermittelt der Dienst automatisch in jedem Status-Frame die Lage-Daten des gekoppelten VerseGrip – ganz ohne Abfrage.probe_orientation Gilt nur für eigenständige Geräte zur Handhaltungsüberwachung wie das in diesem Tutorial vorgestellte.
Gelesene Statusfelder
Aus data.verse_grip[0].state:
orientation—quaternion(w, x, y, z)hall— Messwerte des Hall-Sensors vom Typ „Integer“button— Boolesche Wertetransform.rotation— Drehung des Arbeitsraums (quaternion); Bei Geräten, die ausschließlich die Lage ausgeben, sind Position und Skalierung nicht relevant und werden daher nie ausgegeben.
Wenn transform.rotation entspricht der Einheitsquaternion {w:1,x:0,y:0,z:0} wird weggelassen. Bitte geben Sie beim Auslesen einen Standardwert an. Um dieses Feld immer zu erhalten, aktivieren Sie serialization/explicit_fields。
Senden / Empfangen
WebSocket-Zyklus: Empfang eines Status-Frames, Erstellung und Rücksendung der Handshake-Nachricht sowie probe_orientation Keep-Alive-Befehl. Die erste Nachricht enthält die Verbindungskonfiguration; alle nachfolgenden Frames enthalten lediglich den Keep-Alive-Befehl.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Eine einzelne asynchrone Schleife –recv() → Befehl zum Erstellen → send() → Wiederholung.
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 WebSockets über seinen I/O-Thread – die Verarbeitungslogik für jeden Frame befindet sich in ws.onmessage . Der Hauptthread ist blockiert und wartet darauf, dass die ENTER-Taste 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
Es wird das gleiche libhv-Callback-Modell beibehalten. Typstarke Strukturen ersetzen nlohmann::json——probe_orientation_cmd Leere Struktur (Glaze schreibt dies als {}).std::optional<session_cmd> Zur Verarbeitung eines einmaligen Handshakes: Wird nach der ersten Übertragung aufgerufen .reset(), damit es in der nachfolgenden JSON-Ausgabe nicht mehr erscheint.
// 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
Befehlszeilenargumente (Python)
Die Python-Variante akzeptiert dieselben zwei Parameter wie Tutorial 01:
--full— Die ursprünglichen JSON-Lastdaten sollen in formatierter Form ausgegeben werden, nicht als einzeilige Zusammenfassung.--query-config— Bei jedem Ausgabzyklus erneut einspeisensession.force_render_full_state, sodass jedes EinzelbildconfigBlöcke (Gerätetyp, Firmware, Installationsänderungen usw.) und nicht nur Statusänderungen. Dies ist besonders nützlich, wenn Debugging-Einstellungen in Echtzeit geändert werden.
Die beiden Parameter können kombiniert verwendet werden. Die C++-Variante bietet diese Parameter nicht an.
Das Tutorial 02 wird ebenfalls zusammen mit dem SDK lokal installiert – bitte im Installationsverzeichnis des Dienstes unter tutorials/02-haply-inverse-print-verse-grip/ dort suchen.
Quelle: Python · C++ · C++ Glaze
Verwandte Themen: Typ (Quaternionen) · Steuerbefehle (probe_orientation) · WebSocket-Protokoll · Tutorial 03 (Wireless VG)