Друзья, всем привет! С каждым днем на дорогах становится все больше беспилотных автомобилей. Waymo уже обошел Lyft по количеству поездок в Сан-Франциско и подбирается к Uber. В Нью-Йорке, Шанхае и Москве можно увидеть сотни машин с датчиками на крыше. Что за магия приводит их в движение? 

В этой статье мы рассмотрим основные части self driving систем и создадим свой беспилотный автомобиль на Python в симуляторе Carla (UE4). В начале он будет просто стоять на месте, а в конце сможет ехать по маршруту и останавливаться перед препятствиями.

Статья получилась объёмной, поэтому я разделил её на две части. В первой части мы установим симулятор Carla, создадим наш первый беспилотный автомобиль и обучим его поддерживать постоянную скорость при движении прямо. Во второй части мы напишем PID-контроллер, реализуем управление рулём с помощью алгоритма Stanley и научим автомобиль останавливаться перед препятствиями.

Основные компоненты

Большинство беспилотных автомобилей состоят из модулей:

  1. Локализация — это точное определение местонахождения автомобиля с точностью до сантиметров. Используются сенсоры и высокоточные карты, в которых хранится множество данных: разметка, светофоры, знаки. Благодаря этому беспилотник не теряется даже при отсутствии разметки дороги, например, зимой.

  2. Восприятие — автомобиль распознает и отслеживает другие объекты при помощи датчиков, фиксируя их положение, скорость и направление движения.

  3. Предсказание — прогнозируется действие других участников движения. Это наиболее сложная задача, поскольку поведение человека часто непредсказуемо.

  4. Планирование — на основе локализации, восприятия и предсказания принимается решение о дальнейших действиях и передаются соответствующие команды системам управления. Все это происходит за миллисекунды.

  5. Управление (Control) — автомобиль реализует команды, которые непосредственно управляют движением.

Симулятор Carla

К сожалению, реальной машины с радарами и лидарами у нас нет. Зато у нас есть открытый симулятор беспилотного вождения Carla, где уже все настроено. Версий Carla много, и чем выше версия, тем мощнее нужен компьютер. Мы будем использовать Python 3.8 и Carla 0.9.15. Весь код доступен на GitHub.

Установка (для Windows)

  1. Скачайте и распакуйте симулятор Carla 0.9.15 (Windows). После распаковки перейдите в полученную папку. Убедитесь, что внутри есть папка WindowsNoEditor.

  2. Запустите WindowsNoEditor/CarlaUE4.exe. Откроется окно оператора. В нём вы можете полетать по карте.  После тестовых полетов можно выключить симулятор, он очень требователен к ресурсам ПК.

  3. Установите Python 3.8 (Windows Store)

  4. Откройте PowerShell в папке Carla (CARLA_0.9.15): Для этого кликните правой кнопкой мыши на пустом месте внутри папки и выберите «Open in Terminal». Если опция не отображается, зажмите Shift и снова нажмите правой кнопкой мыши — пункт должен появиться.

  5. Создайте и активируйте виртуальное окружение. Выполните следующие команды в терминале (важно использовать именно Python 3.8 из-за возможных проблем совместимости с более новыми версиями):
    py -3.8 -m venv venv
    venv\Scripts\activate

    После активации должна появиться надпись (venv).

  6. Установите необходимые пакеты:
    pip3 install -r WindowsNoEditor\PythonAPI\examples\requirements.txt
    pip3 install carla

  7. Запустите симулятор Carla в режиме ручного управления. Для этого снова откройте симулятор, запустив:
    WindowsNoEditor/CarlaUE4.exe
    Затем в терминале выполните команду:
    python WindowsNoEditor\PythonAPI\examples\manual_control.py

  8. Теперь вы можете управлять автомобилем с помощью клавиш WSAD, аналогично управлению в играх серии Need For Speed. Более подробная инструкция будет отображаться в терминале.

  9. Скачайте репозиторий с кодом и распакуйте его в папку: WindowsNoEditor\PythonAPI\
    Получившаяся структура должна выглядеть следующим образом: WindowsNoEditor\PythonAPI\carla-python-examples-main\sd_1, sd_2, и так далее

  10. Перейдите в папку с примерами в терминале:
    cd WindowsNoEditor\PythonAPI\carla-python-examples-main
    Все дальнейшие команды будут приводиться именно для этой папки.

  11. Проверьте работу скрипта:
    python -m sd_1

В окне симулятора камера должна автоматически переместиться к автомобилю:

Создание первого беспилотника

Код на Github 
Команда запуска: python -m sd_1

Создадим наш первый беспилотный автомобиль:

