Aller au contenu principal

05. Commande de position

Déplace le curseur depuis Inverse3 vers set_cursor_position vers une position cible. Le modèle d'interaction varie en fonction du langage de programmation — Le C++ utilise des positions cibles aléatoires uniques, tandis que Python utilise un déplacement continu piloté par le clavier.

Ce que vous apprendrez :

  • Utilisation de set_cursor_position pour la commande en mode position
  • Deux modèles d'interaction différents pour une même commande sous-jacente
  • Limitation de l'objectif à une sphère de travail — Minverse utilise un rayon plus petit que Inverse3
  • Définir un paramètre par défaut pour l'espace de travail afin que l'origine se trouve au centre de celui-ci

Déroulement​

C++ (modèle à objectifs aléatoires)​

  1. Lancez un thread de saisie en arrière-plan qui gère les frappes avec mise en mémoire tampon par ligne (n, +, -, q) lu à partir de stdin.
  2. Ouvrez WebSocket. Lors de la première trame d'état, enregistrez le Fiche de séance et placez-le 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 0,08 m).
  3. Envoyez un message à chaque tick set_cursor_position-Commande vers la cible actuelle. Le curseur la suit de manière régulière — le service limite la vitesse et effectue une interpolation.
  4. Si l'utilisateur n Lorsque l'utilisateur appuie sur la touche + ENTER, le thread de saisie génère une nouvelle cible aléatoire. Avec + / - réglez la vitesse ; q ferme le programme.

Python (modèle à déplacer en maintenant le bouton enfoncé)​

  1. Ouvrez WebSocket. Vérifiez la première trame d'état status.calibrated — demandez à l'utilisateur de procéder à l'étalonnage si l'appareil n'est pas encore étalonné.
  2. Lisez config.type pour déterminer le rayon de l'espace de travail (minverse = 0,04 m, toutes les autres valeurs = 0,10 m).
  3. Enregistrez le Fiche de séance et placez-le configure.preset: arm_front_centered.
  4. À chaque tick : interroger l'état du clavier (W/A/S/D/Q/E), la position cible de SPEED actualiser le long de chaque axe enfoncé, limiter à la sphère de l'espace de travail et set_cursor_position envoyer. R réinitialise le point de départ.

Paramètres​

NomValeur par défaut (C++)Valeur par défaut (Python)Objet
workspace_radius / RADIUS_INVERSE30.08 m0.10 m (Inverse3) / 0.04 m (Minverse)Rayon de la sphère cible
speed_step / SPEED0.01 / pression sur une touche0.00005 m / TicIncrément par interaction
PRINT_EVERY_MS—100Limitation de la télémétrie (Python)
Fiche de séanceco.haply.inverse.tutorials:position-controlgleichIdentifié dans le Hub « Haply »
Test d'étalonnage (Python)

La variante Python vérifie status.calibrated lors de la première trame d'état et invite l'utilisateur à effectuer l'étalonnage si l'appareil n'est pas étalonné. La version C++ suppose que l'étalonnage est déjà terminé.

Champs d'état lus​

  • data.inverse3[0].device_id — pour créer la commande
  • data.inverse3[0].state.cursor_position — Télémétrie
  • (Python, uniquement dans la première image) data.inverse3[0].config.type — permet de choisir entre le rayon de Inverse3 et celui de Minverse
  • (Python, uniquement dans la première image) data.inverse3[0].status.calibrated — invite l'utilisateur à agir si la valeur est « false »

Envoyer / Recevoir​

Déroulement de la communication​

  • C++ exécute un thread stdin en arrière-plan qui std::atomic<float>- Il écrit des cibles ; le thread WebSocket les lit à chaque tick. Lors de n + ENTER : le thread de saisie génère une nouvelle cible aléatoire ; lorsque q les deux threads seront alors arrêtés.
  • Python Il s'agit d'un processus mono-thread et asynchrone : la boucle WebSocket interroge l'état du clavier à chaque tick et met à jour position directement.

Les données utiles de l'API Inverse sont identiques : le premier tick contient le profil de session + configure.preset, les ticks suivants ne contiennent que set_cursor_position.

Boucle asynchrone unique. L'interrogation du clavier (handle_keys) s'exécute en ligne à chaque tick — sans threads. config.type et status.calibrated sont lues 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 dans le programme d'installation du SDK

Le tutoriel 05 est également installé localement avec le SDK — vous le trouverez dans 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 (paramètres par défaut) · Types (vec3) · Tutoriel n° 6 (Combiné)