Diagramme de paquet UDP
Chaque paquet est un message binaire de 45 octets de longueur fixe. Tous les champs multi-octets utilisent petit-boutiste ordre des octets. Les octets inconnus ajoutés à la fin sont ignorés par le récepteur pour des raisons de compatibilité ascendante.
| Championne |
Cône |
Octets |
Description |
| en-tête |
uint8[4] |
4 |
Octets magiques 0x52 0x43 0x54 0x50 —ASCII "RCTP". Les paquets avec une valeur magique incorrecte sont supprimés silencieusement. |
| horodatage |
flotteur 64 |
8 |
Unité Time.realtimeSinceStartup en quelques secondes. Utilisé pour la mesure de la gigue ; non interprété par le contrôleur du robot. |
| pos_x |
flotteur32 |
4 |
Position X de l'effecteur final en mètres, cadre du monde VR (droitier, Y-up). Converti en châssis de robot mm par transform_position(). |
| pos_y |
flotteur32 |
4 |
Position Y de l'effecteur final en mètres, cadre du monde VR. |
| pos_z |
flotteur32 |
4 |
Position Z de l'effecteur final en mètres, cadre du monde VR. |
| rot_x, rot_y, rot_z, rot_w |
flotteur32 × 4 |
16 |
Rotation de l'effecteur final en tant que quaternion unitaire dans le cadre du monde VR. Converti en Euler (roulis/tangage/lacet) par quat_to_euler() avant de passer au SDK du robot. |
| pince |
flotteur32 |
4 |
Ouverture de la pince : 0.0 = entièrement fermé, 1.0 = complètement ouvert. Dérivé de la force de pincement via VRGripperController.cs. |
| drapeaux |
uint8 |
1 |
Bit 0 : suivi valide (1 = main détectée). Bit 1 : arrêt d'urgence demandé (1 = opérateur appuyé sur Menu). Bits restants réservés. |
Résumé du décalage d'octets : en-tête 0 à 3, horodatage 4 à 11, pos_x 12 à 15, pos_y 16 à 19, pos_z 20 à 23, rot_x 24 à 27, rot_y 28 à 31, rot_z 32 à 35, rot_w 36 à 39, pince 40 à 43, drapeaux 44.
Python struct chaîne de format pour le décompression :
importateur struct
PACKET_MAGIC = b'\x52\x43\x54\x50' # "RCTP"
PACKET_FMT = '<4sdfffffffff f B'
# ^ ^ ^^^^^^^^^^ ^ ^
# | | pos xyz | flags (uint8)
# | timestamp gripper (float32)
# header (4 bytes) rot xyzw (float32×4)
déf analyser_paquet(data: bytes) -> dict | None:
et len(data) < struct.calcsize(PACKET_FMT):
retour None
fields = struct.unpack_from(PACKET_FMT, data)
header, ts, px, py, pz, rx, ry, rz, rw, gripper, flags = fields
et header != PACKET_MAGIC:
retour None
retour {
"horodatage": ts,
"position": (px, py, pz),
"rotation": (rx, ry, rz, rw),
"pince": gripper,
"valide": bool(flags & 0x01),
"arrêt": bool(flags & 0x02),
}
Latence de bout en bout
Mesuré sur un réseau local Wi-Fi 6 avec le bras AgileX Piper à un taux de contrôle de 30 Hz et une limite de vitesse de 25 %. Les valeurs sont typiques ; la latence réelle dépend des conditions Wi-Fi et de la fréquence d'asservissement du robot.
~20 ms
Pipeline de suivi Quest 3
Fusion du capteur de pose manuelle avec le rappel Unity
<5 ms
Transit UDP
Réseau local Wi-Fi 6 ; jusqu'à ~ 15 ms sur les anciens réseaux Wi-Fi
<2 ms
Analyse et file d'attente Python
Déballage de la structure + insertion de file d'attente
30 à 80 ms
Exécution de la trajectoire du robot
Dépend de la limite de vitesse et de la distance de déplacement
| Wi-Fi 6 LAN, vitesse de 25 % |
50 à 120 ms |
| Wi-Fi 5 LAN, vitesse de 25 % |
80 à 150 ms |
| Réponse logicielle d'arrêt d'urgence (chemin Bit 1) |
1 cycle de contrôle (~33 ms à 30 Hz) |
| Détection des pertes de suivi |
Immédiat (marque l'octet dans le paquet actuel) |
Gardez les limites de vitesse prudentes pour réduire la sensation de latence.
La composante dominante de la latence perceptuelle est l’exécution de la trajectoire du robot. Un inférieur SPEED_PERCENT rend le système plus prévisible même si le temps aller-retour réel est similaire. Commencez à 25 % et augmentez seulement une fois le mouvement entièrement validé.