Zum Hauptinhalt springen

07. Basis & Mount-Playground

Steuert interaktiv die Mount-Transformation des Geräts und zeigt, wie configure.basis, configure.preset und configure.mount auf Protokollebene zusammenwirken. Es wird ein fester horizontaler Boden beibehalten, damit der interaktive Fokus auf den configure-Befehlen bleibt.

Was Sie lernen werden:

  • Festlegen einer Basis-Permutation ("XZY" → Anwendungsrahmen mit Y-Achse nach oben)
  • Auswahl eines Presets (arm_front) und Nachvollziehen, was es konfiguriert
  • Überschreiben des Mounts zur Laufzeit mit einem Rotations-Quaternion (per Tastatur gesteuert)
  • Die Regel der gegenseitigen Ausschließlichkeit: mount und preset können nicht gemeinsam im selben Block der Gerätekonfiguration vorkommen
  • Einmalige configure-Semantik: Jeder Tastendruck sendet genau eine configure-Nachricht
  • (C++ Glaze) Modellierung sich gegenseitig ausschließender Felder mit std::optional

Ablauf​

  1. Bei der ersten Nachricht: Senden Sie das Session-Profil, configure.basis: "XZY" und configure.preset: arm_front. Beginnen Sie, für den festen Boden set_cursor_force zu senden.
  2. Bei jedem Tick: Cursor-Y auslesen, force_y = max(0, (floor_pos - y) * stiffness) berechnen und senden.
  3. Wenn der Benutzer eine Mount-Rotationstaste drückt, setzen Sie pending_configure = true.
  4. Beim nächsten Tick erstellen Sie einen configure.mount-Block mit einer Transformation, deren rotation ein Einheits-Quaternion ist (Komposition Z-dann-X aus dem aktuellen Pitch- und Yaw-Wert). Lassen Sie preset weg – die beiden schließen sich auf Protokollebene gegenseitig aus.
  5. Die Reset-Taste (R) hebt das Überschreiben auf; die nächste configure-Anweisung greift wieder auf preset zurück.

Parameter​

NameStandardZweck
BASIS"XZY"Achsenpermutation — Anwendungsrahmen mit Y-Achse nach oben
DEVICE_PRESET / DEVICE_CONFIG_PRESET"arm_front"Benanntes Preset — Ursprung an der Gerätebasis
FLOOR_POS_Y0.0 mFeste Bodenebene (Anwendungs-Y)
STIFFNESS1000 N/mFederkonstante des Bodens
MOUNT_STEP_DEG10°Rotation pro Tastendruck
PRINT_EVERY_MS200Drosselung der Telemetrie

Steuerung​

TasteAktion
W / SMount um ±10° um die +X-Achse des Geräts drehen (Pitch)
A / DMount um ±10° um die +Z-Achse des Geräts drehen (Yaw)
RMount zurücksetzen – zurück zum Preset
HSteuerung anzeigen
QBeenden
mount und preset schließen sich gegenseitig aus

Der Dienst lehnt einen Block der Gerätekonfiguration ab, der beide enthält. Sobald der Benutzer den Mount überschreibt, lässt das Tutorial preset in jeder weiteren configure-Anweisung weg. Durch Drücken von R wird preset bei der nächsten configure-Anweisung wieder aktiviert und mount verworfen.

Unterschiedliche Eingabemodelle

Die Varianten C++ lesen zeilenbasierte Eingaben in einem Hintergrund-stdin-Thread (nach jedem Buchstaben ENTER drücken). Python verwendet das Paket keyboard für die Echtzeit-Tastenabfrage in der asynchronen Hauptschleife – ENTER ist nicht nötig. Gleiche Tasten, gleiche Befehle.

Gelesene Zustandsfelder​

Aus data.inverse3[i].state:

  • cursor_position.y — vec3, wird zur Berechnung der Bodendurchdringung verwendet
  • current_cursor_force — wird für die Telemetrie gemeldet

Senden / Empfangen​

Die Struktur der Nutzlast ist in allen Varianten gleich; interessant sind die Unterschiede darin, wie jede Variante die sich gegenseitig ausschließenden Zweige mount / preset aufbaut und wie der Eingabe-Thread den Thread WebSocket signalisiert.

Einzelne asynchrone Schleife mit Echtzeit-Tastenabfrage über das Paket keyboard. pending_configure ist ein globales Flag, das von den Tastaturhandlern gesetzt und jedes Mal zurückgesetzt wird, wenn ein configure-Block gesendet wird.

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["inverse3"][0]["device_id"]
# Handshake: profile + basis + preset
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": build_configure_block(first_handshake=True),
# -> {"basis": {"permutation": "XZY"},
# "preset": {"preset": "arm_front"}}
}],
}
else:
handle_key_inputs() # may set pending_configure = True (classic, not shown)

y = data["inverse3"][0]["state"]["cursor_position"]["y"]
force_y = 0.0 if y > FLOOR_POS_Y else (FLOOR_POS_Y - y) * STIFFNESS

entry = {
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": force_y, "z": 0.0}}},
}
if pending_configure:
entry["configure"] = build_configure_block(first_handshake=False)
# -> {"mount": {...}} OR {"preset": {...}} (never both)
pending_configure = False

request_msg = {"inverse3": [entry]}

await websocket.send(json.dumps(request_msg))
Im SDK-Installer enthalten

Tutorial 07 ist auch lokal mit dem SDK installiert – zu finden unter tutorials/07-haply-inverse-basis-and-mount/ im Installationsverzeichnis des Dienstes.

Quelle: Python · C++ · C++ Glaze

Verwandt: Basis-Permutation · Mount & Workspace · Gerätekonfiguration · Steuerbefehle (set_cursor_force) · Typen (transform) · Tutorial 04 (Hello Floor)