|
|
# Испытательный UDP-протокол
|
|
|
|
|
|
## 1. Назначение
|
|
|
|
|
|
Этот backend нужен для испытаний, когда проект должен отправлять состояние цели в блок наведения по UDP в локальной сети.
|
|
|
|
|
|
В проекте этот режим включается через:
|
|
|
|
|
|
```text
|
|
|
AUTOPILOT_BACKEND = "proto_udp"
|
|
|
PROTO_UDP_ENABLE = True
|
|
|
```
|
|
|
|
|
|
или через env:
|
|
|
|
|
|
```text
|
|
|
FPV_AUTOPILOT_BACKEND=proto_udp
|
|
|
FPV_PROTO_UDP_ENABLE=1
|
|
|
```
|
|
|
|
|
|
## 2. Куда это встроено
|
|
|
|
|
|
Реализация находится в:
|
|
|
|
|
|
- `autopilot_bridge.py`
|
|
|
|
|
|
Новый backend называется:
|
|
|
|
|
|
- `proto_udp`
|
|
|
|
|
|
## 3. Транспорт
|
|
|
|
|
|
- протокол: UDP
|
|
|
- адрес: `PROTO_UDP_HOST`
|
|
|
- порт: `PROTO_UDP_PORT`
|
|
|
|
|
|
По умолчанию:
|
|
|
|
|
|
```python
|
|
|
PROTO_UDP_HOST = "127.0.0.1"
|
|
|
PROTO_UDP_PORT = 5005
|
|
|
```
|
|
|
|
|
|
## 4. Формат пакета
|
|
|
|
|
|
Формат упаковки:
|
|
|
|
|
|
```text
|
|
|
<BBBhhbbB
|
|
|
```
|
|
|
|
|
|
Это little-endian бинарный пакет фиксированной длины.
|
|
|
|
|
|
Поля идут в таком порядке:
|
|
|
|
|
|
1. `descriptor`
|
|
|
2. `object_id`
|
|
|
3. `target_state`
|
|
|
4. `offset_y_px`
|
|
|
5. `offset_x_px`
|
|
|
6. `offset_y_pct`
|
|
|
7. `offset_x_pct`
|
|
|
8. `bbox_area_pct`
|
|
|
|
|
|
## 5. Таблица полей
|
|
|
|
|
|
### `descriptor`
|
|
|
|
|
|
- тип: `uint8`
|
|
|
- стандартное значение: `1`
|
|
|
- задается через `PROTO_UDP_DESCRIPTOR`
|
|
|
|
|
|
### `object_id`
|
|
|
|
|
|
- тип: `uint8`
|
|
|
- диапазон: `0..255`
|
|
|
- `0`, если цели нет
|
|
|
|
|
|
### `target_state`
|
|
|
|
|
|
- тип: `uint8`
|
|
|
- значения:
|
|
|
- `0` — цели нет
|
|
|
- `1` — цель есть, идет обычная детекция/сопровождение
|
|
|
- `3` — детекция потеряна, но проект удерживает цель трекингом
|
|
|
|
|
|
Значение `2` в пользовательском протоколе зарезервировано под сценарий "несколько целей". Текущая реализация проекта его не отправляет, потому что runtime всегда выбирает одну основную цель.
|
|
|
|
|
|
### `offset_y_px`
|
|
|
|
|
|
- тип: `int16`
|
|
|
- знак:
|
|
|
- вверх `+`
|
|
|
- вниз `-`
|
|
|
|
|
|
### `offset_x_px`
|
|
|
|
|
|
- тип: `int16`
|
|
|
- знак:
|
|
|
- вправо `+`
|
|
|
- влево `-`
|
|
|
|
|
|
### `offset_y_pct`
|
|
|
|
|
|
- тип: `int8`
|
|
|
- диапазон: `-100..100`
|
|
|
- считается относительно половины высоты кадра
|
|
|
|
|
|
### `offset_x_pct`
|
|
|
|
|
|
- тип: `int8`
|
|
|
- диапазон: `-100..100`
|
|
|
- считается относительно половины ширины кадра
|
|
|
|
|
|
### `bbox_area_pct`
|
|
|
|
|
|
- тип: `uint8`
|
|
|
- диапазон: `0..100`
|
|
|
- отношение площади bbox к площади кадра в процентах
|
|
|
|
|
|
## 6. Что отправляется при отсутствии цели
|
|
|
|
|
|
Если цели нет, пакет отправляется с нулями:
|
|
|
|
|
|
- `object_id = 0`
|
|
|
- `target_state = 0`
|
|
|
- все offsets = `0`
|
|
|
- `bbox_area_pct = 0`
|
|
|
|
|
|
Это соответствует вашему описанию:
|
|
|
|
|
|
> если цели нет, все значения остаются нулевыми
|
|
|
|
|
|
## 7. Как вычисляются значения
|
|
|
|
|
|
### Смещение в пикселях
|
|
|
|
|
|
Берется из guidance-точки:
|
|
|
|
|
|
- `aim_x`
|
|
|
- `aim_y`
|
|
|
|
|
|
Относительно центра кадра:
|
|
|
|
|
|
- `offset_x_px = aim_x - center_x`
|
|
|
- `offset_y_px = center_y - aim_y`
|
|
|
|
|
|
То есть знак сделан так, чтобы:
|
|
|
|
|
|
- вправо было положительным;
|
|
|
- вверх было положительным.
|
|
|
|
|
|
### Смещение в процентах
|
|
|
|
|
|
Нормируется к половине размера кадра:
|
|
|
|
|
|
- по X к `frame_w / 2`
|
|
|
- по Y к `frame_h / 2`
|
|
|
|
|
|
И затем ограничивается до `-100..100`.
|
|
|
|
|
|
### Площадь bbox
|
|
|
|
|
|
Используются:
|
|
|
|
|
|
- `box_w`
|
|
|
- `box_h`
|
|
|
- `frame_w`
|
|
|
- `frame_h`
|
|
|
|
|
|
Формула:
|
|
|
|
|
|
```text
|
|
|
bbox_area_pct = 100 * (box_w * box_h) / (frame_w * frame_h)
|
|
|
```
|
|
|
|
|
|
## 8. Включение в локальном запуске
|
|
|
|
|
|
### PowerShell
|
|
|
|
|
|
```powershell
|
|
|
$env:FPV_AUTOPILOT_BACKEND = "proto_udp"
|
|
|
$env:FPV_PROTO_UDP_ENABLE = "1"
|
|
|
$env:FPV_PROTO_UDP_HOST = "192.168.1.50"
|
|
|
$env:FPV_PROTO_UDP_PORT = "5005"
|
|
|
python main.py
|
|
|
```
|
|
|
|
|
|
### Bash
|
|
|
|
|
|
```bash
|
|
|
export FPV_AUTOPILOT_BACKEND=proto_udp
|
|
|
export FPV_PROTO_UDP_ENABLE=1
|
|
|
export FPV_PROTO_UDP_HOST=192.168.1.50
|
|
|
export FPV_PROTO_UDP_PORT=5005
|
|
|
python3 main.py
|
|
|
```
|
|
|
|
|
|
## 9. Включение в Docker
|
|
|
|
|
|
В `docker-compose.yml`:
|
|
|
|
|
|
```yaml
|
|
|
FPV_AUTOPILOT_BACKEND: "proto_udp"
|
|
|
FPV_PROTO_UDP_ENABLE: "1"
|
|
|
FPV_PROTO_UDP_HOST: "192.168.1.50"
|
|
|
FPV_PROTO_UDP_PORT: "5005"
|
|
|
```
|
|
|
|
|
|
## 10. Пример приема пакета на Python
|
|
|
|
|
|
```python
|
|
|
import socket
|
|
|
import struct
|
|
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
sock.bind(("0.0.0.0", 5005))
|
|
|
|
|
|
while True:
|
|
|
data, addr = sock.recvfrom(1024)
|
|
|
descriptor, object_id, target_state, off_y_px, off_x_px, off_y_pct, off_x_pct, bbox_area_pct = struct.unpack(
|
|
|
"<BBBhhbbB", data
|
|
|
)
|
|
|
print(addr, descriptor, object_id, target_state, off_y_px, off_x_px, off_y_pct, off_x_pct, bbox_area_pct)
|
|
|
```
|
|
|
|
|
|
## 11. Что важно проверить на стенде
|
|
|
|
|
|
1. Совпадает ли порядок байтов с ожидаемым приемником.
|
|
|
2. Совпадает ли знак вертикали и горизонтали.
|
|
|
3. Устраивает ли блок значение `target_state = 3` при удержании без детекции.
|
|
|
4. Не нужен ли вам в будущем отдельный режим `target_state = 2`.
|