
Друзья, всем привет! С каждым днем на дорогах становится все больше беспилотных автомобилей. Waymo уже обошел Lyft по количеству поездок в Сан-Франциско и подбирается к Uber. В Нью-Йорке, Шанхае и Москве можно увидеть сотни машин с датчиками на крыше. Что за магия приводит их в движение?
В этой статье мы рассмотрим основные части self driving систем и создадим свой беспилотный автомобиль на Python в симуляторе Carla (UE4). В начале он будет просто стоять на месте, а в конце сможет ехать по маршруту и останавливаться перед препятствиями.
Статья получилась объёмной, поэтому я разделил её на две части. В первой части мы установим симулятор Carla, создадим наш первый беспилотный автомобиль и обучим его поддерживать постоянную скорость при движении прямо. Во второй части мы напишем PID-контроллер, реализуем управление рулём с помощью алгоритма Stanley и научим автомобиль останавливаться перед препятствиями.
Основные компоненты
Большинство беспилотных автомобилей состоят из модулей:
Локализация — это точное определение местонахождения автомобиля с точностью до сантиметров. Используются сенсоры и высокоточные карты, в которых хранится множество данных: разметка, светофоры, знаки. Благодаря этому беспилотник не теряется даже при отсутствии разметки дороги, например, зимой.
Восприятие — автомобиль распознает и отслеживает другие объекты при помощи датчиков, фиксируя их положение, скорость и направление движения.
Предсказание — прогнозируется действие других участников движения. Это наиболее сложная задача, поскольку поведение человека часто непредсказуемо.
Планирование — на основе локализации, восприятия и предсказания принимается решение о дальнейших действиях и передаются соответствующие команды системам управления. Все это происходит за миллисекунды.
Управление (Control) — автомобиль реализует команды, которые непосредственно управляют движением.

Симулятор Carla
К сожалению, реальной машины с радарами и лидарами у нас нет. Зато у нас есть открытый симулятор беспилотного вождения Carla, где уже все настроено. Версий Carla много, и чем выше версия, тем мощнее нужен компьютер. Мы будем использовать Python 3.8 и Carla 0.9.15. Весь код доступен на GitHub.
Установка (для Windows)
Скачайте и распакуйте симулятор Carla 0.9.15 (Windows). После распаковки перейдите в полученную папку. Убедитесь, что внутри есть папка WindowsNoEditor.
-
Запустите WindowsNoEditor/CarlaUE4.exe. Откроется окно оператора. В нём вы можете полетать по карте. После тестовых полетов можно выключить симулятор, он очень требователен к ресурсам ПК.
Установите Python 3.8 (Windows Store)
Откройте PowerShell в папке Carla (CARLA_0.9.15): Для этого кликните правой кнопкой мыши на пустом месте внутри папки и выберите «Open in Terminal». Если опция не отображается, зажмите Shift и снова нажмите правой кнопкой мыши — пункт должен появиться.
Создайте и активируйте виртуальное окружение. Выполните следующие команды в терминале (важно использовать именно Python 3.8 из-за возможных проблем совместимости с более новыми версиями):
py -3.8 -m venv venv
venv\Scripts\activate
После активации должна появиться надпись (venv).Установите необходимые пакеты:
pip3 install -r WindowsNoEditor\PythonAPI\examples\requirements.txt
pip3 install carla-
Запустите симулятор Carla в режиме ручного управления. Для этого снова откройте симулятор, запустив:
WindowsNoEditor/CarlaUE4.exe
Затем в терминале выполните команду:
python WindowsNoEditor\PythonAPI\examples\manual_control.py Теперь вы можете управлять автомобилем с помощью клавиш WSAD, аналогично управлению в играх серии Need For Speed. Более подробная инструкция будет отображаться в терминале.
Скачайте репозиторий с кодом и распакуйте его в папку: WindowsNoEditor\PythonAPI\
Получившаяся структура должна выглядеть следующим образом: WindowsNoEditor\PythonAPI\carla-python-examples-main\sd_1, sd_2, и так далееПерейдите в папку с примерами в терминале:
cd WindowsNoEditor\PythonAPI\carla-python-examples-main
Все дальнейшие команды будут приводиться именно для этой папки.Проверьте работу скрипта:
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, а также научим автомобиль останавливаться перед препятствиями. До встречи через неделю!
Yurij_LL
очень интересно. ждем продолжения.