3

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

 2 years ago
source link: https://mathpretty.com/14088.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

王 茂南 2021年9月26日07:09:05评论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 文件,也就是生成的路网文件:

  1. 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.         if self.GUI:
  11.             sumo_gui = 'sumo-gui'
  12.         else:
  13.             sumo_gui = 'sumo'
  14.         traci.start([
  15.             sumo_gui,
  16.             "-c", self.sumo_config,
  17.             "--no-warnings",
  18.             "--seed", "2"])
  19.         self.launch_env_flag = True
  20.     def close(self):
  21.         """关闭实验环境
  22.         traci.close()
  23.         self.launch_env_flag = False
  24.         sys.stdout.flush()
  25.     def reset(self):
  26.         """关闭当前环境, 并开启一个新的环境
  27.         self.close()
  28.         self.launchEnv()
  29.     def step(self):
  30.         steps = 0
  31.         assert self.launch_env_flag
  32.         while traci.simulation.getMinExpectedNumber() > 0: # 当路网里面还有车
  33.             traci.simulationStep()
  34.             steps = steps + 1
  35.     def runSim(self):
  36.         """开始模拟
  37.         self.launchEnv()  # 初始化环境
  38.         self.step()  # 进行模拟
  39.         self.close()  # 关闭环境
  40. if __name__ == '__main__':
  41.     sumo_sim = Sim(sumo_config='./env/hello.sumocfg')
  42.     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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK