03. Ausgabe des Wireless VerseGrip
Es handelt sich um dasselbe Muster wie in Tutorial 02, allerdings für den Wireless VerseGrip. Fügen Sie den Status „Wird gestreamt“ sowie die Tasten (A/B/C) und den Akkustand hinzu.
Was Sie in diesem Tutorial lernen werden:
- Auslesen der gerätespezifischen Statusfelder bei drahtlosen Verbindungen:
buttons.{a,b,c}、battery_level、hall - Als Keep-Alive für eigenständige Beobachter
probe_orientationverwenden - Ein Muster, bei dem der Handshake – genau wie in Tutorial 02 – ausschließlich anhand der ersten Nachricht erfolgt
Workflow
ws://localhost:10001Öffnen Sie eine WebSocket-Verbindung zu und warten Sie auf den ersten Status-Frame.wireless_verse_gripAus der Reihe, der erste Wireless VerseGripdevice_idwird abgerufen.- Sitzungsprofil und für jedes Gerät
probe_orientationErstellen Sie eine Anfrage, die „Keep-Alive“ enthält. - Nachdem Sie die Anfrage abgeschickt haben,
sessionDas Feld wird gelöscht. Dies ist ein einmaliger Handshake. - In jedem folgenden Frame werdendie Quaternionenin Euler-Winkel umgewandelt und die Telemetriedaten – einschließlich des Tastenstatus und des Akkustands – mit einer bestimmten Abtastrate ausgegeben. Der Keep-Alive-Befehl wird bei jedem Tick erneut gesendet.
Parameter
| Name | Standard | Verwendungszweck |
|---|---|---|
URI | ws://localhost:10001 | URL des Simulationskanals WebSocket |
PRINT_EVERY_MS | 100 | Intervall für die Ausdünnung der Konsolenausgabe |
| Name des Sitzungsprofils | co.haply.inverse.tutorials:print-wireless-verse-grip | Haply Identifizieren Sie diese Simulation im Hub. |
Die Konvertierung erfolgt im Anwendungsframework +X right, +Y forward, +Z up Es handelt sich um die endogenen (intrinsischen) Z-X-Y-Werte (Yaw → Pitch → Roll).glm::eulerAngles Bitte nicht verwenden. Da diese Funktion anderen Konventionen folgt, ergibt sich hier ein falscher Wert. Alle drei Sprachversionen implementieren dieselbe Berechnung. Die Berechnungsformel entnehmen Sie bitte dem Quellcode.
probe_orientation falls dies tatsächlich erforderlich sein sollteprobe_orientation Das ist nützlich, wenn man Befehle aus der Sitzung an Inverse3 sendet.Gar nichts sendenDies gilt nur in bestimmten Fällen. Sobald Befehle an den „Inverse3“ (Kraft, Position, Drehmoment usw.) gesendet werden, überträgt der Dienst automatisch die Lage des gekoppelten VerseGrip in allen Status-Frames. Daher ist keine Sonde erforderlich.probe_orientation Verwenden Sie es bitte ausschließlich in eigenständigen Tools zur Griffüberwachung, wie sie in diesem Tutorial vorgestellt werden.
Feld „Lesezustand“
data.wireless_verse_grip[0].state Aus dem folgenden Text geht hervor:
orientation—quaternion(w, x, y, z)hall— Messwerte des Hall-Sensors für ganzzahlige Wertebuttons.a、buttons.b、buttons.c— Wahr-Falsch-Wertbattery_level— Gleitkommazahlen (0,0 bis 1,0)transform.rotation— Drehen des Arbeitsbereichs (quaternion). Bei Geräten, die ausschließlich die Körperhaltung erfassen, sind Position und Skalierung nicht relevant und werden daher nicht ausgegeben.
transform.rotation ist ein Einheitsquaternion {w:1,x:0,y:0,z:0} wird weggelassen, wenn es gleich ist. Bitte legen Sie beim Lesen einen Standardwert fest. Um stets Empfang zu gewährleisten, serialization/explicit_fields Bitte aktivieren Sie diese Funktion.
Senden / Empfangen
wireless_verse_grip Abgesehen davon, dass eine Geräteanordnung verwendet wird,Tutorial 02 Es handelt sich um dasselbe Format. Die WebSocket-Schleife empfängt Status-Frames, führt den Handshake durch und probe_orientation Es wird ein Keep-Alive-Paket zurückgesendet. Die erste gesendete Nachricht enthält ein Sitzungsprofil, während die nachfolgenden Frames nur Keep-Alive-Pakete enthalten.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Es handelt sich um eine einzelne 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
Es handelt sich um eine typisierte Struktur.button_state (buttons (sondern) ausdrücklich benannt. Dies bedeutet, dasswvg_state の buttons Dies dient dazu, ein „Shadowing“ der Felder zu vermeiden. Da Glaze die Zuordnung anhand der Feldnamen vornimmt, kann der Typname der Struktur frei gewählt werden.
// 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-Version akzeptiert dieselben zwei Flags wieTutorial 01:
--full— Anstelle einer einzeiligen Zusammenfassung wird die rohe JSON-Nutzlast aufbereitet und ausgegeben.--query-config— Bei jedem gesendeten Ticksession.force_render_full_statewieder einspeisen, sodass jeder Frame nicht nur die Zustandsdifferenz, sondern auchconfigStellen Sie sicher, dass es Blöcke enthält.
--query-config ist besonders nützlich beim Wireless VerseGrip.config Blöcke verfügen neben den üblichen Einstellungsfeldern über WVG-spezifische sub_type Feld (stylus / prototype / ruko / kingfisher(da es 3.5 enthält) (hinzugefügt in 3.5).python 03-haply-inverse-print-wireless-verse-grip.py --full --query-config Wenn man dies ausführt, wird es neben den anderen Einstellwerten angezeigt config.sub_type Sie können beobachten, wie diese in Echtzeit aktualisiert werden.
In der C++-Version sind diese Flags nicht verfügbar.
Tutorial 03 wird zusammen mit dem SDK auch lokal installiert. Im Installationsverzeichnis des Dienstes unter tutorials/03-haply-inverse-print-wireless-verse-grip/ Siehe dazu.
Quelle: Python · C++ · C++ Glaze
Siehe auch: Tutorial 02 (Wired VG) · Typ (Quaternion) · Steuerbefehle (probe_orientation) · WebSocket-Protokoll