SUMO的停车场仿真 您所在的位置:网站首页 自己制作停车场模型教程 SUMO的停车场仿真

SUMO的停车场仿真

2024-07-12 04:33| 来源: 网络整理| 查看: 265

停车场仿真

github地址: https://github.com/JunXie-ZH/SUMO_Tutorial/tree/main/Parking

在SUMO中实现停车场仿真需要掌握三个部分的工作(如图1),分别为:

停车设施构建:在SUMO中停车设施的定义以及构建方式;停车需求加载:灵活应用各种停车需求加载方式;停车区重选择:车辆的目标车位被占时,如何找到新的停车位。

本章节将围绕这三部分工作对SUMO停车仿真进行详细介绍。

在这里插入图片描述

图1 停车仿真框架 1 停车设施构建 1.1 停车区域与停车位

在SUMO中,停车设施由元素*定义,该元素定义在.add.xml*文件中。

一个元素**详细定义如下:

元素**包含的各个属性定义:

表1 parkingArea元素属性 属性值类型定义id字符串停车区域ID,由用户自定义,且必须为唯一的lane字符串停车区域所在的车道,在SUMO中停车区域通常与车道是绑定在一起的startPos浮点数停车区域的起始位置,以车道起始点为标准,数值表示为距离车道起始点x米endPos浮点数停车区域的终止位置,以车道起始点为标准,数值表示为距离车道起始点x米,endPos>startPosfriendlyPos布尔型变量是否自动纠正无效的停车位置(默认为false)name字符串对停车场的描述,可以用任意文字,仅用于可视化目的。roadsideCapacity整型该停车区域的路内停车位容量,默认值为0onRoad布尔型变量车辆停车时是否停留在路中,默认值为false,若设置为true,则只使用roadsideccapacity,不允许定义space给车辆停车width浮点数路内停车位的宽度length浮点数路内停车位的长度angle浮点数路边停车位的角度相对于车道角度,正值表示顺时针

从元素**所具备的属性,我们进行了如下的总结:

①*所描述的是一个停车区域,包含多个停车位,且*与路段车道(lane)是绑定的;②可以定义任意停车位置和角度(angle),从而实现多种停车方式,如鱼骨式停车、平行停车等;③*包含了路内停车位(roadsideCapacity)与路外停车位(),因此一个停车区域的总容量等于roadsideCapacity的数值与*元素的个数之和。

对于③,在示例中,roadsideCapacity=5,**元素个数为10个,因此该停车区域的总容量为15个车位,在仿真中的可视化如图2:

在这里插入图片描述

图2 停车区域可视化 ### 1.2 构建方式

停车设施的构建可以通过netedit编辑器实现,用户可以在已有道路路网的基础上,根据使用需求在合适的区域构建停车设施。

Step 1 用netedit编辑器打开路网文件(.net.xml),在Network模式下,点击上方工具栏[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vxOmxzn-1643112936687)(./pic/addtionalMode.png)] 打开Additional mode(或点击上方菜单栏 Edit–>Additional mode打开)。在界面左侧Additionals中,点击Additional elements的下拉菜单选择parkingArea;

在这里插入图片描述

图3 Additional elements选择

Step 2 完成上一步后,即可点击某个车道构建停车区域。在SUMO中,停车区域与车道是绑定的,表示为车辆需通过该车道进入该停车区域。一个车道上可以构建多个停车区域(图4.a),也可以只构建一个停车区域(图4.b),并可以在左侧的属性栏中的修改该停车区域的属性:如id,roadsideCapacity等,详细可见表1。

在这里插入图片描述

图4 停车区域构建 ***Step 3*** 上一步完成了路内停车位的构建。在部分交通场景中,某个车道除了拥有路内停车区域外,通过该车道还可以进入一些路外停车场,这部分路外停车场的构建则可以通过*\

在这里插入图片描述

图5 停车区域构建 ***Step 4*** 点击构建好的*sapce*,可以通过调整其*pos*等属性,使车辆在仿真可视化时的停放排列更加规整。调整*angle*属性可以调整车位的摆放角度,实现鱼骨式停车等场景。如图6。

在这里插入图片描述

图6 sapce位置角度调整 ***Step 5*** 点击保存,即可将新建立的停车设施保存为*.add.xml*文件。 2 停车需求加载

停车是出行的其中一个环节,在前面的章节中,我们介绍了如何加载车辆的出行需求,停车需求的加载则是在出行需求的基础上进行实现的。停车需求的实战加载方式如图1所示主要有三种:直接在*.rou.xml文件中编写,通过netedit编辑器加载,通过TraCI*进行动态加载。

2.1 直接在*.rou.xml*文件中编写

首先可以直接在*.rou.xml文件中编写实现停车需求的加载。停车需求在.rou.xml*文件中的编写是在出行的基础上完成的,我们以一个出行(trip)为例:

我们在一个OD级的个体出行需求的基础上,添加了一个*元素,使其要停靠在一个parkingArea中。对于parkingArea*的选择:

可以选择终点路段的停车场(如上例中的parkingArea_e),则车辆行驶到终点路段后进入停车位停车,到停车时间(duration)后,车辆在终点路段行驶到尽头后消失在路网。也可以选择在非起点和终点的停车场,但一定要在车辆的行驶路径中。但由于OD级的出行需求中,车辆的行驶路径是由SUMO决定的,停车场有可能并不在SUMO所决定的行驶路径中,运行仿真后会报如下错误: Error: parkingArea 'parkingArea_x' for vehicle 'type2.0' on lane 'c_0' is not downstream the current route.

因此,在OD级的车辆出行需求中,停车区域最好选择在终点路段,或者采用路径级车辆出行需求,为车辆指定出行路径,并在路径中间路段的停车场进行停车:

以上均是个体级的停车需求在*.rou.xml*文件中的表示,对于集计型的交通需求(OD级与路径级)同样可以加载停车需求。

OD级:

路径级:

2.2 通过netedit编辑器加载

netedit编辑器提供了可视化界面来进行停车需求的加载,但本质上还是为了生成一个*.rou.xml*文件来加载停车需求。

Step 1 如上所述,停车需求是在出行需求的基础上完成的,因此,首先在编辑器中加载好对应的出行需求,各粒度出行需求的编辑方式在前面已经介绍。假设已经创建了一个个体OD级出行需求,如下图:

在这里插入图片描述

图7 加载个体OD级出行需求 ***Step 2*** 在*Demand*模式下,选择[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fUf1tW6Q-1643112936688)(./pic/stopElements.png)](Edit-->Stop mode),打开添加停车需求模块。左方状态栏中,*Parent element*选择为要添加停车行为的出行需求,*Stops*选择为*stopParkingArea*,其他属性(如停留时间等)用户均可根据自身实践需求进行设定;

在这里插入图片描述

图8 设置停车需求

Step 3 将停车需求的各项属性设置好后,点击车辆在出行过程中需要停留的停车区域(需要在出行路径上),即可给该需求添加上停车行为;

在这里插入图片描述

图9 添加停车需求

Step 4 点击保存后,打开保存好的*.rou.xml*文件,可以看到,相应的停车需求生成成功。

2.3 通过TraCI动态加载

在实际的一些应用中,通过直接在*.rou.xml文件中编写和通过netedit编辑器来加载停车需求并不方便,这时候可以通过TraCI来动态加载出行需求和停车需求。TraCI动态加载需求的方式更加灵活,可以在每一个仿真步长中判断是否添加出行需求,且借助Python*的部分工具可以实现对车辆运动的精准监控。

我们以一个简单的例子来介绍如何通过TraCI动态加载停车需求。仿真时间一小时,每半分钟加载一个车辆出行需求,OD均为a–>e,并均在parkingArea_e停放100秒。应用的TraCI相关命令有traci.route.add、traci.vehicle.add以及traci.vehicle.setParkingAreaStop。

for step in range(3600): # 仿真一小时(3600s) if step % 30==0: # 每半分钟添加一个出行需求,并指定其停车位置 # 定义相关变量 tripid="trip"+str(step) # 出行ID origin="a" # 出发路段 destination="e" # 终点路段 vehid="v"+str(step) # 车辆ID parkingArea='parkingArea_e' # 停车区域 # 添加出行需求 & 停车需求 traci.route.add(tripid, [origin,destination]) traci.vehicle.add(vehid, tripid, typeID="type1") traci.vehicle.setParkingAreaStop(vehid, parkingArea,duration=100) 3 停车区重选择

