使用 SUMO 进行仿真(4)-信号灯(Traffic Light)

王 茂南 2021年9月26日07:09:05
评论
6 6390字阅读21分18秒
摘要本文会介绍 SUMO 中信号灯(Traffic Light)的相关内容。包括如何生成带有 Traffic Light 的路网,如何设置信号灯,如何利用 TraCI 来控制信号灯。

简介

在前面几篇的内容中,我们介绍了如何生成路网(network)和生成车流文件(route),在本文中我们会着重介绍信号灯(Traffic Light)的部分,同时也会涉及到如何使用 Traci 来控制信号灯。

参考资料

 

Traffic Light 的介绍

这里还是延续使用使用 SUMO 进行仿真(2)-Node 和 Edge 中的路网文件,其中包含着 hello.edge.xmlhello.node.xml

为了能够设置红绿灯,我们首先需要将 node 的 type 设置为 traffic_light,如下所示:

  1. <node id="0" x="0.0" y="0.0" type="traffic_light"/>

之后,我们在 id=0node 上创建一个信号灯。我们单独将其保存到一个文件,这里保存为 hello.add.xml

  1. <additional>
  2.     <tlLogic id="0" type="static" programID="0" offset="0">
  3.         <phase duration="37" state="GGGggrrrrrGGGggrrrrr"/> <!--绿灯-->
  4.         <phase duration="8"  state="yyyyyrrrrryyyyyrrrrr"/> <!--黄灯-->
  5.         <phase duration="37" state="rrrrrGGGggrrrrrGGGgg"/> <!--红灯-->
  6.         <phase duration="8"  state="rrrrryyyyyrrrrryyyyy"/> <!--黄灯-->
  7.     </tlLogic>
  8. </additional>

我们对上面的文件进行一下简单的解释:

  • id 为节点 id,即这个信号灯会设置在哪个节点上;
  • programID 是该节点一套信号配时方案的 ID 名。这代表在一个节点可以设置多套信号配时方案。这为后续的实时控制打下了基础。
  • offset 则为信号灯启动的时间。
  • type 代表了信号灯的类型(static,actuated),其中 static 代表固定相位的信号灯,actuated 则可根据车流状况适当延长与缩短相位,类似感应控制。

这样我们就有了三个文件,分别为 hello.edge.xml, hello.node.xmlhello.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 来查看路网文件。我们也可以切换到信号灯的模式下,查看信号灯的相位情况:

使用 SUMO 进行仿真(4)-信号灯(Traffic Light)

下面通过 NetEdit 来查看信号灯的情况:

使用 SUMO 进行仿真(4)-信号灯(Traffic Light)

再详细看一下上面的 phase 的内容。共有 4 个 phase,每一个 phase 中的 state 表示每一个路口在当前这个 phase 的状态。第一个 state 就对当前整个十字路口进行的一个红绿灯的状态表示,这个整个状态会持续 37 秒。接着切换下一个 state,持续是 8s。依次类推。

 

使用 TraCI 控制信号灯

上面我们介绍了如何在 network 中设置红绿灯,这里我们看一下如何结合 TraCI 来对信号灯进行控制。 在开始实验之前,我们需要确保 traci 在系统环境变量中。可以使用下面的代码来进行确认:

  1. import os
  2. import sys
  3. # 确保 traci 在系统环境变量中
  4. if 'SUMO_HOME' in os.environ:
  5.     tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
  6.     sys.path.append(tools)
  7.     print('SUMO_HOME is In Environment!')
  8. else:
  9.     sys.exit("please declare environment variable 'SUMO_HOME'")

 

使用 TraCI 开启并运行仿真

