正好项目中用到Sumo软件, 发现网上关于sumo的中文材料非常少, 所以我想记录一些自己使用sumo过程中的经验和教训;

SUMO的官方网站是 https://sumo.dlr.de/pydoc/
本文大部分代码相关内容都来源于此网站

一.Sumo的安装

我的环境是macos
安装可以直接使用homebrew安装
brew install sumo
具体的安装教程在sumo官网有详细说明;
mac版的安装在这个网址
安装完后记得要在bash-profile(bash)或者zshrc中(zsh)设置SUMO_HOME, 我这里的配置是这样:

#设置sumo, 这个是用homebrew装的
export SUMO_HOME="/usr/local/opt/sumo/share/sumo"

当一切都配置完成之后, 应该可以在终端使用命令sumo看到以下内容

~->sumo
Eclipse SUMO Version 1.3.1Build features: Darwin-17.7.0 x86_64 Clang 10.0.0.10001044 Release Proj GUICopyright (C) 2001-2019 German Aerospace Center (DLR) and others; https://sumo.dlr.deLicense EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>Use --help to get the list of options.

也可以使用sumo-gui命令进入sumo的gui客户端, 在mac中是基于XQuartz的

二.Sumo地图导入

我当时参考了这篇博文的内容veins车载通信仿真框架(2)–SUMO地图替换
大家可以去看一下这篇博文, 我简单介绍一下流程:
1.在OpenStreetMap网站上导出你想要研究的区域的地图, 在网页左上方有导出按钮, 然后选择区域之后就可以下载地图文件了, 应该是一个osm文件, 比如map.osm
2.在获得osm文件之后, 我们要用我们研究的地图替换sumo地图中的默认地图

  • 第一步, 根据osm文件生成.net.xml道路文件, 进入osm文件所在的目录下, 使用命令
netconvert --osm-files map.osm -o map.net.xml

此时我们会得到一个map.net.xml文件, 在这一步, 我们就可以在终端中输入sumo-gui命令打开gui软件, 然后File - open network 然后选中我们生成的net.xml地图, 就可以在软件中看到我们刚才下载的地图了

  • 第二步, 在刚才得到的地图中加入车辆, 因为我是要研究车流量相关的内容, 所以得到一个带车流的地图才有意义, 我们要生成一个.rou.xml车辆行为文件, 首先利用脚本randomTrips.py生成一个.trip.xml文件, 在这个文件中记录了随机生成的车辆的"旅程", 也就是每一辆车从哪儿开到哪儿, 至于具体的路线还是使用randomTrips脚本生成起点到终点的最短路径, 所以这一步使用2个命令
# 这一步是生成trips文件
/usr/local/opt/sumo/share/sumo/tools/randomTrips.py -n map.net.xml -e 100 -l
#生成车的路径行为xml的脚本,
#每p个步长生成1个,
#binomial二项分布系数是8, 1的时候退化成伯努利分布,
#s是种子数,
#e结束时间,
#l是根据道路长度来划分权重, L是根据车道数划分权重, 我这里选的是l;
/usr/local/Cellar/sumo/1.3.1/share/sumo/tools/randomTrips.py -n map.net.xml -r map.rou.xml -e 3600 -l --binomial=8 -p 5 -s 830 -v
  • 第三步, 生成.poly.xml地形文件
polyconvert --net-file map.net.xml --osm-files map.osm -o map.poly.xml
  • 生成.sumo.cfg文件
<?xml version="1.0" encoding="iso-8859-1"?><configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.sf.net/xsd/sumoConfiguration.xsd"><input><net-file value="map.net.xml"/><route-files value="map.rou.xml"/><additional-files value="map.poly.xml"/></input><time><begin value="0"/><end value="1000"/><step-length value="0.1"/></time><report><no-step-log value="true"/></report><gui_only><start value="true"/></gui_only></configuration>

这个文件要和net, poly, rou那些文件放一起;

