引言
SUMO 本身可以實現(xiàn)很多實際交通場景的模擬。當(dāng) SUMO 被用作智能交通控制算法的測試平臺時,需要其與外界程序/算法實現(xiàn)很好的互動,例如用戶自定義的控制算法可以從 SUMO 獲取實時交通信息,然后對其中車輛狀態(tài)、信號燈狀態(tài)等進行實時控制。TraCI 就是實現(xiàn)這類互動的接口。
TraCI: Traffic Control Interface. 交通控制接口。
作用:獲取 SUMO 交通模擬環(huán)境中的數(shù)據(jù),并實時修改、控制。
目前該接口支持多種主流語言,包括 python, c++, .NET, MATLAB, Java,其中 python 版本的 TraCI 功能最全面。下面就以 python 版本的 TraCI 為例,介紹一下如何實現(xiàn) SUMO 與外部控制算法的互動。關(guān)于 TraCI 中類、函數(shù)的詳細(xì)說明,可以參考官方文檔。
Demo: 通過 TraCI 控制 SUMO 中的交通燈狀態(tài)
來自 SUMO 官網(wǎng)教程。所有程序可以在 https://github.com/eclipse/sumo/tree/master/tests/complex/tutorial/traci_tls 中找到。
考慮如下所示路口:

基礎(chǔ)信號燈變換順序如下:
<tlLogic id="0" type="static" programID="0" offset="0">
<phase duration="31" state="GrGr"/>
<phase duration="6" state="yryr"/>
<phase duration="31" state="rGrG"/>
<phase duration="6" state="ryry"/>
</tlLogic>
其中各參數(shù)含義可以參考本博客中另一篇文章。
然后,希望通過 TraCI 修改信號燈轉(zhuǎn)換機制:
當(dāng)南北方向沒有車輛過來時,東西方向一直保持綠燈
當(dāng)南北方向有車過來時,切換信號燈,東西方向黃燈 6 s,然后變紅,同時南北方向變綠,持續(xù) 31 s,然后變黃,再變紅,東西方向變綠。
重復(fù)上述兩個過程
現(xiàn)在假設(shè) net.xml 文件已經(jīng)得到。實際上,通過 netedit 可以很容易的構(gòu)造上述交通路網(wǎng)。
SUMO 與 TraCI 的交互是在文件 runner.py 中實現(xiàn)的,主要包括如下內(nèi)容:
- 首先檢查系統(tǒng)路徑,以便后續(xù) python 的 module 調(diào)用,主要是調(diào)用 traci
if 'SUMO_HOME' in os.environ:
tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
sys.path.append(tools)
else:
sys.exit("please declare environment variable 'SUMO_HOME'")
import traci
- 定義函數(shù) generate_routefile(),用來創(chuàng)建 .rou.xml 文件
def generate_routefile():
random.seed(42) # make tests reproducible
N = 3600 # number of time steps
# demand per second from different directions
pWE = 1. / 10
pEW = 1. / 11
pNS = 1. / 30
with open("data/cross.rou.xml", "w") as routes:
print("""<routes>
<vType id="typeWE" accel="0.8" decel="4.5" sigma="0.5" length="5" minGap="2.5" maxSpeed="16.67" guiShape="passenger"/>
<vType id="typeNS" accel="0.8" decel="4.5" sigma="0.5" length="7" minGap="3" maxSpeed="25" guiShape="bus"/>
<route id="right" edges="51o 1i 2o 52i" />
<route id="left" edges="52o 2i 1o 51i" />
<route id="down" edges="54o 4i 3o 53i" />""", file=routes)
vehNr = 0
for i in range(N):
if random.uniform(0, 1) < pWE:
print(' <vehicle id="right_%i" type="typeWE" route="right" depart="%i" />' % (vehNr, i), file=routes)
vehNr += 1
if random.uniform(0, 1) < pEW:
print(' <vehicle id="left_%i" type="typeWE" route="left" depart="%i" />' % (vehNr, i), file=routes)
vehNr += 1
if random.uniform(0, 1) < pNS:
print(' <vehicle id="down_%i" type="typeNS" route="down" depart="%i" color="1,0,0"/>' % (vehNr, i), file=routes)
vehNr += 1
print("</routes>", file=routes)
運行上述函數(shù)之后,會在 data/ 目錄下生成 cross.rou.xml 文件,里面包含了由東向西、由西向東、由北向南的交通流信息。
- 從 SUMO 中獲取交通信息,然后對交通燈狀態(tài)施加控制
def run():
"""execute the TraCI control loop"""
step = 0
traci.trafficlight.setPhase("0", 2) # 這里 0 是 traffic light 的 ID,四個 phase 依次編號為 0, 1, 2, 3,初始時設(shè)置 phase 為 2,即 東西道路為 G,南北道路為 r。
while traci.simulation.getMinExpectedNumber() > 0: # 該函數(shù)得到當(dāng)前 net 中的車輛數(shù)目加上還沒有進入 net 的車輛數(shù)目。只要該數(shù)值 > 0,就表明還有車輛需要處理。
traci.simulationStep() # 運行一步仿真
if traci.trafficlight.getPhase("0") == 2:
if traci.inductionloop.getLastStepVehicleNumber("0") > 0: # 在上一步仿真中,經(jīng)過 induction loop 的汽車數(shù)量
traci.trafficlight.setPhase("0", 3) # 如果有車進來,則切換 phase
else:
traci.trafficlight.setPhase("0", 2) # 否則依然保持 phase。注意,這里是重置了 phase, 所以會重新計時。
step += 1
traci.close()
- 運行主程序
if __name__ == "__main__":
generate_routefile()
traci.start(["sumo-gui", "-c", "data/cross.sumocfg",
"--tripinfo-output", "tripinfo.xml"]) # tripinfo 中記錄了每一輛車在網(wǎng)絡(luò)中的行駛信息,包括出發(fā)時間、出發(fā)車道、到達(dá)時間、等待時間、車輛類型等等。
run()
首先是生成 .rou.xml 文件,然后運行已經(jīng)設(shè)置好的 sumocfg 文件,里面實際上是調(diào)用了 .net.xml 文件、.rou.xml 文件以及感應(yīng)線圈的設(shè)置文件,通過 traci.start 啟動 SUMO,建立 traci 與 SUMO 的通信連接。最后運行 run 函數(shù),實現(xiàn)兩者的交互。
在官方給出的程序中,并沒有直接調(diào)用 sumo-gui ,而是通過 sumolib 中的 checkBinary 函數(shù)先查找 sumo-gui 程序的位置,然后再運行它。這兩者效果是一樣的。