Руководство по продвинутому моделированию трафика в сложных дорожных сетях при помощи SUMO и Python

Как давно вы оказывались в пробке? Чтобы решить проблему дорожных заторов, до принятия важных решений нужно моделировать движение. Сделать это можно на платформе с открытым кодом The Simulation of Urban Mobility (SUMO). Но как создать сложную симуляцию? На этот вопрос отвечает материал, которым мы решили поделиться к старту курса по Data Science.


Зачем изучать движение в городских дорожных сетях?

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

Интересно вот что: увеличение пропускной способности за счёт полос или длины дорог может не сработать так хорошо, как вы можете ожидать. В другой статье я показываю, как «Парадокс Брэсса» приводит к необычному результату: увеличение количества дорог в городской сети может ухудшить движение!

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

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

Начало работы с SUMO

Моделирование дорожного движения, по-видимому, относится к нишевому сообществу исследователей транспортных потоков или инженерных компаний-подрядчиков. Anylogic, VISSIM и Aimsun — предлагают решения моделирования трафика и мобильности. Но SUMO открыта, и с ней довольно легко начать работать.

Способов установки SUMO несколько, но я предпочитаю команду pip install: она установит SUMO и библиотеки взаимодействия с ней.

python -m pip install sumo

Приступим к первой симуляции!

Моделирование

В городском планировании довольно часто встречаются дороги в виде сети. При помощи SUMO мы создали сетку 5×5 с дорогами длиной 200 метров и тремя полосами движения:

netgenerate --grid --grid.number=5 -L=3 --grid.length=200 --output-file=grid.net.xml

Воспользуемся randomTrips.py из папки tools в домашнем каталоге SUMO (sumo → tools) для генерации случайных маршрутов двухсот автомобилей. Время в begin и end обозначает время, за которое транспортные средства входят в симуляцию. Я выбрал 0 и 1, то есть все автомобили въезжают в первую секунду симуляции. Период обозначает скорость притока.

randomTrips.py -n grid.net.xml -o flows.xml --begin 0 --end 1 --period 1 --flows 200

Генерируем маршруты отдельных автомобилей командой jtrrouter в SUMO.

jtrrouter --flow-files=flows.xml --net-file=grid.net.xml --output-file=grid.rou.xml --begin 0 --end 10000 --accept-all-destinations

Ради простоты хочется сохранить постоянную плотность. Самое очевидное — случайное движение только внутри симуляции. Воспользуемся Манхэттенской моделью движения, где транспортные средства на перекрёстке выбирают движение прямо, налево или направо на основе заданных вероятностей. По умолчанию в SUMO транспортные средства выходят из симуляции, достигая пункта назначения. Однако SUMO имеет реализацию Манхэттенской модели движения со скриптом непрерывного перенаправления:

generateContinuousRerouters.py -n grid.net.xml --end 10000 -o rerouter.add.xml

Создаём файл конфигурации SUMO для запуска симуляции — это .xml с определёнными атрибутами, содержащий имена файлов с сетью, маршрутом и перенаправления. Выходной файл будет содержать данные о моментах в симуляции:

<configuration>
  <input>
    <net-file value="grid.net.xml" />
    <route-files value="grid.rou.xml" />
    <additional-files value="rerouter.add.xml" />
  </input>
  <time>
    <begin value="0" />
    <end value="10000" />
  </time>
   <output>
     <fcd-output value="grid.output.xml" />
   </output>
</configuration>

Запустим модель. Period здесь — это интервал времени сохранения данных о скорости и положении транспорта:

sumo-gui -c grid.sumocfg — device.fcd.period 100

Эта команда открывает графический интерфейс SUMO со всей симуляцией:

При оптимизации для HabraStorage из анимации удалён каждый второй фрейм

Цвета транспортных средств указывают на их скорость, от самой медленной (красный) до самой быстрой (зелёный).

Как насчёт нескольких запусков?