简单总结一下, net文件存了路网的节点和道路信息, rou存储了道路中所有车辆的信息, 比如车速, 多少辆车, 每辆车从哪儿开到哪儿这些信息, poly存储的是地形信息;

三.Traci接口

这个Traci接口是用来和Sumo模拟器通信的, 因为你不可能总是在sumo-gui里点图形化界面, 肯定得通过python, java之类的语言来和sumo通信, 靠的就是traci接口;

我尝试了java和python两种语言, java给了一个webservice的接口TraaS和一个tra4j的接口, 但是这两个接口都不太好用, 文档也不全, 真的要做开发的话建议可以尝试python的接口;

1.java接口

先讲一下用java来连sumo的过程中我遇到的一些问题;

首先呢说明一下那个tra4j很简单, 但是也是在不好用, 这里只介绍traas;

这里是traas的源码, 在github上
https://github.com/eclipse/sumo/tree/master/tools/contributed/traas

由于我没找到任何文档…只能通过猜测来用这个库了, 可能是通过运行traas/src/main/java/de/tudresden/ws/WebService.java这个类里面的main函数, 建一个webservice项目, 然后重新建一个jws客户端去访问这个服务, 和sumo通信;
然后其他的一些功能比如获取车辆位置什么的, 都可以通过源码里给的一些接口来获取, 但是我在这里当时遇到了一些问题, 甚至连dostep都无法进行;

2.python接口

这里是python接口的pydoc https://sumo.dlr.de/pydoc/
这里面都有比较详细对方法的说明;
有不太好理解的方法都可以去官网查到, 我这里简单介绍一下:

首先, 我这里先导入traci库, 还有一些其他以后可能会用到的库

# coding=utf-8
import sys
import random
import sumolib
import traci  # noqa
import csv

然后最基本的操作, 就是让程序跑起来

# coding=utf-8
import traci  # noqatraci.start(["sumo-gui", "-c", "/location/map.sumo.cfg", "--emission-output", "emission"], port=7911)
while traci.simulation.getMinExpectedNumber() > 0:traci.simulationStep()
traci.close()
sys.exit()

第一步是启动traci.start, 里面给的几个参数, sumo-gui是指用这个命令来启动sumo的gui界面, 这一步要确认你在终端里面输入sumo-gui能出来sumo的gui界面, 才能继续;
然后下一个参数-c我也不知道啥意思;
下一个参数是sumo的cfg文件的位置, 里面提供了net, rou之类的文件信息;
然后emission这个是能耗的一些信息是我自己用的, 就不介绍了;

然后下面这一段就是让traci控制sumo开始一步步运行, 直到程序结束

while traci.simulation.getMinExpectedNumber() > 0:traci.simulationStep()

到什么时候结束呢? 大家应该注意到在sumo.cfg文件中有下面这样的设置:

    <time><begin value="0"/><end value="1000"/><step-length value="0.1"/></time>

所以应该是跑1000次, 然后0.1算一次, 也就是要跑1w个时间步长;
但是我实测发现, 如果地图里还有车没跑完, sumo就会继续跑, 不会受这里设置的时长限制;

然后下面介绍traci的一些其他功能, 比如我第一步想自己实现一个车辆的寻径功能, 也就是说, 在某一个时间点, 我要加一辆车到sumo里, 比如说叫"newcar", 然后用traci控制sumo设置这辆车的路线, 颜色, 速度之类的一系列信息;

第一步是获取当前路网的拓扑图, 也就是说我要先在python里构建一张图, 这里我用的是邻接表的方式创建图, 代码如下:
首先

net = sumolib.net.readNet("/Users/zhangpeiwen/Downloads/map/cmap/map.net.xml")
newVehicletype = 'evehicle'
AdjacencyList = generateTopology()

这个generateTopology方法如下:

def generateTopology():  AdjacencyList = {}for e in net.getEdges():if e.allows(newVehicletype)==False:continue;if AdjacencyList.__contains__(str(e.getFromNode().getID()))==False:AdjacencyList[str(e.getFromNode().getID())]={}AdjacencyList[str(e.getFromNode().getID())][str(e.getToNode().getID())] = e.getLanes()[0].getLength()return AdjacencyList;