首先我们来看一下如何使用 TraCI 来启动仿真。我们定义一个 Sim 的类,其中包括「启动仿真」,「逐步运行仿真」,「关闭仿真」等功能:

  1. import sys
  2. import traci
  3. class Sim(object):
  4.     def __init__(self, sumo_config, GUI=False):
  5.         self.sumo_config = sumo_config # sumo config 文件
  6.         self.launch_env_flag = False
  7.         self.GUI = GUI
  8.     def launchEnv(self):
  9.         """开始模拟(通过traci来获得其中数据)
  10.         """
  11.         if self.GUI:
  12.             sumo_gui = 'sumo-gui'
  13.         else:
  14.             sumo_gui = 'sumo'
  15.         traci.start([
  16.             sumo_gui,
  17.             "-c", self.sumo_config,
  18.             "--no-warnings",
  19.             "--seed", "2"])
  20.         self.launch_env_flag = True
  21.     def close(self):
  22.         """关闭实验环境
  23.         """
  24.         traci.close()
  25.         self.launch_env_flag = False
  26.         sys.stdout.flush()
  27.     def reset(self):
  28.         """关闭当前环境, 并开启一个新的环境
  29.         """
  30.         self.close()
  31.         self.launchEnv()
  32.     def step(self):
  33.         steps = 0
  34.         assert self.launch_env_flag
  35.         while traci.simulation.getMinExpectedNumber() > 0: # 当路网里面还有车
  36.             traci.simulationStep()
  37.             steps = steps + 1
  38.     def runSim(self):
  39.         """开始模拟
  40.         """
  41.         self.launchEnv()  # 初始化环境
  42.         self.step()  # 进行模拟
  43.         self.close()  # 关闭环境
  44. if __name__ == '__main__':
  45.     sumo_sim = Sim(sumo_config='./env/hello.sumocfg')
  46.     sumo_sim.runSim()

运行上面的代码,如果成功运行,会出现以下的结果:

使用 SUMO 进行仿真(4)-信号灯(Traffic Light)

在上面的代码中,traci.simulation.getMinExpectedNumber() 表示路网中车辆数。如果车辆数为 0,说明所有车辆已经离开路网。仿真可以停止了。

 

使用 TraCI 获得信号灯的信息

在上面使用 TraCI 开启仿真之后,我们可以获得一些信号灯的属性。具体的使用方法,我们可以查看链接, TraCI/Traffic Lights Value Retrieval

首先我们可以获得「当前的 phase 的持续时间」,以及「切换到下一次 phase 的时间」:

  1. print(traci.trafficlight.getNextSwitch('haxl_htxdj')) # 到下一次切换信号灯的时间
  2. print(traci.trafficlight.getPhaseDuration('haxl_htxdj')) # 目前这个phase的持续时间

也可以使用下面的获得这个交叉路口整个的信号灯的属性,这里需要给的参数是是「信号灯的ID」.

  1. traci.trafficlight.getCompleteRedYellowGreenDefinition

最终可以获得一个信号灯的配时(数据类型为,list(Logic))。其中一个 Logic 包含以下的内容,一共有多少个 phase,每个 phase 的持续时间,每个 phase 中不同的 connection 是什么状态的信号灯:

  1. Logic(programID='0', type=0, currentPhaseIndex=0, phases=(
  2.     Phase(duration=35.0, state='GGGrrrrrrGGGGrrrrr', minDur=35.0, maxDur=35.0, next=()),
  3.     Phase(duration=5.0, state='yyyrrrrrryyyyrrrrr', minDur=5.0, maxDur=5.0, next=()),
  4.     Phase(duration=6.0, state='rrrGrrrrrrrrrGrrrr', minDur=6.0, maxDur=6.0, next=()),
  5.     Phase(duration=5.0, state='rrryrrrrrrrrryrrrr', minDur=5.0, maxDur=5.0, next=()),
  6.     Phase(duration=5.0, state='rrrrrrrrrrrrrrrrrr', minDur=5.0, maxDur=5.0, next=()),
  7.     Phase(duration=34.0, state='rrrrGGGGgrrrrrGGGg', minDur=34.0, maxDur=34.0, next=()),
  8.     Phase(duration=5.0, state='rrrryyyyyrrrrryyyy', minDur=5.0, maxDur=5.0, next=()),
  9.     Phase(duration=5.0, state='rrrrrrrrrrrrrrrrrr', minDur=5.0, maxDur=5.0, next=())),
  10.     subParameter={}),

 