Раздражает необходимость вручную изменять каждый параметр и выходной файл. Работы ещё больше, когда у вас есть 100+ прогонов для изучения изменения трафика с различными параметрами и прогоны для статистического усреднения. Поэтому я написал обёртку для SUMO на Python:

def initialize(grids=5,lanes=3,length=200):
    os.system("netgenerate --grid --grid.number=5 -L="+str(lanes)+" --grid.length="+str(length)+" --output-file=grid.net.xml")

def single(path,vehicles):
    os.system(path + "randomTrips.py -n grid.net.xml -o flows.xml --begin 0 --end 1 --period 1 --flows "+str(vehicles))
    os.system("jtrrouter --flow-files=flows.xml --net-file=grid.net.xml --output-file=grid.rou.xml --begin 0 --end 10000 --accept-all-destinations")
    os.system(path + "generateContinuousRerouters.py -n grid.net.xml --end 10000 -o rerouter.add.xml")
    tree = ET.parse("grid.sumocfg")
    root = tree.getroot()
    for child in root:
        if (child.tag == 'output'):
            for child2 in child:
                child2.attrib['value'] = 'grid.output'+str(vehicles)+'.xml'
    with open('grid.sumocfg', 'wb') as f:
        tree.write(f)
    os.system("sumo -c grid.sumocfg --device.fcd.period 100")

    if __name__ == '__main__':
    import os
    import numpy as np
    import analysis
    from matplotlib import pyplot as plt

    import xml.etree.ElementTree as ET
    path = "C:\\Users\\BLAH\\AppData\\Local\\Programs\\Python\\Python39\\Lib\\site-packages\\sumo\\tools\\"
    initialize()
    vehicles_arr=np.r_[np.linspace(10,100,10).astype(int),np.linspace(100,2000,20).astype(int)]
    for i in range(0,len(vehicles_arr)):
        single(path,vehicles_arr[i])

Анализ

Выходные данные SUMO в xml содержат информацию о времени, положении и скорости конкретного транспортного средства на каждом промежутке времени. Я хочу проанализировать, как скорость зависит от плотности, или количества автомобилей, — в основном, получить скорость, усредняя её по всему транспорту.

def textify(vehicles):
    tree = ET.parse("grid.output"+str(vehicles)+".xml")
    root = tree.getroot()

    l = 0
    for child in root:
        for child2 in child:
            l += 1

    c = 0
    t = 0
    a = ''
    speeds = np.zeros(l)
    times = np.zeros(l)
    ids = np.zeros(l)
    for child in root:
        for child2 in child:
            # print(child2.tag, child2.attrib)
            if (child2.tag == 'vehicle'):
                a = (child2.attrib)
                speeds[c] = np.float(a['speed'])
                ids[c] = np.float(a['id'])
                times[c] = t
                c = c + 1
        t = t + 1
        # print(t)
    data = np.c_[ids, times, speeds]

    tt = len(np.unique(data[:, 1]))
    vel = np.zeros(tt)
    nc = np.zeros(tt)
    flux = np.zeros(tt)

    for i in range(0, tt):
        w = np.where(data[:, 1] == i)
        vel[i] = np.mean(data[w, 2])
        nc[i] = len(w[0])
        flux[i] = np.sum(data[w, 2])
        velm = np.c_[nc, vel, flux]

    np.savetxt('velm'+str(vehicles)+'.txt',velm)

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

import numpy as np
from matplotlib import pyplot as plt
def plots(vehicles):
    vel=np.zeros(len(vehicles))
    nc=np.zeros(len(vehicles))
    flux=np.zeros(len(vehicles))
    for i in range(0,len(vehicles)):
        txt=np.loadtxt('velm'+str(vehicles[i])+'.txt')
        nc[i] = np.mean(txt[95:100, 0])
        vel[i] = np.mean(txt[95:100, 1])
        flux[i]=np.mean(txt[95:100,2])
        print(nc[i],vel[i],flux[i])
    fig=plt.figure()
    plt.plot(nc/3000,vel,'o')
    plt.plot(nc / 3000, vel, color='k')
    plt.xlabel('Density')
    plt.ylabel('Velocity')
    plt.tight_layout()
    plt.savefig('vel-density.png',dpi=600)
    #plt.show()

    fig=plt.figure()
    plt.plot(nc / 3000,flux,'o',label='Traffic Simulation')
    plt.plot(nc / 3000, flux, color='k')
    plt.plot(vehicles/3000,vehicles*vel[0],'r--',label='No Interaction')
    plt.ylim(0,2800)
    plt.legend()
    plt.xlabel('Density')
    plt.ylabel('Flux')
    plt.tight_layout()
    plt.savefig('flux-density.png', dpi=600)
