文章目录(Table of Contents)
简介
在前面几篇的内容中,我们介绍了如何生成路网(network)和生成车流文件(route),在本文中我们会着重介绍信号灯(Traffic Light)的部分,同时也会涉及到如何使用 Traci 来控制信号灯。
参考资料
- 使用 SUMO 进行仿真(1)-快速入门
- 使用 SUMO 进行仿真(2)-Node 和 Edge
- 使用 SUMO 进行仿真(3)-车流的生成(Route 文件)
- Simulation/Traffic Lights,SUMO 中对 Traffic Light 的介绍;
- TraCI/Interfacing TraCI from Python,使用 TraCI 来进行控制;
Traffic Light 的介绍
这里还是延续使用使用 SUMO 进行仿真(2)-Node 和 Edge 中的路网文件,其中包含着 hello.edge.xml
和 hello.node.xml
。
为了能够设置红绿灯,我们首先需要将 node 的 type 设置为 traffic_light
,如下所示:
- <node id="0" x="0.0" y="0.0" type="traffic_light"/>
之后,我们在 id=0
的 node
上创建一个信号灯。我们单独将其保存到一个文件,这里保存为 hello.add.xml
。
- <additional>
- <tlLogic id="0" type="static" programID="0" offset="0">
- <phase duration="37" state="GGGggrrrrrGGGggrrrrr"/> <!--绿灯-->
- <phase duration="8" state="yyyyyrrrrryyyyyrrrrr"/> <!--黄灯-->
- <phase duration="37" state="rrrrrGGGggrrrrrGGGgg"/> <!--红灯-->
- <phase duration="8" state="rrrrryyyyyrrrrryyyyy"/> <!--黄灯-->
- </tlLogic>
- </additional>
我们对上面的文件进行一下简单的解释:
- id 为节点 id,即这个信号灯会设置在哪个节点上;
- programID 是该节点一套信号配时方案的 ID 名。这代表在一个节点可以设置多套信号配时方案。这为后续的实时控制打下了基础。
- offset 则为信号灯启动的时间。
- type 代表了信号灯的类型(static,actuated),其中 static 代表固定相位的信号灯,actuated 则可根据车流状况适当延长与缩短相位,类似感应控制。
这样我们就有了三个文件,分别为 hello.edge.xml
, hello.node.xml
和 hello.add.xml
。接着我们来生成 hello.net.xml
文件,也就是生成的路网文件:
netconvert --node-files=hello.nod.xml --edge-files=hello.edg.xml --tllogic-files=hello.add.xml --output-file=hello.net.xml
运行上面的命令,会生成hello.net.xml
文件,可以使用 NetEdit 来查看路网文件。我们也可以切换到信号灯的模式下,查看信号灯的相位情况:
下面通过 NetEdit
来查看信号灯的情况:
再详细看一下上面的 phase 的内容。共有 4 个 phase,每一个 phase 中的 state 表示每一个路口在当前这个 phase 的状态。第一个 state 就对当前整个十字路口进行的一个红绿灯的状态表示,这个整个状态会持续 37 秒。接着切换下一个 state,持续是 8s。依次类推。
使用 TraCI 控制信号灯
上面我们介绍了如何在 network 中设置红绿灯,这里我们看一下如何结合 TraCI 来对信号灯进行控制。 在开始实验之前,我们需要确保 traci 在系统环境变量中。可以使用下面的代码来进行确认:
- import os
- import sys
- # 确保 traci 在系统环境变量中
- if 'SUMO_HOME' in os.environ:
- tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
- sys.path.append(tools)
- print('SUMO_HOME is In Environment!')
- else:
- sys.exit("please declare environment variable 'SUMO_HOME'")
使用 TraCI 开启并运行仿真
首先我们来看一下如何使用 TraCI 来启动仿真。我们定义一个 Sim 的类,其中包括「启动仿真」,「逐步运行仿真」,「关闭仿真」等功能:
- import sys
- import traci
- class Sim(object):
- def __init__(self, sumo_config, GUI=False):
- self.sumo_config = sumo_config # sumo config 文件
- self.launch_env_flag = False
- self.GUI = GUI
- def launchEnv(self):
- """开始模拟(通过traci来获得其中数据)
- """
- if self.GUI:
- sumo_gui = 'sumo-gui'
- else:
- sumo_gui = 'sumo'
- traci.start([
- sumo_gui,
- "-c", self.sumo_config,
- "--no-warnings",
- "--seed", "2"])
- self.launch_env_flag = True
- def close(self):
- """关闭实验环境
- """
- traci.close()
- self.launch_env_flag = False
- sys.stdout.flush()
- def reset(self):
- """关闭当前环境, 并开启一个新的环境
- """
- self.close()
- self.launchEnv()
- def step(self):
- steps = 0
- assert self.launch_env_flag
- while traci.simulation.getMinExpectedNumber() > 0: # 当路网里面还有车
- traci.simulationStep()
- steps = steps + 1
- def runSim(self):
- """开始模拟
- """
- self.launchEnv() # 初始化环境
- self.step() # 进行模拟
- self.close() # 关闭环境
- if __name__ == '__main__':
- sumo_sim = Sim(sumo_config='./env/hello.sumocfg')
- sumo_sim.runSim()
运行上面的代码,如果成功运行,会出现以下的结果:
在上面的代码中,traci.simulation.getMinExpectedNumber() 表示路网中车辆数。如果车辆数为 0,说明所有车辆已经离开路网。仿真可以停止了。
使用 TraCI 获得信号灯的信息
在上面使用 TraCI 开启仿真之后,我们可以获得一些信号灯的属性。具体的使用方法,我们可以查看链接, TraCI/Traffic Lights Value Retrieval。
首先我们可以获得「当前的 phase 的持续时间」,以及「切换到下一次 phase 的时间」:
- print(traci.trafficlight.getNextSwitch('haxl_htxdj')) # 到下一次切换信号灯的时间
- print(traci.trafficlight.getPhaseDuration('haxl_htxdj')) # 目前这个phase的持续时间
也可以使用下面的获得这个交叉路口整个的信号灯的属性,这里需要给的参数是是「信号灯的ID」.
- traci.trafficlight.getCompleteRedYellowGreenDefinition
最终可以获得一个信号灯的配时(数据类型为,list(Logic))。其中一个 Logic 包含以下的内容,一共有多少个 phase,每个 phase 的持续时间,每个 phase 中不同的 connection 是什么状态的信号灯:
- Logic(programID='0', type=0, currentPhaseIndex=0, phases=(
- Phase(duration=35.0, state='GGGrrrrrrGGGGrrrrr', minDur=35.0, maxDur=35.0, next=()),
- Phase(duration=5.0, state='yyyrrrrrryyyyrrrrr', minDur=5.0, maxDur=5.0, next=()),
- Phase(duration=6.0, state='rrrGrrrrrrrrrGrrrr', minDur=6.0, maxDur=6.0, next=()),
- Phase(duration=5.0, state='rrryrrrrrrrrryrrrr', minDur=5.0, maxDur=5.0, next=()),
- Phase(duration=5.0, state='rrrrrrrrrrrrrrrrrr', minDur=5.0, maxDur=5.0, next=()),
- Phase(duration=34.0, state='rrrrGGGGgrrrrrGGGg', minDur=34.0, maxDur=34.0, next=()),
- Phase(duration=5.0, state='rrrryyyyyrrrrryyyy', minDur=5.0, maxDur=5.0, next=()),
- Phase(duration=5.0, state='rrrrrrrrrrrrrrrrrr', minDur=5.0, maxDur=5.0, next=())),
- subParameter={}),
使用 Traci 设置信号灯信息
我们也可以使用 setCompleteRedYellowGreenDefinition
来设置信号灯的信息。这个的用法和 setProgramLogic(tlsID, tls) 的使用方法是一样的。具体的函数,可以参考下面的链接,traci traffic light。
- 其中 tlsID 表示信号灯的 ID。
- 其中 tls 是一个 Logic 类型的数据,实际使用的时候,我们可以通过
getCompleteRedYellowGreenDefinition
获得数据,接着对 Logic 类型的数据进行修改即可。
我们下面看一个简单的例子,修改某一个 phase 的持续时间,我们将所有的绿灯时间都延长 10 秒:
- import traci
- from sim import Sim
- sumo_sim = Sim(sumo_config='./env/hello.sumocfg')
- sumo_sim.launchEnv() # 开启仿真
- # 获取信号灯信息
- logic_list = traci.trafficlight.getCompleteRedYellowGreenDefinition('0')[0] # 只有一个 program
- print(logic_list)
- # 修改信号灯信息, 将绿灯时长都加 10 秒
- for phase in logic_list.phases:
- if 'G' in phase.state:
- phase.duration += 10
- traci.trafficlight.setCompleteRedYellowGreenDefinition(tlsID='0', tls=logic_list)
- # 重新打印修改后的红绿灯信息
- logic_list = traci.trafficlight.getCompleteRedYellowGreenDefinition('0')[0]
- print(logic_list)
TraCI 控制信号灯变化例子-根据车流数量进行调整
我们使用下面的十字路口作为例子来进行说明。为了突出效果,当车道 edge4_0 和车道 edge16_0 的车辆大于车道 edge11_2 和车道 edge9_3,将车道 edge4_0 和车道 edge16_0 设置为红灯。也就是此时堵车会越来越厉害(这样方便最后看效果)。
我们通过:
- traci.edge.getLastStepVehicleNumber 获得指定 edge 的车辆的数量;
- traci.trafficlight.setRedYellowGreenState 修改某个 node 的当前的信号灯状态;
通过上面两个函数来控制信号灯。完整的代码如下:
- import traci
- import time
- # traci.start其实就是将sumo的命令行指令以列表形式读取, 运行sumo-gui程序, 其指令方式与cmd命令行相同
- traci.start(["C:/Program Files (x86)/Eclipse/Sumo/bin/sumo-gui.exe","-c","D:/cross.sumocfg"])
- step=0
- while step<3600:
- traci.simulationStep() # 每次触发一次
- # 通过time.sleep命令降低仿真的速度
- time.sleep(0.1)
- step=step+1
- # 比较路段车辆数多少,改变信号灯相位状态
- if traci.edge.getLastStepVehicleNumber('edge4_0')+traci.edge.getLastStepVehicleNumber('edge16_0')>traci.edge.getLastStepVehicleNumber('edge11_2')+traci.edge.getLastStepVehicleNumber('edge9_3'):
- traci.trafficlight.setRedYellowGreenState('node10','GGGggrrrrrGGGggrrrrr') # 设置node的红绿灯状态
- print('绿灯状态')
- else:
- traci.trafficlight.setRedYellowGreenState('node10','rrrrrGGGggrrrrrGGGgg')
- traci.close() # 关闭仿真
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论