03. Impression sans fil avec VerseGrip
Identique aumode du tutoriel 02, mais adapté au VerseGrip sans fil : les boutons (A/B/C) et le niveau de batterie ont été ajoutés en mode streaming.
Vous apprendrez :
- Lire les champs d'état spécifiques à l'appareil sans fil :
buttons.{a,b,c},battery_level,hall - Va
probe_orientationMécanisme de maintien en vie utilisé par un observateur indépendant - Mode de négociation de connexion avec un seul message d'ouverture, identique à celuidu tutoriel 02
Processus de travail
- Ouvrir à la page
ws://localhost:10001WebSocket, puis attendre la première trame d'état. - Depuis
wireless_verse_gripRécupérer le premier VerseGrip sans fil dans le tableaudevice_id。 - Utilisation Fichier de configuration de session Construire la requête et joindre à chaque appareil
probe_orientationDirective relative à la recherche d'une place en crèche. - Envoyer la demande, puis supprimer
sessionChamp — Il ne s'agit que d'une poignée de main ponctuelle. - À chaque image suivante, les quaternions seront convertis en angles d'Euler, et les données de télémétrie filtrées seront affichées, notamment l'état des boutons et le niveau de charge de la batterie. Une commande de maintien de connexion sera renvoyée à chaque cycle.
Paramètres
| Nom | Valeur par défaut | Utilisation |
|---|---|---|
URI | ws://localhost:10001 | URL du canal de simulation WebSocket |
PRINT_EVERY_MS | 100 | Limitation de la sortie de la console |
| Nom du profil de session | co.haply.inverse.tutorials:print-wireless-verse-grip | Identifier cette simulation dans le Hub « Haply » |
La conversion s'effectue selon l'ordre intrinsèque Z-X-Y (cap → tangage → roulis), le système de coordonnées utilisé étant +X right, +Y forward, +Z up。Veuillez ne pasUtilisation glm::eulerAngles——Il suit des conventions différentes, ce qui donne ici un résultat erroné. Les trois versions linguistiques mettent en œuvre le même calcul mathématique ; pour les formules, veuillez vous reporter au code source.
probe_orientationprobe_orientation Uniquement pendant ta sessionNonCette fonctionnalité n'est utile que lorsque vous envoyez des commandes à Inverse3 . Dès que vous transmettez des commandes (force, position, couple, etc.) à Inverse3 , le service envoie automatiquement les données d'orientation correspondantes du VerseGrip dans chaque trame d'état, sans qu'aucune détection ne soit nécessaire. À utiliser uniquement dans le cadre d'outils de surveillance de préhension autonomes, comme dans ce tutoriel. probe_orientation。
Champs d'état lus
Source : data.wireless_verse_grip[0].state:
orientation—quaternion(w, x, y, z)hall— Valeurs de lecture des capteurs à effet Hall de type entierbuttons.a,buttons.b,buttons.c— Valeurs booléennesbattery_level— Nombres à virgule flottante (0,0 – 1,0)transform.rotation— Rotation de l'espace de travail (quaternion) ; la position et le zoom ne s'appliquent pas aux appareils sans orientation, ils ne sont donc jamais affichés
Lorsque transform.rotation Équivaut à un quaternion unitaire {w:1,x:0,y:0,z:0} Dans ce cas, ce champ sera omis. Veuillez indiquer une valeur par défaut lors de la lecture. Activer serialization/explicit_fields Ce champ peut être reçu à tout moment.
Envoyer / Recevoir
etTutoriel 02La structure est identique, à la seule différence qu'on utilise wireless_verse_grip Tableau d'équipements. WebSocket reçoit en boucle des trames d'état et renvoie les informations de négociation ainsi que probe_orientation Commande de maintien de connexion ; le premier paquet contient le profil de session, tandis que chaque trame suivante ne contient que la commande de maintien de connexion.
- Python
- C++ (nlohmann)
- C++ (Glaze)
Boucle asynchrone unique.
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["wireless_verse_grip"][0]["device_id"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-wireless-verse-grip"}}},
"wireless_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
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["wireless_verse_grip"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-wireless-verse-grip"}}}}}}},
{"wireless_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
Structure à typage fort. Remarque : button_state est explicitement nommé (et non buttons), afin d'éviter tout masquage wvg_state sur buttons Champs — Glaze effectue le mappage des champs en fonction de leur nom ; par conséquent, le nom du type de la structure peut être défini librement.
// Struct models
struct quat { float w{1.0f}, x{}, y{}, z{}; };
struct button_state { bool a{}, b{}, c{}; };
struct wvg_state {
quat orientation{};
uint8_t hall{};
button_state buttons{};
float battery_level{};
};
struct wvg_device { std::string device_id; wvg_state state; };
struct devices_message { std::vector<wvg_device> wireless_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> wireless_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;
request_msg.session = session_cmd{ /* profile = print-wireless-verse-grip */ };
device_commands dc{ .device_id = data.wireless_verse_grip[0].device_id };
dc.commands.probe_orientation = probe_orientation_cmd{};
request_msg.wireless_verse_grip.push_back(std::move(dc));
}
std::string out_json;
(void)glz::write_json(request_msg, out_json);
ws.send(out_json);
request_msg.session.reset(); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
Paramètres de ligne de commande (Python)
La version Python accepte les deux mêmes paramètres quele tutoriel 01:
--full——Afficher la charge JSON d'origine sous forme formatée, et non sous forme de résumé sur une seule ligne.--query-config——Réinjection à chaque cycle de sortiesession.force_render_full_state, afin que chaque image contienneconfigdes blocs, et pas seulement des incréments d'état.
--query-config C'est particulièrement utile pour le VerseGrip sans fil, car config Outre les champs de configuration habituels, le bloc contient également des champs spécifiques à WVG sub_type Champ (stylus / prototype / ruko / kingfisher(Nouveauté de la version 3.5). Exécuter python 03-haply-inverse-print-wireless-verse-grip.py --full --query-config Il est possible d'observer en temps réel config.sub_type Mise à jour avec les autres valeurs de configuration.
La version C++ ne prend pas en charge ces paramètres.
Le tutoriel 03 est également installé localement avec le SDK — veuillez vous rendre dans le répertoire d'installation du service, sous tutorials/03-haply-inverse-print-wireless-verse-grip/ Rechercher dans.
Code source : Python · C++ · C++ Glaze
Contenu associé : Tutoriel 02 (VG filaire) · Type (quaternion) · Commandes de contrôle (probe_orientation) · Protocole WebSocket