02. VerseGrip drucken
Überträgt die Ausrichtung (Quaternion + Z-X-Y-Eulerwinkel), den Hall-Sensor-Pegel und den Tastenstatus vom ersten verkabelten VerseGrip.
Was Sie lernen werden:
- Lesen
quaternionAusrichtung vom staatlichen Rahmen - Umwandlung eines Quaternions in Z-X-Y-Eulerwinkel in Grad (+X nach rechts, +Y nach vorne, +Z nach oben)
- Verwendung von
probe_orientationals eigenständiger Keepalive-Beobachter - Das Handshake-Muster „Nur erste Nachricht“ (wie in Tutorial 01)
Arbeitsablauf
- Öffne einen WebSocket zu
ws://localhost:10001und auf den ersten Status-Frame warten. - Wähle den ersten kabelgebundenen VerseGrip aus
device_idaus demverse_gripArray. - Erstellen Sie eine Anfrage mit dem Sitzungsprofil und pro Gerät
probe_orientationkeepalive (ein Befehl für leere Objekte, der den Fluss der Griffausrichtung in Status-Frames aufrechterhält). - Sende die Anfrage und entferne anschließend das
sessionFeld – es handelt sich um einen einmaligen Handshake. - Wandle in jedem nachfolgenden Frame das Quaternion in Eulerwinkel um und gib die gedrosselte Telemetrie aus. Sende das Keepalive bei jedem Tick erneut.
Parameter
| Name | Standard | Zweck |
|---|---|---|
URI | ws://localhost:10001 | WebSocket-URL des Simulationskanals |
PRINT_EVERY_MS | 100 | Drosselung der Konsolenausgabe |
| Name des Sitzungsprofils | co.haply.inverse.tutorials:print-verse-grip | Identifiziert diese Simulation im Haply |
Die Umrechnung erfolgt im Anwendungsbezugssystem nach dem intrinsischen Z-X-Y-Schema (Gieren → Nicken → Rollen) +X right, +Y forward, +Z up. Nicht Verwendung glm::eulerAngles — Es folgt einer anderen Konvention und wird hier falsch angezeigt. Alle drei Sprachvarianten verwenden dieselbe mathematische Formel; die Formel findest du im Quellcode.
probe_orientation tatsächlich benötigt wirdprobe_orientation ist nur dann sinnvoll, wenn deine Sitzung nicht Senden Sie einen beliebigen Befehl an einen Inverse3. Sobald Sie einen Befehl an einen Inverse3 senden Inverse3 Kraft, Position, Drehmoment ...), überträgt der Dienst automatisch die Ausrichtung des gekoppelten VerseGrip in jedem Zustandsframe – ganz ohne Sensor. Verwenden Sie probe_orientation nur für eigenständige Tools zur Griffüberwachung wie in diesem Tutorial.
Statusfelder gelesen
Von data.verse_grip[0].state:
orientation—quaternion(w, x, y, z)hall— Ganzzahliger Messwert des Hall-Sensorsbutton— boolesch
Senden / Empfangen
Die WebSocket-Schleife: einen Status-Frame empfangen, den Handshake aufbauen und zurücksenden + probe_orientation Keepalive. Die erste ausgehende Nachricht enthält das Sitzungsprofil; jeder nachfolgende Frame enthält nur das Keepalive.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Einzelne asynchrone Schleife — recv() → Build-Befehl → send() → Wiederholen.
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 den WebSocket in seinem E/A-Thread – die Frame-spezifische Struktur befindet sich in ws.onmessage. Der Hauptthread bleibt bei ENTER hängen.
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-Callback-Modell. Typisierte Strukturen ersetzen nlohmann::json — probe_orientation_cmd ist eine leere Struktur (Glaze schreibt sie als {}). std::optional<session_cmd> übernimmt den einmaligen Handshake: .reset() Nach dem ersten Senden wird es in der nachfolgenden JSON-Ausgabe nicht mehr angezeigt.
// 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
Quelle: Python · C++ · C++ Glaze
Siehe auch: Typen (Quaternionen) · Steuerbefehle (probe_orientation) · WebSocket-Protokoll · Tutorial 03 (Wireless VG)