这里先从net文件中读取所有的edges和nodes信息, 然后存进AdjacencyList里面, 这里注意我设置了一个newVehicletype, 因为道路是有自己的特点的, 不是所有类型的车都可以在任意道路上开, 我的研究对象是电动车, 所以我只考虑电动车能走的道路, 关于所有的vehicle class的信息大家可以去官网上找, 如下:
Abstract Vehicle Class

然后到这里为止我们就获得了一个AdjacencyList里面存着道路的拓扑图, 包括所有的道路, 节点信息;

在这里我想介绍一下sumo里的道路一些类的关系;

首先在traci for py里定义的一些类:

  • node
  • edge
  • junction
  • connection
  • lane

可以这么理解, 道路上的每一个路口都是一个node, 每条道路都是一个edge, 然后两个道路在某个点交汇会形成一个junction, 然后每一个node里可能会有很多的connection, 这个connection是用来连接两个edge的, 至于lane是车道的意思, 每一条edge里面都可能有多个lane, 一般节点的id长这样:601709881, 然后edge的id长这样-47228917#2, 这里的负号指的是方向, 可能存在一个edge和这条edge有一样的id, 就是负号不同, 然后后面#后面的数字指的就是车道, 就是lane编号;

然后下一步就是要把车辆加入到sumo中, 可以这么做:

def  addCar():
#   edge fromef = "27437516#2"
#   edge toet = "-38280723#0"
#   edge setes = generateRoute(ef, et)print(es)traci.route.add(routeID="newRoute", edges=es)traci.vehicle.add(routeID="newRoute", vehID="newCar", typeID="ElectricBus")traci.vehicle.setVehicleClass(vehID="newCar", clazz="evehicle")traci.vehicle.setEmissionClass(vehID="newCar", clazz="Energy/unknown")traci.vehicle.setColor(color=(0,255,0,238), vehID="newCar")pass;

代码意思应该都比较明显, 这里我的es = generateRoute(ef, et)方法是给定ef和et, 会返回一个集合, 包含了从ef到et经过的所有edge的集合;
这里其实有个问题, 如果我想随机的生成一组ef和et, 怎么来做呢?
如果随机从nodelist取出两个点肯定是不可以的, 因为道路中并不一定任意两点都可达, 所以我的做法如下, 有的时候还是会有bug, 大部分时候都是正常的:

 lenOfEdges = len(net.getEdges())while True:ef = net.getEdges()[random.randint(0,lenOfEdges-1)].getID()et = net.getEdges()[random.randint(0,lenOfEdges-1)].getID()if net.getEdge(ef).allows(newVehicletype)==False or net.getEdge(et).allows(newVehicletype)==False:continue;try:if len(traci.simulation.findRoute(fromEdge=ef, toEdge=et, vType="DEFAULT_VEHTYPE").edges)>0:breakexcept:continue;print "ef is "+ef+"\net is "+et

其实本质是利用traci内置的方法traci.simulation.findRoute(fromEdge=ef, toEdge=et, vType="DEFAULT_VEHTYPE").edges去判断一下这两个点是不是可达的…这样的做法挺蠢的-.- 希望有大佬能给我提供一些更好的办法;

然后是寻找路径的办法, 这里可以直接用sumo提供的traci.simulation.findRoute(fromEdge=ef, toEdge=et, vType="DEFAULT_VEHTYPE").edges这个方法, 直接生成es路径, 在启动sumo的时候可以设置默认寻路器使用的算法:
traci.start(["sumo-gui", "-c", "/location/map.sumo.cfg", "--start", "false", "--routing-algorithm", "astar"], port=7911)
具体的可以在这里找到Routing Algorithms
默认提供了dijkstra, a*, ch和chwrapper四种方法