在停车场的实践过程中,经常会遇到这种情况:当车辆行驶到系统为其分配的停车区域时,发现已经无空闲的停车位。此时车辆是无法在该区域进行停车的,在这种情况下,车辆可以在道路上等待,直到该停车区域出现了可用的停车位。但是在现实场景中,司机会根据自己的需求与实际情况重新选择一个新的停车区域,因此,SUMO中也提供了让车辆重新选择一个停车区的功能。

针对停车区重选择行为,在SUMO中可以通过定义*中的元素来实现。该元素中会定义一组可以相互用作替代的停车区,定义在.add.xml*文件中,示例如下:

**的相关定义属性如下:

属性值类型定义id字符串所定义的 rerouter的ID,用户自定义edges浮点型一个路段id或一个路段id列表,车辆在这些路段上可以触发rerouterprobability浮点型车辆触发rerouter的概率,默认为1timeThreshold时间(秒)rerouter触发时需与上一次触发时的间隔时间(默认为0)vTypes字符串列表该rerouter适用的车辆类型,默认为适用所有车辆类型off布尔型变量rerouter初始是否为不活动的,默认值:false

的属性主要是begin: rerouter生效的开始时间,以及end: rerouter生效的结束时间。

**的各个属性含义如下表所示:

属性值类型定义id字符串已经定义好的停车区域IDprobability浮点型每个可选方案(即备用停车区域被选中概率)被选中的概率(默认为1)。在重路由中,所有定义的概率都自动归一化。visible布尔型在车辆到达停车区域所在路段之前,是否知道该停车区的占用情况

通常而言,在以下两种情况时会触发停车的rerouter:

当车辆到达停车区域并且由于容量不足而无法停车时;当*元素具有属性 visible="true"时,车辆在没到达目标停车区域时就可知道目标停车区域的情况。若目标停车区域已满,且车辆位于元素中edge*属性中的路段时,车辆则会在备选停车区域中重新选择一个进行停车。

由于备选停车区域可能有多个,如上例,加入车辆初始的目标停车区域为parkingArea_a,若该停车区域已满,那么车辆则会根据一些因素的权重设置,选择parkingArea_b与parkingArea_c其中一个进行停车。相关的因素如下表:

属性默认值定义值是否越大越好parking.probability.weight0parkingAreaReroute元素中所定义的probabilityyesparking.capacity.weight0备选停车区域的总容量yesparking.absfreespace.weight0备选停车区域的绝对空闲车位数yesparking.relfreespace.weight0备选停车区域的相对空闲车位数yesparking.distanceto.weight1车辆到达备选停车区域的距离noparking.timeto.weight0车辆到达备选停车区域所要花费的时间(假设值)noparking.distancefrom.weight0备选停车区域到车辆目的地的距离noparking.timefrom.weight0备选停车区域到车辆目的地的行驶时间(假设值)no

SUMO会根据每个备选停车位的上述因子相对应的属性值,最终得出一个评估值 P i P_i Pi​。根据评估值的大小来决定选择哪一个备选停车位。 f j f_j fj​为第j个因子的值, α j \alpha_j αj​为第j个因子的权重。 P i = ∑ j = 0 n α j f j P_i=\sum_{j=0}^{n}\alpha_j f_j Pi​=j=0∑n​αj​fj​ 若用户未对这部分因子进行设置,则默认parking.distanceto.weight因子的权重为1,其余因子的权重均为0,即可只有parking.distanceto.weight生效。另外需要注意得是,当**元素具有属性 visible="true"时,因子的值均是准确的;若visible=“false”,各项因子的值均为一个随机数,即便一个备选停车区域停满了也很有可能被选中作为重新选择的停车区域。

用户定义这些因子的权重,可以在*元素或*元素中定义:

...... ...... 4 停车场景仿真实例