Скорость и плотность симуляции

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

Поток измеряет количество автомобилей, проходящих через определённую точку за конкретное время, то есть он — мера пропускной способности. Параметр задаётся так:

Все скорости транспорта внутри длины L суммируются, а количество полос равно nl.

При низкой плотности каждый автомобиль движется на предельной скорости, поэтому поток увеличивается линейно с ростом плотности (красная линия на графике ниже). Однако при более высокой плотности транспорт не может двигаться на максимуме, и в определённый момент эффект от большего количества транспортных средств компенсируется движением с меньшей скоростью, поток снижается. Выше определённой плотности (здесь это ~0,1–0,2) возникают пробки, и поток убывает.

Поток и плотность

Моделирование реалистичного дорожного движения

Я показал, как с помощью SUMO+Python настроить базовое моделирование трафика и ансамблевые прогоны в ровно вычерченных сетях.

В недавнем исследовании с использованием данных о городских уличных сетях OpenStreetMap и OSMnx было обнаружено, что в некоторых городах можно заметить сети, похожие на сетку, а в других — нет. В SUMO есть возможность моделирования движения в городских уличных сетях путём конвертирования данных OpenStreetMap в файлы SUMO .net.

Но помимо моделирования на реалистичных городских дорожных сетях существвует проблема калибровки этих симуляторов трафика в соответствии с ежедневными схемами движения. Нужно учитывать приливы и отливы людей на дорогах — где и когда люди входят в здания и выходят из них. Это очень сложно: невозможно знать маршрут каждого автомобиля. Многие автомобили передают GPS-данные. Google и Apple используют данные мобильных телефонов. Но транспортный поток нелинеен. Небольшие изменения могут иметь огромные последствия.

При этом хочется, чтобы результаты моделирования трафика были устойчивы в смысле начальных условий. Эти результаты должны чётко указывать, улучшает ли проект транспортный поток — в широком спектре ситуаций. Детальное, реалистичное крупномасштабное моделирование трафика за приемлемое время — сложная задача.

На странице SUMO имеется лишь несколько таких реалистичных сценариев. А в верхней части страницы написано:

Создать сценарий — большая работа. Если вы создали сценарий SUMO, которым можете поделиться, пожалуйста, напишите нам.

Код проекта.

Ссылки
  • R. E. Stern, et al. “Dissipation of stop-and-go waves via control of autonomous vehicles: Field experiments,” arXiv (2017).

  • G. Boeing, “Urban spatial order: street network orientation, configuration, and entropy,” Applied Network Science (2019).

  • P. L. Alvarez, et al. “Microscopic traffic simulation using SUMO,” International Conference on Intelligent Transportation Systems. IEEE (2018).

Остаётся надеяться, что демократизация данных о трафике, растущая доступность вычислительных ресурсов и платформы для моделирования трафика с открытым исходным кодом сделают эти крупномасштабные моделирования доступнее. Городской трафик — это удивительно сложная игра, дающая представление о дышащих, живых городах. Если вам интересно работать с данными — отражением жизни города, вы можете посмотреть программу нашего курса по Data Science, где научитесь решать проблемы множества людей, принимая взвешенные решения на основе данных. Также вы можете обратить внимание на другие курсы, чтобы начать карьеру или прокачаться в других направлениях IT.

Data Science и Machine Learning

Python, веб-разработка

Мобильная разработка

Java и C#

От основ — в глубину

А также: