Aller au contenu principal

05. Contrôle de position

Déplace le curseur Inverse3 vers une position cible au moyen de set_cursor_position. Le modèle d'interaction varie selon le langage — C++ utilise des cibles aléatoires à usage unique, Python utilise un déplacement continu commandé par le clavier.

Ce que vous allez apprendre :

  • Utiliser set_cursor_position pour le contrôle en mode position
  • Deux modèles d'interaction différents pour la même commande sous-jacente
  • Limiter la cible à une sphère de l'espace de travail — Minverse utilise un rayon plus petit que Inverse3
  • Définir un préréglage d'espace de travail afin que l'origine se trouve au centre de l'espace de travail

Flux de travail​

C++ (modèle à cible aléatoire)​

  1. Démarrez un fil d'exécution en arrière-plan qui lit les frappes de touches, tamponnées par ligne (n, +, -, q), depuis l'entrée standard (stdin).
  2. Ouvrez WebSocket. À la première trame d'état, enregistrez le profil de session et définissez configure.preset: arm_front_centered. Générez la première cible aléatoire à l'intérieur d'une sphère (échantillonnage par rejet, rayon de 0.08 m).
  3. À chaque cycle, envoyez une commande set_cursor_position à la cible actuelle. Le curseur suit la cible en douceur — le service limite le débit et interpole les positions.
  4. Lorsque l'utilisateur tape n puis ENTRÉE, le fil d'entrée génère une nouvelle cible aléatoire. + / - ajustent la vitesse ; q quitte.

Python (modèle de déplacement maintenu)​

  1. Ouvrez WebSocket. À la première trame d'état, vérifiez status.calibrated — affichez une invite à l'utilisateur si l'appareil n'est pas encore calibré.
  2. Lisez config.type pour choisir le rayon de l'espace de travail (minverse = 0.04 m, toute autre valeur = 0.10 m).
  3. Enregistrez le profil de session et définissez configure.preset: arm_front_centered.
  4. À chaque cycle : interrogez l'état du clavier (W/A/S/D/Q/E), déplacez la cible de SPEED le long de chaque axe enfoncé, limitez-la à la sphère de l'espace de travail, puis envoyez set_cursor_position. R réinitialise la cible à l'origine.

Paramètres​

NomValeur par défaut (C++)Valeur par défaut (Python)Rôle
workspace_radius / RADIUS_INVERSE30.08 m0.10 m (Inverse3) / 0.04 m (Minverse)Rayon de la sphère cible
speed_step / SPEED0.01 / appui0.00005 m / cyclePas par interaction
PRINT_EVERY_MS—100Limitation de la télémétrie (Python)
Profil de sessionco.haply.inverse.tutorials:position-controlidentiquePermet l'identification dans Haply Hub
Vérification de la calibration (Python)

La variante Python vérifie status.calibrated dès la première trame d'état et affiche une invite à l'utilisateur si l'appareil n'est pas calibré. La variante C++ suppose que la calibration est déjà effectuée.

Champs d'état lus​

  • data.inverse3[0].device_id — pour construire la commande
  • data.inverse3[0].state.cursor_position — télémétrie
  • (Python, première trame seulement) data.inverse3[0].config.type — détermine le rayon à utiliser : Inverse3 ou Minverse
  • (Python, première trame seulement) data.inverse3[0].status.calibrated — affiche une invite à l'utilisateur si la valeur est fausse

Envoi et réception​

Déroulement des communications​

  • C++ exécute un fil d'arrière-plan qui lit l'entrée standard et écrit les cibles std::atomic<float> ; le fil WebSocket les lit à chaque cycle. Sur n puis ENTRÉE, le fil d'entrée génère une nouvelle cible aléatoire ; sur q, les deux fils s'arrêtent.
  • Python est asynchrone et monofil — la boucle WebSocket interroge l'état du clavier à chaque cycle et met à jour position directement.

Les charges utiles de l'Inverse-API sont identiques : le premier cycle transporte le profil de session et configure.preset, et les cycles suivants ne transportent que set_cursor_position.

Boucle asynchrone unique. L'interrogation du clavier (handle_keys) s'exécute directement dans la boucle à chaque cycle — sans fil d'exécution. config.type et status.calibrated sont lus une seule fois, à partir de la première trame d'état.

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))
Inclus avec l'installateur du SDK

Le tutoriel 05 est également installé localement avec le SDK — consultez tutorials/05-haply-inverse-position-control/ dans le répertoire d'installation du service.

Source : Python · C++ · C++ Glaze

Voir aussi : Commandes de contrôle (set_cursor_position) · Montage et espace de travail (préréglages) · Types (vec3) · Tutoriel 06 (Combiné)