随着城市汽车保有量的不断增长,停车问题愈发突出,体现在:城市停车位短缺巨大,我国车位配比远低于全球平均水平;停车资源缺乏统一管理,城市停车位空置率高。为了缓解停车难问题,降低车辆寻找停车位的难度,提高整体的停车效率,相关的学者们提出了各种停车管理模型来优化停车管理,这些管理模型的验证则是需要仿真来进行。

在本章节的实例中,我们来实现一个简单的智能停车场管理系统。如下图,该系统主要包含两个模块:停车场状态感知模块与车位选择模块。感知模块我们将实现两种程度的感知系统:半感知与全感知,车位选择模块我们将对贪婪策略与随机选择策略进行实现。另外,该场景的实现将主要基于TraCI进行。

图10 智能停车管理系统模块 4.1 路网与停车设施构建

我们首先构建如图11所示的停车场路网,出入口分别有两个。

在这里插入图片描述

图11 停车场路网

构建该路网的小技巧:可以利用netgenerate.exe工具,先构建一个方格路网,再通过删减部分路段从而构建如图11所示的停车场路网。相关命令示例:

netgenerate --grid --grid.number=6 --grid.length=50 --output-file=park.net.xml

在该路网的基础上,在部分路段中构建停车区域,如图11所示:

在这里插入图片描述

图12 停车场设施构建 4.2 停车场状态感知模块

本场景中,分别实现了两种程度的停车场状态感知模块:半感知与全感知。

半感知:仅感知当前停车场内所有区域的占用状态;全感知:能感知当前停车场内所有区域的占用状态以及车辆的停车动向。

在半感知的条件下,车辆仍然可能出现抢占车位资源的现象,导致不必要的巡航时间。而在全感知的条件下,则能够真正实现高效停车。

(1)半感知

我们定义了一个类Half_perception来实现半感知模块。此次仿真场景中我们暂时只需要感知各个停车区域的占有率,因此该类下只定义了两个函数。在有更加多元化的感知需求时,同样可以直接在该类下添加函数实现。

class Half_perception: # 感知停车场桩体,以“停车区域--占有率”表示 def Perceived_occupancy(self): # 获取所有停车区域的ID ParkingAreaList=traci.parkingarea.getIDList() # 遍历所有停车区域,获取每一个区域的占有率 Occ=list(map(lambda x: float(traci.simulation.getParameter(x, "parkingArea.occupancy")), ParkingAreaList)) # 构建一个datafrmame来存储“停车区域--占有率” ParkingOcc=pd.DataFrame({"ParkingArea":ParkingAreaList,"Occupancy":Occ}) return ParkingOcc # 获取可用停车区域 def get_AvailableParking(self): # 获取“停车区域--占有率”关系表 ParkingOcc=self.Perceived_occupancy() # 获取占有率小于1的停车区域列表 AvailableParking=list(ParkingOcc[ParkingOcc["Occupancy"]"ParkingArea":ParkingAreaList, "EntryDistance_1":EntryDistance_1, "EntryDistance_2":EntryDistance_2}) # 获取“停车区域--距离1--距离2” def get_ParkingInformation(self): return self.ParkingInformation

在有了Environment类后,感知模块的初始化函数也需要变动,以全感知为例:我们会将Environment类中的ParkingInformation输入到感知模块,在此基础上将各个区域的容量信息加入。

class Com_perception: def __init__(self,ParkingInformation): self.ParkingCapacity = ParkingInformation # 遍历所有停车区域,获取每一个区域的总容量 Capacity = list(map(lambda x: float(traci.simulation.getParameter(x, "parkingArea.capacity")), list(ParkingInformation["ParkingArea"]))) # 将容量信息加入 self.ParkingCapacity["Capacity"]=Capacity

在此基础上,我们要实现贪婪策略将变得较为容易,在Allocation类下加入实现贪婪策略的函数Greedy_allocation。函数需输入车辆进入停车场的入口:

