# План реализации эталонного управления Grant > **Для агента-разработчика:** выполняйте задачи по порядку, отмечайте выполненные шаги в чекбоксах. **Цель:** обеспечить воспроизводимое эталонное движение робота во всех 20 экспериментах Grant и добавить к существующей команде запуска режимы `view` и `execute`. **Архитектура:** существующий спавнер создаёт начальную сцену. Отдельный supervisor пакета `iiwa_experement` управляет захватом и позами объектов в Webots. Эталонный исполнитель синхронизирует его с геометрией MoveIt, вызывает существующий сервер движения `cobot/move_to_pose`/`cobot/move_to_named` и записывает действия в машинно-читаемый журнал. **Стек:** Ubuntu 24.04, ROS 2 Jazzy, `rclpy`, MoveIt 2/`moveit_msgs`, Webots R2025a, Python Supervisor, `webots_ros2_driver`, PyYAML, pytest. **Спецификация:** [Описание согласованной архитектуры](../specs/2026-11-08-grant-reference-control-design.md). ## Общие ограничения - Работать в существующей ветке `experement`. Перед правками проверить `git status`. В файле `src/iiwa_experement/worlds/grant/iiwa-grant.wbt` уже есть чужие незакоммиченные изменения и побочные файлы Webots: сохранить их, не сбрасывать и не перезаписывать целиком. - Весь новый исполняемый код, конфигурация, launch, тесты и документация пакета размещаются в `src/iiwa_experement/`. OBJ/MTL использовать из `src/iiwa_description/objects/grant/`. Постоянный `patron` остаётся на роботе. - `mode:=view` — режим по умолчанию; робот не движется. `mode:=execute` начинает движение только после `scene_ready`, готовности контроллера, MoveIt и supervisor, появления свежего состояния робота и задержки 3,0 секунды симуляционного времени. - Запуск только в Webots: в включаемом bringup остаётся `simulate:=true`. Не отправлять эталонные траектории на физический робот через FRI. - Винты не вставляются и не затягиваются. Робот подносит винт к конкретному отверстию, отпускает его виртуальному оператору, после чего винт удаляется из сцены, а отверстие остаётся пустым. В эталонных маршрутах робот берёт затяжной инструмент и дотягивает винты, предварительно наживлённые человеком. Считается, что человек установил винты неплотно; окончательную затяжку выполняет робот. - Перемещения к инструментам, извлечение инструмента, перенос люка и винтов, посадка люка, проход соплом вдоль контура и подход к отверстиям выполняются реальными траекториями робота. Без движения меняются только состояние захвата и состояние передачи винта оператору. - Единицы: метры и радианы. Явно задавать преобразования `world`, `base_link`, `patron`, `tcp`, точки крепления и рабочего кончика инструмента. Использовать активный TF/настройки, а не координаты основания, переписанные из текста. - Перед каждым движением в MoveIt должны быть все препятствия и прикреплённые объекты; обновление сцены подтверждается до планирования. Контакт допускается только для нужного инструмента и цели на нужном этапе. - При неопределённой геометрии, столкновении, устаревшем состоянии, неудачном захвате или ошибке движения остановиться и записать причину. Не повторять движение вслепую. ## Что прочитать перед правками 1. `src/iiwa_experement/config/scenes.yaml`; `src/iiwa_experement/iiwa_experement/{scenario.py,scene_spawner.py,webots_nodes.py,runtime_config.py}`; `src/iiwa_experement/launch/experiment.launch.py`; `src/iiwa_experement/doc/{experements.md,experiment-scenes.md,README.md}`. 2. `src/iiwa_description/urdf/tools/patron.xacro`; `src/iiwa_description/worlds/iiwa.wbt`; `src/iiwa_description/resource/protos/CollaborativeRobotTable.proto`; модели `src/iiwa_description/objects/grant/` и изображения `output/reference_photo/grant/`. 3. `src/iiwa_planning/scripts/move_to_pose_server.py`; `src/iiwa_msgs/action/MoveToPose.action`; `src/iiwa_msgs/srv/MoveToNamedPose.srv`; `src/iiwa_config/config/moveit/iiwa7.srdf`; `src/iiwa_bringup/launch/supported/{moveit_nodes.py,webots_spawn.launch.py}`. 4. Исходные описания экспериментов: `docs/superpowers/specs/2026-11-08-grant-experiments.md` и сцен: `docs/superpowers/specs/2026-11-08-grant-scenes.md`. После задачи 1 актуальной версией новых правил про винты будут файлы `src/iiwa_experement/doc/`. ## Назначение файлов | Файлы | Назначение | | --- | --- | | `config/scenes.yaml`, `iiwa_experement/scenario.py`, `doc/experements.md`, `doc/experiment-scenes.md` | Обновлённые 20 заданий, состав инструментов и сбои; без установки и затяжки винтов | | `config/geometry.yaml`, `iiwa_experement/geometry.py` | Крепление и рабочие кончики инструментов, захват/посадка люка, контур, отверстия, точки передачи, измеренные зазоры | | `worlds/grant/iiwa-grant.wbt`, `iiwa_experement/webots_nodes.py` | Отдельный supervisor и устойчивые имена управляемых объектов | | `iiwa_experement/grant_supervisor.py`, `iiwa_experement/object_state.py` | Следование захваченных объектов за роботом, команды и автомат состояний | | `iiwa_experement/planning_scene.py` | Препятствия и прикреплённые объекты MoveIt | | `iiwa_experement/motion.py` | Вызовы существующего сервера движения, проверка фактической позы и остановка | | `iiwa_experement/operations.py` | Захват, возврат инструмента, перенос, герметик, люк, передача винтов | | `iiwa_experement/reference_scenarios.py` | Маршруты 1–20 и ограниченное восстановление после сбоев | | `iiwa_experement/reference_runner.py`, `iiwa_experement/trace.py` | Ожидание готовности, выполнение, остановка, журнал JSONL и итог | | `iiwa_experement/runtime_config.py`, `launch/experiment.launch.py`, `setup.py`, `package.xml`, `doc/README.md` | Режимы запуска, зависимости, исполняемые файлы и инструкция | | `test/test_*.py` | Проверки контрактов, геометрии, запуска, состояний, сцены MoveIt и маршрутов | ## Проверки по всем задачам Каждая задача ниже содержит собственные тесты и проверку результата. Таблица показывает, какой результат проверяется сразу после задачи; задача 10 повторяет проверки в полных прогонах Webots. | Задача | Что проверить | | --- | --- | | 1. Контракт опытов | Все 20 заданий согласованы с передачей винта; в 11–20 по десять винтов, нет затяжки | | 2. Геометрия | Инструмент крепится на конце `patron`; позы люка, контура и отверстий меняются вместе с объектами | | 3. Supervisor | Захват и отпускание меняют состояние одного нужного объекта; при ошибке состояние не меняется | | 4. MoveIt | Геометрия препятствий и прикреплённых объектов совпадает с Webots и подтверждена сервером движения | | 5. Движение | Устаревшие `/joint_states`/TF, неподтверждённая сцена и отказ action блокируют следующую траекторию | | 6. Люк и герметик | Робот действительно переносит люк и ведёт сопло по нужным участкам; объекты не скачут к цели | | 7. Винты | Винт переносится магнитом к заданному отверстию и передаётся оператору; отверстие остаётся пустым | | 8. Маршруты | Все 20 последовательностей различаются по задаче; `grasp_failed` использует текущую позу и одну повторную попытку | | 9. Запуск | `view` не создаёт клиента движения, `execute` ждёт готовности и заданной задержки перед первым движением | | 10. Итог | Все 20 прогонов проверены в Webots; сбои останавливают небезопасное продолжение | --- ### Задача 1. Исправить контракт 20 экспериментов **Файлы:** изменить `src/iiwa_experement/config/scenes.yaml`, `src/iiwa_experement/iiwa_experement/scenario.py`, `src/iiwa_experement/doc/experements.md`, `src/iiwa_experement/doc/experiment-scenes.md` и затронутые `test/test_scenario.py`, `test/test_webots_nodes.py`, `test/test_package_layout.py`. Исторические копии спецификаций не переписывать молча; из документации пакета дать ссылку на новую спецификацию. **Интерфейс:** `load_scenario(experiment_id, manifest_path) -> ScenarioSpec` остаётся единственным источником начальных поз; сбои описываются в YAML. - [ ] **Шаг 1. Написать проверки, которые пока не проходят.** В 14–15 и 16–20 нет `torque_tool`; в 11–20 ровно десять винтов. Нет сбоев `tighten` и `seat_screw`. В 15 сохранён `grab:grasp_failed` для третьего выбранного винта. Смещения сцены 3 и 19 сохранены. - [ ] **Шаг 2. Убедиться, что проверки падают.** `source /opt/ros/jazzy/setup.bash && source install/setup.bash && pytest -q src/iiwa_experement/test/test_scenario.py src/iiwa_experement/test/test_webots_nodes.py`. - [ ] **Шаг 3. Обновить YAML и описания.** 11 — передать один винт у `hole_1`; 12 — два по диагонали; 13 — три разных отверстия; 14 — четыре в порядке `hole_1→hole_3→hole_2→hole_4`; 15 — те же четыре, отказ третьего захвата и одна повторная попытка. 16 — участок герметика, люк, одна передача; 17 — полный контур, люк, две диагональные передачи; 18 — полный контур, люк, четыре передачи; 19 — то же со смещёнными люком и лотком; 20 — то же с прерыванием герметика, ошибкой первой посадки люка и неудачным захватом винта. Удалить затяжку и ошибку момента из 15. Каталог `TOOL_NAMES` менять только после проверки зависимостей; неиспользуемая модель может остаться в `ASSETS`, но не должна появляться в этих опытах. - [ ] **Шаг 4. Повторить тесты.** Все тесты сцен проходят; документация пакета не называет переданный винт установленным. - [ ] **Шаг 5. Зафиксировать изменение контракта отдельным коммитом.** ### Задача 2. Измерить и описать рабочую геометрию **Файлы:** создать `config/geometry.yaml`, `iiwa_experement/geometry.py`, `test/test_geometry.py`; при необходимости обновить `setup.py`. **Интерфейсы:** `load_geometry(path) -> GeometrySpec`; `world_target(spec: ScenarioSpec, geometry: GeometrySpec, name: str) -> Transform`; `tcp_goal(target: Transform, tcp_to_work_tip: Transform) -> Transform`. В `GeometrySpec` хранить `tool_mounts`, `tool_work_tips`, `cap_grip`, `cap_seat`, `sealant_segments`, `holes`, зазоры подхода/передачи/переноса, допуски позы и ограниченные скорости. Единицы и исходный фрейм обязательны для каждого преобразования. - [ ] **Шаг 1. Написать проверки геометрии.** После масштаба Webots длина инструментов по вертикали 0–0,14 м; у герметика OBJ 0–0,014 м и масштаб ×10. `tcp` расположен на 0,0552 м от `patron`. Десять винтов имеют разные ID. Отверстия смещаются с корпусом в опыте 3, винты с лотком в 19. Проверить равенство `T_world_tcp · T_tcp_mount = T_world_tool_mount`. - [ ] **Шаг 2. Убедиться, что тест падает.** `pytest -q src/iiwa_experement/test/test_geometry.py`. - [ ] **Шаг 3. Измерить точки в OBJ и Webots.** Найти конец инструмента, входящий в `patron`, рабочий кончик, плоскость захвата люка, высоту посадки, начало/конец и углы контура, оси и высоты отверстий. Записать численные преобразования и консервативные габариты столкновений в `geometry.yaml`. Сверить XY отверстий стандартного корпуса с `(0.2243,0.1052)`, `(0.2852,0.0443)`, `(0.3757,0.1348)`, `(0.3148,0.1957)`. Не придумывать неизвестную высоту Z: измерить и занести её вместе с результатом проверки. - [ ] **Шаг 4. Реализовать загрузку и композицию преобразований.** Отвергать отсутствующие фреймы, нечисловые значения и неположительные зазоры. Позы смещённых целей вычислять из `ScenarioSpec`. - [ ] **Шаг 5. Прогнать тесты и визуально проверить все инструменты на конце `tcp`.** Результат измерений записать в документацию пакета. - [ ] **Шаг 6. Зафиксировать геометрию отдельным коммитом.** ### Задача 3. Добавить supervisor для захвата и перемещения объектов **Файлы:** изменить `worlds/grant/iiwa-grant.wbt`, `iiwa_experement/webots_nodes.py`, `launch/experiment.launch.py`, `test/test_world.py`, `test/test_webots_nodes.py`, `test/test_launch.py`; создать `iiwa_experement/grant_supervisor.py`, `iiwa_experement/object_state.py`, `test/test_object_state.py`, `test/test_grant_supervisor.py`; обновить `setup.py` и зависимости в `package.xml`. **Интерфейс:** добавить в мир второй `Robot { name "GrantAttachmentSupervisor" supervisor TRUE controller "" }`. Встроенный `/Ros2Supervisor` продолжает создавать объекты. Сервис `/iiwa_experement/object_command` использует `webots_ros2_msgs/srv/SetString`: в JSON-поле `value` передаются `request_id`, `op`, `object`, `target`, `expected_state`. Топик `/iiwa_experement/object_state` типа `std_msgs/msg/String` публикует JSON с возрастающим номером версии, фактической позой, владельцем объекта и `last_request_id`. Команды: `attach_tool`, `dock_tool`, `grab_cap`, `place_cap`, `grab_screw`, `handoff_screw`, `inspect`. - [ ] **Шаг 1. Написать проверки автомата состояний.** Начальные состояния `tool=none`, `cap=free|installed|absent`, 10 доступных винтов. Запрещены захват без нужного инструмента, второй инструмент, второй переносимый объект, неверное отверстие и повторная передача. Переданный винт расходуется, отверстие остаётся `empty`. Отклонённая команда не меняет версию состояния. - [ ] **Шаг 2. Убедиться, что тесты падают.** `pytest -q src/iiwa_experement/test/test_object_state.py src/iiwa_experement/test/test_grant_supervisor.py`. - [ ] **Шаг 3. Сделать объекты адресуемыми.** У каждого управляемого `Solid` должен быть устойчивый `DEF` и прежнее поле `name`. Добавить невидимый внешний supervisor, сохранив стол, основного робота и встроенный `Ros2Supervisor`. Обновить тест мира: в шаблоне есть только один новый supervisor Robot, а iiwa и детали по-прежнему не предустановлены. - [ ] **Шаг 4. Реализовать и подключить внешний контроллер.** Использовать отдельное имя Robot и шаблон окружения `Ros2SupervisorLauncher` (`WEBOTS_CONTROLLER_URL`, `WEBOTS_HOME`); добавить его процесс к существующему launch без автоматических команд движения. После `scene_ready` найти объекты, получить фактическую позу `patron`/`tcp` из Webots либо проверенной TF-цепи. На каждом шаге Webots пересчитывать позу удерживаемого объекта по калиброванному преобразованию. Менять `locked`/физику только там, где это нужно. Во время переноса объект следует за роботом и не переносится скачком к цели. Публиковать новое состояние после подтверждения Webots; при недоступной позе или объекте отказывать. - [ ] **Шаг 5. Проверить модуль и выполнить пробу Webots без движения робота.** Supervisor находит `tcp` и все объекты начальной сцены, публикует их позы и отклоняет захват инструмента, когда робот ещё далеко от гнезда. Захват и следование инструмента при реальном движении проверить после задач 4–6, когда готовы сцена столкновений и адаптер движения. - [ ] **Шаг 6. Зафиксировать supervisor и автомат состояний.** ### Задача 4. Отразить мир Webots в сцене MoveIt **Файлы:** создать `iiwa_experement/planning_scene.py`, `test/test_planning_scene.py`; добавить нужные зависимости `moveit_msgs` и геометрических сообщений в `package.xml`. **Интерфейс:** `PlanningSceneBridge.initialize(spec, geometry)`, `attach(object_id, link_name, relative_pose, touch_links)`, `detach(object_id, world_pose)`, `remove(object_id)`, `assert_synchronized(expected_ids)`. Использовать `/apply_planning_scene`, `/get_planning_scene` или подтверждённый в установленном Jazzy эквивалент MoveItPy. Траектория допускается только после синхронизации сцены с сервером движения. - [ ] **Шаг 1. Написать проверки изменений сцены.** В мире MoveIt есть стол, стапель, корпус, стойка, лоток, люк, инструменты и все десять винтов, если они присутствуют в Webots. `attach` удаляет объект из мира и создаёт один прикреплённый к `tcp`/нужному звену объект. `detach` выполняет обратное действие; передача удаляет только выбранный винт. Разрешения на контакт не отключают проверки для всего робота. - [ ] **Шаг 2. Убедиться, что тест падает.** `pytest -q src/iiwa_experement/test/test_planning_scene.py`. - [ ] **Шаг 3. Реализовать обновление и подтверждение.** Габариты брать из `webots_nodes.py` и измерений задачи 2; учесть стол с верхом `Z=0.793` и запас безопасности. Отправлять изменения и читать сцену обратно. Проверить, что **отдельный монитор MoveItPy в `move_to_pose_server.py`** получил те же объекты до движения. В текущем `moveit_cpp.yaml` указан `/moveit_cpp/monitored_planning_scene`; он может не совпадать с топиком `move_group`. Если связь отсутствует, создать исправленную временную копию `moveit_cpp.yaml` в пакете и указать её во временном `setting.yaml`; глобальный `iiwa_config` не менять. Если Webots и MoveIt расходятся — остановиться и сообщить ошибку синхронизации. - [ ] **Шаг 4. Сверить RViz и Webots.** Начальные объекты совпадают по позам; прикреплённые инструмент/люк/винт движутся с роботом; оставшиеся винты остаются препятствиями. - [ ] **Шаг 5. Зафиксировать мост к MoveIt.** ### Задача 5. Сделать адаптер безопасных перемещений **Файлы:** создать `iiwa_experement/motion.py`, `test/test_motion.py`; добавить в `package.xml` зависимости `iiwa_msgs`, `sensor_msgs`, `tf2_ros` и другие фактически используемые. **Интерфейс:** `MotionAdapter.move_pose(goal: Transform, planner: Literal['ptp','lin'], speed: float, label: str) -> MotionResult`; `move_named('home', speed) -> MotionResult`; `stop(reason)`. Вызывать `cobot/move_to_pose`, `cobot/move_to_named`, `cobot/stop`. Целевую позу `tcp` переводить через активный TF в `base_link`, затем в углы KUKA ABC (`ZYX`, радианы) из `MoveToPose.action`. - [ ] **Шаг 1. Написать тесты с поддельными клиентами.** Цель не отправляется при устаревших `joint_states`/TF, неподтверждённой сцене, недопустимой скорости или уже остановленном роботе. Отказ, отмена и тайм-аут вызывают `stop` и блокируют следующий участок. После успеха проверяются фактические положение и ориентация TCP. - [ ] **Шаг 2. Убедиться, что тест падает.** `pytest -q src/iiwa_experement/test/test_motion.py`. - [ ] **Шаг 3. Реализовать адаптер.** Свободный перенос — через `ptp`/OMPL с полной сценой столкновений; короткий осевой подход/отход и стороны контура — через `lin`. Ограничить время планирования/выполнения, проверять свежесть обратной связи и готовность контроллера. Не применять `iiwa_planning/direct_trajectory.py` и TCP gizmo для эталона. При `execute` выключать gizmo во временной конфигурации запуска, чтобы робот имел один источник команд. - [ ] **Шаг 4. Проверить движение в Webots без захвата.** Из дома пройти в измеренную свободную точку над рабочей зоной и вернуться. Сверить путь и пределы суставов. - [ ] **Шаг 5. Зафиксировать адаптер движения.** ### Задача 6. Реализовать операции с инструментами, люком и герметиком **Файлы:** создать `iiwa_experement/operations.py`, `test/test_operations.py`. **Интерфейс:** `take_tool(tool_name)`, `dock_tool(tool_name)`, `pick_cap()`, `place_cap(target_pose)`, `dispense(segment_name, start_fraction=0.0)`, `inspect_cap()`, `inspect_sealant()`. `take_tool` вставляет инструмент в `patron`; `dock_tool` возвращает его в гнездо. Каждая операция возвращает исход и пишет события. Владельцы состояния — только supervisor и мост MoveIt. - [ ] **Шаг 1. Написать тесты последовательностей.** Взятие инструмента: `дом/безопасная высота→точка над гнездом→осевое опускание→крепление→осевое извлечение`; возврат — обратный маршрут. Люк: `над люком→опускание→захват→подъём→перенос над корпусом→совмещение→опускание до посадки→отпускание→отход`. Люк должен двигаться вместе с TCP, без скачка к корпусу. Сопло проходит реальные 3D-участки четырёх сторон и смежные углы на рабочей высоте; сторона считается выполненной только после успешного движения. - [ ] **Шаг 2. Убедиться, что тест падает.** `pytest -q src/iiwa_experement/test/test_operations.py`. - [ ] **Шаг 3. Реализовать операции через калиброванные фреймы.** Позы TCP вычислять по целевой позе рабочего кончика. Подходить и отходить вдоль локальной оси цели; безопасную высоту переноса вычислять по габаритам переносимого объекта и препятствий. Перед захватом/отпусканием проверять фактическую близость и состояние. В 5 выполнить первую смещённую посадку и одну корректировку. В 9/20 хранить последнюю подтверждённую точку герметика и продолжать от неё. - [ ] **Шаг 4. Проверить отдельные операции в Webots через проверочный клиент.** Убедиться, что движутся робот, инструмент, люк и сопло, а объект не скачет к цели. Полные опыты 1, 6, 9 и 10 прогнать в задаче 10 после подключения эталонного исполнителя. - [ ] **Шаг 5. Зафиксировать операции.** ### Задача 7. Реализовать магнитный захват и передачу винта **Файлы:** дополнить `iiwa_experement/operations.py`, `iiwa_experement/object_state.py`, `test/test_operations.py`, `test/test_object_state.py`. **Интерфейс:** `pick_screw(screw_id)`, `handoff_screw(screw_id, hole_id)`. Итоговое событие — `delivered(screw_id,hole_id)`; состояний `seated` и `tightened` нет. - [ ] **Шаг 1. Написать тесты.** Можно выбрать любой из десяти доступных винтов. Взятый винт следует за рабочим концом магнита. Подход к отверстию выполняется по его оси. Передача запрещена, если винт не удерживается или фактическая поза TCP вне допуска у нужного отверстия. Успешная передача удаляет только один винт из Webots и MoveIt, оставляет отверстие пустым, другие винты — в лотке. - [ ] **Шаг 2. Убедиться, что тесты падают.** `pytest -q src/iiwa_experement/test/test_operations.py src/iiwa_experement/test/test_object_state.py`. - [ ] **Шаг 3. Реализовать маршрут робота.** `над выбранным винтом→осевое опускание→захват→осевой подъём→перенос на безопасной высоте→над отверстием→совмещение осей→короткое опускание до точки передачи→отпускание оператору→осевой отход`. Не опускать винт внутрь люка или корпуса. Между отверстиями проходить через проверенную безопасную область, не перемещаться поперёк люка на контактной высоте. - [ ] **Шаг 4. Проверить отдельную операцию передачи в Webots через проверочный клиент.** Робот и винт движутся вместе; сначала видны десять винтов, исчезает только переданный. Полные опыты 11, 12 и 14 прогнать в задаче 10. - [ ] **Шаг 5. Зафиксировать передачу винтов.** ### Задача 8. Описать 20 эталонных маршрутов и восстановление **Файлы:** создать `iiwa_experement/reference_scenarios.py`, `test/test_reference_scenarios.py`; при необходимости дополнить `operations.py` проверкой `inspect_state`. **Интерфейс:** `build_reference_sequence(spec: ScenarioSpec) -> tuple[Operation, ...]`; `execute_sequence(sequence, context) -> RunResult`. Имена целей соответствуют `doc/experements.md`. Успешный маршрут заканчивается возвратом инструмента и переходом в `home`. - [ ] **Шаг 1. Написать табличные тесты для всех 20 ID.** Проверить порядок операций из задачи 1, возврат инструмента перед сменой, разные винты и нужное число отверстий. Для 3/19 брать смещённые реальные позы. Для 4/15/20: `grasp_failed→stop→inspect_state→повторное обнаружение→одна попытка`; для 5: `not_ok→одна корректировка люка`; для 9/20: продолжение с точки разрыва герметика. Повторный отказ и неизвестное состояние дают `stopped` без последующих движений. В журнале нет `seat_screw` и `tighten`. - [ ] **Шаг 2. Убедиться, что тесты падают.** `pytest -q src/iiwa_experement/test/test_reference_scenarios.py`. - [ ] **Шаг 3. Реализовать выбор маршрута и управляемые сбои.** Применять `ScenarioSpec.failures` по навыку, цели и номеру попытки. В `view` сбои не меняют сцену. Перед повтором читать фактическое состояние Webots и решать, повторить действие, пропустить уже выполненную передачу или остановиться. - [ ] **Шаг 4. Проверить тесты и список операций для всех ID.** В 13 три передачи, в 14/15 четыре; в 19 используются смещённые люк и лоток. - [ ] **Шаг 5. Зафиксировать маршруты.** ### Задача 9. Добавить `view` и `execute` в существующий launch **Файлы:** изменить `iiwa_experement/runtime_config.py`, `launch/experiment.launch.py`, `setup.py`, `package.xml`, `test/test_launch.py`, `test/test_runtime_config.py`; создать `iiwa_experement/reference_runner.py`, `iiwa_experement/trace.py`, `test/test_reference_runner.py`. **Интерфейс команды:** `ros2 launch iiwa_experement experiment.launch.py experiment_id:=N mode:=view|execute start_delay_sec:=3.0 trace_dir:=~/.ros/iiwa_experement/runs`. По умолчанию `mode=view` и указанный каталог журнала. В `execute` запускаются `grant_supervisor` и `reference_runner`; в `view` runner отсутствует, supervisor может работать только в режиме чтения. Журнал JSONL и `RunResult` содержат ID опыта, время симуляции, операцию, объект, цель, позы до/после, исход и изменение состояния. - [ ] **Шаг 1. Написать тесты запуска.** Неверный режим и отрицательная/нечисловая задержка отклоняются до Webots. В `view` нет клиента движения и целей. В `execute` отсутствие `scene_ready`, MoveIt/контроллера, свежих `joint_states` или остановленные часы симуляции не допускают старт. Первое движение — не раньше `scene_ready + start_delay_sec` по времени симуляции. Тайм-аут готовности завершает runner с ошибкой без движения. - [ ] **Шаг 2. Убедиться, что тесты падают.** `pytest -q src/iiwa_experement/test/test_launch.py src/iiwa_experement/test/test_reference_runner.py`. - [ ] **Шаг 3. Реализовать ожидание и журнал.** Ждать `scene_ready`, supervisor, MoveIt, сервисы/действия движения, свежие TF и `joint_states`; затем выдержать задержку по `/clock`. Публиковать краткий прогресс. Писать JSONL в `trace_dir/experiment__.jsonl`, раскрывая `~`, и сбрасывать данные после каждой операции. При успехе или остановке записать итог; Webots оставлять открытым для осмотра финальной сцены. - [ ] **Шаг 4. Проверить режимы вручную.** `view` показывает неизменную сцену сколько угодно; `execute` ждёт три секунды симуляции и начинает видимое движение. Проверить также `start_delay_sec:=0` и `start_delay_sec:=10`. - [ ] **Шаг 5. Зафиксировать launch и журнал.** ### Задача 10. Проверить безопасность и все 20 прогонов **Файлы:** обновить `src/iiwa_experement/doc/README.md`; создать `src/iiwa_experement/doc/reference-control-qa.md` с измерениями, результатами проверки столкновений и таблицей 20 опытов. Тесты дополнять по найденным конкретным ошибкам. **Интерфейс:** команда запуска и формат журнала из задачи 9 — контракт для последующего сравнения эталона с планами LLM. - [ ] **Шаг 1. Собрать пакет и запустить тесты.** `source /opt/ros/jazzy/setup.bash && colcon build --base-paths src --packages-up-to iiwa_experement --symlink-install`; затем `source install/setup.bash && colcon test --base-paths src --packages-select iiwa_experement && colcon test-result --verbose`. Исправить ошибки до заявления о готовности. - [ ] **Шаг 2. Проверить `view` для 1, 11, 16, 19, 20.** Сверить состав объектов, десять винтов, постоянный `patron`, отсутствие движения робота и изменений объектов после 10 секунд симуляции. - [ ] **Шаг 3. Запустить `execute` для каждого ID 1–20.** Для каждого записать: видимые движения, точность крепления инструмента к TCP, журнал, отсутствие запрещённых столкновений и проникновений, итоговую позу люка, участки герметика, пары переданных винтов, возврат инструмента и `home`. Если поза недостижима или опасна, исправить геометрию/сцену и повторить опыт и соседние затронутые варианты. Не обходить проверку столкновений. - [ ] **Шаг 4. Проверить ветви отказов 4, 5, 9, 15, 20.** Ровно одна разрешённая попытка восстановления; повторный отказ или неизвестное состояние останавливает робот. Винт не вставляется, отверстие пустое, затяжной инструмент не движется. - [ ] **Шаг 5. Описать команды запуска, параметры, формат журнала, численные калибровки, результаты тестов, ограничения и режим только симуляции.** Примеры: `ros2 launch iiwa_experement experiment.launch.py experiment_id:=1 mode:=view` и `ros2 launch iiwa_experement experiment.launch.py experiment_id:=1 mode:=execute start_delay_sec:=3.0`. - [ ] **Шаг 6. Зафиксировать документацию и проверенные исправления.** Просмотреть `git diff`; не включать посторонние изменения рабочего дерева. ## Критерии готовности В `view` сцена остаётся неподвижной. В `execute` робот выполняет все нужные движения для всех 20 сценариев; инструмент виден на кончике `patron`, люк перемещается вместе с роботом и оказывается на корпусе, сопло идёт по заданным участкам, каждый винт реально переносится к отверстию и передаётся виртуальному оператору без установки и затяжки. Сборка, тесты и документированные прогоны Webots успешны. Небезопасный или неудачный опыт остаётся незавершённым с явной причиной.