SUMO(二)——与Python的基本联合

这篇文章主要 S U M O SUMO SUMO P y t h o n Python Python 的基本联合写法,还不涉及 T r a C I TraCI TraCI 接口。本文举的例子的目的是探索一个路口中到达率大小与停车率的关系

文章目录

  • 0 基础准备
    • 0.1 知识准备
    • 0.2 文件准备
  • 1 生成相关数据
    • 1.1 生成路由文件
      • 1.1.1 生成随机车流
      • 1.1.2 其他生成方式
    • 1.2 对仿真结果进行处理
    • 1.3 主函数
  • 2 数据处理

0 基础准备

0.1 知识准备

在看这篇文章之前,你需要:

  • 掌握基本的 P y t h o n Python Python 知识(链接是我的好兄弟 川 s h i t shit shit 整理的相关知识);
  • 掌握基本的 N u m P y NumPy NumPy 知识;
  • 掌握基本的 x m l . e t r e e . E l e m e n t T r e e xml.etree.ElementTree xml.etree.ElementTree (或者其他处理 x m l xml xml 文件的方法)知识;
  • 掌握基本的 M a t p l o t l i b Matplotlib Matplotlib 知识;
  • 掌握基本的 S U M O SUMO SUMO 相关文件 的知识。

0.2 文件准备

在项目文件夹中,你需要:

  • 一个路网文件( . n e t . x m l .net.xml .net.xml)
  • 一个仿真文件 ( . s u m o c f g .sumocfg .sumocfg)

我的路网文件名称是 c r o s s . n e t . x m l cross.net.xml cross.net.xml ,内容则是一个很简单的十字路口。
SUMO(二)——与Python的基本联合_第1张图片

仿真文件的内容为:

<configuration>
    <input>
        <net-file value="cross.net.xml" />
        <route-files value="cross.rou.xml" />
    input>
    <time>
        <begin value="0" />
        <end value="1000" />
    time>
    <output>
        <fcd-output value="cross.output.xml" />
    output>
configuration>

(仿真了1000秒的)

完成了这些工作,我们就继续进行吧!

1 生成相关数据

在项目文件夹里新建 m a i n . p y main.py main.py ,开始编写程序吧!

1.1 生成路由文件

1.1.1 生成随机车流

这是最简单的生成路由文件的方法,一般来说就会用这种方法。

def GenerateRouXml(path, period) :
    os.system(path + "randomTrips.py " + "-n cross.net.xml -o cross.trips.xml -b 0 -e 1000 -p " + str(period))
    os.system("duarouter -n cross.net.xml -t cross.trips.xml -o cross.rou.xml --ignore-errors")
    tree = ET.parse("cross.sumocfg")
    root = tree.getroot()
    for child in root :
        if(child.tag == 'output') :
            for child2 in child:
                child2.attrib['value'] = 'cross.output' + str(period) + '.xml'
    with open("cross.sumocfg", 'wb') as f :
        tree.write(f)
    os.system("sumo -c cross.sumocfg --device.fcd.period 100")

代码部分解释:

  • 函数参数中的 p a t h path path 是指 S U M O SUMO SUMO 文件夹中 t o o l s tools tools 文件夹的路径。 p e r i o d period period 是指每隔多长时间生成一辆车,单位为秒,实际上也就是到达率的倒数。
  • 2 2 2 3 3 3行就是 S U M O SUMO SUMO 自带的一些命令,通过 o s os os 模块来模拟命令提示符的使用来输入指令。第二行就是生成随机车流,我们设定仿真时间为 1000 1000 1000。第三行将其转化为路由文件。
  • 后面则是对仿真文件进行转换。由于我们会调用好多次生成路由文件的函数,而每次调用的 p e r i o d period period 都不一样,我们要保留每种情况的仿真结果。于是我们让每次仿真的仿真结果保存为 c r o s s . o u t p u t + s t r ( p e r i o d ) + . x m l cross.output + str(period) + .xml cross.output+str(period)+.xml 形式。比如说我们某一次仿真的 p e r i o d period period 0.05 0.05 0.05 ,那么我们保存的输出结果就会是 c r o s s . o u t p u t 0.05. x m l cross.output0.05.xml cross.output0.05.xml 。这样就不会出现名字重复的问题,我们也可以找到各个 p e r i o d period period 所对应的仿真结果。如果这一部分代码看不明白请参照 0.1 中的 “掌握基本的 x m l . e t r e e . E l e m e n t T r e e xml.etree.ElementTree xml.etree.ElementTree (或者其他处理 x m l xml xml 文件的方法)知识;”
  • 最后则是运行 S U M O SUMO SUMO 进行仿真,每 100 100 100 秒保存一次结果。

