# Эксперименты для LLM-планировщика сборки люка Цель набора — проверить, как LLM-планировщик преобразует пользовательский запрос и `scene_json` в граф действий `skill` с переходами по событиям. В наборе 20 экспериментов: четыре категории по пять заданий. Сложность внутри каждой категории возрастает. **Обновление эталона:** по решению заказчика в опытах 14–20 затяжной инструмент `torque_tool` используется для **имитации** затяжки: винт передаётся виртуальному оператору и удаляется, отверстие остаётся пустым, а инструмент подходит к отверстию и выполняет видимый поворот. Винты по-прежнему не вставляются. Эталонное исполнение этих заданий описано в [спецификации](../../../docs/superpowers/specs/2026-11-08-grant-reference-control-design.md) и [плане](../../../docs/superpowers/plans/2026-11-08-grant-reference-control.md) репозитория. Винты в эталоне не вставляются: робот подносит винт к отверстию и передаёт его виртуальному оператору, отверстие остаётся пустым. Эксперименты опираются на технологическую карту «ТехнКарта вер. 1.0.xlsx» и модели из `src/iiwa_description/objects/grant`. В сцене работает один робот-манипулятор. Корпус закреплён в стапеле; оператора, мобильной платформы и вспомогательного робота нет. Отверстия для четырёх винтов считаются подготовленными, поскольку модели сверлильного шпинделя среди доступных объектов нет. Во всех экспериментах на роботе постоянно установлен `patron`: рабочие инструменты вставляются в него. После установки люк удерживается виртуальной фиксацией: это позволяет роботу отпустить присоску и сменить рабочий инструмент. Начальные положения объектов описаны отдельно в [experiment-scenes.md](experiment-scenes.md). ## Контракт для экспериментов В исходном примере доступны `detect`, `approach`, `lower`, `grab`, `lift`, `transfer`, `align`, `release`, `retreat`, `capture`, `verify`, `stop`, `wait_scene_clear`, `inspect_state`, `notify_operator`. План должен вставлять присоску из стойки в постоянный `patron`, если начальное состояние содержит `tool_docked`; перед `grab` требуется контактное `lower`, а перед отпусканием люка в посадочном месте — его опускание до контакта. После отпускания нужен безопасный отвод и возврат. `dock_tool` извлекает рабочий инструмент из `patron`; сам `patron` остаётся на роботе. Для остальных операций соответствующий `scene_json` расширяется следующими навыками: | Навык | Действие | Основные исходы | | --- | --- | --- | | `dispense` | Пройти соплом именованный участок контура с условной подачей герметика | `completed`, `interrupted`, `failed` | | `press` | Прижать установленный люк в заданной зоне | `success`, `failed` | | `handoff_screw` | Поднести винт к отверстию и передать его виртуальному оператору | `delivered`, `grasp_failed`, `pose_out_of_tolerance` | | `dock_tool` | Вернуть инструмент в его гнездо и отсоединить от робота | `success`, `failed` | Контракты `verify` и `inspect_state` нужно расширить на контур герметика и винты. Для тестов достаточно состояния симулятора; `capture` не входит в основные маршруты. Предлагаемые идентификаторы: `cap`, `hatch_seat`, `body`, `sealant_path`, `side_1`…`side_4`, `hole_1`…`hole_4`, `screw_1`…`screw_10`, `screw_tray`, `patron`, `vacuum`, `sealant_tool`, `magnetic_tool`, `torque_tool`. Каждый эксперимент начинается в домашней позиции. К объекту робот идёт через безопасную точку над ним; после контакта сначала отходит по оси инструмента. В конце он возвращает инструмент в стойку и идёт домой. Ветви движения при `collision` должны вести к остановке; при неопределённом состоянии нельзя продолжать перенос или отпускание детали. Число повторных попыток задаёт `recovery_attempts_per_component`. ## I. Захват и установка люка ### 1. Базовая установка **Запрос модели:** «Возьми люк присоской, перенеси на корпус, установи, отпусти и проверь итоговое положение». **Условия:** люк лежит в известной области; присоска в стойке; посадочное место свободно. **Действия робота:** найти присоску → подойти и опуститься к гнезду → захватить инструмент → подняться → найти люк → подойти над ним и опуститься до контакта → включить захват → поднять люк → найти посадочное место → перенести люк над ним → совместить → опустить до посадки → отпустить → отвести присоску → проверить положение люка → вернуть присоску в стойку → домой. **Проверка плана:** работа с деталью не начинается раньше подтверждённого захвата инструмента; `verify` следует за `release`. ### 2. Люк с изменённой исходной ориентацией **Запрос:** «Установи люк на корпус, даже если он лежит повёрнутым относительно посадочного места». **Условия:** положение люка обнаруживается во время выполнения; его начальный угол отличается от целевого. **Действия робота:** взять присоску → обнаружить фактическую позу люка → подойти и захватить его → поднять выше стапеля → перенести в безопасную точку над корпусом → повернуть и совместить с `hatch_seat` через `align` → опустить → отпустить → отвести инструмент → проверить установку → вернуть инструмент → домой. **Проверка плана:** план использует результат `detect`, а не предполагает исходную ориентацию детали из текста запроса. ### 3. Люк и посадочное место обнаруживаются отдельно **Запрос:** «Найди люк и его посадочное место, затем установи люк и проверь результат». **Условия:** обе позы уточняются при выполнении. **Действия робота:** взять присоску → `detect(cap)` → подойти, опуститься, захватить и поднять люк → `detect(hatch_seat)` → перенести над найденной точкой → совместить → опустить → отпустить → отвести инструмент → проверить → вернуть инструмент → домой. **Ветви и проверка:** `not_found` у любого обнаружения ведёт к `replan_required`; перенос к посадочному месту не начинается, пока оно не найдено. ### 4. Неудачный первый захват **Запрос:** «Установи люк; если присоска не удержала его с первого раза, безопасно повтори попытку». **Условия:** исполнитель один раз возвращает `grasp_failed` при захвате люка; `recovery_attempts_per_component = 1`. **Действия робота:** взять присоску → найти люк → подойти и опуститься → попытаться захватить. При `grasp_failed`: остановиться → проверить, пуст ли захват и где находится люк → заново обнаружить люк → повторить подход, опускание и захват один раз → поднять, перенести, совместить, установить, отпустить и проверить → вернуть инструмент → домой. **Проверка плана:** после `grasp_failed` робот не выполняет `lift` вслепую; второй отказ заканчивается остановкой и уведомлением. ### 5. Исправление положения после установки **Запрос:** «Установи люк и проверь положение. Если он оказался вне допуска, поправь его одной повторной установкой». **Условия:** первая посадка даёт `verify:not_ok`; люк остаётся доступным для повторного захвата. **Действия робота:** выполнить установку как в эксперименте 1 → получить `not_ok` → обнаружить фактическое положение люка → подойти присоской, опуститься, снова захватить и поднять → повторно обнаружить посадочное место → перенести, совместить, опустить и отпустить → повторить `verify` → вернуть инструмент → домой при `ok`. **Проверка плана:** не более одной повторной установки; при неизвестном состоянии детали или второй неуспешной проверке граф заканчивается `operator_required`. ## II. Работа с герметиком Для этих экспериментов `sealant_path` разбит на именованные участки `side_1`…`side_4`. Герметик условный: проверяются маршрут робота и изменение состояния контура в симуляторе. ### 6. Один прямой участок **Запрос:** «Нанеси герметик на первый прямой участок посадочного контура корпуса». **Условия:** инструмент в стойке; корпус закреплён. **Действия робота:** найти инструмент → подойти, опуститься и взять его → подняться → найти `side_1` → подойти над началом участка → опустить сопло → выполнить `dispense(side_1)` → отвести сопло вверх → вернуть инструмент → домой → проверить, что обработан именно `side_1`. **Проверка плана:** нет подачи при перемещении от стойки к корпусу. ### 7. Два смежных участка **Запрос:** «Нанеси непрерывную линию герметика на первую и вторую стороны посадочного контура». **Условия:** стороны соединяются одним углом. **Действия робота:** взять инструмент → обнаружить контур → подойти к началу `side_1` → опустить сопло → пройти `side_1` → перейти через угол без отвода от рабочей высоты → пройти `side_2` → остановить подачу → поднять инструмент → вернуть в стойку → домой → проверить оба участка. **Проверка плана:** план сохраняет порядок смежных участков и не поднимает сопло посреди непрерывной линии. ### 8. Замкнутый контур **Запрос:** «Нанеси герметик по всему замкнутому контуру посадочного места люка». **Условия:** четыре стороны доступны инструменту. **Действия робота:** взять инструмент → найти контур → подойти к выбранному началу → опустить сопло → последовательно выполнить `dispense` для четырёх сторон и углов → завершить контур в точке начала → поднять сопло → вернуть инструмент → домой → проверить полноту контура. **Проверка плана:** перечислены все четыре участка ровно по одному разу; конечная проверка относится ко всему контуру. ### 9. Прерывание подачи **Запрос:** «Нанеси герметик по контуру; если подача прервётся, возобнови её с места разрыва и закончи контур». **Условия:** на третьем участке один раз приходит `dispense:interrupted`. **Действия робота:** взять инструмент → обработать первые два участка → начать третий → при прерывании остановить действие и отвести сопло → проверить состояние обработанных участков и последнюю подтверждённую точку → снова подойти к началу недостающего отрезка → опустить сопло → закончить третий и четвёртый участки → проверить контур → вернуть инструмент → домой. **Проверка плана:** повторно не проходится весь контур; неизвестная точка разрыва ведёт к `replan_required`. ### 10. Герметик и установка люка **Запрос:** «Нанеси герметик по всему посадочному контуру, затем возьми люк присоской и установи его на корпус». **Условия:** люк лежит отдельно; для его установки после отпускания предусмотрена виртуальная фиксация. **Действия робота:** взять инструмент герметика → обработать четыре участка → проверить контур → вернуть инструмент → взять присоску → найти и захватить люк → поднять и перенести его над корпусом → совместить с посадочным местом → опустить и прижать → отпустить → проверить положение люка → вернуть присоску → домой. **Проверка плана:** присоска не берётся, пока инструмент герметика не возвращён; люк не опускается на неполный контур. ## III. Подача винтов виртуальному оператору В этих сценах люк уже установлен и зафиксирован, а четыре отверстия подготовлены. В лотке всегда находятся десять одинаковых винтов. Для задания можно выбрать любой доступный винт; проверка плана должна требовать нужное число **разных** винтов, а не заранее заданные номера. Магнитный инструмент переносит винт к конкретному отверстию. Винт не вставляется: после подтверждённой позы передачи робот отпускает винт виртуальному оператору, винт удаляется из сцены, а отверстие остаётся пустым. Затяжка выполняется имитационно: в опытах 14–20 берётся затяжной инструмент, подходит к каждому отверстию и выполняет видимый поворот. ### 11. Один винт **Запрос:** «Возьми винт из лотка и поднеси его к первому отверстию люка для передачи оператору». **Действия робота:** взять магнитный инструмент → найти один доступный винт в лотке → подойти и опуститься к нему → захватить и поднять → найти `hole_1` → перенести винт над отверстием → совместить оси → коротко опуститься до точки передачи → отпустить винт виртуальному оператору → отвести инструмент по оси → вернуть инструмент → домой. **Проверка плана:** передача невозможна до подтверждённого захвата винта и достижения позы передачи у нужного отверстия; отверстие после передачи остаётся пустым. ### 12. Два винта по диагонали **Запрос:** «Передай два винта в диагонально противоположные отверстия люка». **Действия робота:** взять магнитный инструмент → передать первый винт по цепочке «найти → захватить → поднять → перенести → совместить → опустить → передать → отвести» → подняться над люком → вернуться к лотку → повторить для диагонально противоположного отверстия → проверить обе передачи → вернуть инструмент → домой. **Проверка плана:** второму винту соответствуют другая ячейка лотка и другое отверстие. ### 13. Три винта в разные отверстия **Запрос:** «Передай три разных винта в три разных отверстия люка». **Действия робота:** взять магнитный инструмент → последовательно взять три винта из разных ячеек и передать их в отверстия `hole_1`, `hole_2`, `hole_3`; перед каждым переносом подтвердить захват, перед передачей — достигнутую позу → проверить три пары винт-отверстие → вернуть инструмент → домой. **Проверка плана:** нет повторного использования ячейки или отверстия; после каждой передачи робот возвращается к лотку с пустым магнитным захватом. ### 14. Четыре винта крестовым порядком **Запрос:** «Передай четыре винта в отверстия люка в порядке `hole_1 → hole_3 → hole_2 → hole_4`». **Действия робота:** выполнить четыре передачи в крестовом порядке, каждый раз подтверждая захват нового винта и позу передачи у нужного отверстия → проверить четыре пары винт-отверстие → вернуть магнитный инструмент → домой. **Проверка плана:** каждый винт используется ровно один раз; затяжной инструмент не берётся. ### 15. Ошибка захвата винта **Запрос:** «Передай четыре винта; если один винт не захватится с первого раза, проверь состояние и повтори захват один раз». **Условия:** первый захват третьего выбранного винта даёт `grasp_failed`; разрешена одна повторная попытка. **Действия робота:** передать первые два винта → на третьем остановиться, проверить состояние захвата и повторно обнаружить винт → повторить его захват один раз → передать третий и четвёртый винты → проверить четыре пары → вернуть инструмент → домой. **Проверка плана:** ошибка обрабатывается у соответствующего винта; уже переданные винты не возвращаются в сцену; повторный отказ ведёт к остановке. ## IV. Полная сборка и перепланирование ### 16. Герметик, люк и один винт **Запрос:** «Нанеси герметик на заданный участок, установи люк и передай один винт к отверстию». **Действия робота:** взять инструмент герметика → обработать участок → вернуть инструмент → взять присоску → захватить, перенести, совместить, опустить и отпустить люк → проверить его положение → вернуть присоску → взять магнитный инструмент → перенести один винт к отверстию и передать оператору → вернуть инструмент → домой. **Проверка плана:** зависимости «герметик → люк → винт» соблюдены; винт не устанавливается. ### 17. Полный контур и две передачи **Запрос:** «Нанеси полный контур герметика, установи люк и передай два диагональных винта». **Действия робота:** пройти четыре участка герметика и проверить контур → вернуть инструмент → присоской установить и проверить люк → вернуть присоску → магнитным инструментом передать два диагональных винта → проверить две пары → вернуть инструмент → домой. **Проверка плана:** если контур не подтверждён, план не переходит к установке люка. ### 18. Полная сборка **Запрос:** «Нанеси герметик по контуру, установи люк и передай четыре винта крестовым порядком». **Действия робота:** полный контур герметика → проверка → смена на присоску → захват и установка люка → проверка → смена на магнит → четыре передачи винтов → итоговая проверка люка и пар винт-отверстие → вернуть инструмент → домой. **Проверка плана:** у каждого из четырёх винтов есть собственные узлы захвата и передачи; итоговая проверка охватывает всю сборку. ### 19. Полная сборка при смещённых деталях **Запрос:** «Собери узел полностью. Люк и лоток могут лежать не в номинальных местах — используй их фактическое положение». **Условия:** симулятор смещает люк и лоток в допустимой рабочей области; `detect` возвращает их актуальные позы. **Действия робота:** нанести и проверить герметик → обнаружить фактическую позу люка, захватить и установить его → проверить → обнаружить фактическую позу лотка и по очереди найти четыре винта → передать их в четыре отверстия → проверить сборку → вернуть инструмент → домой. **Проверка плана:** план не подставляет прежние координаты люка и винтов после нового `detect`; зависимости операций сохраняются. ### 20. Полная сборка с несколькими сбоями **Запрос:** «Выполни полную сборку. При прерывании подачи герметика, неудачном захвате винта или ошибке установки люка восстанови работу, если состояние известно и разрешена одна повторная попытка». **Условия:** один раз прерывается `dispense` на `side_3`, первый захват одного винта неудачен, первая проверка положения люка возвращает `not_ok`. **Действия робота:** начать контур → определить необработанный отрезок и завершить его с последней подтверждённой точки → проверить контур → установить люк → при `not_ok` заново обнаружить и один раз переставить его → проверить → передавать винты; при `grasp_failed` остановиться, проверить состояние и один раз повторить захват нужного винта → проверить четыре пары винт-отверстие и положение люка → вернуть инструмент → домой. **Проверка плана:** восстановление начинается от подтверждённого состояния; лимит попыток соблюдён; любое `unknown` или повторный отказ ведёт к остановке и уведомлению. ## Общие критерии оценки графа Для каждого ответа планировщика следует проверить существование всех узлов, указанных в `next`; соблюдение зависимостей операций; отсутствие физически невозможных переходов, например `transfer` без подтверждённого захвата; корректное завершение аварийных ветвей; соблюдение лимита повторных попыток. Семантическая проверка графа полезнее посимвольного сравнения с одним образцом JSON.