# Подключение к серверу Carla
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
world = client.get_world()

# Удаление предыдущего автомобиля
remove_previous_vehicle(world)

# Создание нового автомобиля
spawn_point = world.get_map().get_spawn_points()[0]
vehicle_bp = world.get_blueprint_library().filter('vehicle.tesla.model3')[0]
vehicle_bp.set_attribute('role_name', 'my_car')
vehicle = world.try_spawn_actor(vehicle_bp, spawn_point)

Мы создали машину, однако в окне симулятора изменений не видно. Это связано с особенностями архитектуры Carla: наблюдатель (spectator) не знает о машине, созданной одним из клиентов, и поэтому не перемещается к ней. Чтобы решить эту проблему, привяжем камеру наблюдателя к машине.

# Установка камеры наблюдателя
spectator = world.get_spectator()
transform = vehicle.get_transform()
forward_vector = transform.get_forward_vector()
offset = carla.Location(x=-20 * forward_vector.x, y=-20 * forward_vector.y, z=15)
spectator_transform = carla.Transform(transform.location + offset,
                                      carla.Rotation(pitch=-30, yaw=transform.rotation.yaw, roll=transform.rotation.roll))
spectator.set_transform(spectator_transform)

Автомобиль создан и готов к первой поездке!

Полный вперед!

Код на Github 
Команда запуска: python -m sd_2

Первым делом научим машину двигаться вперед по прямой линии. Основные инструменты управления автомобилем с автоматической коробкой передач — это газ, тормоз и руль. В симуляторе Carla для управления автомобилем используется специальная команда VehicleControl, которая отправляется машине в каждой итерации взаимодействия с виртуальным миром. Одновременно мы будем обновлять позицию камеры наблюдателя:

# Управление автомобилем
control = carla.VehicleControl()
control.steer = 0.0
control.throttle = 1.0
control.brake = 0.0
vehicle.apply_control(control)

# Обновляем позицию наблюдателя
spectator.set_transform(get_transform_for_spectator(vehicle.get_transform()))

Запустим этот код и насладимся прямой и быстрой поездкой до ближайшего здания! Поздравляем — наш беспилотник успешно проехал свои первые метры!

На практике то, что мы сделали всего за несколько строк кода в симуляторе, является сложной задачей для инженеров в реальной жизни. Нужен современный автомобиль с возможностью управлять его основными системами, надежное подключение к бортовому компьютеру и стабильную работу всех соединений и оборудования, чтобы машина не зависла во время движения на скорости 100 км/ч.

Localization — где мы находимся?

После создания автомобиля нам нужно понять, где именно он находится.

Локализация — это процесс определения местоположения беспилотника с точностью до сантиметров. Для этой цели используются данные с сенсоров и высокоточные карты. Современные беспилотники редко используют GPS из-за низкой точности. Вместо этого применяются более точные методы лидарной ориентации: автомобиль в движении сканирует окружающее пространство и затем сопоставляет его с заранее подготовленными картами. Благодаря этому местоположение автомобиля определяется с сантиметровой точностью.

Получив данные локализации, мы можем использовать высокоточные карты, содержащие множество полезной информации: разметку, светофоры и дорожные знаки. Благодаря таким картам отсутствие разметки на дороге, например, зимой, не вызывает трудностей для ПО беспилотника.

В рамках нашего симулятора будем считать, что модуль локализации уже реализован и корректно передает текущее местоположение.

# Функции получения данных об автомобиле
# Пример координат в системе координат симулятора:
# Location(x=65.516640, y=7.807766, z=0.038288)
def get_location(vehicle):
   return vehicle.get_location()

Аналогично можем получить и текущую скорость автомобиля. В симуляторе скорость получаем напрямую, а в реальной жизни она вычисляется бортовым компьютером на основе данных о вращении колес и вала:

def get_speed(vehicle):
   """Возвращает текущую скорость автомобиля в км/ч."""
   velocity_vector = vehicle.get_velocity()
   # Вычисляем магнитуду вектора скорости (в м/с)
   speed_meters_per_second = np.linalg.norm([velocity_vector.x, velocity_vector.y, velocity_vector.z])
   # Конвертируем скорость в км/ч
   return 3.6 * speed_meters_per_second

Кратко: модуль Localization отвечает за получение максимально точного текущего местоположения автомобиля. На вход поступают данные с сенсоров, а на выходе формируются координаты автомобиля, его скорость, ускорение и направление движения.

Визуализация данных

Код на Github 
Команда запуска: python -m sd_3

