Skip to content

这篇文章将会介绍如何使用GoTrackIt这个开源路径匹配框架将带误差的GPS定位数据转化为SUMO的车流仿真。

Image title

写在前面

GoTrackIt主页上表明从0.3.15版本开始,将会支持:直接将路径匹配结果转化为SUMO仿真。

整体思路

整个工作思路如下:

  1. 解析高精度地图为路段中心线图层,为中心线图层创建点线拓扑关联,得到宏观路网;
  2. 记录edge_id和link_id的映射关系;
  3. GPS数据清洗;
  4. 将GPS数据匹配到宏观路网;
  5. 将匹配结果表转化为rou车流文件;
  6. 生成sumocfg仿真配置文件。

具体步骤

本示例项目的工作目录如下:

Image title
工作目录

注:本次的测试路网ns.net.xml使用SUMO的osmWebWizard从OSM获取

1.解析.net.xml路网

GoTrackIt目前还不支持高精度地图的车道级路径匹配,但是他却提供了解析高精地图路段中心线的方法,文档截图如下:

Image title
解析高精地图
Image title
get_net_shp函数解释

那我们可以借助这个函数对.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'))
Image title
解析前后对比

在gotrackit解析出来的宏观路网中,发现不仅生产了新的link_id、from_node、to_node,还保留了原来SUMO路网中高精地图路段的edge_id,这个映射关系我们存为一个字典,方便后面使用

Image title
link_id和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数据进行路口停留点删除、降频、卡尔曼滤波平滑等操作。

Image title
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')
Image title
GPS点清洗前后对比

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)
Image title
匹配结果可视化
Image title
匹配结果可视化(局部)

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)
Image title
生成的仿真文件
Image title
仿真运行

总结

总的来说,只需要调用gotrackit的函数即可完成GPS点位到SUMO仿真的转化,个人认为整个过程的核心是高精地图的解析和路径匹配,欢迎大家交流、讨论!

Comments