1.1.2 其他生成方式

其他生成方式请参照0.1中所介绍的 S U M O SUMO SUMO 基本知识。但是应用那些方式的话需要从头开始创建 . x m l .xml .xml 文件,好麻烦。等什么时候有需求了我再来填坑吧。

1.2 对仿真结果进行处理

先来看看看仿真结果长什么样子吧。找一个短一点的展示,就找 p e r i o d period period 9 9 9 的来吧!



<fcd-export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/fcd_file.xsd">
    <timestep time="0.00">
        <vehicle id="0" x="-94.90" y="-8.00" angle="90.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="5.10" lane="-E2_0" slope="0.00"/>
    timestep>
    <timestep time="100.00">
        <vehicle id="10" x="-11.46" y="11.78" angle="182.98" type="DEFAULT_VEHTYPE" speed="5.60" pos="1.83" lane=":J0_0_0" slope="0.00"/>
        <vehicle id="11" x="11.20" y="-92.50" angle="0.00" type="DEFAULT_VEHTYPE" speed="2.40" pos="7.50" lane="-E1_0" slope="0.00"/>
        <vehicle id="9" x="-1.60" y="14.60" angle="180.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="85.40" lane="-E3_3" slope="0.00"/>
    timestep>
    <timestep time="200.00">
        <vehicle id="16" x="-17.80" y="-1.60" angle="90.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E2_2" slope="0.00"/>
        <vehicle id="20" x="10.68" y="-8.00" angle="90.00" type="DEFAULT_VEHTYPE" speed="9.81" pos="27.48" lane=":J0_15_0" slope="0.00"/>
        <vehicle id="21" x="17.82" y="1.60" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.06" pos="82.18" lane="-E0_2" slope="0.00"/>
    timestep>
    <timestep time="300.00">
        <vehicle id="28" x="17.80" y="8.00" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E0_0" slope="0.00"/>
        <vehicle id="29" x="17.80" y="1.60" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E0_2" slope="0.00"/>
    timestep>
    <timestep time="400.00">
        <vehicle id="41" x="-17.80" y="-1.60" angle="90.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E2_2" slope="0.00"/>
        <vehicle id="43" x="-41.28" y="8.00" angle="270.00" type="DEFAULT_VEHTYPE" speed="11.44" pos="24.48" lane="E2_0" slope="0.00"/>
    timestep>
    <timestep time="500.00">
        <vehicle id="46" x="-11.20" y="13.19" angle="180.00" type="DEFAULT_VEHTYPE" speed="1.41" pos="0.41" lane=":J0_1_0" slope="0.00"/>
        <vehicle id="52" x="1.60" y="-14.60" angle="0.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="85.40" lane="-E1_3" slope="0.00"/>
        <vehicle id="54" x="11.20" y="-12.73" angle="0.00" type="DEFAULT_VEHTYPE" speed="1.93" pos="0.87" lane=":J0_10_0" slope="0.00"/>
        <vehicle id="55" x="-11.20" y="65.97" angle="180.00" type="DEFAULT_VEHTYPE" speed="10.11" pos="34.03" lane="-E3_0" slope="0.00"/>
    timestep>
    <timestep time="600.00">
        <vehicle id="62" x="17.80" y="1.60" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E0_2" slope="0.00"/>
        <vehicle id="65" x="37.35" y="-8.00" angle="90.00" type="DEFAULT_VEHTYPE" speed="9.18" pos="20.55" lane="E0_0" slope="0.00"/>
    timestep>
    <timestep time="700.00">
        <vehicle id="74" x="17.80" y="1.60" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E0_2" slope="0.00"/>
        <vehicle id="76" x="1.60" y="-14.60" angle="0.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="85.40" lane="-E1_3" slope="0.00"/>
    timestep>
    <timestep time="800.00">
        <vehicle id="76" x="-2.63" y="-2.71" angle="324.01" type="DEFAULT_VEHTYPE" speed="6.12" pos="11.99" lane=":J0_13_0" slope="0.00"/>
        <vehicle id="78" x="1.60" y="-17.10" angle="0.00" type="DEFAULT_VEHTYPE" speed="3.56" pos="82.90" lane="-E1_3" slope="0.00"/>
        <vehicle id="82" x="0.62" y="5.48" angle="160.76" type="DEFAULT_VEHTYPE" speed="4.84" pos="8.56" lane=":J0_4_0" slope="0.00"/>
        <vehicle id="83" x="17.80" y="8.00" angle="270.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E0_0" slope="0.00"/>
        <vehicle id="87" x="-1.60" y="18.35" angle="180.00" type="DEFAULT_VEHTYPE" speed="2.79" pos="81.65" lane="-E3_3" slope="0.00"/>
        <vehicle id="88" x="26.61" y="1.60" angle="270.00" type="DEFAULT_VEHTYPE" speed="9.58" pos="73.39" lane="-E0_2" slope="0.00"/>
    timestep>
    <timestep time="900.00">
        <vehicle id="91" x="-1.60" y="14.60" angle="180.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="85.40" lane="-E3_3" slope="0.00"/>
        <vehicle id="93" x="-1.60" y="22.10" angle="180.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="77.90" lane="-E3_3" slope="0.00"/>
        <vehicle id="98" x="-17.80" y="-8.00" angle="90.00" type="DEFAULT_VEHTYPE" speed="0.00" pos="82.20" lane="-E2_0" slope="0.00"/>
    timestep>
fcd-export>

前面的注释部分已经被我删掉了。具体来看一下:

因为我们之前已经提到,仿真时间为 1000 1000 1000 秒,而我们又是每 100 100 100 秒进行一次数据的获取,所以就有 10 10 10 组结果。

基本结构很简单,就是 r o o t root root 下面有 10 10 10 t a g tag tag t i m e s t e p timestep timestep 子节点,也就是记录了每 100 100 100 秒的情况。 t i m e s t e p timestep timestep 下面则是 t a g tag tag v e h i c l e vehicle vehicle 子节点,记录了在这个时间点的每一辆车的具体数据,包括 i d id id ,坐标,速度,车辆类型,所在车道,所在车道的位置等信息。不同的应用会用到不同的信息,而我们所用到的信息就是速度,也就是 s p e e d speed speed 这个 a t t r i b u t e attribute attribute

了解了仿真结果的结构,接下来就来看看怎么进行分析吧:

def DealOutputXml(period, num) :
    tree = ET.parse("cross.output" + str(period) + ".xml")
    root = tree.getroot()
    p = 0
    l = 0
    for child in root :
        if(child.attrib['time'] == "900.00") :
            for child2 in child :
                if(child2.tag == 'vehicle') :
                    p = p + 1
            speed = np.zeros(p)
            for child2 in child :
                if(child2.tag == 'vehicle') :
                    speed[l] = np.float(child2.attrib['speed'])
                    l = l + 1
    zer = np.where(speed < 1.0)
    ans = np.size(zer[0]) / p
    data[(0, num)] = period
    data[(1, num)] = ans

代码解释:

  • 函数的参数和上面一样, n u m num num 代表的是正在处理的是第几个 p e r i o d period period,也就是标号。
  • 由于我们所期望的是得到一个相对稳定的结果,所以我们肯定是选择仿真了一段时间之后的结果,也就是选择所经过时间最长,也就是经过了 900 900 900 秒的结果。
  • 将这个时间里面的静止车辆(1 m / s m/s m/s 以下我也算作是静止了)的车辆数统计来,与总的车辆数相除,得到静止车辆的比例。
  • p e r i o d period period 与相对应的静止车辆的比例记录到 d a t a data data 中。(第 0 0 0 行是 p e r i o d period period,第 1 1 1 行是比例,记录到第 n u m num num 列中)

1.3 主函数

if __name__ == '__main__':
    import os
    import numpy as np
    import xml.etree.ElementTree as ET
    from decimal import *

    path = "/Users/(我的用户名保密就不给你看)/Desktop/tools/"
    num = 0 # 存储总共有多少period
    now_num = 0 # 目前正在处理第几个period
    for i in range(1, 21) :
        b = i * 0.05
        a = Decimal(b).quantize(Decimal('0.00'))
        GenerateRouXml(path, a)
        num += 1
    for i in range(2, 10) :
        GenerateRouXml(path, i)
        num += 1
    # 上面的代码就是先生成了每个period对应的路由文件 顺便统计了有几个period
    data = np.zeros((2, num))
    for i in range(1, 21) :
        b = i * 0.05
        a = Decimal(b).quantize(Decimal('0.00'))
        DealOutputXml(a, now_num)
        now_num += 1
    for i in range(2, 10) :
        DealOutputXml(i, now_num)
        now_num += 1
    np.savetxt("data.txt", data)

代码解释:

  • 感觉……没啥好解释的。写的很简单。
  • 就是要注意我用了 D e c i m a l Decimal Decimal 保留两位小数。不这么干的话,由于二进制储存小数的机制,就会出现储存的小数不准的问题,就会出现如下情况:

SUMO(二)——与Python的基本联合_第2张图片

经过运行后,就获得了 d a t a . t x t data.txt data.txt

5.000000000000000278e-02 1.000000000000000056e-01 1.499999999999999944e-01 2.000000000000000111e-01 2.500000000000000000e-01 2.999999999999999889e-01 3.499999999999999778e-01 4.000000000000000222e-01 4.500000000000000111e-01 5.000000000000000000e-01 5.500000000000000444e-01 5.999999999999999778e-01 6.500000000000000222e-01 6.999999999999999556e-01 7.500000000000000000e-01 8.000000000000000444e-01 8.499999999999999778e-01 9.000000000000000222e-01 9.499999999999999556e-01 1.000000000000000000e+00 2.000000000000000000e+00 3.000000000000000000e+00 4.000000000000000000e+00 5.000000000000000000e+00 6.000000000000000000e+00 7.000000000000000000e+00 8.000000000000000000e+00 9.000000000000000000e+00
8.571428571428570953e-01 8.571428571428570953e-01 8.571428571428570953e-01 7.373737373737373479e-01 8.910891089108911034e-01 9.480519480519480346e-01 8.155339805825242427e-01 7.951807228915662717e-01 7.582417582417582125e-01 8.295454545454545858e-01 7.727272727272727071e-01 9.499999999999999556e-01 8.915662650602409478e-01 7.978723404255319007e-01 8.421052631578946901e-01 8.255813953488372325e-01 8.823529411764705621e-01 8.550724637681159646e-01 8.474576271186440302e-01 8.913043478260869179e-01 7.619047619047618625e-01 6.363636363636363535e-01 7.777777777777777901e-01 7.142857142857143016e-01 5.999999999999999778e-01 1.000000000000000000e+00 5.000000000000000000e-01 1.000000000000000000e+00

2 数据处理

这个时候我又新建了一个 p y t h o n python python 文件,代码如下:

if __name__ == '__main__' :
    import matplotlib.pyplot as plt
    import numpy as np
    from decimal import *
    data = np.loadtxt("data.txt")
    meanspeedr = np.around(data[1, :], 2)
    x = np.around(1 / data[0, :], 2)

    plt.plot(x, meanspeedr)
    plt.xlabel("Arrive Rate")
    plt.ylabel("Stop Rate")
    plt.show()

在写这个的时候,我注意到一个问题—— N u m P y NumPy NumPy 本身也有保留小数的函数……算了,多学一点也没坏处对吧!

写的也很简单,就是读取了之前我们所存储的数据,然后进行了转化:

  • p e r i o d period period 取了倒数,成为了我们更常用的到达率;
  • 停车所占的比例也取了两位小数。

然后让到达率当 x x x 轴,停车比例当 y y y 轴进行画图,来实现数据的可视化。
SUMO(二)——与Python的基本联合_第3张图片
就到这里结束啦。毕竟,从数据获得结论并不是本教程的人物捏。

我是竞赛出身的,所以之前一直是面向过程编程,现在正在努力面向对象编程,所以可能写的代码有些四不像,请大家多多见谅。

你可能感兴趣的:(sumo,python,xml,开发语言)