Aller au contenu principal

05. Contrôle de position

Déplace le curseur « Inverse3 » vers une position cible à l'aide de set_cursor_position. Le modèle d'interaction varie selon la langue — Le C++ utilise des cibles aléatoires à usage unique, tandis que Python utilise un déplacement continu commandé par le clavier.

Ce que vous allez apprendre :

  • Utiliser set_cursor_position pour la commande 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. Lancez un thread d'exécution en arrière-plan qui lit les frappes au clavier, enregistrées ligne par ligne (n, +, -, q), depuis l'entrée standard (stdin).
  2. Ouvrez WebSocket. Dès 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 vers 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. + / - réglent la vitesse ; q quitte.

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

  1. Ouvrez WebSocket. À la première trame d'état, vérifiez status.calibrated — affichez un message à l'intention de 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 : vérifiez 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 à sa valeur initiale.

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 / soutien0.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'authentification sur Haply Hub
Vérification de l'étalonnage (Python)

La variante Python vérifie status.calibrated dès la première trame d'état et affiche un message à l'utilisateur si l'appareil n'est pas calibré. La variante C++ part du principe que le calibrage a déjà été effectué.

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 uniquement) data.inverse3[0].config.type — détermine le rayon à utiliser : Inverse3 ou Minverse
  • (Python, première trame uniquement) data.inverse3[0].status.calibrated — affiche un message à l'utilisateur si la valeur est fausse

Envoi et réception​

Déroulement des interventions​

  • C++ lance un processus d'arrière-plan qui lit l'entrée standard et écrit les cibles std::atomic<float> ; le thread WebSocket les lit à chaque itération. 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 le programme d'installation 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 n° 6 (Combiné)