Skip to content

ПР07. Gazebo и мост ros_gz ​

Работа продолжает проектную систему. Рассчитывайте на одну лабораторную пару и примерно 2,5 часа самостоятельной доработки.

Для командного робота применяем этот опыт в маршруте «симуляция → реальный робот»: сначала проверяем дифференциальный проектный профиль, затем аппаратные источники данных. Учебные Ackermann-входы и критерии этой ПР сохраняются.

Входные данные ​

Распакуйте course kit: исходные файлы находятся в models/course_ackermann/, worlds/mcity-minimal.sdf и config/bridge.yaml. Скопируйте их в src/course_simulation/{models,worlds,config} и зафиксируйте версию комплекта. Контракты типов и QoS находятся в contracts/topics.md. Используйте дерево frames из ПР06; создайте launch-файл, адаптер Ackermann→Gazebo, адаптер одометрии и тесты. Запись этой работы сохраните локально как fixtures/pr07-sensors: она станет неизменным входом ПР08.

Цель ​

Запустить мир без графического окна и доказать по топикам полный путь /drive/safe → адаптер → bridge → Gazebo → odometry на единой временной шкале, а также зафиксировать частоты и системы координат всех сенсоров.

Критерии зачтено ​

Работа зачтена, когда выполнены все три стадии и прошла автопроверка:

СтадияЧто требуется
1 · Собратьheadless-запуск mcity-minimal.sdf с роботом и конфигурацией моста
2 · Сломатьв копии конфигурации неверны направление или топик IMU/команды
3 · Доказатьошибка найдена сопоставлением ros2 topic info -v, данных Gazebo и YAML; исправлена без второго моста; команда видна на обеих сторонах
Автопроверкаcheck_practice.py PR07 проверит src/**/*.sdf, src/**/bridge*.yaml, evidence/pr07/{sensors.json,headless.txt,bag-info.txt} и заявления public_tests_passed, defect_reproduced, defect_fixed, headless_verified, recording_verified, odometry_tf_owned; также требуется odom-adapter.md

Что получится ​

Вы запустите SDF-модель без графического окна, передадите безопасную команду Ackermann через адаптер и ros_gz, а затем прочитаете камеру, лидар, IMU, одометрию и их системы координат.

Главная проверка — полный путь /drive/safe → adapter → bridge → Gazebo → odometry и единая временная шкала.

Перед началом ​

  • подготовлен результат ПР06: дерево frames связно и у каждого ребра один владелец;
  • .course-kit/v1/tools/workstation_report.sh сохранён в evidence и не показывает критического сбоя выбранного рендерера;
  • нормативная связка записана в .course-kit/v1/config/baseline.lock.yaml (Ubuntu 26.04, ROS 2 Lyrical, Gazebo Jetty); фиксируйте фактические версии и результаты проверок в evidence. Перед началом обновите course kit: в прежней версии lock-файла оставалась связка Jazzy/Harmonic;
  • для Docker-пути workspace собран внутри образа, чьи Ubuntu/ROS/Gazebo версии соответствуют выбранной связке; каталоги build/, install/, log/ из другой ROS-среды не используются.

На лекции 06 успели разобрать tf2; URDF/Xacro и подробную работу в RViz завершим в начале занятия 07. Перед самостоятельным запуском симуляции разверните Xacro, запустите robot_state_publisher и проверьте в RViz с Fixed Frame=base_link фиксированные связи камеры, лидара и IMU по ПР06. Эти темы не считаются уже освоенными; новых условий зачёта к ПР06 этот разбор не добавляет.

Проверьте ros2 pkg prefix ackermann_msgs и ros2 pkg prefix ros_gz_bridge. ackermann_msgs может отсутствовать даже в образе desktop-full. После настройки официального apt-репозитория ROS 2 по инструкции установки установите недостающие пакеты выбранного дистрибутива:

bash
# В терминале уже выполнен source /opt/ros/<ваш-дистрибутив>/setup.bash
sudo apt update
sudo apt upgrade
sudo apt install "ros-${ROS_DISTRO}-ackermann-msgs" "ros-${ROS_DISTRO}-ros-gz"

Для Docker добавьте эти пакеты при сборке своего образа на базе выбранного osrf/ros:…-desktop-full; внутри Dockerfile команды apt-get выполняются от root без sudo. После apt-get update выполните apt-get upgrade -y --with-new-pkgs перед установкой новых ROS-пакетов: свежие интерфейсы могут быть несовместимы со старыми библиотеками базы. Повторите проверку внутри той же среды, где собираете workspace. Наличие пакета в ros2 pkg prefix ещё не доказывает, что его библиотека типов загружается; после установки проверьте создание publisher или сериализацию:

