Хабрахабр

Снижает ли скорость снижение скорости?

image

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

На значительную долю людей, вынужденно ездящих на работу по 50 км ежедневно через локации, не охваченные общественным транспортом, приходится не меньшая доля ездящих на машине «в булочную», что хорошо видно по этим самым машинам, оставленным утром у дома в первый же мало-мальский снег. Аргументы автовладельцев в массе сводятся к огрызаниям «мне надо», которые, разумеется, не тождественны.

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

У урбанистов всегда есть набор аргументов и готовых решений, подкрепленных данными. А когда наступает такая ситуация, нет ничего лучше, чем отбросить чужие эмоции и призвать двух беспристрастных помощников — матана и Питона.
Слабость позиции автовладельцев – они не выдвигают путей решения своих проблем (на мой взгляд, путем решения была бы отмена стихийной застройки каждого свободного клочка земли в крупных городах и развитие регионов, чтобы людям не приходилось заниматься в родной стране трудовой миграцией в столицу, но кто я такой, чтобы предлагать такие вещи?). Этими данными в большинстве случаев напирают на идею об общем снижении скорости движения в городах. Но иногда это исследования серьезных институтов, иногда – не очень внятная статистика без адекватного нормирования. И самый частый аргумент в ее пользу звучит так:
«все равно из-за светофоров нельзя все время двигаться с максимальной скоростью, ваша средняя скорость будет ниже разрешенной, так почему бы и не снизить немного разрешенную?»

Если снизить максимальную разрешенную, средняя тоже немножко упадет. «Хммм», думал я всегда над этим аргументом. Что же это за апория такая? И что это доказывает — что можно ее снова снизить? Нужно проверить, насколько это правда.

Дальнейшую статью я хотел бы представить вашему вниманию с оговорками, чтобы направить ее критику в комментариях в конструктивное русло:

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

Поскольку светофор — это готовый программный цикл, симуляция построена вокруг отрезка дороги со светофором в конце и его фаз. Суть моей модели очень проста. Они реализуются в 9 вариантах. Следующая итерация — следующий отрезок маршрута со своей длиной, разрешенной скоростью, фазой светофора в конце.
Автомобиль имеет три фазы движения: равноускоренное, движение с максимальной разрешенной для данного участка скоростью, равнозамедленное.

  • Если автомобиль двигался до нынешнего отрезка с такой же скоростью, которая разрешена на этом, то он двигается без изменений;
  • Если он двигался медленнее (или стоял) — то он сначала разгоняется;
  • Варианта «он двигался быстрее» нет, и вот почему: если на следующем отрезке разрешенная скорость меньше, он снижает скорость на этом. Логично?

К этим вариантам добавляются еще условия:

  • Автомобиль едет как ехал или замедлялся, и приезжает к концу отрезка на красный. Это значит, что надо остановиться перед светофором. Полагая, что он тормозит всегда с одинаковым ускорением, откладываем от светофора назад путь, необходимый для торможения. Это — расстояние Sкритическое, по аналогии с критической скоростью взлета самолетов. Теперь, если по условиям авто проезжает на красный свет, он должен начать тормозить до нуля, начиная с точки Sкритическое;
  • Черт, а может, отрезок такой короткий или авто такой овощной, что он не успеет набрать максимальную скорость до наступления Sкрит? А программа баганет, продолжая его разгонять, хотя ему пора тормозить перед светофором? Если так случится, заставим его не набирать скорость выше достигнутой в точке Sкрит, а далее реализуем один из вариантов. Вот что получается в итоге:

    image

А дальше нужно реализовать это в коде. Вот так я это сделал, снабдив подробными комментариями:

образец индуизма

