这篇文章将会介绍如何使用GoTrackIt这个开源路径匹配框架将带误差的GPS定位数据转化为SUMO的车流仿真。
写在前面
GoTrackIt主页上表明从0.3.15版本开始,将会支持:直接将路径匹配结果转化为SUMO仿真。
整体思路
整个工作思路如下:
- 解析高精度地图为路段中心线图层,为中心线图层创建点线拓扑关联,得到宏观路网;
- 记录edge_id和link_id的映射关系;
- GPS数据清洗;
- 将GPS数据匹配到宏观路网;
- 将匹配结果表转化为rou车流文件;
- 生成sumocfg仿真配置文件。
具体步骤
本示例项目的工作目录如下:
注:本次的测试路网ns.net.xml使用SUMO的osmWebWizard从OSM获取
1.解析.net.xml路网
GoTrackIt目前还不支持高精度地图的车道级路径匹配,但是他却提供了解析高精地图路段中心线的方法,文档截图如下:
那我们可以借助这个函数对.net.xml的车道级路网进行路段中心线的提取,然后再使用创建点层这个方法的到一个宏观路网,代码如下:
import os
import gotrackit.netreverse.NetGen as ng
from gotrackit.netxfer.SumoConvert import SumoConvert
fldr = r'C:/Users/Administrator/Desktop/temp/gps2sumo/'
sc = SumoConvert()
# 1.从ns.net.xml解析得到路段中心线
lane_gdf, \
junction_gdf, \
lane_polygon_gdf, \
avg_edge_gdf, \
conn_gdf = \
sc.get_net_shp(net_path=os.path.join(fldr, r'input/ns.net.xml'))
avg_edge_gdf = avg_edge_gdf.to_crs('EPSG:4326')
# 2.基于中心线avg_edge_gdf创建点线关联,在out_fldr下生成gotrackit的标准宏观路网(LinkAfterModify.shp、NodeAfterModify.shp)
ng.NetReverse.create_node_from_link(link_gdf=avg_edge_gdf, plain_crs='EPSG:32650', fill_dir=1,
update_link_field_list=['link_id', 'from_node', 'to_node',
'dir', 'length'],
out_fldr=os.path.join(fldr, r'output'))
2. 记录edge_id和link_id的映射关系
在gotrackit解析出来的宏观路网中,发现不仅生产了新的link_id、from_node、to_node,还保留了原来SUMO路网中高精地图路段的edge_id,这个映射关系我们存为一个字典,方便后面使用
import geopandas as gpd
link_gdf = gpd.read_file(os.path.join(fldr, 'output/LinkAfterModify.shp'))
node_gdf = gpd.read_file(os.path.join(fldr, 'output/NodeAfterModify.shp'))
# 记录link_id和edge_id的映射关系
link_edge_map = {link:edge for link, edge in zip(link_gdf['link_id'], link_gdf['edge_id'])}
3.清洗GPS数据
我们使用的这份GPS数据是高频高误差数据,尤其在路口等灯时,会有很多噪声数据,所以我们需要对GPS数据进行路口停留点删除、降频、卡尔曼滤波平滑等操作。
GPS数据的处理如下(GPS数据已经是经过行程切分的,所以直接做清洗即可):
import os
import geopandas as gpd
from gotrackit.gps.Trajectory import TrajectoryPoints
fldr = r'C:/Users/Administrator/Desktop/temp/gps2sumo/'
# 读取GPS数据
gps_df = gpd.read_file(os.path.join(fldr, 'input/gps.shp'))
# 取一辆车测试
gps_df = gps_df[gps_df['agent_id'] == 122].copy()
# 构建TrajectoryPoints类方便进行清洗
tp = TrajectoryPoints(gps_points_df=gps_df)
# 删除路口停留点、降频、卡尔曼滤波平滑
tp.del_dwell_points(dwell_n=1, dwell_l_length=30).lower_frequency(5).kf_smooth()
gps_df = tp.trajectory_data(_type='df')
# 输出HTML查看清洗前后的数据情况
tp.export_html(out_fldr=os.path.join(fldr, 'output'), file_name=r'gps_process')
4.将清洗后的GPS数据匹配到路网
gotrackit的官方文档对于路径匹配的介绍非常详细,这里就不多赘述了,代码如下:
import os
import geopandas as gpd
from gotrackit.map.Net import Net
from gotrackit.MapMatch import MapMatch
fldr = r'C:/Users/Administrator/Desktop/temp/gps2sumo/'
link_gdf = gpd.read_file(os.path.join(fldr, 'output', 'LinkAfterModify.shp'))
node_gdf = gpd.read_file(os.path.join(fldr, 'output','NodeAfterModify.shp'))
# 构建Net
my_net = Net(link_gdf=link_gdf, node_gdf=node_gdf, cut_off=600, is_hierarchical=True)
my_net.init_net()
# map-match
mpm = MapMatch(net=my_net, flag_name='ns-gps2sumo',
export_html=True, export_geo_res=True,
use_sub_net=True, time_unit='ms', dense_gps=False,
gps_buffer=300, top_k=30,
use_heading_inf=True, omitted_l=20,
use_gps_source=False, gps_radius=6.0,
out_fldr=os.path.join(fldr, 'output'))
match_res_df, warn_info, error_info = mpm.execute(gps_df=gps_df)
match_res_df.to_csv(os.path.join(fldr, 'output/match_res.csv'), encoding='utf_8_sig', index=False)
5.基于匹配结果表生产.rou.xml
由于我们最终的路径文件是要用.net.xml文件中的edge_id来表示路径的,那么我们先要把这个匹配结果表中的link_id映射为edge_id, 然后再调用match2rou函数,即可生成ROU文件。
from gotrackit.netxfer.SumoConvert import SumoConvert
# 增加一列edge_id,用于将link_id映射为原SUMO路网中的edge_id
match_res_df['edge_id'] = match_res_df['link_id'].map(link_edge_map)
# 匹配结果转化为flow.rou.xml并且存储在out_fldr下
sc.match2rou(match_res_df=match_res_df, time_format='%Y-%m-%d %H:%M:%S.%f',
out_fldr=os.path.join(fldr, 'output/sim'), file_name='flow')
在output/sim目录下会生成flow.rou.xml文件,这样一来,我们的车流文件就得到了,我们只需要再写一个仿真启动的配置文件即可。
6.生产仿真配置文件
其实这个文件很简单,手写一下也可以,在这里,也是调用gotrackit的函数完成:
# 指定.net.xml路网的绝对路径、rou文件的绝对路径
# 再指定仿真配置文件的输出目录、仿真起始时间(0-3600秒)
sc.generate_sumocfg(net_file_path=os.path.join(fldr, 'input', 'ns.net.xml'),
rou_file_path=os.path.join(fldr, 'output/sim/flow.rou.xml'),
out_fldr=os.path.join(fldr, 'output/sim'), start_time=0, end_time=3600)
总结
总的来说,只需要调用gotrackit的函数即可完成GPS点位到SUMO仿真的转化,个人认为整个过程的核心是高精地图的解析和路径匹配,欢迎大家交流、讨论!