当前位置: 首页 > article >正文

【Agorversev1.1数据转换】Agorverse高清地图转OpenStreetMap及SUMO路网

在这里插入图片描述

文章目录

  • Agorverse高清地图转OpenStreetMap及SUMO路网
    • 1. Agorverse osm转换说明
    • 2. 转换源码
    • 3. 处理效果
    • 4. SUMO-Carla联合仿真

Agorverse高清地图转OpenStreetMap及SUMO路网

1. Agorverse osm转换说明

根据作者的描述,其高清地图的osm文件与标准osm的区别在于以下几点:

在 OpenStreetMap (OSM) 中,“Node”是指一个兴趣点,或者是某条线性特征(例如道路)的组成点。在 OSM 中,Node 可以包含标签(tags),例如:

  • natural:如果是自然特征,标明类型(如山顶等)。
  • man_made:如果是人工设施,标明类型(如水塔、信号塔等)。
  • amenity:如果是服务设施(如酒吧、餐馆、回收中心等),标明类型。

在 OSM 中,“Way”通常是由一系列有序的“Nodes”组成的道路中心线。OSM 中的 Way 通常表示线性或多边形特征,例如道路、溪流、森林或湖泊。Way 至少包含两个或更多的 Node,并可能具有以下标签:

  • highway:道路的类别(如高速公路、主干道、次干道等)。
  • maxspeed:道路的最高限速(单位:km/h)。
  • ref:道路的参考编号。
  • oneway:是否为单行道(布尔值)。

然而,在 Argoverse 中,“Way”对应的是车道段的中心线。一个 Argoverse Way 包含以下 9 个属性:

  • id:整数,唯一的车道 ID,作为此“Way”的标识符。
  • has_traffic_control:布尔值,表示是否有交通管制。
  • turn_direction:字符串,表示转向方向(“RIGHT”、“LEFT”或“NONE”)。
  • is_intersection:布尔值,表示是否为交叉路口。
  • l_neighbor_id:整数,左侧相邻车道的唯一 ID。
  • r_neighbor_id:整数,右侧相邻车道的唯一 ID。
  • predecessors:整数列表或 None,表示前序车道。
  • successors:整数列表或 None,表示后续车道。
  • centerline_node_ids:列表,包含中心线的 Node IDs。

在 Argoverse 中,一个 LaneSegment 对象由一个 Way 和两个或更多的 Node 组合而成。

2. 转换源码

转换源码如下:

from pyproj import Proj, transform

# 假设原始坐标是 UTM Zone 33N,投影信息需要根据具体数据集调整
proj_utm = Proj(proj="utm", zone=33, ellps="WGS84", south=False)
proj_wgs84 = Proj(proj="latlong", datum="WGS84")

# Load the Argoverse XML file
input_file = "/home/moresweet/Downloads/hd_maps/map_files/pruned_argoverse_PIT_10314_vector_map.xml"
output_file = "/home/moresweet/Downloads/hd_maps/map_files/converted_osm_file.osm"
import xml.etree.ElementTree as ET
import datetime
from concurrent.futures import ThreadPoolExecutor
import math

# 经纬度参考点 (假设参考点在赤道和本初子午线交汇处)
REFERENCE_LAT = 0.0
REFERENCE_LON = 0.0


# 将笛卡尔坐标转换为 WGS84 经纬度
def convert_coordinate(x, y):
    earth_radius = 6378137  # 地球半径,单位为米
    lon = REFERENCE_LON + (x / (earth_radius * math.cos(math.pi * REFERENCE_LAT / 180))) * (180 / math.pi)
    lat = REFERENCE_LAT + (y / earth_radius) * (180 / math.pi)
    return lon, lat


# 格式化 XML 节点为美观的缩进格式
def prettify_xml(elem, level=0):
    indent = "  "
    i = "\n" + level * indent
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + indent
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for child in elem:
            prettify_xml(child, level + 1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i
    return ET.tostring(elem, encoding="unicode")


# 计算边界 (bounds)
def calculate_bounds(nodes):
    coords = [convert_coordinate(float(n.attrib["x"]), float(n.attrib["y"])) for n in nodes]
    lons, lats = zip(*coords)
    return min(lons), min(lats), max(lons), max(lats)


# 处理单个节点
def process_node(node, id_offset):
    x, y = float(node.attrib["x"]), float(node.attrib["y"])
    lon, lat = convert_coordinate(x, y)
    node_id = str(int(node.attrib["id"]) + id_offset)
    return ET.Element(
        "node",
        id=node_id, lat=str(lat), lon=str(lon),
        visible="true", version="1", changeset="1",
        timestamp=datetime.datetime.now().isoformat(),
        user="converter", uid="1"
    )


# 处理单个路径
def process_way(way, id_offset):
    osm_way = ET.Element(
        "way",
        {
            "id": way.attrib["lane_id"],
            "visible": "true",
            "version": "1",
            "changeset": "1",
            "timestamp": datetime.datetime.now().isoformat(),
            "user": "converter",
            "uid": "1"
        }
    )
    for nd in way.findall("nd"):
        ref_id = str(int(nd.attrib["ref"]) + id_offset)
        ET.SubElement(osm_way, "nd", {"ref": ref_id})
    for tag in way.findall("tag"):
        key, value = tag.attrib["k"], tag.attrib["v"]
        if key == "turn_direction":
            key = "turn"
        elif key == "has_traffic_control":
            key = "traffic_control"
        elif key == "is_intersection":
            key = "junction" if value.lower() == "true" else "no_junction"
        if key not in {"highway", "building"}:
            key = "highway"
            value = "service"
        ET.SubElement(osm_way, "tag", {"k": key, "v": value})
    return osm_way


# 主函数
def convert_argoverse_to_osm_with_id_fix(input_file, output_file, id_offset=1):
    # 解析输入文件
    tree = ET.parse(input_file)
    root = tree.getroot()

    # 创建 OSM 根节点
    osm_root = ET.Element(
        "osm",
        version="0.6",
        generator="argoverse_to_osm_converter",
        copyright="OpenStreetMap and contributors",
        attribution="http://www.openstreetmap.org/copyright",
        license="http://opendatacommons.org/licenses/odbl/1-0/"
    )

    # 转换节点
    nodes = root.findall("node")
    with ThreadPoolExecutor() as executor:
        converted_nodes = list(executor.map(lambda n: process_node(n, id_offset), nodes))

    # 添加 <bounds>
    min_lon, min_lat, max_lon, max_lat = calculate_bounds(nodes)
    ET.SubElement(osm_root, "bounds", {
        "minlat": str(min_lat),
        "minlon": str(min_lon),
        "maxlat": str(max_lat),
        "maxlon": str(max_lon)
    })

    # 添加 <node> 元素
    osm_root.extend(converted_nodes)

    # 转换路径 (Way)
    ways = root.findall("way")
    with ThreadPoolExecutor() as executor:
        converted_ways = list(executor.map(lambda w: process_way(w, id_offset), ways))
    osm_root.extend(converted_ways)

    # 保存格式化的 XML
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(prettify_xml(osm_root))
    print(f"Converted OSM file saved to {output_file}")


# 示例调用
convert_argoverse_to_osm_with_id_fix(
    input_file, output_file, id_offset=1
)

转换为SUMO所用的文档格式:

netconvert --osm-files converted_osm_file.osm -o argoverse_sumo.net.xml
python /usr/share/sumo/tools/randomTrips.py -n argoverse_sumo.net.xml -r argoverse_sumo.rou.xml --allow-fringe
polyconvert --net-file argoverse_sumo.net.xml --osm-files converted_osm_file.osm -o argoverse_sumo.poly.xml
sumo-gui argoverse_sumo.sumocfg

其中convert.sumocfg的内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <input>
        <net-file value="bbb.net.xml"/>
        <route-files value="bbb.rou.xml"/>
        <additional-files value="bbb.poly.xml"/>
    </input>

    <time>
        <begin value="0"/>
        <end value="120"/>
    </time>
</configuration>

在这里插入图片描述

3. 处理效果

最终效果:转换的高精地图与原始版本一致。QGIS、JOSM、SUMO、Agorverse-api得到的结果均一致。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4. SUMO-Carla联合仿真

cd ~/carla/Co-Simulation/Sumo
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

在这里插入图片描述

moresweet@moresweet-System-Product-Name:~/carla$ ./CarlaUE4.sh
moresweet@moresweet-System-Product-Name:~/carla/PythonAPI/util$ python config.py --map Town01
load map 'Town01'.
moresweet@moresweet-System-Product-Name:~/carla/Co-Simulation/Sumo$ python run_synchronization.py ./examples/Town01.sumocfg --sumo-gui
INFO: Starting new sumo server...
INFO: Remember to press the play button to start the simulation
 Retrying in 1 seconds

在这里插入图片描述

问题:

python run_synchronization.py ./examples/Town01.sumocfg --sumo-gui
INFO: Starting new sumo server...
INFO: Remember to press the play button to start the simulation
 Retrying in 1 seconds
Traceback (most recent call last):
  File "run_synchronization.py", line 319, in <module>
    synchronization_loop(arguments)
  File "run_synchronization.py", line 237, in synchronization_loop
    args.sumo_port, args.sumo_gui, args.client_order)
  File "/home/moresweet/carla/Co-Simulation/Sumo/sumo_integration/sumo_simulation.py", line 336, in __init__
    self.net = _get_sumo_net(cfg_file)
  File "/home/moresweet/carla/Co-Simulation/Sumo/sumo_integration/sumo_simulation.py", line 304, in _get_sumo_net
    sumo_net = traci.sumolib.net.readNet(net_file)
AttributeError: module 'traci' has no attribute 'sumolib'

在这里插入图片描述


http://www.kler.cn/a/412581.html

相关文章:

  • 用Java爬虫“搜刮”工厂数据:一场数据的寻宝之旅
  • 打开windows 的字符映射表
  • Python 爬虫入门教程:从零构建你的第一个网络爬虫
  • python爬虫安装教程
  • 【排版教程】Word、WPS 分节符(奇数页等) 自动变成 分节符(下一页) 解决办法
  • 爬虫获取的数据如何用于市场分析?
  • 第29天:安全开发-JS应用DOM树加密编码库断点调试逆向分析元素属性操作
  • 【文献阅读】自动化构音障碍严重程度分类:声学特征与深度学习技术的研究
  • Vuex中通过action触发mutation是为什么?[AI]
  • BERT的配置
  • 消息队列实战指南
  • uni-app 自定义平台如何进行 static 目录的条件编译
  • 排序算法之插入排序篇
  • NestJS中使用useClass注入
  • 【ubuntu24.04】hnsw liblibstdc++.so.6: version GLIBCXX_3.4.32‘ not found
  • 【docker集群应用】Docker网络与资源控制
  • vscode中json文件的注释飘红
  • 实现跨语言通信:Rust 和 Thrift 的最佳实践
  • Python初始化变量
  • CodeIgniter中的重映射方法调用
  • 如何借助AI生成PPT,让创作轻松又高效
  • WPS表格学习计划与策略
  • 35 基于单片机的精确电压表DA-AD转换
  • UniApp开发实战:常见报错解析与解决方案
  • VTK中对于相机camera的设置
  • 前端 vue3 + element-plus + ts 隐藏表头的全选框