03. Drahtlose VerseGrip-Ausgabe
Das Muster entspricht dem aus Tutorial 02, ist jedoch für den kabellosen VerseGrip gedacht – es werden die Tasten (A/B/C) und der Akkustand im Streaming-Modus hinzugefügt.
Lerninhalte:
- Feld „Nur-Funk-Status“ lesen:
buttons.{a,b,c},battery_level,hall - Als Keepalive für unabhängige Beobachter
probe_orientationVerwendung - Ein Handshake-Muster, das – wie in Tutorial 02– nur in der ersten Nachricht ausgeführt wird
Arbeitsablauf
ws://localhost:10001Es wird eine WebSocket-Verbindung geöffnet und auf den ersten Status-Frame gewartet.wireless_verse_gripDer erste kabellose VerseGrip in der Reihedevice_idholt.- Sitzungsprofilund nach Gerät
probe_orientationKonfigurieren Sie Anfragen, einschließlich Keepalive. - Nachdem Sie die Anfrage gesendet haben
sessionDas Feld wird entfernt – da es sich um einen einmaligen Handshake handelt. - Anschließend werden in jedem Frame die Quaternionenin Euler-Winkel umgewandelt, und die Telemetriedaten – einschließlich des Tastenstatus und des verbleibenden Akkustands – werden in festgelegten Intervallen ausgegeben. Bei jedem Tick wird ein Keepalive-Signal erneut gesendet.
Parameter
| Name | Standardwert | Verwendungszweck |
|---|---|---|
URI | ws://localhost:10001 | WebSocket-URL des Simulationskanals |
PRINT_EVERY_MS | 100 | Intervall für die Begrenzung der Konsolenausgabe |
| Name des Sitzungsprofils | co.haply.inverse.tutorials:print-wireless-verse-grip | Haply Diese Simulation im Hub identifizieren |
Die Transformation erfolgt im Koordinatensystem der Anwendung +X right, +Y forward, +Z upDabei wird die inhärente Reihenfolge Z-X-Y (Gieren → Nicken → Rollen) eingehalten. glm::eulerAnglesBitte verwenden Sie dies nicht — Da diese Funktion einer anderen Konvention folgt, wird hier ein falscher Wert ausgelesen. Alle drei Sprachvarianten implementieren dieselbe Formel; die Formel entnehmen Sie bitte dem Quellcode.
probe_orientationFalls dies tatsächlich erforderlich istprobe_orientationDies ist nur dann sinnvoll, wenn in dieser Sitzung keine Befehle an Inverse3 gesendet werden. Sobald Befehle (Kraft, Position, Drehmoment usw.) an Inverse3 gesendet werden, überträgt der Dienst automatisch die zu jedem Status-Frame gehörenden Orientierungswerte von VerseGrip, sodass keine Sonde erforderlich ist. probe_orientationVerwenden Sie dies bitte ausschließlich in eigenständigen Tools zur Griffüberwachung, wie in diesem Tutorial beschrieben.
Feld „Lesestatus“
data.wireless_verse_grip[0].stateDer dort abgelesene Wert:
orientation—quaternion(w, x, y, z)hall— Ganzzahlige Hall-Sensor-Wertebuttons.a,buttons.b,buttons.c— Booleanbattery_level— Gleitkommatyp (0,0 – 1,0)transform.rotation— Arbeitsbereich drehen (quaternion). Da für Richtungsangaben weder Position noch Maßstab gelten, werden sie überhaupt nicht übertragen.
transform.rotation Der Wert ist ein identischer Quaternion {w:1,x:0,y:0,z:0}In diesem Fall wird es weggelassen. Geben Sie beim Lesen einen Standardwert an. Wenn Sie es immer erhalten möchten, serialization/explicit_fieldsAktivieren Sie dies.
Senden / Empfangen
Tutorial 02hat denselben Aufbau, lediglich die Anordnung der Bauteile wireless_verse_gripwird zu. Die WebSocket-Schleife empfängt einen Status-Frame und führt den Handshake durch sowie probe_orientation Es wird erneut ein Keepalive gesendet. Die erste gesendete Nachricht enthält das Sitzungsprofil, alle nachfolgenden Frames enthalten nur noch das Keepalive.
- 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 wird eine typisierte Struktur verwendet. Zur Information: wvg_statevon buttons damit die Felder nicht verdeckt werden button_statehat diesen Namen ausdrücklich verwendet (buttons (oder). Da Glaze Felder anhand ihrer Namen zuordnet, können die Namen der Strukturtypen 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-Variante unterstützt dieselben zwei Flags wie in Tutorial 01:
--full— Anstelle einer einzeiligen Zusammenfassung wird die JSON-Rohdaten-Payload übersichtlich ausgegeben.--query-config— bei jedem Sende-Ticksession.force_render_full_statedurch erneutes Einfügen, sodass nicht jeder Frame die gesamten Statusänderungen enthält, sondernconfigStellen Sie sicher, dass der Block enthalten ist.
--query-configist config Der Block ist zusätzlich zu den allgemeinen Konfigurationsfeldern WVG-spezifisch sub_type Feld (stylus / prototype / ruko / kingfisher(in Version 3.5 hinzugefügt) enthält, weshalb er besonders für den kabellosen VerseGrip nützlich ist. python 03-haply-inverse-print-wireless-verse-grip.py --full --query-configWenn man das ausführt, werden zusammen mit anderen Konfigurationswerten config.sub_type Sie können sehen, dass die Werte in Echtzeit aktualisiert werden.
Die C++-Variante macht dieses Flag nicht sichtbar.
Auch Tutorial 03 wird zusammen mit dem SDK lokal installiert. Im Installationsverzeichnis des Dienstes unter tutorials/03-haply-inverse-print-wireless-verse-grip/ Bitte überprüfen Sie den Ordner.
Quelle: Python · C++ · C++ Glaze
Verwandte Dokumente: Tutorial 02 (VG mit Kabel) · Typ (Quaternion) · Steuerbefehl (probe_orientation) · WebSocket-Protokoll