Zum Hauptinhalt springen

05. Positionssteuerung

Steuert den Cursor von Inverse3 über set_cursor_position zu einer Zielposition. Das Interaktionsmodell unterscheidet sich je nach Programmiersprache — C++ verwendet einmalige zufällige Zielpositionen, Python verwendet eine kontinuierliche, tastaturgesteuerte Bewegung.

Was Sie lernen:

  • Verwendung von set_cursor_position für die Steuerung im Positionsmodus
  • Zwei unterschiedliche Interaktionsmodelle für denselben zugrunde liegenden Befehl
  • Begrenzung des Ziels auf eine Arbeitsraumkugel — Minverse verwendet einen kleineren Radius als Inverse3
  • Festlegen einer Arbeitsraum-Voreinstellung, damit der Ursprung in der Mitte des Arbeitsraums liegt

Ablauf​

C++ (Modell mit zufälligen Zielen)​

  1. Starten Sie einen Hintergrund-Eingabethread, der zeilengepufferte Tastenanschläge (n, +, -, q) von stdin liest.
  2. Öffnen Sie WebSocket. Registrieren Sie beim ersten Zustandsframe das Sitzungsprofil und setzen Sie configure.preset: arm_front_centered. Erzeugen Sie das erste zufällige Ziel innerhalb einer Kugel (Rejection Sampling, Radius 0.08 m).
  3. Senden Sie bei jedem Tick einen set_cursor_position-Befehl an das aktuelle Ziel. Der Cursor folgt ihm gleichmäßig — der Dienst begrenzt die Rate und interpoliert.
  4. Wenn der Benutzer n + ENTER eingibt, erzeugt der Eingabethread ein neues zufälliges Ziel. Mit + / - passen Sie die Geschwindigkeit an; q beendet das Programm.

Python (Modell mit Gedrückthalten zum Bewegen)​

  1. Öffnen Sie WebSocket. Prüfen Sie beim ersten Zustandsframe status.calibrated — fordern Sie den Benutzer auf, falls das Gerät noch nicht kalibriert ist.
  2. Lesen Sie config.type aus, um den Radius des Arbeitsraums zu bestimmen (minverse = 0.04 m, alle anderen Werte = 0.10 m).
  3. Registrieren Sie das Sitzungsprofil und setzen Sie configure.preset: arm_front_centered.
  4. Bei jedem Tick: Tastaturstatus abfragen (W/A/S/D/Q/E), die Zielposition um SPEED entlang jeder gedrückten Achse aktualisieren, auf die Arbeitsraumkugel begrenzen und set_cursor_position senden. R setzt das Ziel auf den Ursprung zurück.

Parameter​

NameStandardwert (C++)Standardwert (Python)Zweck
workspace_radius / RADIUS_INVERSE30.08 m0.10 m (Inverse3) / 0.04 m (Minverse)Radius der Zielkugel
speed_step / SPEED0.01 / Tastendruck0.00005 m / TickSchrittweite pro Interaktion
PRINT_EVERY_MS—100Telemetrie-Drosselung (Python)
Sitzungsprofilco.haply.inverse.tutorials:position-controlgleichIdentifiziert in Haply Hub
Kalibrierungsprüfung (Python)

Die Variante Python prüft status.calibrated beim ersten Zustandsframe und fordert den Benutzer auf, falls das Gerät nicht kalibriert ist. Die Variante C++ setzt voraus, dass die Kalibrierung bereits abgeschlossen ist.

Gelesene Statusfelder​

  • data.inverse3[0].device_id — zum Erstellen des Befehls
  • data.inverse3[0].state.cursor_position — Telemetrie
  • (Python, nur im ersten Frame) data.inverse3[0].config.type — wählt zwischen dem Radius von Inverse3 und dem Minverse-Radius
  • (Python, nur im ersten Frame) data.inverse3[0].status.calibrated — fordert den Benutzer auf, falls der Wert false ist

Senden / Empfangen​

Kommunikationsablauf​

  • C++ führt einen Hintergrund-stdin-Thread aus, der std::atomic<float>-Ziele schreibt; der WebSocket-Thread liest diese bei jedem Tick. Bei n + ENTER erzeugt der Eingabethread ein neues zufälliges Ziel; bei q werden beide Threads beendet.
  • Python ist single-threaded und asynchron — die Schleife von WebSocket fragt bei jedem Tick den Tastaturstatus ab und aktualisiert position direkt.

Die Nutzdaten der Inverse-API sind identisch: Der erste Tick enthält das Sitzungsprofil + configure.preset, die folgenden Ticks enthalten nur set_cursor_position.

Einzelne asynchrone Schleife. Die Tastaturabfrage (handle_keys) läuft bei jedem Tick inline — ohne Threads. config.type und status.calibrated werden einmalig aus dem ersten Zustandsframe gelesen.

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"]
radius = get_workspace_radius(data["inverse3"][0].get("config", {}))
# Handshake: profile + preset (one-shot)
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": {"preset": {"preset": "arm_front_centered"}},
}],
}
else:
# Per tick: update position from keyboard (classic polling, not shown), send command
position = handle_keys(position, radius)
request_msg = {
"inverse3": [{
"device_id": device_id,
"commands": {"set_cursor_position": {"position": position}},
}],
}

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

Tutorial 05 wird zusätzlich lokal mit dem SDK installiert — Sie finden es in tutorials/05-haply-inverse-position-control/ im Installationsverzeichnis des Dienstes.

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

Siehe auch: Steuerbefehle (set_cursor_position) · Montage & Arbeitsraum (Voreinstellungen) · Typen (vec3) · Tutorial 06 (Kombiniert)