但是我这里是自己实现的, 我尝试了dijkstra, a*和best-first seach三种方法, 效果上看dijkstra效果还不错, 甚至表现比默认提供的dijkstra方法更好, 我猜测可能是sumo的默认实现的寻路考虑了一些别的东西, 比如道路车辆啊什么的, 这里我没有仔细去看源码取证;

这里放一个我实现的a*寻路:

def generateRoute(ef, et, INF=float("inf")):return generateMyRoute(ef, et)def generateMyRoute(ef, et, INF=float("inf")):nf = str(net.getEdge(ef).getToNode().getID())nt = str(net.getEdge(et).getToNode().getID())print("nf is "+nf+"\nnt is "+nt)nodes = findNodeRoute(nf, nt)edges = []edges.append(ef)s = net.getEdge(ef).getLanes()[0].getLength()for i in range(0,len(nodes)-1):for x in net.getNode(nodes[i]).getOutgoing():if x.getToNode().getID()==nodes[i+1] and x.allows(newVehicletype):edges.append(x.getID())s+=x.getLanes()[0].getLength()breakprint len(nodes)print len(edges)print "dis is ",sreturn edgesdef findNodeRoute(nf, nt, INF=float("inf")):openlist = {}closelist = {}openlist[nf] = [getF(nf, nt, 0), nf]while openlist.__contains__(nt)==False:u = -1minu = INFfor x in openlist:if openlist[x][0]<minu :minu = openlist[x][0]u = xcloselist[u] = openlist[u]del openlist[u]for x in AdjacencyList[u]:if closelist.__contains__(x)==False:if openlist.__contains__(x)==False:openlist[x] = [getF(x, nt, closelist[u][0]), u]
#               print(x+" is added into openlist")else:f = getF(x, nt, closelist[u][0])if f<openlist[x][0]:openlist[x] = [f, u]if x==nt:breakif openlist.__contains__(nt):break#  backtrace to find the routecloselist[nt] = openlist[nt]del openlist[nt]u = ntnodes = []while u!=nf:nodes.insert(0, u)u = closelist[u][1]nodes.insert(0, nf)print "nodes are", nodesreturn nodespass;def getF(nf, nt, g):return getAF(nf, nt, g)#A* Algoritym, f = h+g
def getAF(nf, nt, g):nfc = net.getNode(nf).getCoord()nft = net.getNode(nt).getCoord()return pow(pow(nfc[0]-nft[0], 2)+pow(nfc[1]-nft[1], 2), 0.5)+g#Greedy Algorithm, Best-First Search
def getBF(nf, nt, g):nfc = net.getNode(nf).getCoord()nft = net.getNode(nt).getCoord()return pow(pow(nfc[0]-nft[0], 2)+pow(nfc[1]-nft[1], 2), 0.5)

上面的代码中, 核心A部分是findNodeRoute方法, 这里是通过A算法找到了最优路径走的所有的node, 返回一个nodelist, 然后通过generateMyRoute方法, 把找到的nodelist转换成edgelist, 返回给es作为路径参数;

然后这里的getF函数就是启发式函数, 我提供了两种, 一种是A*的, 也就是getAF, 一种是Greedy Algorithm, Best-First Search, 也就是getBF

到这里就基本能实现一个简单的寻路算法了;

还有一些我用到的其他方法,
比如traci.vehicle.getWaitingTime获取某个车辆在上一个step的等待时间;

比如traci.vehicle.getElectricityConsumption获取某个车辆在上一个step的能源消耗;

更多的方法都可以在官网和pydoc里面找到;