使用 Traci 设置信号灯信息

我们也可以使用 setCompleteRedYellowGreenDefinition 来设置信号灯的信息。这个的用法和 setProgramLogic(tlsID, tls) 的使用方法是一样的。具体的函数,可以参考下面的链接traci traffic light

  • 其中 tlsID 表示信号灯的 ID。
  • 其中 tls 是一个 Logic 类型的数据,实际使用的时候,我们可以通过 getCompleteRedYellowGreenDefinition 获得数据,接着对 Logic 类型的数据进行修改即可。

我们下面看一个简单的例子,修改某一个 phase 的持续时间,我们将所有的绿灯时间都延长 10 秒:

  1. import traci
  2. from sim import Sim
  3. sumo_sim = Sim(sumo_config='./env/hello.sumocfg')
  4. sumo_sim.launchEnv() # 开启仿真
  5. # 获取信号灯信息
  6. logic_list = traci.trafficlight.getCompleteRedYellowGreenDefinition('0')[0] # 只有一个 program
  7. print(logic_list)
  8. # 修改信号灯信息, 将绿灯时长都加 10 秒
  9. for phase in logic_list.phases:
  10.     if 'G' in phase.state:
  11.         phase.duration += 10
  12. traci.trafficlight.setCompleteRedYellowGreenDefinition(tlsID='0', tls=logic_list)
  13. # 重新打印修改后的红绿灯信息
  14. logic_list = traci.trafficlight.getCompleteRedYellowGreenDefinition('0')[0]
  15. print(logic_list)

 

TraCI 控制信号灯变化例子-根据车流数量进行调整

我们使用下面的十字路口作为例子来进行说明。为了突出效果,当车道 edge4_0 和车道 edge16_0 的车辆大于车道 edge11_2 和车道 edge9_3,将车道 edge4_0 和车道 edge16_0 设置为红灯。也就是此时堵车会越来越厉害(这样方便最后看效果)。

使用 SUMO 进行仿真(4)-信号灯(Traffic Light)

我们通过:

  • traci.edge.getLastStepVehicleNumber 获得指定 edge 的车辆的数量;
  • traci.trafficlight.setRedYellowGreenState 修改某个 node 的当前的信号灯状态;

通过上面两个函数来控制信号灯。完整的代码如下:

  1. import traci
  2. import time
  3. # traci.start其实就是将sumo的命令行指令以列表形式读取, 运行sumo-gui程序, 其指令方式与cmd命令行相同
  4. traci.start(["C:/Program Files (x86)/Eclipse/Sumo/bin/sumo-gui.exe","-c","D:/cross.sumocfg"])
  5. step=0
  6. while step<3600:
  7.     traci.simulationStep() # 每次触发一次
  8.     # 通过time.sleep命令降低仿真的速度
  9.     time.sleep(0.1)
  10.     step=step+1
  11.     # 比较路段车辆数多少,改变信号灯相位状态
  12.     if traci.edge.getLastStepVehicleNumber('edge4_0')+traci.edge.getLastStepVehicleNumber('edge16_0')>traci.edge.getLastStepVehicleNumber('edge11_2')+traci.edge.getLastStepVehicleNumber('edge9_3'):
  13.         traci.trafficlight.setRedYellowGreenState('node10','GGGggrrrrrGGGggrrrrr') # 设置node的红绿灯状态
  14.         print('绿灯状态')
  15.     else:
  16.         traci.trafficlight.setRedYellowGreenState('node10','rrrrrGGGggrrrrrGGGgg')
  17. traci.close() # 关闭仿真

 

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南
  • 本文由 发表于 2021年9月26日07:09:05
  • 转载请务必保留本文链接:https://mathpretty.com/14088.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: