欢迎来到 Eins 教程¶
目录:¶
数学模型及其使用¶
通用规则¶
车辆运行更新方式¶
在此和之后的元胞自动机模型都采用 并行更新 ,即在每个时刻刷新状态时,所有车辆同时刷新状态,而非顺序更新,因为并行更新中自发堵塞产生的机理更加真实。
更新车辆的边界条件¶
这里提供两种默认的边界条件:
- 周期性边界条件(periodical boundary):
- 在每次更新结束后,监测道路上头车的位置 xlead,如果 xlead> lroad,那么这辆车将从道路的另一端进入系统,变成道路上的尾车,并且 xlast= xlead- lroad, vlast= vlead。这里,xlead, xlast, vlead, vlast分别表示道路上头车和尾车的位置和速度,Lroad代表所研究的道路系统的长度。
- 开口边界性条件(open boundary):
- 假设道路最左边的元胞对应于x = 1,并且道路的入口端包含Vmax个元胞,也就是说,车辆可以从元胞(1,2,... vmax)进入到道路中。在 t->t+1 时刻,当道路上的车辆更新完成后,监测道路上的头车和尾车的位置xlead和xlast。如果xlast>vmax,则一辆速度为vmax的车将以概率α进入元胞min(xlast-vmax, vmax)。在道路的出口处,如果xlead> lroad,那么道路上的头车以概率β驶出路段,而紧跟其后的第二辆车成为新的头车。
注解
当然,用户也可以自行定义边界条件
Nagel-Schreckenberg(NaSch) 模型¶
[Ref] | K.Nagel and M.Schreckenberg. A Cellular automaton model for freeway traffic. J. Phys. I(France) 2, 2221~2229(1992) |
数学定义¶
- ★ 此模型是184号模型的推广。
在这一模型中,时间、空间以及速度都被 整数离散化 。道路被划分为离散的格子(即元胞),每个元胞或是为空,或是被一辆车占据,每辆车的速度可以取0,1,2...,vmax,vmax为最大速度。在 t->t+1 的过程中,模型按照如下规则进行演化:
- 加速:vn-> min( vn+1, vmax)
- 减速:vn-> min( vn-1, dn)
- 随机慢化:以概率p,vn-> max(vn-1, 0)
- 运动: xn-> xn+ vn
其中 dn= xn+1- xn- lveh,代表序列号为n的车辆与在其前方序号为n+1的车辆之间的距离 xn代表序列号为n的车辆的位置,vn为其速度,lveh为其车辆长度
其他¶
- 改模型说明了即使没有任何外在原因,道路也可能发生拥堵
- 该模型是简约的,即该模型定义的任何元素的缺失会立即导致交通仿真的关键性能损失。
- 利用并行计算机模拟以该模型为基础的数百万辆汽车是可能的
- NaSch模型是可以重现道路交通流基本特征的一个最小化模型,四个规则缺一不可。如果要捕捉更为复杂的交通条件,就需要添加新的规则。
- § 运用实例
- 北莱茵-威斯特法伦州的脱机模拟交通预测系统(德国)
- 城市智能交通项目TRANSIMS(美国)
- 杜伊斯堡的内城交通
- 达拉斯/福斯-华斯地区的交通规划
- 北莱茵-魏斯特伐亚地区的交通公路网
Takayasu-Takayasu(TT) 慢启动规则¶
[Ref] |
|
数学定义¶
在这一模型中,如果一辆静止车辆前面恰好只有一个空元胞,那么该车以概率 qt=1-pt加速,而对其他所有情况, 车辆均按确定性规则进行加速。其他更新过程均和NS模型中的 step2 ~ step4 是完全一致的。
注解
实际上是依赖车辆前方空元胞数的 ‘空间’ 规则
BJH 慢启动规则¶
[Ref] |
|
数学定义¶
此规则也是通过引入慢启动规则对NS模型进行修正,但是其慢启动规则不同于TT规则,其演化规则如下:
- 加速:vn-> min(vn+1, vmax)
- 慢启动规则:如果flag eq 1,则以概率ps,vn-> 0
- 减速:vn-> min(vn, dn)
- 随机慢化:以概率p,Vn -> max(vn-1, 0)
- 运动:xn-> xn+ vn
这里,flag是一个标示,用来区分一辆车是采用慢启动规则(flag=1),还是不采用(flag=0)。
注解
实际上是依赖车辆的 ‘记忆效应’ 的 ‘时间’ 规则
Velocity-dependent-randomization(VDR) 慢启动规则¶
[Ref] |
|
数学定义¶
此规则也是通过引入慢启动规则对NS模型进行修正,但是其慢启动规则不同于TT和BJH规则,其演化规则如下:
确定随机慢化概率:
- if (v == 0) then
p(v) = p0
- else
p(v) = p
加速:vn-> min(vn+1, vmax)
减速:vn-> min(vn, dn)
随机慢化:如果 flag eq 1,那么以概率p0有 vn-> max(vn-1, 0);如果 flag eq 0,那么以概率p有 vn-> max(vn-1, 0)
运动: xn-> xn+ vn
注解
随机慢化概率不再是固定不变的,而是车辆速度的函数 p=p(v)
Velocity-effect(VE) 速度效应¶
[Ref] |
|
数学定义¶
以外的绝大多数元胞自动机模型中,有一个共同特征,在从 t->t+1 的时间步中,车辆速度更新规则只考虑了t时刻两车的距离,而没有记入前车运动的影响,即都把前车作为静止的粒子处理。由此造成模拟速度小于实际车辆速度。
- 规则将减速步改为:
vn-> min(vn+1, dn+ v‘ n+1)
v‘ n+1= min(vmax-1, vn+1, max(0, dn+1-1))
其中v‘ n+1是n+1车在 t->t+1 时间步里的虚拟速度。它由NS模型演化规则所能得到的最小可能速度。一方面考虑了前车的速度效应,另一方面又确保在模型的更新过程中不会发生撞车。
Comfortable-driving(CD) 舒适驾驶模型¶
[Ref] |
|
数学定义¶
注解
由于在该模型中引入了刹车灯的效应,所以又称为刹车灯模型(breaking light,BL)
在CD模型中,引入了随机慢化函数:
- if (bn+1 and th< ts) then
- p(vn(t), bn+1(t), th, ts) = pb
- elif (vn == 0 ) then
- p(vn(t), bn+1(t), th, ts) = p0
- else
- p(vn(t), bn+1(t), th, ts) = pd
和有效距离:
dneff = dn+ max(vanti- gapsafety , 0)
其中bn是车辆n的刹车灯状态,bn= 1(0) 表示刹车灯亮(灭)。th= dn/ vn(t) 是车辆的时间车头距,ts= min(vn(t), h)为安全车间间距,h用来确定刹车灯的影响范围。vanti= min(dn+1, vn+1)是前车的期望速度,gapsafety是控制参数。演化规则如下:
确定随机慢化概率: p = p(vn(t), bn+1(t), th, ts)
- 加速:
- if [ (bn+1(t) == 0 and bn(t) == 0) or th>= ts] then
vn(t+1) = min(vn(t) + 1, vmax)
- else
vn(t+1) = vn(t)
- 减速:
vn(t+1) = min(dneff , vn(t+1)) if (vn< vn(t)) then
bn(t+1) = 1
- 慢化:
- if (rand() < p) then
vn(t+1) = max(vn(t+1)-1, 0)
- if (p == pb) then
bn(t+1) = 1
位置更新: xn(t+1) = xn(t) + vn(t+1)
这里rand()是0~1之间均匀分布的随机数。
Modified-comfortable-driving(MCD) 改进的舒适驾驶模型¶
[Ref] |
|
数学定义¶
慢启动规则的引入是为了描述驾驶员的不敏感的反应。一般认为静止车辆驾驶员没有运动车辆驾驶员敏感,因此静止车辆的慢化概率较大。然而,在这个模型中认为:刚停下的车辆的驾驶员仍十分敏感,只有停止时间超过一定时间 tc,驾驶员才会变得不那么敏感。
- 在MCD模型中,随机慢化函数为:
- if (bn+1 and th< ts) then
- p(vn(t), bn+1(t), th, ts) = pb
- elif (vn == 0 and tst>= tc) then
- p(vn(t), bn+1(t), th, ts) = p0
- else
- p(vn(t), bn+1(t), th, ts) = pd
- 演化规则为:
- 确定随机慢化概率: p = p(vn(t), bn+1(t), th, ts)
- 加速:
- if [(bn+1(t) == 0 or th>= ts) and vn(t) > 0] then
- vn(t+1) = min(vn(t) + 2, vmax)
- elif (vn(t) == 0) then
- vn(t+1) = min(vn(t) + 1, vmax)
- else
- vn(t+1) = vn(t)
- 减速:vn(t+1) = min(dneff , vn(t+1))
- 慢化:
- if(rand() < p) then
- vn(t+1) = max(vn(t+1) -1, 0)
- 确定刹车灯状态 bn+1(t+1):
- if (vn(t+1) < vn(t)) then
- bn(t+1) = 1
- elif(vn(t+1) > vn(t)) then
- bn(t+1) = 0
- else
- bn(t+1) = bn(t)
- 确定tst:
- if (vn(t+1) == 0) then
- tst+= 1
- elif (vn(t+1) > 0) then
- tst= 0
- 位置更新: xn(t+1) = xn(t) + vn(t+1)
视野规则¶
[Ref] | ZHU Liu-hua,KONG Ling-jiang,LIU Mu-ren Investigation of an IImproved Nagel-Schreckenberg Traffic Flow Model Guangxi Sciences 2007, 14(3):253~256 |
数学定义¶
与VDR规则类似,视野规则也是采用动态随机慢化概率,概率受到司机的 视野距离内车的数目 和 当前自身车速 的影响。
视野范围、两个指数参数分别为δ,α,β
计算公式为:
随机减速概率: p = ρlα (vi(t)/vmax)β
其中局部密度: ρl= sum(ηi)/δ
ηi为布尔量,有车占据此细胞则为1,否则为0 ,sum(ηi) 是视野内车的数量之和
两个指数参数的值需要使用者自己给定
注解
指数参数可以依据公路实际数据化为最优化问题来解得
快速上手¶
车辆¶
新建一辆车辆:
from eins import road
car = road.Car()
需用户定义的车辆属性:
属性名 | 含义 | 初始值 | 单位 |
---|---|---|---|
length | 车辆长度 | 1.0 | m |
safedistance | 最小安全车距 | 0.0 | m |
acc | 加速度 | 1.0 | m/s^2 |
slowacc | 慢加速度 | 0.5 | m/s^2 |
negacc | 减速度 | 1.0 | m/s^2 |
view | 视野距离 | 0.0 | m |
speed | 速度 | 0.0 | m/s |
注解
- 不一定需要定义全部属性,根据要使用数学模型的需求来定义即可
- 速度定义的含义是车辆的初始速度
- 车辆在道路上运行时会具有许多其他的属性,详情请往后阅读
以NaSch模型为例,模拟现实生活中普通车辆时,可以定义如下:
car.length = 3.5
car.acc = 3.2
car.negacc = 3.0
car.safedistance = 50.0
小技巧
合适的值可以查询国家的相关规定来得到
道路¶
class ExecRoad(Road):
def __init__(self, carbox, vmax, length, lanes=1,
entercars=0, enterflag=False, connectroad=None, exitflag=False, roadname='default'):
super(ExecRoad, self).__init__(carbox, vmax, length, lanes, \
entercars, enterflag, connectroad, exitflag, roadname)
ExecRoad Parameters¶
- carbox :
- 车辆集合
- vmax :
- 车速上限
- length :
- 道路长度
- lanes :
- 道路车道数目
- entercars :
- 已通过的车辆数目
- connectroad :
- 连接的道路
- enterflag :
- 入口标记
- exitflag :
- 出口标记
- roadname :
- 道路名称
道路具有非常多的属性和方法,我们慢慢道来
得到一条道路¶
为了初始化一条道路,有3个参数是必不可少的:
- 初始化的车辆 — carbox
- 道路长度 — length
- 道路的最大限定速度 — vmax
其中carbox必须要符合特定的格式,由两层嵌套的list构成:
[
[car_1, car_2, ..., car_n],
[car_1, car_2, ..., car_n],
...
[car_1, car_2, ..., car_n]
]
可以想象为第一层这个整体就是整条道路,而道路中有N条车道即第一层里面的各个元素(list),各个元素(list)里面的元素(Car)就是车辆
- 同时,有如下规定:
在一条车道中,第n+1辆车一定位于第n辆车前方
车道号从小到大对应着从左到右,比如 lane_0 对于 lane_1 是在左方, lane_2 对于 lane_1 是在右方
同一条道路上的所有车道行驶方向相同
carbox第一层内的元素个数必须严格等于初始化时规定的车道数。比如车道数为3的道路,其carbox必须这样构成:
[ [...], [...], [...] ]
注解
第二层中的元素可以为空,即那条车道上没有任何车辆
下面来举个简单的例子,初始化一条长度为500m,最大速度60km/h,车道为2,每条车道上有2辆车的道路:
from eins import road
vmax = 16.7 # 这里的单位为 m/s
length = 500
car1 = road.Car()
car1.locate = 100.0 # 这里定义locate实际上是定义车辆的初始化所在的位置
car1.lane = 0 # 定义车道标号
car2 = road.Car()
car2.locate = 300.0
car2.lane = 0
car3 = road.Car()
car3.locate = 150.0
car3.lane = 1
car4 = road.Car()
car4.locate = 250.0
car4.lane = 1
carbox = [[car1, car2], [car3, car4]]
rd = road.ExecRoad(carbox=carbox, length=length, vmax=vmax) # 得到一条道路
虽然的确得到了要的道路,但是过于繁琐,而且车辆数一多肯定不能这样手动的一辆一辆去创建
这里提供2个帮助新建用于初始化carbox的函数:
- init_empty_road(lanes)
- —— 用于初始化空的道路,需要的参数只有道路的车道数量
- init_cars_distributed(length, carTemplateBox, carsNum=None, lanes=1, dis=’normal’, pers=None)
- —— 可以按预定的格式初始化道路,必须的参数为分布长度、车辆模板
示例:
from eins import road
import copy
lanes = 3
length = 1000
vmax = 20
carbox1 = road.init_empty_road(lanes)
rd = road.ExecRoad(carbox=carbox1, length=length, vmax=vmax) # 空的道路
rd1 = road.ExecRoad(carbox=copy.deepcopy(carbox1), length=length, vmax=vmax) # 一定要记得使用deepcopy!!!不要直接重复使用!!!
car = road.Car() # 使用提供的初始化函数时不需要指定初始车道编号
car.speed = vmax # 定义车辆初始速度
car.safedistance = 50
car.length = 4
car.acc = 3.2
car.negacc = 3
carbox2 = road.init_cars_distributed(length=length, carTemplateBox=[car], lanes=lanes)
rd2 = road.ExecRoad(carbox=carbox2, length=length, vmax=vmax) # 有初始车辆的道路
注解
如果想让道路上有多种车型,只需提前定义好车辆模板,然后加入carTemplateBox中,提供它们的pers(比例)即可(pers之和必须为1.0),如:
carbox = road.init_cars_distributed(length=length, carTemplateBox=[car1, car2], pers=[0.7, 0.3], lanes=lanes)
开始仿真¶
得到初始好的道路后,设定运行规则便可以进行仿真了
设rd为一条初始化完毕的道路,使用NaSch规则仿真 100s:
...
exectime = 100
rd.set_exec_rule('NS')
for t in xrange(exectime):
rd.reflush_status()
ExecRoad.reflush_status()
—— 刷新道路上所有车辆的状态,timestep为1s
注意
运行规则一定要记得设定,如果不设定的话默认值为 NS
Key的可选值为
- NS
- CD
- MCD
可以用 print rd 来获取道路的当前信息。输出的一个例子:
+===================+
- 运行规则及道路HashValue:<bound method ExecRoad.NS of <road.ExecRoad object at 0x7f108147e5d0>>
- 车道数:3 - 道路长度(m):2000 - 运行时间(s):0 - 是否为入口:False - 是否为出口:False
--------------------
- 车道_0 - 车辆数目:12 - 平均车速:14.167 - 已通过车辆数:0
- 车道_1 - 车辆数目:12 - 平均车速:15.833 - 已通过车辆数:0
- 车道_2 - 车辆数目:12 - 平均车速:14.167 - 已通过车辆数:0
- 整体 - 车辆数目:36 - 平均车速:14.722 - 已通过车辆数:0
--------------------
- 连接道路:None - 时间边界条件:False - 循环边界条件:False
+===================+
道路自身也有许多方法获取当前的信息,下面介绍几个常用的:
ExecRoad.get_cars_locate()
—— 可以得到目前时刻车辆的位置信息,返回值的形式为 list 中嵌套 numpy.array
ExecRoad.get_cars_v()
—— 可以得到目前时刻车辆的速度信息,返回值的形式为 list 中嵌套 numpy.array
ExecRoad.get_mean_speed()
—— 可以得到目前时刻各车道的平均速度和整体的平均速度,第一个返回值的形式为为 numpy.array ,第二个返回值的形式为 float
一个简单的例子:
...
rd = road.ExecRoad(carbox=carbox, length=length, vmax=vmax)
lane_v, whole = rd.get_mean_speed()
注意
当车道上没有车时,车道的平均速度被 -1 所标记。当所有车道上都无车时,整体平均速度为 None
当然,也可以直接得到整个carbox,从而直接得到车辆操纵权:
ExecRoad.get_cars()
警告
事实上,在没有特殊的要求时,十分不推荐直接从外部影响车辆的状态。但是可能本库在某些方面并不能满足您的需求,所以还是开放了车辆对象的直接获取。希望您在做出行动时一定要清楚自己在干什么。 (更推荐研究reflush_status这个方法后,直接在源码上进行扩充或修改。)
车辆自动循环¶
一般只有初始的车辆是不够的,大多都需要持续观察道路一定时间。所以在仿真过程中需要持续的添加车辆。
如果您阅读了之前的数学模型中的通用规则,就会知道这里提供两种边界更新方式,具体的规则前面有提到过,这里不再赘述。
循环边界条件¶
ExecRoad.cycle_boundary_condition(switch, carTemplateBox, pers=None)
循环边界相对简单,switch 为开关,设定为True便开启了,carTemplateBox 与 pers 的定义同前面的默认初始化函数
时间边界条件¶
ExecRoad.time_boundary_condition(switch, carTemplateBox, pers=None, timeStep=1, nums=1)
—— timeStep 添加车辆的时间间隔,必须为整数
—— nums 添加时添加的车辆数目
注解
- 无论是哪种边界条件,都不需要给模板设置车道号
- 更新时,如果道路入口处没有车身长度的空位时,不会添加车辆,使用循环边界条件时请特别注意这一点
统计¶
def road_runner(roadbox, exectime, savepath, timestep='sec',
st=True, sm=True, bar=True, ownfun=None):
road_runner Parameters¶
- roadbox :
- 道路集合
- exectime :
- 仿真时间
- savepath :
- 保存路径(无需加后缀)
- timestep :
- SummmaryData 统计的间隔,Key可为 sec min hour
- st :
- 是否统计SummaryData
- sm :
- 是否统计SpaceTimeData
- bar :
- 是否开启进度条
- ownfun :
- 自定义函数,可以在统计过程中输出自己想要的其他信息(返回值必须为str)
统计功能统计数据一览(.xlsx形式)
Sheet:SummaryData
属性名 | 含义 |
---|---|
ROAD_HASH_ID | 道路Hash值 |
LANE_ID | 车道号 |
TIME_STAMP | 时间戳 |
AVR_SPEED | 平均速度 |
FLUX | 车流量 |
DENSITY | 密度 |
CARS_NUM | 车辆数目 |
LEAVE_CARS | 累计通车数目 |
注解
- 当ROAD_ID为 -1 时,指整条道路
- 如果有值为空,则说明那条道路或那条车道上无任何车辆,为空和为0的含义是不同的
Sheet:SpaceTimeData
属性名 | 含义 |
---|---|
ROAD_HASH_ID | 道路Hash值 |
LANE_ID | 车道号 |
TIME_STAMP | 时间戳 |
LOCATE | 坐标集 |
如未能满足您的需求,则需要自行开发函数
使用十分简单,只需设定 仿真时间 与预先设定好的 道路box 即可,一个简单的例子:
from eins import statistics as st
from eins import road
savepath = './data' # 不需要加后缀名
car = Car()
exectime = 600
length = 1000
vmax = 5
lanes = 3
carbox = road.init_cars_distributed(length, [car], lanes = lanes)
emptybox = road.init_empty_road(lanes)
rd = road.ExecRoad(carbox, length, vmax)
rd1 = road.ExecRoad(emptybox, length, vmax)
rd.set_connect_to(rd1) # 连接rd rd1
rd.cycle_boundary_condition(True, [car]) # 设定循环边界条件
st.road_runner([rd, rd1], exectime, savepath)
时空图¶
时空图的数据不是特别好读取,所以提供了一个辅助绘制时空图的功能,其他的图形需要自行根据采集数据绘制。 接上面统计代码的示例:
...
from eins import plot
plot.read_data('./data', 0, '0x7f7b07ac3d50')
动态测试图¶
如果想查看某条道路的动态可视化情况,这里提供了简易的函数来帮助您
testplot.addRoad(roadbox)
testplot.plot()
这个仅是为您测试提供的函数,所以只能显示一条道路的情况(roadbox中的第一个元素)
eg:
from eins import testplot as tp
from eins import road
import copy
length = 2000
vmax = 5
lanes = 3
carTemp = road.Car()
carTemp.safedistance = 0
carTemp.length = 1
carTemp.speed = vmax
InitCar = road.init_cars_distributed(length, [carTemp], lanes = lanes)
EmptyCar = road.init_empty_road(lanes = lanes)
rd = road.ExecRoad(InitCar, vmax, length, lanes=lanes)
rd1 = road.ExecRoad(EmptyCar, vmax, length, lanes=lanes)
rd.set_connect_to(rd1)
rd.cycle_boundary_condition(True, [carTemp])
tp.addRoad([rd, rd1]) # 绘制rd的运行情况,所以将rd放在第一个元素
tp.plot() # 显示
注解
显示的图中的车道从上到下对应着规则中的从左至右
车辆颜色代表的含义:
红: 速度 <= vmax*0.2
黄: vmax*0.2< 速度 <= vmax*0.6
绿: 速度 > vmax*0.6