Sumo入门和Traci接口使用相关推荐

  1. SUMO入门(八) - TraCI

    SUMO入门(八) - TraCI TraCI TraCI/Interfacing TraCI from Python TraCI 是 "Traffic Control Interface& ...

  2. sumo添加车辆_Sumo入门和Traci接口教程

    因为正好项目中用到Sumo软件, 发现网上关于sumo的中文材料非常少, 所以我想记录一些自己使用sumo过程中的经验和教训; SUMO的官方网站是 https://sumo.dlr.de/pydoc ...

  3. python文件路径过滤器_SUMO入门(八) - 从Python引入TraCI接口

    SUMO入门(八) - 从Python引入TraCI接口 TraCI TraCI/Interfacing TraCI from Python TraCI命令分为13个部分,它们对应于各个模块: gui ...

  4. SUMO轨迹图的绘制和traci接口的简单使用

    文章目录 前言 一.SUMO绘制轨迹图 二.Traci接口的简单使用 1.引入库 2.启动SUMO 3.Traci接口的简单调用 总结 前言 本人小白,blog主要记录本人的一些学习过程和存在的疑惑, ...

  5. SUMO入门(六) - 交通灯

    SUMO入门(六) - 交通灯 Simulation/Traffic Lights 通常,NETCONVERT 和 NETGENERATE 在计算网络期间为交叉口生成交通信号灯和程序. 尽管如此,这些 ...

  6. 快速入门脑机接口:BCI基础(二)

    关于脑机接口的分类可以查看<快速入门脑机接口:BCI基础(一)> 组成成分 脑活动 神经系统由两个主要部分组成:中枢神经系统和周围神经系统.大脑是中枢神经系统的主要器官,它包含约1000亿 ...

  7. SUMO入门(三) - 路网

    SUMO入门(三) - 路网 Networks/SUMO Road Networks SUMO network file 网络文件描述了交通相关的地图.道路和交叉口. 虽然XML文件我们可读,但SUM ...

  8. Tablestore入门手册-UpdateRow接口详解

    表格存储Tablestore入门手册系列主要介绍表格存储的各个功能接口和适用场景,帮助客户了解和使用表格存储Tablestore.本文对表格存储Tablestore的UpdateRow接口进行介绍,包 ...

  9. SUMO如何用traci以毫秒单位仿真

    实际使用中发现需要traci里和sumocfg文件里格式一致才能正确仿真 python中:要写上仿真步长0.5 for step in np.arange(0, step_time,0.5): sum ...

最新文章

  1. Python基础之标准库datetime 时间与日期的使用
  2. 「SAP技术」SE16和SE16N修改后台表数据方法
  3. 8张图让你一步步看清 async/await 和 promise 的执行顺序
  4. ai钢笔工具怎么描线_AI钢笔工具技巧,怎么画出完美的贝塞尔曲线
  5. h5的横线_CSS文字两边添加横线的几种方法
  6. 统计0-n数字中出现k的次数
  7. linux服务器组件有哪些,推荐几个linux服务器面板
  8. python中正则表达式的默认匹配方式为_Python模式匹配与正则表达式
  9. 浅谈Linux文件系统
  10. WPS企业免费版,无广告
  11. 摄影测量与遥感资质乙级标准内容
  12. 信息系统高级项目管理师:十大知识领域+五大过程组
  13. 解决七彩虹断剑C.A320M-K PRO V14在Win11下前置面板音频接口无法使用的问题
  14. Python开发——8.模块
  15. Activiti流程引擎架构概述
  16. 10个最佳的大数据处理编程语言
  17. 用python获取指定路径下的所有目录路径和文件路径
  18. scorm 1.2标准基本知识
  19. java 校验网站域名格式是否为(xxx.xxx.xxx/xxx/xxx..)正则
  20. 医药板块企稳“回春”

热门文章

  1. Echarts之饼图内外圆重叠且图例不统一
  2. 12年计算机考研大纲,2012计算机考研大纲解析之计算机组成原理
  3. 基于瞬态自适应的麻雀搜索算法
  4. GF1WFV数据预处理
  5. 学画画要花多少钱_高二学美术的费用大概是多少 要花多少钱
  6. 音频工程,语音识别,语音学,机器学习相关网站和资料
  7. ArcGIS10联网无法启动问题解决
  8. Linux防火墙开放端口
  9. 查字典?看唐诗?为孩子起名何必那么麻烦,来看女孩起名大全
  10. Gitee+PicGo上传图片失败404 - {“status“:“404“,“error“:“Not Found“}