docs(simulation): record polygon experiments and bounded route acceptance
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
# AI-полигон на Worker 006
|
||||
|
||||
Актуальная схема после миграции 21.09.2026: Worker исполняет мир, физику,
|
||||
камеры, AI и команды виртуального ровера. Mission Core на Mac принимает видео,
|
||||
компактную телеметрию и передаёт команды оператора. В AI-профиле нет локального
|
||||
3D-движка, загрузки PLY в браузер или передачи RGB через Mac для inference.
|
||||
LCC Gaussian Mirror и AI Inference остаются отдельными профилями.
|
||||
|
||||
## Установка и постоянный запуск
|
||||
|
||||
- Единственный Core: `http://127.0.0.1:8000`.
|
||||
- Worker: существующий Windows / RTX 4090 / Docker Desktop, SSH `mission-gpu`.
|
||||
- Native Isaac 6.1.0: `D:\NDC_MISSIONCORE\runtime\simulation\isaac-sim-6.1.0`.
|
||||
- Служба: Windows Scheduled Task `ndc-ai-polygon-worker`, запускается при входе
|
||||
текущего пользователя и восстанавливается после аварийного выхода. Это запуск
|
||||
**при пользовательском входе**, не headless-запуск до входа в Windows.
|
||||
- Управление использует существующий постоянный reverse tunnel:
|
||||
Worker `127.0.0.1:18080` → Core `127.0.0.1:8000`. Старый foreground launcher
|
||||
`start_worker.py` и дополнительный туннель 18081 больше не нужны.
|
||||
- Поток: официальный NVIDIA Omniverse Web SDK 6.7.0, native livestream Isaac,
|
||||
1280×720, запрос 30 FPS. TCP49100 / UDP47998 идут напрямую по Tailscale;
|
||||
Windows Firewall разрешает только текущий приватный адрес операторского Mac.
|
||||
Публичного доступа нет. Сам Isaac endpoint не предоставляет pairing/auth:
|
||||
доверенная граница здесь — Tailscale и ограничение firewall. Один viewer.
|
||||
|
||||
`build_bundle.py --output <private-directory>` создаёт архив и манифест хешей.
|
||||
Доставить их в `simulation/releases`, проверить SHA-256 архива и каждого файла,
|
||||
распаковать в новый `ai-polygon-<16-hex-release>`. Из SSH-сеанса с приватного
|
||||
операторского адреса выполнить поставляемый установщик:
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\simulation\ai-polygon\Install-RealtimeWorker.ps1 -Release <16-hex-release>
|
||||
```
|
||||
|
||||
Установщик отказывается менять службу при `state/active.json`. Он сохраняет
|
||||
исходные Windows Kit Block rules в `private/stream-firewall-backup.json`,
|
||||
ограничивает исключение двумя портами и одним операторским адресом, оставляя
|
||||
остальные подключения заблокированными. Ошибка firewall откатывает изменения.
|
||||
Обновление выполняется только при отсутствии активного прогона; после него
|
||||
проверить регистрацию актуальных `runtime_sources` перед новым Start.
|
||||
|
||||
Откат: установить предыдущий проверенный release тем же установщиком. Удаление
|
||||
службы: его параметр `-Remove` при отсутствии активного прогона. Он также
|
||||
восстанавливает исходные Block rules и удаляет собственные исключения.
|
||||
|
||||
Isaac на новом Worker устанавливает `Prepare-IsaacRuntime.ps1`. Модельные
|
||||
пакеты предварительно готовятся штатным AI Inference workflow. При старте
|
||||
проверяются закреплённые images/checkpoints/runners/labels из
|
||||
`models.worker-006.json`; существующие профили не заменяются. Новый realtime
|
||||
ровер использует метрическую геометрию `rover_profile.py`, Jetbot не требуется.
|
||||
|
||||
Токен существует только в защищённых файлах: `ai-polygon/worker.token` текущего
|
||||
Core data directory и `simulation/private/worker.token` на Worker. Не выводить
|
||||
его и не передавать аргументом командной строки.
|
||||
|
||||
## Работа оператора
|
||||
|
||||
1. LAB → «Симуляции» → «AI-полигон» → «Доступные локации» → «Открыть».
|
||||
Выбрать настройки и состав AI, затем «Открыть симуляцию».
|
||||
Worker подготавливает выбранный мир; открывается настоящая удалённая сцена
|
||||
с состоянием «Сцена открыта · AI выключен».
|
||||
2. В «Старт и масштаб» задаются подготовка сцены и состав AI через общий
|
||||
конструктор модулей. «Запустить AI» поднимает три временных контейнера
|
||||
и включает локальные физику/камеру/inference. Пока модели не готовы,
|
||||
виртуальный привод получает нулевую команду.
|
||||
3. «За ровером», «Обзор», «Камера AI» переключают **камеру на Worker**.
|
||||
Камера AI в потоке показывает тот же ракурс; модели получают локальный
|
||||
несжатый RGB800×600 до операторского видеокодека.
|
||||
4. «Пауза» после подтверждения Worker останавливает физику и новые inference-
|
||||
запросы. Уже выполнявшийся запрос не может вернуть команду после паузы.
|
||||
Контейнеры остаются готовы к продолжению. После продолжения сбрасываются
|
||||
локальная визуальная цель и причинное состояние ROS-планировщика; требуется
|
||||
новое наблюдение. Номер точки маршрута и зафиксированный отказ сохраняются.
|
||||
5. «Завершить симуляцию» останавливает native runtime и удаляет только собственные
|
||||
временные контейнеры по точным ID. Терминальный статус появляется после
|
||||
подтверждения освобождения ресурсов.
|
||||
6. Круглая стрелка возвращает к локациям и отсоединяет видео. **Сам прогон
|
||||
продолжает работать на Worker**. Повторное «Открыть» присоединяет тот же run.
|
||||
Для завершения нужен отдельный явный Stop. Развёрнутый viewer сохраняет
|
||||
управление; Escape возвращает обычный размер.
|
||||
|
||||
Частоты разделены: «Видео» — реально декодируемые кадры браузера; «Камера» —
|
||||
кадры сенсора, переданные очереди AI; «AI» — завершённые решения; «Симуляция» —
|
||||
отношение физического времени к реальному. 30 FPS видео не означают AI30Hz.
|
||||
Интерфейс также показывает inference и возраст кадра. Очередь хранит только
|
||||
последний кадр. При возрасте наблюдения более800мс или готовой команды более500мс привод получает
|
||||
ноль, но физический мир продолжает идти. Горизонт проверки корпуса включает те же
|
||||
800мс, запас500мс и торможение; эти пределы нельзя менять независимо.
|
||||
|
||||
## Жизненный цикл и архив
|
||||
|
||||
Один coordinator владеет episode. Windows Job Object завершает его native
|
||||
потомков при аварийном выходе coordinator. Сведения об exact container IDs
|
||||
сохраняются до ожидания готовности моделей. После перезапуска coordinator
|
||||
сверяет владельца, очищает собственные остатки и подтверждает завершение Core;
|
||||
старое движение не воспроизводится заново.
|
||||
|
||||
Потеря Core/viewer не блокирует физический tick. При потере управляющей связи
|
||||
Worker продолжает **ограниченный по времени лабораторный прогон** по последней
|
||||
команде; локальный watchdog inference остаётся активен. UI показывает отсутствие
|
||||
связи, а Core удерживает reservation до явного finish. После reconnect возвращается
|
||||
тот же run, без повторного Start. Stop без связи не считается подтверждённым.
|
||||
Стандартная длительность — 30 минут, API допускает 10 секунд–2 часа. Это политика
|
||||
только виртуального полигона, не политика физического аппарата.
|
||||
|
||||
Журналы хранятся на Worker: `simulation/state/<run_id>/`:
|
||||
|
||||
- `run.json`, `robot-profile.json`, `motion.jsonl`, `result.json`, `worker-result.json`;
|
||||
- `camera/decisions.jsonl`: monotonic и simulation timestamps, решения, boxes,
|
||||
ссылки и SHA-256 сохранённых кадров;
|
||||
- `camera/*.jpg`: кадры, использованные для решений, JPEG quality90. Это архивные
|
||||
копии, не lossless RGB. Inference получает исходный массив до JPEG-кодирования.
|
||||
- `composition.json`: точная композиция, контракты и идентичности модулей;
|
||||
- `camera/*.labels.png`, `*.surface.png`, `*.range.npz`: исходная таксономия модели,
|
||||
кандидаты поверхности, наблюдённый лидар и калибровка камеры;
|
||||
- `terrain.json`, `initial-contact.json`: идентичность collision proxy и размещение;
|
||||
- `local-state.sqlite3`: оперативный IPC coordinator/Isaac, WAL, не архив кадров.
|
||||
|
||||
Core получает только последний ограниченный snapshot, команды и итог. Полного
|
||||
автоматического переноса архива на Mac в этом этапе нет. Прежний lockstep-архив
|
||||
остаётся историческим и не является источником текущего видео.
|
||||
|
||||
## Миры и границы проверки
|
||||
|
||||
Forest Scan (draftmode, CC BY4.0): 2 142 439 splats, 505 617 082 bytes.
|
||||
Bamboo Trail (luckysplat, CC BY4.0): LOD0, 5 818 957 splats, 325 861 955 bytes.
|
||||
Оба PLY скачаны, проверены и кэшируются на Worker по SHA-256. Открытие не
|
||||
пересылает их повторно при совпавшем кэше. Импорт дополнительных авторизованных
|
||||
PLY остаётся отдельной операцией; author/license/source URL сохраняются.
|
||||
|
||||
Ровер имеет явный footprint1×1м и независимые левый/правый приводы. Радиус колёс
|
||||
0.25м, база0.5м, колея0.9м, масса24кг и коэффициенты трения — лабораторные
|
||||
допущения, не калибровка настоящего аппарата. Масштаб Gaussian-сцен пока не
|
||||
подтверждён измеренной опорой. Скрытая плоскость заменена collision mesh,
|
||||
полученным SplatTransform3.4.0: voxel0.06м, opacity0.25, floor-fill0.3м.
|
||||
Загрузка сверяет источник, преобразование, охват тайла и SHA-256 меша.
|
||||
Старт проверяет поддержку footprint, локальную плоскость и остаточную неровность.
|
||||
Изменение камеры/старта внутри готового тайла не пересобирает коллизии.
|
||||
|
||||
Gaussian collision proxy не имеет семантики мягкой растительности: часть травы
|
||||
и листвы может попасть в геометрию. Это известное ограничение, а не проверенная
|
||||
модель деформируемой травы. Полное прохождение тропы, реальный тормозной путь,
|
||||
скользкий грунт и динамические люди/животные требуют отдельной сценарной приёмки.
|
||||
|
||||
## Композиция восприятия и движения
|
||||
|
||||
Используется тот же `ModuleRegistry/ModuleSpec/CompositionSpec`, что в AI Inference,
|
||||
с режимом `worker-local-simulation` и изолированным пространством портов pinhole.
|
||||
Контракты recorded K1 не переиспользуются под другим смыслом. Выбор хранит
|
||||
module id, SHA-256 и параметры; произвольный код в выборе не принимается.
|
||||
|
||||
По умолчанию: NVIDIA SegFormer B2 ADE150 → кандидаты грунта/камня/дороги,
|
||||
RF-DETR → люди/животные, «Маршрут · контроль продвижения» → локальная цель
|
||||
и состояние миссии, CMU terrain_analysis/localPlanner/pathFollower → путь
|
||||
и скорость/поворот. DDRNet GOOSE64 остаётся доступной альтернативой; его прежний
|
||||
image/checkpoint и профили не меняются. Два независимых провайдера исполняются
|
||||
параллельно, навигация — после их результата. Исходные class IDs сохраняются.
|
||||
SegFormer использует официальный pinned checkpoint и лицензию NVIDIA для
|
||||
research/evaluation; этот профиль предназначен для лабораторных испытаний.
|
||||
|
||||
Виртуальный лидар: 15 вертикальных колец ×144 азимута, дальность8м, первый
|
||||
физический контакт, наблюдения около5Гц. Дополнительные кольца −80/−75/−70°
|
||||
наблюдают ближний грунт перед корпусом. Меш/карта мира не передаются AI.
|
||||
Необязательные операторские точки XY задают задачу, но не разрешённую траекторию.
|
||||
Семантический waypoint выбирается по RGB и диапазону,
|
||||
готовый CMU-планировщик проверяет рельеф. Дополнительный монитор проверяет
|
||||
квадратный корпус на дуге текущей команды с горизонтом watchdog и торможения.
|
||||
Пределы лабораторного профиля: ступень0.10м, уклон25°, аварийный наклон30°.
|
||||
Ступени0.15м и выше не прошли отдельную физическую проверку. Нулевая команда
|
||||
удерживает физические колёсные шарниры приводом положения, без фиксации корпуса
|
||||
или телепортации. На ровных уклонах10–25° проверено удержание после остановки.
|
||||
Параметры solver/contact и привода фиксируются в `robot-profile.json`.
|
||||
Колёса используют штатную выпуклую аппроксимацию цилиндров PhysX:
|
||||
`SETTING_COLLISION_APPROXIMATE_CYLINDERS=true`. Точная форма цилиндра вызывала
|
||||
зависание контакта с Forest; размеры колёс сохранены. Для новой формы повторены
|
||||
проверки прямого/обратного движения, торможения, ступеней и уклонов.
|
||||
В pinned localPlanner image добавлена проверка метрового прямоугольного корпуса
|
||||
с запасом5см при выборе примитива и начального поворота. Если наблюдаемая точка
|
||||
уже находится в запасе, допускается только сохранение или увеличение расстояния
|
||||
от неё. Пересечение самого корпуса запрещено; отдельный монитор команды сохранён.
|
||||
Это объявленное изменение upstream C++, воспроизводимое через
|
||||
`navigation/Prepare-RectangleNavigation.ps1`, а не неизменённый CMU бинарник.
|
||||
`terrain_costs.py` нормализует ложную высоту препятствия только для достаточно
|
||||
наблюдённого гладкого уклона2–25°. Резкие ступени и неподтверждённые поверхности
|
||||
остаются заблокированными; геометрия сцены не меняется.
|
||||
Оценка грунта CMU использует штатный `quantileZ=0.25`. Экспериментальная медиана
|
||||
0.5 отклонена: стены искажали оценку опоры в узком коридоре. Штатная настройка
|
||||
прошла23 сценария, включая уступы12/15см и обрыв40см.
|
||||
CMU сохраняет наблюдённые точки ближе4м, пока они скрыты корпусом; исчезновение
|
||||
возврата не означает свободный путь. Это консервативная память статического
|
||||
рельефа; очистка следов динамических участников отдельно не квалифицирована.
|
||||
Наблюдения и решения должны быть свежими, иначе привод получает ноль.
|
||||
|
||||
Маршрут задаётся до открытия симуляции через существующую форму подготовки
|
||||
сцены (до32точек XY). Пустой маршрут оставляет режим видимой тропы. После8с без продвижения к цели хотя бы0.10м модуль поведения проверяет
|
||||
короткий отход назад по наблюдаемой опоре и выбирает другую цель. Отход ограничен
|
||||
6с и скоростью0.1м/с; CMU и проверка корпуса сохраняют право остановки. Максимум
|
||||
три попытки. Отход от цели не обновляет бюджет попыток. Это локальное восстановление,
|
||||
не готовый глобальный объезд. Достижение последней точки, исчерпание попыток или
|
||||
опасный наклон завершают прогон с явной причиной и освобождением ресурсов.
|
||||
Физическое время используется для контроля продвижения; пауза его не расходует.
|
||||
|
||||
Подготовка новых зависимостей выполняется версионированными
|
||||
`segformer/Prepare-Segformer.ps1`, `navigation/Prepare-Navigation.ps1`,
|
||||
`navigation/Prepare-RectangleNavigation.ps1`, `navigation/Prepare-Terrain.ps1`. Образы и checkpoints закреплены в
|
||||
`models.worker-006.json`. ROS-обмен остаётся внутри собственного контейнера;
|
||||
его Fast DDS профиль использует loopback UDP и не зависит от старых SHM-сегментов.
|
||||
|
||||
Core резервирует существующую AI Inference queue. Другие Gaussian GPU jobs
|
||||
пока не используют общий reservation: их нельзя запускать параллельно с этой
|
||||
симуляцией. Не запускать второй coordinator, не удалять markers вручную и не
|
||||
делать `docker system prune` или `compose down` чужих стеков.
|
||||
|
||||
Планировщик сокращает локальный путь шагом0.1м до минимальных0.2м, чтобы не
|
||||
пропускать короткие манёвры на изгибе тропы. Дистанция остановки follower0.08м
|
||||
относится к этому локальному отрезку; достижение маршрутной точки и полный
|
||||
тормозной контур проверяются независимо. Простое снижение минимальной длины
|
||||
при штатном шаге0.5м не включает эти короткие кандидаты.
|
||||
|
||||
Наблюдённые низкие точки уже внутри корпуса (отступ5см от краёв) сверяются с
|
||||
измеренной позой и высотой опоры. Полоса±8см не расширяет предел ступени10см.
|
||||
Точки впереди, высокие препятствия и провалы сохраняются; исходная память CMU
|
||||
не изменяется. Поза должна принадлежать той же транзакции наблюдения, с допуском
|
||||
округления ROS не более1000нс. Число коррекций записывается в журнал решений.
|
||||
|
||||
Последний установленный release `0d4c0a73bbbe2066` (2026-09-23 07:34:31 UTC); предыдущие версии сохранены
|
||||
для воспроизводимости. Версии до `0ad2b4bf9b3dcfa6` используют неверное совпадение визуальной
|
||||
и физической сцены; откат к ним не является рабочим режимом для Gaussian-навигации. При изменении Python registry композиции нужно
|
||||
согласованно перезапустить Core8000 перед новым прогоном, затем проверить
|
||||
health, Worker registration и module identities. Не выполнять перезапуск
|
||||
во время активных операций других операторских задач.
|
||||
|
||||
Текущие проверки и ограничения (результаты каждой локации отдельно):
|
||||
[Route recovery audit](../audits/2026-09-22-ai-polygon-route-recovery.md).
|
||||
Исходная интеграция композиции и UI:
|
||||
[Composed terrain navigation audit](../audits/2026-09-22-ai-polygon-composed-terrain-navigation.md).
|
||||
|
||||
Предыдущие результаты миграции:
|
||||
[Realtime audit](../audits/2026-09-21-ai-polygon-realtime-placement.md).
|
||||
Ранние lockstep-измерения остаются в integration/operator-revision audits и не
|
||||
должны трактоваться как текущая архитектура.
|
||||
|
||||
### Совпадение Gaussian-изображения и коллизий
|
||||
|
||||
SplatTransform 3.4.0 присваивает входному PLY поворот Rz(180). Подготовка сначала
|
||||
отменяет его, применяет пользовательское XYZ-вращение и масштаб, затем Rx(-90)
|
||||
для glTF. Загрузка физики выполняет обратный переход `(x,y,z) → (x,-z,y)`.
|
||||
Версионный `qualify_terrain_coordinates.py` проверяет реальный генератор на
|
||||
контрольном объекте с известными координатами.
|
||||
|
||||
Только известный старый генератор и точная ориентация `[-90,0,180]` допускают
|
||||
миграцию готового меша поворотом на180° по горизонтали. Это точное преобразование
|
||||
всех вершин, без изменения препятствий; оригинал остаётся неизменным. SHA обоих
|
||||
мешей и исходный manifest сохраняются в `coordinate-correction.json`. Область
|
||||
подготовленной геометрии также переносится в правильные координаты. Прежние
|
||||
стартовые положения и маршруты необходимо проверить заново.
|
||||
|
||||
### Готовая пара Gaussian и collision mesh на Worker
|
||||
|
||||
`register_paired_scene.py` принимает `--root`, `--source`, `--collider` и
|
||||
`--descriptor` в подготовленном Python Worker. Descriptor соответствует
|
||||
`WorkerWorldCreate`: происхождение и лицензия, размер, SHA-256 обоих файлов,
|
||||
число splats и настройки сцены. После проверки реальных байтов инструмент
|
||||
сохраняет исходник в `state/worlds/<sha>.ply`, collision manifest — в
|
||||
`assets/prepared-worlds/<sha>/terrain.json`. В Core передаётся только описание.
|
||||
Регистрация требует токен и текущий Worker-Instance. В этой ветке нет загрузки
|
||||
больших файлов через операторскую машину; повреждённый локальный исходник
|
||||
приводит к ошибке, а не к сетевому fallback.
|
||||
|
||||
Перед стартом проверяется весь метровый объём, включая узкие стволы между
|
||||
лучами определения грунта. Старые стартовые точки Forest/Bamboo эту проверку
|
||||
не проходят. Не отключать проверку и не повышать предел ступени ради запуска.
|
||||
Riverside Path использует готовую пару Habitat-GS scene62; её физическая
|
||||
проверка прошла4.68м. Полный маршрут R29 завершён по трём точкам с остановкой
|
||||
у цели; ограничения и дальнейшие замеры зафиксированы в route audit.
|
||||
|
||||
|
||||
Ожидание одной транзакции CMU —600мс: накопленная карта реальной сцены
|
||||
может обрабатываться дольше300мс. Независимые deadman800мс для камеры и500мс
|
||||
для команды сохраняются; это не разрешение исполнять просроченные результаты.
|
||||
|
||||
Когда заданная маршрутная точка подтверждена семантическими и дальномерными
|
||||
наблюдениями, локальная цель сохраняет её XY. Уже наблюдённая точка может
|
||||
войти в слепую полосу камеры; её граница определяется реальной проекцией,
|
||||
а не фиксированной дистанцией. Текущая видимая поверхность, причинная карта
|
||||
CMU и контроль полного корпуса продолжают ограничивать команды. Новая локальная
|
||||
цель не должна уводить ровер дальше от пропущенной маршрутной точки.
|
||||
|
||||
|
||||
Предыдущая проверка: R30 прошёл три точки Riverside Path и остановился у цели,
|
||||
4.675м за32.67с. Медиана полного inference234мс, AI3.77решения/с,
|
||||
рендер16.54FPS. Оптимизация расчёта расстояний побитно совпала на130 картах,
|
||||
все23 проверки опасных сценариев прошли; deadman и пределы проходимости сохранены.
|
||||
Это подтверждение данного паркового маршрута, а не Forest/Bamboo или бездорожья.
|
||||
|
||||
|
||||
### Непрерывное управление и полное задание — 23 сентября
|
||||
|
||||
Текущая сборка `22c28daad98da60b`; подробные изменения и receipts:
|
||||
[Continuous motion audit](../audits/2026-09-23-ai-polygon-continuous-motion.md).
|
||||
|
||||
Привод обновляется в native physics pre-step на 60 Гц, с ограничением
|
||||
нарастания скорости поверхности колёс 0.2 м/с². Защитное снижение/остановка
|
||||
не задерживаются. Камера и калибровка обновляются после физики; скорость берётся
|
||||
из articulation velocity. `physics-motion.jsonl` и `motion-control.json`
|
||||
остаются на Worker. Пауза останавливает физику и inference; продолжение требует
|
||||
свежих наблюдений и сохраняет уже наблюдённую маршрутную цель.
|
||||
|
||||
Для CPU-ускорения существующего анализа связности используется отдельный образ
|
||||
`ndc-ai-polygon-cmu:connectivity-v1`, image digest
|
||||
`sha256:698228d0d378b166ceaf299aa86ab95b5e74179837532f2378c036c7f7621dcc`.
|
||||
Подготовка — `navigation/Prepare-Connectivity.ps1` поверх проверенного
|
||||
`rectangle-v3`. Не подменять numerical thresholds, не включать fast-math.
|
||||
NumPy-эквивалентность и hazard qualification обязательны для новой сборки.
|
||||
|
||||
Полные paired-source коллайдеры допускают старт по всему исходному мешу;
|
||||
ограничение 30-метрового generated tile к ним не применяется. Hash/calibration
|
||||
и физическая проверка полной опоры/объёма сохраняются. `route_author` использует
|
||||
готовый Detour для чтения Habitat navmesh; маршрут задаёт миссию и не передаёт
|
||||
привилегированную геометрию в runtime inference.
|
||||
|
||||
Сейчас Riverside Path настроен на полное задание 97: шесть точек, путь около
|
||||
11 м. R34 достиг исходного конечного положения в допуске миссии 0.4 м
|
||||
(ошибка 0.344 м), перемещение 10.59 м за 86.2 с мира. Медиана inference125мс,
|
||||
AI4.57Гц, рендер17.12FPS. Пять кратких остановок из-за семантической
|
||||
неуверенности остаются; пауза/продолжение проверены. Это не подтверждение
|
||||
всей карты, исходного задания3, бездорожья или реального привода.
|
||||
Reference in New Issue
Block a user