import random
import math #период красного света:
redlight = ("100", "10", "90", "1", #это своего рода "стоп-кодон" . Машина должна обязательно остановиться в пункте назначения. Этому служит 1 секунда красного света "1") # и 1 секунда зеленого, превращающаяся в 0. А еще коду нужен последний отрезок и следующий за ним. Поэтому две последние цифры кортежей условны.
#период зеленого света:
greenlight = ("30", "120", "30", "1", "1")
#расстояние до следующего светофора:
distance = ("400", "400", "250", "500", "500")
#разрешенная скорость на участке
velocity = ("60", "60", "60", "60", "40") #переменные-счетчики для кортежей
r=0
g=0
d=0
v=0 #переменная текущей скорости:
vcurrent=float(0)
#переменная текущего времени проезда отрезка:
t=0
#переменная суммарного времени всего пути:
gtime=0
#примем, что наша машина разгоняется до 100 км/ч за 15 секунд.
#тогда ускорение разгона до разрешенной скорости будет:
accel=float(100/(3.6*15))
#примем, что наша машина тормозит вдвое резче.
#тогда ускорение торможения до разрешенной скорости будет:
decel = float(accel*2) #пока мы перебираем все элементы кортежей, кроме последнего (и помним, что начинается с 0):
while r<=2: red=float(redlight[r]) grn=float(greenlight[g]) dis=float(distance[d]) vel=float(float(velocity[v])/3.6) vnext=float(float(velocity[v+1])/3.6) #создаем переменную для расчета пути разгона машины с ускорением accel до разрешенной на участке скорости: #saccel = float(((vcurrent*vel-vcurrent)/accel) + ((vel-vcurrent)*((vel-vcurrent)/(2*accel) saccel = float((vcurrent*(vel-vcurrent)/accel) + (vel-vcurrent)*(vel-vcurrent)/(2*accel)) #смотрим вдаль: эта переменная определяет точку пути, с которой мы сможем затормозить точно к светофору с ускорением decel: scrit = float(dis-(vel/decel) - (vel*vel)/(2*decel))
#рандомизируем время, в которое загорается первый красный свет цикла светофора.
#это даст нам некоторое смещение по фазе у каждого светофора: startingred = random.randint(0, (int(grn)-1)) print ("startingred= ", startingred) #в самом главном условии сравниваем, если текущая скорость _равна_ разрешенной - то у нас отсутствует фаза разгона: if vcurrent == vel: #если на следующем отрезке разрешенная скорость выше или равна текущей, едем как ехали, набирать будем на том участке: if vnext>= vcurrent: t = int (dis/vel) #если красный, тормозим до нуля: if (t+startingred)%(red+grn)<=red: t = int (scrit/vel + (vel/decel) + red-((t+startingred)%(red+grn))) ### 2 vcurrent = 0 print ("Скорость равнялась разрешенной и будет такой же или выше, КРАСНЫЙ") #если зеленый, то проезжаем: else: t = int (dis/vel)### 1 vcurrent = vel print ("Скорость равнялась разрешенной и будет такой же или выше, ЗЕЛЕНЫЙ", " v=", vcurrent) #если на следующем отрезке разрешенная скорость ниже текущей, снижаем скорость, начиная с scrit: else: t = int ((scrit/vel) + (vcurrent - (vnext)/((vcurrent*(vcurrent - (vnext/(dis-scrit))- ((vcurrent - vnext)*(vcurrent - vnext)/(2*(dis-scrit)))))))) #но если красный, то останавливаемся: if (t+startingred)%(red+grn)<=red: t = int (scrit/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 2 vcurrent = 0 print ("Скорость была разрешенной и будет ниже текущей, КРАСНЫЙ") #а если зеленый - замедляемся до скорости, разрешенной на следующем участке, весь путь от scrit до светофора: else: t = int (scrit/vel + (vcurrent - vnext)/((vcurrent*(vcurrent - (vnext/(dis-scrit))- ((vcurrent - vnext)*(vcurrent - vnext)/(2*(dis-scrit)))))))### 3 vcurrent = float(vnext/3.6) print ("Скорость была разрешенной и будет ниже текущей, ЗЕЛЕНЫЙ", " v=", vcurrent) #в самом главном условии сравниваем, если текущая скорость _меньше_ разрешенной - у нас появляется фаза разгона до новой разрешенной: elif vcurrent < vel: #вводим переменную скорости, достигнутой в точке scrit: vcrit=math.sqrt(2*accel*scrit+vcurrent*vcurrent) #если путь разгона до разрешенной скорости превышает расстояние scrit, то разгоняемся до проезда пункта scrit, запоминаем скорость vcrit и проверяем, надо ли тормозить: if saccel >= scrit: #если на следующем участке ограничение скорости такое же или выше - набирать ее будем на том участке, не тормозим if vnext >= vcrit: t = int(((vcrit-vcurrent)/ accel) + (dis-scrit)/vcrit) #если красный, то останавливаемся: if (t+startingred)%(red+grn)<=red: t = int(((vcrit-vcurrent)/ accel) + ((dis-scrit)*2/vcrit) + red-((t+startingred)%(red+grn)))### 8 vcurrent = 0 print ("Скорость была меньше разрешенной и будет такой же или выше, неполный разгон, КРАСНЫЙ") #если зеленый, то едем с достигнутой скоростью: else: t = int(((vcrit-vcurrent)/ accel) + (dis-scrit)/vcrit) ### 7 vcurrent = vcrit print ("Скорость была меньше разрешенной и будет такой же или выше, неполный разгон, ЗЕЛЕНЫЙ", " v=", vcurrent) #если на следующем участке ограничение скорости ниже - тормозим сразу после разгона else: t = int(((vcrit-vcurrent)/ accel) + (vcrit - vnext)/((vcrit*(vcrit - vnext)/(dis-scrit))- ((vcrit - vnext)*(vcrit - vnext)/(2*(dis-scrit))))) #если красный, то останавливаемся: if (t+startingred)%(red+grn)<=red: t = int(((vcrit-vcurrent)/ accel) + ((dis-scrit)*2/vcrit) + red-((t+startingred)%(red+grn)) ) ### 8 vcurrent = 0 print ("Скорость была меньше разрешенной и будет ниже текущей, неполный разгон, КРАСНЫЙ") #если зеленый - замедляемся до скорости, разрешенной на следующем участке, весь путь от scrit до светофора: else: t = int(((vcrit-vcurrent)/ accel) + (vcrit - vnext)/((vcrit*(vcrit - vnext)/(dis-scrit))- ((vcrit - vnext)*(vcrit - vnext)/(2*(dis-scrit))))) ### 9 vcurrent = vnext print ("Скорость была меньше разрешенной и будет ниже текущей, неполный разгон, ЗЕЛЕНЫЙ", " v=", vcurrent) #если путь разгона до разрешенной скорости не превышает scrit, разгоняемся до разрешенной, дальше едем с ней: else: #если на следующем отрезке разрешенная скорость выше или равна текущей, едем как ехали, набирать будем на том участке: if vnext>= vel: t = int(((vel- vcurrent)/accel) + (dis-saccel)/vel) #если красный, то останавливаемся: if (t+startingred)%(red+grn)<=red: t = int (((vel- vcurrent)/accel) + (scrit-saccel)/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 5 vcurrent = 0 print ("Скорость была меньше разрешенной и будет такой же или выше, КРАСНЫЙ") #если зеленый, то проезжаем: else: t = int (((vel- vcurrent)/accel) + (dis-saccel)/vel)### 4 vcurrent = vel print ("Скорость была меньше разрешенной и будет такой же или выше, ЗЕЛЕНЫЙ", " v=", vcurrent) else: #если красный, то останавливаемся: if (t+startingred)%(red+grn)<=red: t = int (((vel- vcurrent)/accel) + (scrit-saccel)/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 5 vcurrent = 0 print ("Скорость была меньше разрешенной и будет ниже текущей, КРАСНЫЙ") #если зеленый - замедляемся до скорости, разрешенной на следующем участке, весь путь от scrit до светофора: else: print ("scrit ", scrit) print ("vcurrent ", vcurrent) t = int (((vel- vcurrent)/accel) +(scrit-saccel)/vel + (vel - vnext)/((vel*(vel - vnext)/(dis-scrit))-((vel - vnext)*(vel - vnext)/(2*(dis-scrit))))) ### 6 vcurrent = vnext print ("Скорость была меньше разрешенной и будет ниже текущей, ЗЕЛЕНЫЙ", " v=", vcurrent) #в самом главном условии сравниваем, если текущая скорость _выше_ разрешенной - так быть не может, мы же тормозим на предыдущем участке: else: print ("ERROR: v current > v next") print (t) r+=1 g+=1 d+=1 v+=1 gtime+=t print (gtime)

Здесь нужно несколько уточнений.

Мне это показалось реалистичным, кто не согласен — код в ваших руках, экспериментируйте с гружеными фурами и ламборджини мурсилеаго. Я взял условную динамику разгона автомобиля от 0 до 100 за 15 секунд, и задал торможение вдвое резче.

Маловероятно, но такое может быть — равнозамедленное торможение до нуля занимает вдвое больше времени, чем проезд того же расстояния с неизменной скоростью. Да, я пренебрег тем, что пока автомобиль тормозит, красный свет может смениться зеленым. Ради них я не стал городить еще формул, успокоив себя тем, что это — симуляция машины, стоящей перед нами на светофоре. Значит, речь идет о 3-5 секундах. Кстааааати…

Объясняю, почему. Да, я пренебрег трафиком вообще. Просто оттащите мышкой картинку выше на сантиметр влево — наглядная демонстрация. Чисто математически трафик будет выглядеть как смещение точки светофора на несколько корпусов машин и соответственно смещенную по фазе линию скорости.

Машины не будут стартовать одновременно, как вагоны метро, хотя теоретически могут сделать это, не столкнувшись. Но дело в том, что реальность совсем не такова. В противном случае это — всего лишь смещение по фазе и снижение средней скорости на участке (как будто по всему городу действуют более строгие правила). Они стартуют последовательно, и повлияет это на наше время прохождения маршрута следующим образом: мы можем не успеть проехать светофор, если их довольно много. А значит, симулируется ситуация простым добавлением еще одного цикла светофора, вроде:

t = int (scrit/vel + (vel/decel) + red-((t+startingred)%(red+grn)) <b>+ (red+grn)</b>)

Дает ли это что-то нам? Вряд ли, поскольку это просто слагаемое, просто еще одно свойство светофора, если хотите. Вся игра со временем прохождения маршрута происходит во время движения на отрезках. И тут плотный, мешающий друг другу трафик можно симулировать, зарезав ускорение или/и разрешенную скорость на участке.

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

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

клик

image

А еще я модифицировал код. Стер все каменты и принты, добавил более глобальный цикл, чтобы прогнать его, например, 10000 раз:

клик

import random
import math
n=0
overalltime=0
while n<=10000: redlight = ("100", "10", "90", "20", "60", "20", "20", "20", "20", "60", "20", "20", "90", "90", "100", "60", "100", "80", "80", "60", "90", "60", "120", "60", "80", "60", "1", "1") greenlight = ("30", "120", "30", "120", "40", "120", "120", "120", "120", "40", "120", "120", "40", "15", "20", "20", "20", "20", "20", "40", "30", "20", "40", "40", "20", "40", "1", "1") distance = ("400", "400", "250", "250", "250", "450", "300", "650", "1000", "450", "500", "900", "450", "400", "1100", "900", "600", "1000", "450", "450", "400", "450", "200", "500", "350", "400", "500", "500") velocity = ("80", "80", "80", "80", "80", "80", "60", "80", "80", "80", "80", "80", "80", "80", "80", "80", "80", "60", "80", "80", "80", "80", "80", "60", "80", "80", "80", "40") r=0 g=0 d=0 v=0 vcurrent=float(0) t=0 gtime=0 accel=float(100/(3.6*15)) decel = float(accel*2) while r<=26: red=float(redlight[r]) grn=float(greenlight[g]) dis=float(distance[d]) vel=float(float(velocity[v])/3.6) vnext=float(float(velocity[v+1])/3.6) saccel = float((vcurrent*(vel-vcurrent)/accel) + (vel-vcurrent)*(vel-vcurrent)/(2*accel)) scrit = float(dis-(vel/decel) - (vel*vel)/(2*decel)) startingred = random.randint(0, (int(grn)-1)) if vcurrent == vel: if vnext>= vcurrent: t = int (dis/vel) if (t+startingred)%(red+grn)<=red: t = int (scrit/vel + (vel/decel) + red-((t+startingred)%(red+grn))) ### 2 vcurrent = 0 else: t = int (dis/vel)### 1 vcurrent = vel else: t = int ((scrit/vel) + (vcurrent - (vnext)/((vcurrent*(vcurrent - (vnext/(dis-scrit))- ((vcurrent - vnext)*(vcurrent - vnext)/(2*(dis-scrit)))))))) if (t+startingred)%(red+grn)<=red: t = int (scrit/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 2 vcurrent = 0 else: t = int (scrit/vel + (vcurrent - vnext)/((vcurrent*(vcurrent - (vnext/(dis-scrit))- ((vcurrent - vnext)*(vcurrent - vnext)/(2*(dis-scrit)))))))### 3 vcurrent = float(vnext/3.6) elif vcurrent < vel: vcrit=math.sqrt(2*accel*scrit+vcurrent*vcurrent) if saccel >= scrit: if vnext >= vcrit: t = int(((vcrit-vcurrent)/ accel) + (dis-scrit)/vcrit) if (t+startingred)%(red+grn)<=red: t = int(((vcrit-vcurrent)/ accel) + ((dis-scrit)*2/vcrit) + red-((t+startingred)%(red+grn)))### 8 vcurrent = 0 else: t = int(((vcrit-vcurrent)/ accel) + (dis-scrit)/vcrit) ### 7 vcurrent = vcrit else: t = int(((vcrit-vcurrent)/ accel) + (vcrit - vnext)/((vcrit*(vcrit - vnext)/(dis-scrit))- ((vcrit - vnext)*(vcrit - vnext)/(2*(dis-scrit))))) if (t+startingred)%(red+grn)<=red: t = int(((vcrit-vcurrent)/ accel) + ((dis-scrit)*2/vcrit) + red-((t+startingred)%(red+grn)) ) ### 8 vcurrent = 0 else: t = int(((vcrit-vcurrent)/ accel) + (vcrit - vnext)/((vcrit*(vcrit - vnext)/(dis-scrit))- ((vcrit - vnext)*(vcrit - vnext)/(2*(dis-scrit))))) ### 9 vcurrent = vnext else: if vnext>= vel: t = int(((vel- vcurrent)/accel) + (dis-saccel)/vel) if (t+startingred)%(red+grn)<=red: t = int (((vel- vcurrent)/accel) + (scrit-saccel)/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 5 vcurrent = 0 else: t = int (((vel- vcurrent)/accel) + (dis-saccel)/vel)### 4 vcurrent = vel else: if (t+startingred)%(red+grn)<=red: t = int (((vel- vcurrent)/accel) + (scrit-saccel)/vel + (vel/decel)+ red-((t+startingred)%(red+grn)))### 5 vcurrent = 0 else: t = int (((vel- vcurrent)/accel) +(scrit-saccel)/vel + (vel - vnext)/((vel*(vel - vnext)/(dis-scrit))-((vel - vnext)*(vel - vnext)/(2*(dis-scrit))))) ### 6 vcurrent = vnext else: print ("ERROR: v current > v next") #print (t) r+=1 g+=1 d+=1 v+=1 gtime+=t dev=(1476-gtime)*(1476-gtime) #print (gtime) n+=1 dev+=dev overalltime+=gtime print ("mean= ", overalltime/n)
print ("deviation= ", math.sqrt(dev/n))

Представленный вариант отображает «обычную езду» — с максимальным превышением скорости, которое наказывается только устным предупреждением.

А вот теперь занятно будет с помощью симуляции на основе этого маршрута сравнить следующие варианты: введение нештрафуемого интервала в 10 км/ч, сверхзаконопослушную езду точно по ограничениям, педалируемую урбанистами максимальную городскую скорость в 50 км/ч.

Результаты 10 000 симуляций, raw data:

(ограничение, км/ч; время прохождения маршрута, с; стандартное отклонение):

90 1466,6 0,5
80 1475,6 0,4
70 1479,7 0,9
60 1593,7 0,8
50 1701,3 0,5
40 1869,8 0,6

image

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

Во-первых, планируемое уменьшение нештрафуемого порога на данном маршруте действительно не приведет к значимому увеличению времени в пути. Результаты показались мне немного неожиданными. Во-вторых, снижение разрешенной скорости заметно увеличит время, на 8% даже относительно совсем законопослушных граждан и на 13% от негласно эксплуатируемых скоростей «устного предупреждения». И начиная как раз где-то с 70 км/ч увеличение скорости почти ничего не дает. Опять же, в рамках данной модели нельзя показать, будет ли «стакаться» замедление на 10% в условиях плотного трафика и усугублять дорожную обстановку. То есть, утверждение, что снижение разрешенной скорости не отразится на средней, строго говоря неверно.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть