引言
SUMO 本身可以實(shí)現(xiàn)很多實(shí)際交通場景的模擬矛物。當(dāng) SUMO 被用作智能交通控制算法的測試平臺(tái)時(shí)逻杖,需要其與外界程序/算法實(shí)現(xiàn)很好的互動(dòng)怔球,例如用戶自定義的控制算法可以從 SUMO 獲取實(shí)時(shí)交通信息倔矾,然后對其中車輛狀態(tài)他去、信號燈狀態(tài)等進(jìn)行實(shí)時(shí)控制毙驯。TraCI 就是實(shí)現(xiàn)這類互動(dòng)的接口。
TraCI: Traffic Control Interface. 交通控制接口灾测。
作用:獲取 SUMO 交通模擬環(huán)境中的數(shù)據(jù)爆价,并實(shí)時(shí)修改、控制。
目前該接口支持多種主流語言允坚,包括 python, c++, .NET, MATLAB, Java魂那,其中 python 版本的 TraCI 功能最全面。下面就以 python 版本的 TraCI 為例稠项,介紹一下如何實(shí)現(xiàn) SUMO 與外部控制算法的互動(dòng)涯雅。關(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)換機(jī)制:
當(dāng)南北方向沒有車輛過來時(shí)埂软,東西方向一直保持綠燈
當(dāng)南北方向有車過來時(shí)锈遥,切換信號燈,東西方向黃燈 6 s勘畔,然后變紅所灸,同時(shí)南北方向變綠,持續(xù) 31 s炫七,然后變黃爬立,再變紅,東西方向變綠万哪。
重復(fù)上述兩個(gè)過程
現(xiàn)在假設(shè) net.xml 文件已經(jīng)得到侠驯。實(shí)際上,通過 netedit 可以很容易的構(gòu)造上述交通路網(wǎng)奕巍。
SUMO 與 TraCI 的交互是在文件 runner.py 中實(shí)現(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)
運(yùn)行上述函數(shù)之后,會(huì)在 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,四個(gè) phase 依次編號為 0, 1, 2, 3旺芽,初始時(shí)設(shè)置 phase 為 2沪猴,即 東西道路為 G辐啄,南北道路為 r。
while traci.simulation.getMinExpectedNumber() > 0: # 該函數(shù)得到當(dāng)前 net 中的車輛數(shù)目加上還沒有進(jìn)入 net 的車輛數(shù)目运嗜。只要該數(shù)值 > 0壶辜,就表明還有車輛需要處理。
traci.simulationStep() # 運(yùn)行一步仿真
if traci.trafficlight.getPhase("0") == 2:
if traci.inductionloop.getLastStepVehicleNumber("0") > 0: # 在上一步仿真中担租,經(jīng)過 induction loop 的汽車數(shù)量
traci.trafficlight.setPhase("0", 3) # 如果有車進(jìn)來砸民,則切換 phase
else:
traci.trafficlight.setPhase("0", 2) # 否則依然保持 phase。注意奋救,這里是重置了 phase, 所以會(huì)重新計(jì)時(shí)岭参。
step += 1
traci.close()
- 運(yùn)行主程序
if __name__ == "__main__":
generate_routefile()
traci.start(["sumo-gui", "-c", "data/cross.sumocfg",
"--tripinfo-output", "tripinfo.xml"]) # tripinfo 中記錄了每一輛車在網(wǎng)絡(luò)中的行駛信息,包括出發(fā)時(shí)間尝艘、出發(fā)車道演侯、到達(dá)時(shí)間、等待時(shí)間背亥、車輛類型等等秒际。
run()
首先是生成 .rou.xml 文件,然后運(yùn)行已經(jīng)設(shè)置好的 sumocfg 文件狡汉,里面實(shí)際上是調(diào)用了 .net.xml 文件娄徊、.rou.xml 文件以及感應(yīng)線圈的設(shè)置文件,通過 traci.start 啟動(dòng) SUMO轴猎,建立 traci 與 SUMO 的通信連接嵌莉。最后運(yùn)行 run 函數(shù)进萄,實(shí)現(xiàn)兩者的交互捻脖。
在官方給出的程序中,并沒有直接調(diào)用 sumo-gui 中鼠,而是通過 sumolib 中的 checkBinary 函數(shù)先查找 sumo-gui 程序的位置可婶,然后再運(yùn)行它。這兩者效果是一樣的援雇。