2e numéro de VerseGrip
Transmet l'orientation (quaternion + angles d'Euler Z-X-Y), la valeur du capteur Hall et l'état des boutons du premier VerseGrip filaire.
Ce que vous allez apprendre :
- Mourir
quaternion- Lire l'orientation à partir du cadre d'état - Convertir un quaternion en angles d'Euler Z-X-Y exprimés en degrés (+X à droite, +Y à l'avant, +Z en haut)
probe_orientationUtiliser Keepalive pour un observateur indépendant- Le modèle « handshake », qui n'est utilisé que dans le premier message (comme dans le tutoriel 01)
Déroulement
- Établissez une connexion WebSocket vers
ws://localhost:10001et attendez la première trame d'état. - Sélectionnez la
device_idle premier VerseGrip filaire issu de la gamme Arrayverse_grip. - Créez une demande à l'aide du Fiche de séance et un paramètre spécifique à l'appareil
probe_orientation-Keepalive (une commande avec un objet vide qui garantit que l'orientation de la poignée est transmise en continu dans les trames d'état). - Envoyez la demande, puis désélectionnez la case
session— Il s'agit d'une poignée de main unique. - À chaque image suivante, convertissez le quaternion en angles d'Euler et transmettez les données de télémétrie réduites. Renvoyez le message « Keepalive » à chaque cycle.
Paramètres
| Nom | Valeur par défaut | Objet |
|---|---|---|
URI | ws://localhost:10001 | URL du canal de simulation (WebSocket) |
PRINT_EVERY_MS | 100 | Limitation de la puissance de sortie de la console |
| Nom du profil de session | co.haply.inverse.tutorials:print-verse-grip | Marquez cette simulation dans la plateforme « Haply Hub» |
La conversion correspond à une rotation intrinsèque Z-X-Y (lacet → tangage → roulis) dans le système de coordonnées de l'application. +X right, +Y forward, +Z up. N'utilisez pas glm::eulerAngles — Elle suit une autre convention et renvoie ici des valeurs erronées. Les trois variantes linguistiques mettent en œuvre le même calcul mathématique ; vous trouverez la formule dans les sources.
probe_orientation qui est réellement nécessaireprobe_orientation n'a de sens que si votre session aucun Envoyez une commande à Inverse3 . Dès que vous envoyez une commande à Inverse3 (puissance, position, couple, etc.), le service transmet automatiquement l'orientation du VerseGrip couplé dans chaque trame d'état — il n'est alors pas nécessaire d'effectuer un échantillonnage. Utilisez probe_orientation uniquement pour les outils autonomes de surveillance de la prise, comme celui présenté dans ce tutoriel.
Champs d'état lus
Aus data.verse_grip[0].state:
orientation—quaternion(w, x, y, z)hall— valeur entière du capteur à effet Hallbutton— valeur booléennetransform.rotation— Rotation de l'espace de travail (quaternion); La position et la mise à l'échelle ne s'appliquent pas à un appareil qui ne détecte que l'orientation et ne sont jamais fournies
transform.rotation est omis s'il correspond au quaternion d'identité {w:1,x:0,y:0,z:0} correspond à. Définissez une valeur par défaut lors de la lecture. Activez serialization/explicit_fields, afin qu'il soit toujours transmis.
Envoyer / Recevoir
La boucle WebSocket reçoit une trame d'état, établit la poignée de main et le probe_orientation- Keepalive et les renvoie. Le premier message sortant contient le profil de session ; chaque trame suivante ne contient que le Keepalive.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Une boucle asynchrone isolée — recv() → Créer une commande → send() → répéter.
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["verse_grip"][0]["device_id"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-verse-grip"}}},
"verse_grip": [{
"device_id": device_id,
"commands": {"probe_orientation": {}} # empty — keepalive
}]
}
await websocket.send(json.dumps(request_msg))
request_msg.pop("session", None) # one-shot handshake
libhv gère WebSocket sur son thread d'E/S — le traitement par image s'effectue dans ws.onmessage au lieu de cela. Le thread principal reste bloqué jusqu'à ce que l'on appuie sur la touche ENTRÉE.
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["verse_grip"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-versegrip"}}}}}}},
{"verse_grip", json::array({
{{"device_id", device_id},
{"commands", {{"probe_orientation", json::object()}}}},
})},
};
}
ws.send(request_msg.dump());
request_msg.erase("session"); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
Le même modèle de callback libhv. Remplacer les structures typées nlohmann::json — probe_orientation_cmd est une structure vide (Glaze l'écrit sous la forme {}). std::optional<session_cmd> se charge de la procédure d'établissement de connexion unique : .reset() après le premier envoi, il ne l'inclut plus dans les suivants au format JSON.
// Struct models
struct quat { float w{1.0f}, x{}, y{}, z{}; };
struct grip_state { quat orientation{}; bool button{}; uint8_t hall{}; };
struct grip_device { std::string device_id; grip_state state; };
struct devices_message { std::vector<grip_device> verse_grip; };
struct probe_orientation_cmd {}; // empty object on the wire
struct commands_message {
std::optional<session_cmd> session; // one-shot — omitted when unset
std::vector<device_commands> verse_grip;
};
// Send / receive
ws.onmessage = [&](const std::string &msg) {
devices_message data{};
if (glz::read<glz_settings>(data, msg)) return;
if (first_message) {
first_message = false;
out_cmds.session = session_cmd{ /* profile = print-versegrip */ };
device_commands dc{ .device_id = data.verse_grip[0].device_id };
dc.commands.probe_orientation = probe_orientation_cmd{};
out_cmds.verse_grip.push_back(std::move(dc));
}
std::string out_json;
(void)glz::write_json(out_cmds, out_json);
ws.send(out_json);
out_cmds.session.reset(); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
Options de ligne de commande (Python)
La variante Python accepte les deux mêmes indicateurs que le tutoriel 01:
--full— affiche la charge utile JSON brute mise en forme, au lieu du résumé sur une seule ligne.--query-config— ajoute à chaque mesure sortantesession.force_render_full_stateajoute à nouveau, de sorte que chaque image contienne leconfig-Bloc (type d'appareil, micrologiciel, transformation de montage…) et pas seulement le changement d'état. Utile pour le débogage des modifications de paramètres en cours de fonctionnement.
Ces deux options peuvent être combinées. Les versions C++ ne proposent pas ces options.
Le tutoriel 02 est également installé localement avec le SDK — vous le trouverez sous tutorials/02-haply-inverse-print-verse-grip/ dans le répertoire d'installation du service.
Source : Python · C++ · C++ Glaze
Voir aussi : Types (quaternion) · Commandes de contrôle (probe_orientation) · Protocole WebSocket · Tutoriel 03 (VG sans fil)