Чтобы анализировать, как едет машина, мы сделаем простые графики с помощью matplotlib для визуализации текущего местоположения и скорости автомобиля. Пока мы едем по прямой совпадающей с осью X, поэтому изменяется только координата X.

Также изменим подход к отображению камеры наблюдателя. Из-за особенностей движка Carla постоянные перемещения камеры могут создавать эффект рывков. Поэтому используем pygame и отдельное окно для более комфортного наблюдения за движением автомобиля:

pygame_display = PygameDisplay(world, vehicle)
pygame_display.render()

plotter = Plotter()
plotter.init_plot()
plotter.update_plot(current_sim_time_sec, current_location.x, current_speed_kmh, 0.0)

Соединим все вместе и запустим: python -m sd_3

Видео - "Полный вперед + визуализация"

Planning – куда и как поедем?

Код на Github 
Команда запуска: python -m sd_4

После реализации простого режима «полный вперед» перейдем к более сложной задаче — остановке перед перекрестком. Для этого нам потребуется модуль планирования пути, или Planning. Он состоит из нескольких отдельных частей:

  • Global Planning — отвечает за построение общего маршрута. По сути, это навигатор, который знает, где лучше повернуть, какие улицы выбрать и где быстрее проехать.

  • Behavior Planning — принимает решения о конкретных маневрах, которые необходимо выполнить при движении по глобальному маршруту: поворачивать или двигаться прямо, пропускать другие автомобили или ехать самому. Для этого он использует информацию о текущем местоположении автомобиля, распознанных объектах и их предполагаемых действиях.

  • Local Planning (Trajectory Planning) — на основе решений Behavior Planning создает точную траекторию и профиль скорости.

Waypoints (путевые точки)

Для работы с модулем планирования нужно познакомиться с понятием waypoints — это точки на траектории движения с указанием желаемой скорости в каждой точке. Обычно они задаются координатами x, y и желаемой скоростью v (высоту z пока не учитываем). Опционально, можно указывать направление, ускорение и так далее.

Рассмотрим простой пример Waypoints для маршрута до перекрестка:

# Waypoints [x, y, desired_speed_kmh]
WAYPOINTS = [
    [-64, 24.5, 40], # Стартовая точка (скорость 40 км/ч)
    [40,  24.5, 40], # Движение к точке торможения (скорость 40 км/ч)
    [70,  24.5, 0]   # Точка остановки (скорость 0 км/ч)
]

Наша задача — добиться, чтобы машина достигала указанной скорости в каждой путевой точке. Реализуем проверку достижения текущей точки и переключение на следующую (пока только по оси X)

def update(vehicle_location_x, waypoints, target_waypoint_id):
    if abs(waypoints[target_waypoint_id][0] - vehicle_location_x) < 5 
    and target_waypoint_id < len(waypoints) - 1:
        target_waypoint_id += 1
    return target_waypoint_id

Кратко: модуль Planning отвечает за оптимальный выбор маршрута. На входе он получает текущее состояние машины (Localization) и информацию об объектах вокруг (Perception). На выходе формируется массив waypoints, включающий координаты, скорость, направление, ускорение и другие параметры.

Control — управление скоростью и поворотом

После того как мы получили траекторию и профиль скорости, их нужно преобразовать в конкретные команды ускорения и торможения. За это отвечает модуль Control.

Простой двухступенчатый контроллер скорости выглядит так:

if v_current < v_desired:
    control.throttle = 1.0  # Ускорение
    control.brake = 0.0
elif v_current > v_desired:
    control.throttle = 0.0  # Торможение
    control.brake = 1.0

Данный код работает для поддержания скорости в заданном интервале. Добавим блок кода для остановки:

if v_desired == 0:
    control.brake = 1.0  # Полная остановка
    control.throttle = 0.0

Запустим и проверим: python -m sd_4

Видео - Waypoints и контроль скорости

Такой подход позволяет достаточно стабильно удерживать скорость около 40 км/ч, но пассажиры будут испытывать дискомфорт из-за резких переключений газа и тормоза. Чтобы улучшить плавность движения, во второй части статьи мы напишем PID-контроллер.

Заключение или stay tuned!

В первой части статьи мы познакомились с симулятором Carla и прошли весь путь от его установки до создания первого беспилотного автомобиля. Мы научили автомобиль ехать прямо и поддерживать постоянную скорость. Во второй части мы реализуем PID-контроллер для улучшения плавности движения, освоим управление рулём и движение по заданной траектории с помощью алгоритма Stanley, а также научим автомобиль останавливаться перед препятствиями. До встречи через неделю!

Комментарии (1)


  1. Yurij_LL
    26.06.2025 06:33

    очень интересно. ждем продолжения.