bash
python3 - <<'PY'
from ackermann_msgs.msg import AckermannDriveStamped
from rclpy.serialization import serialize_message
serialize_message(AckermannDriveStamped())
print('Ackermann type support загружается')
PY

Если здесь возникает undefined symbol внутри rosidl_typesupport, сначала устраните несовместимость пакетов окружения. Переписывать адаптер движения для исправления такой ошибки не нужно.

Пакеты и понятия ​

  • mcity-minimal.sdf — мир курса; его <include> уже добавляет одного робота. Модель — course-owned SDF с камерой, лидаром, IMU и одометрией; второй spawn той же модели не нужен.
  • Мир подключает модель по URI model://course_ackermann, поэтому рабочее пространство требует путь к моделям: export GZ_SIM_RESOURCE_PATH="$PWD/src/course_simulation/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}". Без этой переменной мир не загрузится («Unable to find uri model://course_ackermann»).
  • Адаптер — ваш код между ROS-командой и Gazebo: переводит смысл команды. Мост ros_gz — передатчик поддерживаемого типа между двумя транспортами.
  • Headless — запуск серверной части без графического окна. Для камеры и gpu_lidar нужен ещё рендерер: при headless:=true ваш launch передаёт Gazebo -s -r --headless-rendering (OGRE2/EGL). Один -s отключает окно клиента, но не обеспечивает работу этих сенсоров без X-сервера.
  • Временная шкала симуляции: сенсоры, контроллер и оценка используют одно время, привязанное через /clock (use_sim_time).

/drive/safe пока получает тестовую команду вашего источника: имя обозначает будущую границу, но supervisor появится только в ПР11. На этом этапе проверяем преобразование в симуляторе.

Пакет course_simulation использует rclpy, ackermann_msgs, geometry_msgs, nav_msgs, tf2_ros, ros_gz_bridge, robot_state_publisher, xacro, launch, launch_ros и ваш course_description из ПР06. Объявите используемые зависимости в package.xml, установите launch/config/model/world-файлы в share/course_simulation и зарегистрируйте исполняемые адаптеры. Это позволяет запускать пакет после colcon build, а не только из каталога с исходниками.

Обязательный контракт адаптера:

Вход /drive/safeВыход /vehicle/gz_cmd_vel
drive.speed, конечное числоv = clamp(speed, 0.0, 1.2); linear.x = v м/с
drive.steering_angle, конечное числоδ = clamp(angle, -0.45, 0.45) рад; angular.z = v · tan(δ) / 0.8 рад/с
NaN/∞ в любом поле или возраст header.stamp > 0,25 снулевой Twist, диагностическая причина; последняя команда не повторяется

Система Gazebo AckermannSteering принимает линейную и угловую скорости и сама вычисляет углы левого и правого колёс. Поэтому адаптер переводит центральный угол руля δ в угловую скорость корпуса ω = v · tan(δ) / L, где колёсная база модели L = 0,8 м. При v = 0 результат равен нулю: Ackermann-шасси не разворачивается на месте.

Что сделать ​

Первый результат на паре — живой /clock и один сенсор через bridge. Затем подключите команду и одометрию. Адаптер frames и запись завершаются на самостоятельной доработке; готовый bag потребуется ПР08.

  1. Запустите mcity-minimal.sdf с уже включённым роботом в headless-режиме и подключите конфигурацию моста из базовой версии курса. Реализуйте адаптер по таблице выше и добавьте модульные тесты как минимум для прямого движения, поворота, нулевой скорости, устаревшей и нечисловой команды. Один simulation.launch.py должен запустить сервер Gazebo, robot_state_publisher, один мост и оба адаптера и оставаться активным до Ctrl+C; проверки из других терминалов выполняются, пока этот launch продолжает работать. ROS-ноды, которые проверяют возраст команды или публикуют tf, используют use_sim_time=true; источник тестовой команды ставит свежий stamp из /clock.
  2. Измените один параметр сенсора и добавьте пространство имён в launch-файл. Namespace нод сам по себе не меняет абсолютные имена в bridge YAML и SDF: для команд ниже сохраните контрактные /vehicle/..., /drive/safe и /clock. Измерьте фактическую частоту (ros2 topic hz) и метки времени симуляции (ros2 topic echo /clock --once): заявленная и измеренная частоты должны быть сопоставлены с real-time factor и нагрузкой; объясните расхождение, а не требуйте точного совпадения с update_rate.
  3. В копии конфигурации моста неверно указаны направление или топик IMU либо команды. Сопоставьте ros2 topic info -v, имя источника в YAML и вывод gz topic -i/-e; исправьте конфигурацию без второго моста. При опечатке в Gazebo-топике bridge может оставаться ROS-издателем, хотя сообщений нет: проверьте это ограниченным по времени ros2 topic echo --once и чтением исходного Gazebo-топика. Для команды сравните содержимое ROS- и Gazebo-сообщений, а затем изменение одометрии и реальное перемещение корпуса в Gazebo. Колёса должны катиться вокруг поперечной оси; передние дополнительно поворачиваются рулём. Одометрия контроллера может изменяться даже при неверной физике модели. Без окна позы корпуса и колёс доступны через gz topic -e -t /world/mcity_minimal/dynamic_pose/info. ros2 topic info -v и gz topic -i показывают участников обмена, но сами по себе не доказывают прохождение команды.
  4. После исправления запишите не менее 30 секунд симуляционного времени в fixtures/pr07-sensors. Обязательные топики записи: /vehicle/camera/image_raw, /vehicle/scan, /vehicle/imu, /vehicle/odom, /tf, /tf_static и /clock. Динамическую связь odom→base_link публикует нормализующий адаптер этой работы; включите /tf в ту же запись. Сохраните вывод ros2 bag info в evidence/pr07/bag-info.txt; он должен показывать длительность ≥ 30 с и ненулевое число сообщений во всех семи топиках.

