Esquema de Pacote UDP
Cada pacote é uma mensagem binária de comprimento fixo de 45 bytes. Todos os campos de múltiplos bytes usam little-endian ordem de bytes. Bytes desconhecidos adicionados no final são ignorados pelo receptor para compatibilidade futura.
| Campo |
Tipo |
Bytes |
Descrição |
| cabeçalho |
uint8[4] |
4 |
bytes mágicos 0x52 0x43 0x54 0x50 — ASCII "RCTP". Pacotes com um valor mágico incorreto são descartados silenciosamente. |
| carimbo de data/hora |
float64 |
8 |
Unity Time.realtimeSinceStartup em segundos. Usado para medição de jitter; não interpretado pelo controlador do robô. |
| pos_x |
float32 |
4 |
Posição X do efetor final em metros, quadro do mundo VR (mão direita, Y para cima). Convertido para mm no quadro do robô por transform_position(). |
| pos_y |
float32 |
4 |
Posição Y do efetor final em metros, quadro do mundo VR. |
| pos_z |
float32 |
4 |
Posição Z do efetor final em metros, quadro do mundo VR. |
| rot_x, rot_y, rot_z, rot_w |
float32 × 4 |
16 |
Rotação do efetor final como um quaternion unitário no quadro do mundo VR. Convertido para Euler (rolagem/inclinação/giro) por quat_to_euler() antes de passar para o SDK do robô. |
| garra |
float32 |
4 |
Abertura da garra: 0.0 = totalmente fechada, 1.0 = totalmente aberta. Derivada da força de pinça via VRGripperController.cs. |
| flags |
uint8 |
1 |
Bit 0: rastreamento válido (1 = mão detectada). Bit 1: parada de emergência solicitada (1 = operador pressionou Menu). Bits restantes reservados. |
Resumo do deslocamento de byte: cabeçalho 0–3, timestamp 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, garra 40–43, flags 44.
Python struct string de formato para desempacotar:
matéria 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)
def analisar_pacote(data: bytes) -> dict | None:
com len(data) < struct.calcsize(PACKET_FMT):
retornar None
fields = struct.unpack_from(PACKET_FMT, data)
header, ts, px, py, pz, rx, ry, rz, rw, gripper, flags = fields
com header != PACKET_MAGIC:
retornar None
retornar {
"timestamp": ts,
"posição": (px, py, pz),
"rotação": (rx, ry, rz, rw),
"garra": gripper,
"válido": bool(flags & 0x01),
"estop": bool(flags & 0x02),
}
Latência de Ponta a Ponta
Medido em uma LAN Wi-Fi 6 com o braço AgileX Piper a 30 Hz de taxa de controle e limite de velocidade de 25%. Os valores são típicos; a latência real depende das condições do Wi-Fi e da taxa de servo do robô.
~20 ms
Pipeline de Rastreamento Quest 3
Fusão de sensor de pose da mão para callback do Unity
<5 ms
Trânsito UDP
LAN Wi-Fi 6; até ~15 ms em Wi-Fi mais antigo
<2 ms
Python Analisar & Fila
Desempacotar struct + inserir na fila
30–80 ms
Execução de Trajetória do Robô
Depende do limite de velocidade e da distância de movimento
| LAN Wi-Fi 6, 25% de velocidade |
50–120 ms |
| LAN Wi-Fi 5, 25% de velocidade |
80–150 ms |
| Resposta de e-stop de software (Caminho do Bit 1) |
1 ciclo de controle (~33 ms a 30 Hz) |
| Detecção de perda de rastreamento |
Imediato (byte de flags no pacote atual) |
Mantenha os limites de velocidade conservadores para reduzir a sensação de latência.
O componente de latência perceptual dominante é a execução da trajetória do robô. Um menor SPEED_PERCENT faz o sistema parecer mais previsível, mesmo que o tempo de ida e volta real seja semelhante. Comece em 25% e aumente apenas após a validação total do movimento.