Фреймворк для заданий соревнований по программированию беспилотников на базе PyBullet. Первое задание — motion planner для гоночного дрона: участник получает трассу (ворота: положения, нормали, размеры) и должен выдать траекторию полёта, которая проходит ворота по порядку и не нарушает лимиты модели дрона. Счёт — время круга в симуляции.
Участник Фреймворк
───────── ───────────────────────────────────────────
solve(task) -> Trajectory → 1. StaticChecker: формат, лимиты (v, тяга,
наклон, рывок), ворота по порядку, клиренсы
2. Rollout: PyBullet-заезд со встроенным
tracking-контроллером; ворота/коллизии/
арена проверяются по фактическому полёту
3. Счёт = время пересечения последних ворот
Обе стадии читают одни и те же Constraints из yaml трассы, а условие задачи
генерируется из того же объекта — правила в условии и в чекере не могут
разойтись.
- Модель дрона —
racer.urdf(0.83 кг, thrust-to-weight 4.17, инерция моторовmotor_tau= 25 мс). Все параметры в<properties>URDF. - Контроллер — геометрический (SE(3)) position→attitude→RPM, один для всех участников; его способность отслеживать траекторию — часть «физики» задания: кинематически идеальная, но агрессивная траектория не пролетится.
- Траектория — сэмплы позиций (N×3) с равномерным dt ≤ 0.01 c; фреймворк строит по ним сплайн (v(0)=0 — старт с висения), yaw считает сам по касательной к скорости.
uv sync# 0. посмотреть трассу в GUI (ворота пронумерованы по порядку прохождения)
uv run clover2gym viz --task tracks/track1.yaml
# 1. быстрый чек без симуляции (секунды) — лимиты, ворота, клиренсы
uv run clover2gym check --task tracks/track1.yaml --solution solutions/template_solution.py
# 2. полный прогон с заездом в GUI и живым HUD (время/ворота/скорость/ошибка)
uv run clover2gym run \
--task tracks/track1.yaml --solution solutions/template_solution.py --gui
# 3. headless-прогон + JSON-отчёт (для CI и сравнения вариантов)
uv run clover2gym run \
--task tracks/track1.yaml --solution solutions/template_solution.py \
--out output/report.json --plot output/report.png
# 4. оценка на наборе трасс (скрытый финал считается так же)
uv run clover2gym run-set --task-dir tracks/ --solution solutions/template_solution.pyОтчёт показывает каждое нарушение с временем, значением и лимитом, а PNG —
план (зелёный) и факт (красный) сверху и в 3D. Стартовый код —
examples/template_solution.py; рабочие референсы — examples/baseline_solution.py
(просто и надёжно) и examples/fast_solution.py (лесенка скоростей с проверкой
полным конвейером — «спланировал → прогнал → уточнил»).
Yaml (clover2gym/tasks/tracks/track1.yaml): ворота (center, normal,
size, shape: rect|circle), старт, арена, constraints (все лимиты),
timing. Тот же файл — «выдача» участнику.
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml
# Машиночитаемый результат:
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml --json
# Без симуляции: повороты, рывок и остальные ограничения StaticChecker:
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml --mode dynamics
# Только прежняя оптимистическая нижняя граница:
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml --mode lower-bound
# Более широкий независимый поиск и сохранение лучшего допустимого полёта:
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml \
--starts 4 --iterations 120 --controller-trials 64 --trajectory-out /tmp/best-flight.npz
# Только явно выбранная готовая траектория (ограниченный поиск по масштабу):
.venv/bin/python examples/theoretical_time.py --task tracks/track2.yaml \
--solution examples/planning_solution.pyПо умолчанию скрипт строит и оптимизирует траекторию по YAML трека,
без загрузки planning_solution.py или другого готового решения. Свободны
точки пересечения ворот внутри проёмов, скорости и ускорения в них,
а также длительности отдельных участков. Участки задаются полиномами пятой
степени с непрерывными позицией, скоростью и ускорением. SLSQP минимизирует
время до последних ворот из нескольких начальных приближений, построенных
по геометрии трека. После разреженной проверки ограничений сетка сгущается
около экстремумов и рамок ворот. Каждый опубликованный допустимый кандидат
повторно проверяется StaticChecker на полной траектории:
скорость, тяга, наклон, рывок, повороты, направление и порядок ворот, клиренс,
арена, старт и хвост. Затем PyBullet проверяет фактический полёт с моделью
дрона, динамикой моторов и контроллером, указанными в YAML трассы.
Выводятся отдельно лучшее статически допустимое плановое время и лучшее
фактическое время успешного полёта. Неудачные кандидаты не становятся
результатом. Если допустимый вариант не найден, скрипт возвращает код 1,
а JSON содержит null в соответствующем поле best_static/best_controller.
JSON также содержит длительности участков, точки/скорости/ускорения в воротах,
статусы нелинейного решателя и отчёты всех проверенных попыток; прогресс идёт
в stderr. --starts, --iterations, --probes, --seed управляют независимой
оптимизацией. При необходимости выполняются до двух дополнительных этапов
сгущения сетки с половиной основного бюджета итераций каждый.
--controller-trials ограничивает число дополнительных кандидатов для
контроллера: варианты геометрии, замедления, уточнения переходов PASS/FAIL,
локальные изменения времени отдельных участков и точек прохода. Исходная
модель дрона, контроллер и ограничения трассы не меняются. В .npz
сохраняются positions и dt лучшего кандидата выбранного режима.
Найденное допустимое время является верхней оценкой оптимума соответствующей
модели. Это локальный численный поиск по выбранному семейству сплайнов:
ни статус сходимости, ни число начальных приближений не доказывают глобальный
минимум. Например, 17.762 с, найденные прежним масштабированием
planning_solution.py, не являются теоретическим пределом трека.
При явно переданном --solution сохраняется прежний ограниченный поиск
по общему масштабу времени solve(task). Только в этом режиме используются
--samples, --refinements, --scale-min, --scale-max; форма пути и
относительное распределение времени исходного решения фиксированы.
Нижняя граница считается отдельно: кратчайшая ломаная через проёмы ворот с
gate_margin, двойственная оценка длины с start_tol, лимит v_max и разгон
с нулевой скорости. Тяга a_max, гравитация и tilt_max_deg задают
оптимистический предел ускорения. Эта упрощённая граница не учитывает
динамику поворотов, рывок и контроллер; для tracks/track2.yaml — около
6.170 с. Она не является гарантированной границей счёта PyBullet.
Каждая трасса (публичная или скрытая) должна проходить измеренную приёмку: baseline проходит, fast-референс быстрее него минимум в 1.3 раза:
.venv/bin/python reference/acceptance.py --task clover2gym/tasks/tracks/track1.yaml
.venv/bin/python reference/acceptance.py --generate --seeds 1 2 3 # сгенерировать и принятьСкрытые трассы генерируются тем же генератором (clover2gym.tasks.generator)
по seed'ам, которые не публикуются.
- Описать спецификацию dataclass'ами в
clover2gym/tasks/spec.py(+ yaml). - Реализовать
solve()-контракт и чекеры под задачу (статика — без pybullet, симуляция — черезsim/world.py+sim/rollout.py). - Подключить команды в
runner.py; условие генерировать из того же спецификационного объекта (см.statement/).
.venv/bin/python -m pytest tests/ -qПроверяются: сплайн-производные и граничные условия, геометрия ворот (проход/промах/не по нормали), статический чекер на хороших/плохих траекториях, hover-и-полёт в headless-симуляции, детерминизм (два идентичных заезда дают одинаковый лог состояний).
- Ядро: спецификация, Trajectory, статика+симуляция, скоринг, CLI, графики
- Два референс-решения и измеренная приёмка трасс
- Генератор скрытых трасс: подобрать параметры и набрать финальный набор
- Условие задачи (
statement/) — генерация из Constraints - Конвертация Clover (xacro → URDF) как второй дрон