class Allocation: def __init__(self,parkingInformation): # 实例化感知模块 # self.Perception = Half_perception(ParkingInformation) # 半感知 self.Perception = Com_perception(ParkingInformation) # 全感知 # 贪婪策略 def Greedy_allocation(self,Entry): # 感知各个区域的剩余容量 AvailableParking = self.Perception.get_ParkingCapacity() # 将可用的停车区域筛选出来 AvailableParking=AvailableParking[AvailableParking["Capacity"] > 0] # 重设索引 AvailableParking=AvailableParking.reset_index(drop=True) # 根据距离进行从小到大排序 AvailableParking=AvailableParking.sort_values(by=Entry, ascending=True) AvailableParking = AvailableParking.reset_index(drop=True) # 将第一个停车区域分配给车辆 TargetParking = AvailableParking["ParkingArea"][0] return TargetParking

由此我们即可实现贪婪策略。

下面我们将整个场景的所有代码展示出来:我们在全感知条件下实现场景,每个入口的车流量为2 veh/min,停车时间为[200s,300s]中的随机数,仿真时长为一小时,车位选择策略为贪婪策略:

# 相关包导入 import random import os import sys import optparse import traci from sumolib import checkBinary import pandas as pd # SUMO环境变量配置,需根据用户的实际配置进行调整 if 'SUMO_HOME' in os.environ: os.environ['SUMO_HOME']='E:\\sumo-1.8.0\\' tools = os.path.join(os.environ['SUMO_HOME'], 'tools') sys.path.append(tools) else: sys.exit("please declare environment variable 'SUMO_HOME'") def get_options(): optParser = optparse.OptionParser() optParser.add_option("--nogui", action="store_true", default=False, help="run the commandline version of sumo") options, args = optParser.parse_args() return options # 全感知模块 class Com_perception: def __init__(self,ParkingInformation): # 获取所有停车区域的ID ParkingAreaList=traci.parkingarea.getIDList() # 遍历所有停车区域,获取每一个区域的总容量 Capacity=list(map(lambda x: float(traci.simulation.getParameter(x, "parkingArea.capacity")),ParkingAreaList)) # 构建一个datafrmame来存储“停车区域--容量” self.ParkingCapacity=pd.DataFrame ({"ParkingArea":ParkingAreaList,"Capacity":Capacity}) # 函数Add_update用于当车辆决定选择某个停车区域后,更新停车区域的剩余容量,即使该车辆还未行驶到停车区 def Add_update(self, parkingArea): # 车辆进入停车场后,选择停在parkingArea(可用),则parkingArea的容量减1 self.ParkingCapacity.loc[(self.ParkingCapacity.ParkingArea == parkingArea), 'Capacity'] -= 1 # 函数Leave_update用于当车辆离开停车位时,更新停车区域的剩余容量 # parkInformation为存储的“车辆--停车区域”关系表,即每个车辆停在了哪个停车区 def Leave_update(self, VehiclePark): for veh in list(traci.simulation.getStopEndingVehiclesIDList()): # 获取这些车辆所停的停车区域 parkingArea = VehiclePark[VehiclePark.Veh == veh]["park"].values[0] # 更新该区域容量 self.ParkingCapacity.loc[(self.ParkingCapacity.ParkingArea == parkingArea), 'Capacity'] += 1 # 获取“停车区域--剩余容量” def get_ParkingCapacity(self): return self.ParkingCapacity # 获取可用停车区域 def get_AvailableParking(self): # 获取“停车区域--剩余容量”关系表 ParkingCapacity = self.get_ParkingCapacity() # 获取剩余容量大于0的停车区域列表 AvailableParking = list(ParkingCapacity[ParkingCapacity["Capacity"] > 0] ["ParkingArea"]) return AvailableParking class Allocation: def __init__(self,parkingInformation): # 实例化感知模块 self.Perception = Com_perception(parkingInformation) # 全感知 # 贪婪策略 def Greedy_allocation(self,Entry): # 感知各个区域的剩余容量 AvailableParking = self.Perception.get_ParkingCapacity() # 将可用的停车区域筛选出来 AvailableParking=AvailableParking[AvailableParking["Capacity"] > 0] # 重设索引 AvailableParking=AvailableParking.reset_index(drop=True) # 根据距离进行从小到大排序 AvailableParking=AvailableParking.sort_values(by=Entry, ascending=True) AvailableParking = AvailableParking.reset_index(drop=True) # 将第一个停车区域分配给车辆 TargetParking = AvailableParking["ParkingArea"][0] return TargetParking class Environment: def __init__(self): # 获取所有停车区域的ID ParkingAreaList = traci.parkingarea.getIDList() # 用于储存每个停车区域和两个入口间的距离 EntryDistance_1 = [] EntryDistance_2 = [] # 遍历所有停车区域 for parkingSpace in ParkingAreaList: # 获取停车区域的车道和路段 lane=traci.parkingarea.getLaneID(parkingSpace) edge=traci.lane.getEdgeID(lane) # 计算入口路段和停车区域路段的距离 distance_1 = traci.simulation.getDistanceRoad("F5E5", 0 , edge , 0) distance_2 = traci.simulation.getDistanceRoad("A2B2", 0, edge , 0) # 存储 EntryDistance_1.append(distance_1) EntryDistance_2.append(distance_2) # 构建一个datafrmame来存储“停车区域--距离1--距离2” self.ParkingInformation = pd.DataFrame({"ParkingArea":ParkingAreaList, "EntryDistance_1":EntryDistance_1, "EntryDistance_2":EntryDistance_2}) # 获取“停车区域--距离1--距离2” def get_ParkingInformation(self): return self.ParkingInformation if __name__ == '__main__' : options = get_options() if options.nogui: sumoBinary = checkBinary('sumo') else: sumoBinary = checkBinary('sumo-gui') # 启动工程文件 traci.start([sumoBinary, "-c", "./P1/test.sumocfg", "--tripinfo-output", "tripinfo.xml", "--quit-on-end"]) # ,"--start" # 将各个路段的最大速度调小,停车场内的行驶速度是较慢的 SetMaxspeed = list( map(lambda x: traci.edge.setMaxSpeed(x, 3), list(traci.edge.getIDList()))) #####将所有道路的最大速度设为6m/s # 实例化Environment类 Env=Environment() ParkingInformation=Env.get_ParkingInformation() # 实例化分配策略 AL=Allocation(ParkingInformation) # 实例化全感知模块 PER=Com_perception(ParkingInformation) # “车辆--停车区域”关系表 VehiclePark=pd.DataFrame(columns=['Veh', 'park']) for step in range(3600): if step%20==0: # 第一个入口 # 选定车位 parkingArea_1=AL.Greedy_allocation("EntryDistance_1") # 各类参数信息 tripid = "trip_1_" + str(step) # 出行ID origin = "F5E5" # 出发路段 destination = traci.lane.getEdgeID(traci.parkingarea.getLaneID(parkingArea_1)) vehid = "v_1_" + str(step) # 车辆ID # 添加需求 traci.route.add(tripid, [origin, destination]) traci.vehicle.add(vehid, tripid) traci.vehicle.setParkingAreaStop(vehid, parkingArea_1, duration=random.randint(600,3600)) # 将该信息加入到VehiclePark VehiclePark=VehiclePark.append({'Veh':vehid,'park':parkingArea_1},ignore_index=True) # 更新全感知模块 PER.Add_update(parkingArea_1) # 第二个入口 # 选定车位 parkingArea_2=AL.Greedy_allocation("EntryDistance_2") # 各类参数信息 tripid_2 = "trip_2_" + str(step) # 出行ID origin_2 = "A2B2" # 出发路段 destination_2 = traci.lane.getEdgeID(traci.parkingarea.getLaneID(parkingArea_2)) vehid_2 = "v_2_" + str(step) # 车辆ID # 添加需求 traci.route.add(tripid_2, [origin_2, destination_2]) traci.vehicle.add(vehid_2, tripid_2) traci.vehicle.setParkingAreaStop(vehid_2, parkingArea_2, duration=random.randint(600,3600)) # 将该信息加入到VehiclePark VehiclePark = VehiclePark.append({'Veh': vehid_2, 'park': parkingArea_2}, ignore_index=True) # 更新全感知模块 PER.Add_update(parkingArea_2) PER.Leave_update(VehiclePark) traci.simulationStep()


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有