Схема UDP-пакетов
Каждый пакет представляет собой двоичное сообщение фиксированной длины длиной 45 байт. Все многобайтовые поля используют прямой порядок байтов порядок байтов. Неизвестные байты, добавленные в конце, игнорируются получателем в целях прямой совместимости.
| Поле |
Тип |
Байты |
Описание |
| заголовок |
uint8[4] |
4 |
Магические байты 0x52 0x43 0x54 0x50 — ASCII «RCTP». Пакеты с неправильным магическим значением автоматически отбрасываются. |
| временная метка |
float64 |
8 |
Единство Time.realtimeSinceStartup в секундах. Используется для измерения джиттера; не интерпретируется контроллером робота. |
| pos_x |
поплавок32 |
4 |
Положение конечного исполнительного органа X в метрах, рамка мира VR (правша, Y-вверх). Робот переоборудован в миллиметровую раму transform_position(). |
| pos_y |
поплавок32 |
4 |
Положение концевого эффектора Y в метрах, рамка мира VR. |
| pos_z |
поплавок32 |
4 |
Положение концевого исполнительного органа Z в метрах, мировая рамка VR. |
| rot_x, rot_y, rot_z, rot_w |
плавающее32 × 4 |
16 |
Вращение конечного эффектора как единичный кватернион в системе координат VR. Преобразовано в Эйлер (крен/тангаж/рыскание) с помощью quat_to_euler() перед переходом к SDK работа. |
| захват |
поплавок32 |
4 |
Открытость захвата: 0.0 = полностью закрыто, 1.0 = полностью открыт. Получено из силы сжатия с помощью VRGripperController.cs. |
| флаги |
uint8 |
1 |
Бит 0: отслеживание действительно (1 = рука обнаружена). Бит 1: запрошен аварийный останов (1 = оператор нажал меню). Остальные биты зарезервированы. |
Сводка смещений байтов: заголовок 0–3, метка времени 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, захват 40–43, флаги 44.
Питон struct строка формата для распаковки:
импортировать struct
PACKET_MAGIC = b'\x52\x43\x54\x50' # "RCTP"
PACKET_FMT = '<4sdffffffff f B'
# ^ ^ ^^^^^^^^^^ ^ ^
# | | pos xyz | flags (uint8)
# | timestamp gripper (float32)
# header (4 bytes) rot xyzw (float32×4)
защита parse_packet(data: bytes) -> dict | None:
если len(data) < struct.calcsize(PACKET_FMT):
возвращаться None
fields = struct.unpack_from(PACKET_FMT, data)
header, ts, px, py, pz, rx, ry, rz, rw, gripper, flags = fields
если header != PACKET_MAGIC:
возвращаться None
возвращаться {
"метка времени": ts,
"позиция": (px, py, pz),
"вращение": (rx, ry, rz, rw),
"захват": gripper,
"действительный": bool(flags & 0x01),
"останавливаться": bool(flags & 0x02),
}
Сквозная задержка
Измерено в локальной сети Wi-Fi 6 с использованием манипулятора AgileX Piper при частоте управления 30 Гц и ограничении скорости 25 %. Значения типичны; фактическая задержка зависит от условий Wi-Fi и скорости сервопривода робота.
~20 мс
Квест 3 Отслеживание конвейера
Объединение датчика положения руки с обратным вызовом Unity
<5 мс
UDP-транзит
Wi-Fi 6 локальных сетей; до ~15 мс на старом Wi-Fi
<2 мс
Разбор и очередь Python
Распаковка структуры + вставка в очередь
30–80 мс
Выполнение траектории робота
Зависит от ограничения скорости и расстояния перемещения
| Wi-Fi 6 LAN, скорость 25% |
50–120 мс |
| Wi-Fi 5 LAN, скорость 25% |
80–150 мс |
| Реакция программного аварийного останова (путь бита 1) |
1 цикл управления (~33 мс при 30 Гц) |
| Обнаружение потери отслеживания |
Немедленно (помечает байт в текущем пакете) |
Сохраняйте ограничения скорости консервативными, чтобы уменьшить ощущение задержки.
Доминирующим компонентом задержки восприятия является выполнение траектории робота. Нижний SPEED_PERCENT делает систему более предсказуемой, хотя фактическое время прохождения туда и обратно одинаково. Начните с 25% и повышайте только после того, как движение будет полностью подтверждено.