利用SUMO进行交叉口速度引导
SUMO使用E2检测器获取信号交叉口车道信息和信号控制方案
SUMO轨迹图的绘制和traci接口的简单使用
SUMO利用转弯率构建车流rou文件
博主最近在进行MP(max_pressure)算法的自适应信号控制的复现,近期完成了初步demo的复现工作,写一篇blog浅浅记录一下。
本文中所使用的MaxPressure算法,主要基于以下两篇论文当中的一些算法进行实现:
Wei H, Chen C, Zheng G, et al. Presslight: Learning max pressure control to coordinate traffic signals in arterial network[C]//Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2019: 1290-1298.
[1]张斯钰. 基于网联车数据的城市网络排队估计和最大压强信号控制[D].浙江大学,2020.DOI:10.27461/d.cnki.gzjdx.2020.003073.
在max_pressure信号控制算法当中,每一相位的压强基本可以定义为进口道车辆数与出口道车辆数之间的差值,如下图所示。
北向南压强可以简单地定义为上游车辆数(4)-下游车辆数(1)=3
当进行至相位切换或延长判断时刻:
根据最大压强相位和当前相位进行判断:
如果当前相位任然为最大压强相位,则延长当前相位
如果最大压强相位不是当前相位,则以最小绿灯时间切换至下一相位
本demo中使用的延长时间为5s一延长,达到最大绿灯时间后,切换下一相位,初始绿灯时间为最小绿灯时间
为了实现本文所需要复现的MP算法,构建了一个简单的交叉口场景,如下图所示。
构建读取net文件数据的类:
import sys, subprocess, os
import inspect
import numpy as np
if 'SUMO_HOME' in os.environ:
tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
sys.path.append(tools)
import sumolib
else:
sys.exit("please declare environment variable 'SUMO_HOME'")
class NetworkData:
def __init__(self, net_fp):
print(net_fp)
self.net = sumolib.net.readNet(net_fp)
###get edge data
self.edge_data = self.get_edge_data(self.net)
self.lane_data = self.get_lane_data(self.net)
self.node_data, self.intersection_data = self.get_node_data(self.net)
print("SUCCESSFULLY GENERATED NET DATA")
def get_net_data(self):
return {'lane':self.lane_data, 'edge':self.edge_data, 'origin':self.find_origin_edges(), 'destination':self.find_destination_edges(), 'node':self.node_data, 'inter':self.intersection_data}
def find_destination_edges(self):
next_edges = { e:0 for e in self.edge_data }
for e in self.edge_data:
for next_e in self.edge_data[e]['incoming']:
next_edges[next_e] += 1
destinations = [ e for e in next_edges if next_edges[e] == 0]
return destinations
def find_origin_edges(self):
next_edges = { e:0 for e in self.edge_data }
for e in self.edge_data:
for next_e in self.edge_data[e]['outgoing']:
next_edges[next_e] += 1
origins = [ e for e in next_edges if next_edges[e] == 0]
return origins
def get_edge_data(self, net):
edges = net.getEdges()
edge_data = {str(edge.getID()):{} for edge in edges}
for edge in edges:
edge_ID = str(edge.getID())
edge_data[edge_ID]['lanes'] = [str(lane.getID()) for lane in edge.getLanes()]
edge_data[edge_ID]['length'] = float(edge.getLength())
edge_data[edge_ID]['outgoing'] = [str(out.getID()) for out in edge.getOutgoing()]
edge_data[edge_ID]['noutgoing'] = len(edge_data[edge_ID]['outgoing'])
edge_data[edge_ID]['nlanes'] = len(edge_data[edge_ID]['lanes'])
edge_data[edge_ID]['incoming'] = [str(inc.getID()) for inc in edge.getIncoming()]
edge_data[edge_ID]['outnode'] = str(edge.getFromNode().getID())
edge_data[edge_ID]['incnode'] = str(edge.getToNode().getID())
edge_data[edge_ID]['speed'] = float(edge.getSpeed())
###coords for each edge
incnode_coord = edge.getFromNode().getCoord()
outnode_coord = edge.getToNode().getCoord()
edge_data[edge_ID]['coord'] = np.array([incnode_coord[0], incnode_coord[1], outnode_coord[0], outnode_coord[1]]).reshape(2,2)
#print edge_data[edge_ID]['coord']
return edge_data
def get_lane_data(self, net):
#create lane objects from lane_ids
lane_ids = []
for edge in self.edge_data:
lane_ids.extend(self.edge_data[edge]['lanes'])
lanes = [net.getLane(lane) for lane in lane_ids]
#lane data dict
lane_data = {lane:{} for lane in lane_ids}
for lane in lanes:
lane_id = lane.getID()
lane_data[ lane_id ]['length'] = lane.getLength()
lane_data[ lane_id ]['speed'] = lane.getSpeed()
lane_data[ lane_id ]['edge'] = str(lane.getEdge().getID())
#lane_data[ lane_id ]['outgoing'] = []
lane_data[ lane_id ]['outgoing'] = {}
###.getOutgoing() returns a Connection type, which we use to determine the next lane
moveid = []
for conn in lane.getOutgoing():
out_id = str(conn.getToLane().getID())
lane_data[ lane_id ]['outgoing'][out_id] = {'dir':str(conn.getDirection()), 'index':conn.getTLLinkIndex()}
moveid.append(str(conn.getDirection()))
lane_data[ lane_id ]['movement'] = ''.join(sorted(moveid))
#create empty list for incoming lanes
lane_data[ lane_id ]['incoming'] = []
#determine incoming lanes using outgoing lanes data
for lane in lane_data:
for inc in lane_data:
if lane == inc:
continue
else:
if inc in lane_data[lane]['outgoing']:
lane_data[inc]['incoming'].append(lane)
return lane_data
def get_node_data(self, net):
#read network from .netfile
nodes = net.getNodes()
node_data = {node.getID():{} for node in nodes}
for node in nodes:
node_id = node.getID()
#get incoming/outgoing edge
node_data[node_id]['incoming'] = set(str(edge.getID()) for edge in node.getIncoming())
node_data[node_id]['outgoing'] = set(str(edge.getID()) for edge in node.getOutgoing())
node_data[node_id]['tlsindex'] = { conn.getTLLinkIndex():str(conn.getFromLane().getID()) for conn in node.getConnections()}
node_data[node_id]['tlsindexdir'] = { conn.getTLLinkIndex():str(conn.getDirection()) for conn in node.getConnections()}
if node_id == '-13968':
missing = []
negative = []
for i in range(len(node_data[node_id]['tlsindex'])):
if i not in node_data[node_id]['tlsindex']:
missing.append(i)
for k in node_data[node_id]['tlsindex']:
if k < 0 :
negative.append(k)
for m,n in zip(missing, negative):
node_data[node_id]['tlsindex'][m] = node_data[node_id]['tlsindex'][n]
del node_data[node_id]['tlsindex'][n]
#for index dir
node_data[node_id]['tlsindexdir'][m] = node_data[node_id]['tlsindexdir'][n]
del node_data[node_id]['tlsindexdir'][n]
#get XY coords
pos = node.getCoord()
node_data[node_id]['x'] = pos[0]
node_data[node_id]['y'] = pos[1]
intersection_data = {str(node):node_data[node] for node in node_data if "traffic_light" in net.getNode(node).getType()}
return node_data, intersection_data
将车道划分为车道组
def phase_lane_get(net):
'''
net- data of road
phase_1-4 - four kind of car flow of four phase
'''
phase_1,phase_2,phase_3,phase_4 = [],[],[],[]
phase = []
for i in net.lane_data:
if net.lane_data[i]['edge'] == 'E2TL' or net.lane_data[i]['edge'] == 'W2TL':
if net.lane_data[i]['movement'] == 's' or net.lane_data[i]['movement'] == 'rs':
phase_1.append(i)
else:
phase_2.append(i)
else:
if net.lane_data[i]['movement'] == 's' or net.lane_data[i]['movement'] == 'rs':
phase_3.append(i)
elif net.lane_data[i]['movement'] == 'l':
phase_4.append(i)
else:
continue
phase.append(phase_1)
phase.append(phase_2)
phase.append(phase_3)
phase.append(phase_4)
return phase
计算相位压强
def pressure_cal(phase,net):
'''
income_pressure - the pressure of income lane
outcome_pressure - the pressure of outcome lane
phase - the set of lane in same phase
'''
total_pressure = 0
for income_lane in phase:
income_pressure = traci.lane.getLastStepVehicleNumber(income_lane)
outcome_pressure = traci.lane.getLastStepVehicleNumber(next(iter(net.lane_data[income_lane]['outgoing'])))
total_pressure += income_pressure - outcome_pressure
#print(total_pressure)
return total_pressure/len(phase)
获取最大压强所对应的相位
def max_pressure_phase(phase):
pressure = []
for j in phase:
#print(j)
pressure.append(pressure_cal(j,net))
pressure = np.array(pressure)
phase_index = pressure.argmax()
if phase_index == 0:
phase_index = 4
elif phase_index == 1:
phase_index = 6
elif phase_index == 2:
phase_index = 0
else :
phase_index = 2
return phase_index
构建信号控制器
def max_pressure_controller(phase_pressure_max,phase_now,current_green_time,current_time):
'''
min_green - min green time of green phase
max_green - max green time of one green phase
phase_pressure_max - current max pressure phase
phase_now - current phase
phase_extend_time - the time of extend in one time
phase_next - next phase
yellow_phase - the index of yellow phase between two green phase
next_swith_time - the time stamp of judge extend or swith
'''
min_green = 16
max_green = 45
phase_extend_time = 5
if phase_pressure_max == phase_now:
phase_next = phase_now
else:
phase_next = phase_now + 2
if phase_next > 7:
phase_next -=8
if phase_now == phase_next and current_green_time <max_green:
traci.trafficlight.setPhase('TL',phase_now)
traci.trafficlight.setPhaseDuration('TL',phase_extend_time)
current_green_time = current_green_time + phase_extend_time
next_swith_time = current_time + phase_extend_time -1
print(current_time,'相位延长',next_swith_time,phase_now)
elif (phase_now == phase_next and current_green_time >=max_green) or (phase_now != phase_next):
if phase_now + 1 <=7 :
yellow_phase = phase_now + 1
else:
yellow_phase = 0
traci.trafficlight.setPhase('TL',yellow_phase)
traci.trafficlight.setPhaseDuration('TL',3)
for step in range(3):
traci.simulationStep()
traci.trafficlight.setPhase('TL',phase_next)
traci.trafficlight.setPhaseDuration('TL',min_green)
current_green_time = min_green -1
next_swith_time = current_time + min_green + 3
print(current_time,'切换相位',next_swith_time,phase_now)
print(phase_next)
return current_green_time , next_swith_time
仿真主程序
def main():
current_green_time = 15
next_swith_time = 200
phase = phase_lane_get(net)
traci.start(["sumo-gui", "-c", "intersection/sumo_config.sumocfg"])
for step in range(3600):
traci.simulationStep()
current_time = traci.simulation.getTime()
if current_time < 200 :
continue
elif current_time >3600:
break
else:
if current_time == next_swith_time:
phase_pressure_max = max_pressure_phase(phase)
phase_now = traci.trafficlight.getPhase('TL')
current_time = traci.simulation.getTime()
current_green_time,next_swith_time = max_pressure_controller(phase_pressure_max,phase_now,current_green_time,current_time)
else:
continue
traci.close()
本文对于MP算法进行了简单的复现,并未实现高适用性的MP算法控制。未来还有待进一步对算法进行优化和完善,例如完善压强的算法、延长时间的优化等待。本文所构建的算法、代码仅当作本人的学习记录资料使用,如有不足,欢迎大家指正并多多交流。