В этой версии модели исходная одометрия Gazebo имеет header.frame_id="course_ackermann/odom" и child_frame_id="course_ackermann/base_link". Зафиксируйте обе строки в sensors.json; нормализация до общеупотребимых odom/base_link выполняется только один раз, адаптером этой работы.

Для каждого сенсора зафиксировать название топика, тип, frame_id, источник времени, заявленную и измеренную частоту.

Ориентиры исходной модели до вашего изменения параметра:

ТопикТип ROS 2FrameЧастота по SDF
/vehicle/camera/image_rawsensor_msgs/msg/Imagecamera_link20 Гц
/vehicle/scansensor_msgs/msg/LaserScanlidar_link10 Гц
/vehicle/imusensor_msgs/msg/Imuimu_link100 Гц
/vehicle/odom_gz → /vehicle/odomnav_msgs/msg/Odometryисходные → нормализованные frames50 Гц

Источник stamp во всех четырёх строках — время симуляции. Для камеры, лидара и IMU настройте QoS sensor data (best effort, keep last) согласно контракту; одометрия и команды используют reliable. Профиль проверяйте через ros2 topic info -v: значения по умолчанию bridge не заменяют эту проверку.

Сохраните .sdf, YAML моста и evidence/pr07/{sensors.json,headless.txt,bag-info.txt,report.json}. Каталог с MCAP нужен локально для ПР08; его не требуется добавлять в обычный Git, если он превышает лимит хостинга.

Проверка ​

Обязательные файлы сдачи ​

  • В корне репозитория — AI_USAGE.md, с отдельной записью для PR07: модель, инструмент, характер помощи и способ проверки. Если ИИ не использовался, напишите это явно и поставьте ai_used: false в отчёте; файл всё равно нужен. Предыдущие записи сохраняйте. Правила и пример.
  • В evidence/pr07/report.json поле commit — SHA уже созданного коммита реализации. Отчёт фиксируется следующим коммитом; его SHA указывается при сдаче вместе со ссылкой на CI. Пошаговый пример двух коммитов.

Сначала соберите workspace. Затем в первом терминале из корня репозитория экспортируйте путь к моделям один раз для всех дочерних процессов launch и не затирайте уже заданные пути:

bash
colcon build --symlink-install --packages-up-to course_simulation
source install/setup.bash
mkdir -p evidence/pr07 fixtures
export GZ_SIM_RESOURCE_PATH="$PWD/src/course_simulation/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
gz sdf -k src/course_simulation/models/course_ackermann/model.sdf
ros2 launch course_simulation simulation.launch.py \
  world:=$PWD/src/course_simulation/worlds/mcity-minimal.sdf \
  bridge_config:=$PWD/src/course_simulation/config/bridge.yaml \
  headless:=true 2>&1 | tee evidence/pr07/headless.txt

Не завершайте первый терминал. Во втором терминале проверяйте живые процессы:

bash
source install/setup.bash
ros2 topic info -v /vehicle/gz_cmd_vel
gz topic -i -t /model/course_ackermann/cmd_vel
ros2 topic echo /clock --once
ros2 topic hz /vehicle/scan

Остановите topic hz через Ctrl+C. Пока ваш тестовый источник публикует свежую ненулевую команду в /drive/safe, проверьте её значения и ответ модели:

bash
ros2 topic echo /vehicle/gz_cmd_vel --once
timeout 3s gz topic -e -t /model/course_ackermann/cmd_vel
ros2 topic echo /vehicle/odom --once
ros2 run tf2_ros tf2_echo odom base_link --ros-args -p use_sim_time:=true

Повторное чтение одометрии при прямой команде должно показывать движение, а tf2_echo — то же положение. Завершив опыт, передайте нулевую команду. Проверку мира с model:// выполняет живой запуск: у gz sdf -k разрешение внешнего URI зависит от resolver и не заменяет runtime-проверку.

В третьем терминале создайте свежий каталог записи. Команда останавливает recorder сигналом SIGINT через 35 секунд; если Gazebo идёт медленнее реального времени и bag info показывает менее 30 секунд, повторите запись дольше. --use-sim-time делает длительность bag длительностью по /clock; без него bag info измеряет время компьютера, и 35 секунд записи не доказывают 30 секунд симуляции. Перед записью дождитесь живого /clock и всех сенсоров.

bash
source install/setup.bash
timeout --preserve-status --signal=INT --kill-after=5s 35s \
  ros2 bag record --storage mcap --use-sim-time -o fixtures/pr07-sensors \
  --topics /vehicle/camera/image_raw /vehicle/scan /vehicle/imu /vehicle/odom \
  /tf /tf_static /clock
ros2 bag info fixtures/pr07-sensors | tee evidence/pr07/bag-info.txt
colcon test --packages-select course_simulation
colcon test-result --verbose
python3 .course-kit/v1/tools/check_practice.py PR07 --submission .

Если fixtures/pr07-sensors уже существует, задайте новый путь вывода и только после успешного bag info используйте его как вход ПР08. Не запускайте отдельно второй экземпляр bridge: он создаст нескольких издателей тех же топиков. В записи /tf_static должны быть сохранённые fixed-преобразования из ПР06: оставьте robot_state_publisher активным и проверьте его transient local QoS, если поздно запущенный recorder не получил их. check_practice.py проверяет файлы и заявления отчёта; содержимое bag и поведение адаптеров проверяете вы.

Готово, если безоконный запуск остаётся активным во время проверок, сенсоры используют время симуляции и корректные системы координат, а /drive/raw не доходит до Gazebo в обход /drive/safe. Дополнительно: повторите запуск в Docker.

Диагностическая последовательность ​

  1. Проверить SDF модели отдельно от runtime: gz sdf -k.
  2. Убедиться, что симуляционное время растёт, а мир не стоит на паузе.
  3. Найти последнюю границу, на которой видна команда: safe-топик, выход адаптера, Gazebo Transport, изменение odometry.
  4. Проверить пару типов и direction в YAML; совпадение названий само по себе ничего не доказывает.
  5. Только после прохождения команды исследовать сочленения, оси, трение и физику.

Такой порядок отделяет ошибку маршрутизации от ошибки кинематики и уменьшает число случайных правок.

Как работаем на лабораторной ​

Сначала завершим разбор URDF/Xacro и RViz, затем проверим дерево tf2 и сопоставим названия топиков ROS 2 и Gazebo. После этого запустите мир только с сервером или устраните первую ошибку моста. Это первая работа проектной части; для экзаменационной траектории выполняется разбор готовой системы без обязательной сдачи ПР07.

Общий порядок: runbook · troubleshooting.

Декларация AI_USAGE.md оформляется по правилам курса, в том числе без ИИ.

Официальные справочники ​

Одометрия для следующего занятия ​

Выданная конфигурация bridge объявляет исходную одометрию на /vehicle/odom. В своей копии замените только её ros_topic_name на /vehicle/odom_gz или используйте remap /vehicle/odom:=/vehicle/odom_gz только на bridge. В отдельном адаптере переименуйте frames course_ackermann/odom → odom и course_ackermann/base_link → base_link, сохранив stamp, pose, twist и обе covariance. Как указано в contracts/topics.md, сообщения с другими исходными frames или нечисловыми значениями отклоняются. Публикуйте /vehicle/odom и единственный dynamic tf odom→base_link. Статические связи публикует robot_state_publisher из ПР06.

В evidence/pr07/odom-adapter.md сохраните исходные и нормализованные frames, владельцев tf и тест сохранения метки времени. Запись fixtures/pr07-sensors должна содержать /vehicle/scan, /vehicle/odom, /tf, /tf_static, /clock не менее 30 секунд; она нужна для ПР08. В эту работу не входит алгоритм сопоставления камеры с одометрией по очередям и P95 задержки.

Учебные материалы: CC BY 4.0 · Код: Apache-2.0