对于carla这其实是一种没有完成的方法,因为没有办法产生xodr文件,但是其实可以通过采集的道路经纬度通过脚本生成osm文件(已经补充了相关脚本)。这种方法的优势是自由度高,因为在ue4里其实什么都可以做。我在别的平台实现类似功能的时候使用了这种方法,也可以为carla的创建地图提供一种思路。
当时需要为一个比较小的城市创建道路模型,由于是小城市所以osm数据很少无法直接使用。当时尝试了两种方法,一种是基于osm的少量数据通过高德地图的api进行道路点位置数据爬取,另一种是通过一个开源程序手动爬取道路数据。两种方法都是可行的,但是当时需要建的只是那个城市的一部分道路,第一种方法需要去除大量数据反而麻烦,所以主要用的第二种方法。
用的是别人开源的程序取的轨迹经纬度,这个程序也是调用的高德的api
轨迹经纬度抓取
每次获得道路数据后,都进行可视化检查一下,可视化代码如下:
import plotly.graph_objects as go
from transform import LngLatTransfer
#轨迹数据
data = [[116.303849,39.9833],[116.304262,39.983314],[116.304262,39.983314],[116.304262,39.983437],[116.304257,39.983603],[116.304257,39.983684],[116.304268,39.983925],[116.304268,39.983957],[116.303983,39.984027],[116.303468,39.984215],[116.303093,39.984349],[116.302621,39.98458],[116.302621,39.98458],[116.303576,39.984762],[116.303683,39.984778],[116.304515,39.984864],[116.3046,39.984869],[116.30519,39.984901],[116.305501,39.984912],[116.305662,39.984918],[116.306027,39.984939],[116.306376,39.98495],[116.306735,39.984961],[116.308076,39.98502],[116.308286,39.98503],[116.308446,39.985036],[116.309326,39.985052],[116.309664,39.985063],[116.309664,39.985063],[116.310083,39.985207],[116.311295,39.98525],[116.315721,39.98539],[116.316713,39.985422],[116.317646,39.985438],[116.320543,39.985524],[116.323241,39.98561],[116.324475,39.985647],[116.331369,39.985889],[116.337082,39.986151],[116.340531,39.986345],[116.344726,39.986511],[116.345064,39.986521],[116.345214,39.986527],[116.348277,39.986581],[116.350595,39.986661],[116.356399,39.98686],[116.358738,39.98694],[116.363888,39.987106],[116.364526,39.987128],[116.366677,39.987198],[116.367187,39.987214],[116.371559,39.987348],[116.37304,39.987358],[116.37658,39.987476],[116.37739,39.987503],[116.378458,39.987546],[116.379546,39.987584],[116.380346,39.987611],[116.381236,39.987643],[116.383151,39.987691],[116.383994,39.987707],[116.384358,39.987713],[116.384948,39.987729],[116.385463,39.98775],[116.387363,39.987788],[116.387738,39.987798],[116.389143,39.987825],[116.38916,39.987825],[116.389911,39.987863],[116.390211,39.987873],[116.393993,39.987997],[116.397265,39.988083],[116.397437,39.988083],[116.397608,39.988083],[116.397608,39.988083],[116.39771,39.98812],[116.397834,39.988152],[116.398118,39.98819],[116.398724,39.988222],[116.400173,39.98826],[116.400371,39.988281],[116.400527,39.988319],[116.400671,39.988372],[116.400779,39.988426],[116.400897,39.988512],[116.400972,39.988587],[116.401047,39.988678],[116.401085,39.988748],[116.401122,39.988855],[116.401165,39.98908],[116.401117,39.990234],[116.401063,39.9915],[116.401058,39.991698],[116.401053,39.992723],[116.401042,39.993115],[116.401036,39.993485],[116.401036,39.993485],[116.402158,39.993517],[116.402361,39.993522],[116.40344,39.993517],[116.403617,39.993517],[116.404003,39.993506],[116.404309,39.993442],[116.404641,39.993324],[116.404824,39.993233],[116.405065,39.993055],[116.405194,39.992932],[116.405301,39.992798],[116.405387,39.992648],[116.405425,39.992508],[116.405425,39.992235],[116.405425,39.992235],[116.405516,39.992208],[116.406186,39.992208],[116.406186,39.992208],[116.406181,39.992095],[116.406186,39.992047],[116.406186,39.992047],[116.406299,39.992041],[116.40654,39.992036],[116.406953,39.992047]]
lon_list = []
lat_list = []
for i in range(len(data)):
lon_ = data[i][0]
lat_ = data[i][1]
lon,lat = LngLatTransfer().GCJ02_to_WGS84(lon_,lat_) #高德经纬度转wgs84经纬度
lon_list.append(lon)
lat_list.append(lat)
scatter = go.Scattermapbox(lon=lon_list #使用Scattermapbox函数画散点图
,lat=lat_list
,name='位置数据'
,mode = "markers+lines"
)
fig = go.Figure(data=scatter) #将散点图导入画布
fig.update_layout(mapbox_style='stamen-terrain') #将地图设置为画布
#可以使用的免费地图:"open-street-map", "carto-positron", "carto-darkmatter", "stamen-terrain", "stamen-toner" or "stamen-watercolor"
fig.show()
这个是一个ue4蓝图类,可以通过拖拽创建道路,淘宝上大概一两块钱吧。也可以自己写一个类似的插件,这个插件就是根据ue4的spline组件开发的,如果只是简单使用就没必要了。
可以改变spline起点和终点的位置和切线进而改变形状,按住alt拖动样条点可以增加新的样条点。
第一步获得了道路位置数据,第二步实现了在ue4里创建道路,将二者结合就可以根据实际道路在ue4里创建道路模型了。但是,这里面还有两个问题坐标转换和脚本自动化实现创建道路。二者都将通过python程序实现。
坐标转换是指需要把采集到的高德经纬坐标(火星坐标)转换成墨卡托坐标系,上面的可视化程序也用到了高德坐标转换wgs84坐标。
坐标转换程序可以参考:Python坐标转换并保存文件
def spaw_road_all(x_list,y_list,frame_list):
#1、生成道路actor
road_class = unreal.EditorAssetLibrary.load_blueprint_class("/Game/SnappyRoads/Blueprints/BP_SnappyRoad")
road = unreal.EditorLevelLibrary.spawn_actor_from_object(road_class,[0,0,0])
road.set_actor_label('road_test1')
#2、获得道路实例的spline组件
print(road.root_component.get_children_components(True)[0])
spline_component = road.root_component.get_children_components(True)[0]
#3、添加样条点
for i in range(len(frame_list)):
point = unreal.SplinePoint(position = unreal.Vector(x_list[i],y_list[i],0),input_key = float(int(i)+2),scale=[1,1,1])
spline_component.add_point(point)
print('生成点:',x_list[i],y_list[i],frame_list[i])
关于ue4的python使用,可以参考:Python 开发学习笔记
整体代码如下:
import unreal
import csv
def spaw_road_all(x_list,y_list,frame_list):
#1、生成道路actor
road_class = unreal.EditorAssetLibrary.load_blueprint_class("/Game/SnappyRoads/Blueprints/BP_SnappyRoad")
road = unreal.EditorLevelLibrary.spawn_actor_from_object(road_class,[0,0,0])
road.set_actor_label('road_test1')
#2、获得道路实例的spline组件
print(road.root_component.get_children_components(True)[0])
spline_component = road.root_component.get_children_components(True)[0]
#3、添加样条点
for i in range(len(frame_list)):
point = unreal.SplinePoint(position = unreal.Vector(x_list[i],y_list[i],0),input_key = float(int(i)+2),scale=[1,1,1])
spline_component.add_point(point)
print('生成点:',x_list[i],y_list[i],frame_list[i])
def main():
#1、数据读取
x_list= []
y_list= []
frame_list= []
with open(r'D:\desktop\unreal\send_road.csv',encoding='UTF8') as f: #这里读取的数据已经经过坐标转换
f_csv = csv.reader(f)
for row in f_csv:
x_list.append(float(row[1]))
y_list.append(-float(row[2]))
frame_list.append(float(row[0]))
#2、创建道路
spaw_road_all(x_list,y_list,frame_list)
main()
效果如下:
import pandas as pd
import numpy as np
#1、获取原始点数据
def read_data(path,start,end,dtype):
# 读取表头
head_row = pd.read_csv(path, nrows=0)
# 表头列转为 list
head_row_list = list(head_row)
# 读取
csv_result = pd.read_csv(path,usecols=head_row_list,dtype=dtype)#{'lon':np.float64,'lat':np.float64}
# row_list = csv_result.values.tolist()
csv_result = csv_result[start:end]
return csv_result
#2、定义函数
#生成点函数
def creat_node(id,lon,lat):
lon = lon - 112.1851171
lat = lat - 30.91744581
content = ' +str(id)+'" '+'version = "3" '+'lat='+'"'+str(lat)+'" '+'lon='+'"'+str(lon)+'"/>'
return content
#生成单位函数
def creat_way(id,node_list):
head = ' + str(id)+'" version = "3" >'
end = ' '
content = []
for node_id in node_list:
content.append(' + str(node_id)+'"/>')
return head , content , end
#3、生成文件
def main():
node_list = []
way_list = []
#基础格式
osm_head1 = ''
osm_head2 = ''
osm_end = ''
#读取数据
data = read_data('data.csv',0,100,{'lon':np.float64,'lat':np.float64})
print('读取数据完成')
#osm数据元素生成
for i in range(len(data)):
id = data['id'].loc[i]
lon = data['lon'].loc[i]
lat = data['lat'].loc[i]
node_content = creat_node(id,lon,lat)
node_list.append(node_content)
group_id_save = []
way_list_ = {}
for i in range(len(data)):
id = data['id'].loc[i]
group_id = data['group_id'].loc[i]
if(group_id in group_id_save):
way_list_[group_id].append(id)
else:
group_id_save.append(group_id)
way_list_[group_id] = [id]
for id in way_list_:
head , content , end = creat_way(id,way_list_[id])
way_list.append([head,content,end])
print('数据处理完成')
#osm存储
Note=open('x3.txt',mode='w')
Note.write(osm_head1+'\n')
Note.write(osm_head2+'\n')
for row in node_list:
Note.write(row+'\n')
for row in way_list:
Note.write(row[0]+'\n')
for nd in row[1]:
Note.write(nd+'\n')
Note.write(' \n') #道路类型设置
Note.write(row[2]+'\n')
Note.write(osm_end+'\n')
Note.close()
main()
生成结果
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" >
<node id="1" version = "3" lat="0.0" lon="0.0"/>
<node id="2" version = "3" lat="0.00019585000000077457" lon="-1.810000000546097e-05"/>
<node id="3" version = "3" lat="0.00035172999999844023" lon="-3.409999999348656e-05"/>
<node id="4" version = "3" lat="0.0011721199999996657" lon="-0.00010939999999948213"/>
<node id="5" version = "3" lat="0.0031980000000011444" lon="-7.750000000328328e-05"/>
<node id="6" version = "3" lat="0.00370274000000137" lon="-5.649999999945976e-05"/>
<node id="7" version = "3" lat="0.004098559999999196" lon="-2.940000000251075e-05"/>
<node id="8" version = "3" lat="0.004200520000001262" lon="-1.8400000001861372e-05"/>
<node id="9" version = "3" lat="0.005037200000000297" lon="7.77000000056205e-05"/>
<node id="10" version = "3" lat="0.005139169999999638" lon="9.369999999364609e-05"/>
<node id="11" version = "3" lat="0.006318690000000515" lon="0.0002178999999955522"/>
<node id="12" version = "3" lat="0.006678560000000999" lon="0.00026099999999473766"/>
<node id="13" version = "3" lat="0.007499249999998625" lon="0.00035710000000221953"/>
<node id="14" version = "3" lat="0.007901090000000721" lon="0.000400200000001405"/>
<node id="15" version = "3" lat="0.008549859999998688" lon="0.0004863000000057127"/>
<node id="16" version = "3" lat="0.009713389999998157" lon="0.0006094999999959327"/>
<node id="17" version = "3" lat="0.010570959999999019" lon="0.0006474999999994679"/>
<node id="18" version = "3" lat="0.011332519999999846" lon="0.0006474000000054048"/>
<node id="19" version = "3" lat="0.011665329999999585" lon="0.0006524000000069918"/>
<node id="20" version = "3" lat="0.011992140000000262" lon="0.0006524000000069918"/>
<node id="21" version = "3" lat="0.01204510999999897" lon="0.0006522999999987178"/>
<node id="22" version = "3" lat="0.01232994000000076" lon="0.0006522999999987178"/>
<node id="23" version = "3" lat="0.012479849999998294" lon="0.0006472999999971307"/>
<node id="24" version = "3" lat="0.013187439999999384" lon="0.0006522000000046546"/>
<node id="25" version = "3" lat="0.013606199999998125" lon="0.0006522000000046546"/>
<node id="26" version = "3" lat="0.014082919999999888" lon="0.0006522000000046546"/>
<node id="27" version = "3" lat="0.014173870000000477" lon="0.0006522000000046546"/>
<node id="28" version = "3" lat="0.014500680000001154" lon="0.0006580999999954429"/>
<node id="29" version = "3" lat="0.014865470000000158" lon="0.0006580999999954429"/>
<node id="30" version = "3" lat="0.015139309999998574" lon="0.0006580999999954429"/>
<node id="31" version = "3" lat="0.016093749999999574" lon="0.0006580000000013797"/>
<node id="32" version = "3" lat="0.01820053999999871" lon="0.0006678000000022166"/>
<node id="33" version = "3" lat="0.018924129999998485" lon="0.0006738000000012789"/>
<node id="34" version = "3" lat="0.01933189000000013" lon="0.0006737000000072157"/>
<node id="35" version = "3" lat="0.01964771999999826" lon="0.000683699999996179"/>
<node id="36" version = "3" lat="0.020168410000000137" lon="0.000678699999994592"/>
<node id="37" version = "3" lat="0.020345309999999728" lon="0.000678699999994592"/>
<node id="38" version = "3" lat="0.0207310800000009" lon="0.0006786000000005288"/>
<node id="39" version = "3" lat="0.021138839999998993" lon="0.0006786000000005288"/>
<node id="40" version = "3" lat="0.021948370000000494" lon="0.0006785000000064656"/>
<node id="41" version = "3" lat="0.0223131599999995" lon="0.0006834999999938418"/>
<node id="42" version = "3" lat="0.02257000999999903" lon="0.0006785000000064656"/>
<node id="43" version = "3" lat="0.02282685999999856" lon="0.0006783999999981916"/>
<node id="44" version = "3" lat="0.023175680000001364" lon="0.0006944999999944912"/>
<node id="45" version = "3" lat="0.02350257999999883" lon="0.0007485000000002628"/>
<node id="46" version = "3" lat="0.024039510000001485" lon="0.0008878000000009934"/>
<node id="47" version = "3" lat="0.025117349999998595" lon="0.0011623000000042794"/>
<node id="48" version = "3" lat="0.02613621000000066" lon="0.001425800000006916"/>
<node id="49" version = "3" lat="0.026158209999998405" lon="0.0014307999999942922"/>
<node id="50" version = "3" lat="0.026313189999999764" lon="0.0014739000000076885"/>
<node id="51" version = "3" lat="0.02661913000000027" lon="0.001543999999995549"/>
<node id="52" version = "3" lat="0.027102049999999878" lon="0.0016621999999983927"/>
<node id="53" version = "3" lat="0.027621960000001167" lon="0.0017854000000028236"/>
<node id="54" version = "3" lat="0.02777193000000011" lon="0.0018175000000013597"/>
<node id="55" version = "3" lat="0.02838883000000081" lon="0.001968800000000215"/>
<node id="56" version = "3" lat="0.0290167100000005" lon="0.0021129999999942584"/>
<node id="57" version = "3" lat="0.029349660000001165" lon="0.00219920000000684"/>
<node id="58" version = "3" lat="0.02944065000000151" lon="0.002226199999995515"/>
<node id="59" version = "3" lat="0.029939679999998248" lon="0.0024145999999944934"/>
<node id="60" version = "3" lat="0.0301916999999996" lon="0.0025107999999960384"/>
<node id="61" version = "3" lat="0.03101172999999946" lon="0.0028123999999962734"/>
<node id="62" version = "3" lat="0.031586730000000784" lon="0.0030107000000043627"/>
<node id="63" version = "3" lat="0.03261674999999897" lon="0.0033764000000076066"/>
<node id="64" version = "3" lat="0.0329647400000006" lon="0.0034947000000045136"/>
<node id="65" version = "3" lat="0.0332977200000002" lon="0.003596799999996847"/>
<node id="66" version = "3" lat="0.03339370999999858" lon="0.003623899999993796"/>
<node id="67" version = "3" lat="0.033731639999999175" lon="0.0036989999999974543"/>
<node id="68" version = "3" lat="0.034010550000001416" lon="0.0037420999999966398"/>
<node id="69" version = "3" lat="0.03439137999999886" lon="0.0037741000000011127"/>
<node id="70" version = "3" lat="0.03959140999999988" lon="0.0038167999999956237"/>
<node id="71" version = "3" lat="0.03992921000000038" lon="0.0038167000000015605"/>
<node id="72" version = "3" lat="0.042663650000001496" lon="0.003837500000003047"/>
<node id="73" version = "3" lat="0.0437900099999986" lon="0.003848399999995422"/>
<node id="74" version = "3" lat="0.04427772000000019" lon="0.003848399999995422"/>
<node id="75" version = "3" lat="0.04491536000000096" lon="0.0038533999999970092"/>
<node id="76" version = "3" lat="0.04562294000000122" lon="0.003853300000002946"/>
<node id="77" version = "3" lat="0.047692749999999506" lon="0.003864100000001258"/>
<node id="78" version = "3" lat="0.04786465000000106" lon="0.003864100000001258"/>
<node id="79" version = "3" lat="0.049247879999999356" lon="0.003885999999994283"/>
<node id="80" version = "3" lat="0.050540120000000854" lon="0.0038859000000002197"/>
<node id="81" version = "3" lat="0.05170839999999899" lon="0.003863800000004858"/>
<node id="82" version = "3" lat="0.05308055000000067" lon="0.0038365999999996347"/>
<node id="83" version = "3" lat="0.05347729000000001" lon="0.003820500000003335"/>
<node id="84" version = "3" lat="0.05445869000000059" lon="0.0038044000000070355"/>
<node id="85" version = "3" lat="0.054839449999999346" lon="0.0037942999999955873"/>
<node id="86" version = "3" lat="0.055257199999999784" lon="0.003788299999996525"/>
<node id="87" version = "3" lat="0.05605669999999918" lon="0.0037722000000002254"/>
<node id="88" version = "3" lat="0.0564044800000012" lon="0.003761100000005513"/>
<node id="89" version = "3" lat="0.05645845000000094" lon="0.003761100000005513"/>
<node id="90" version = "3" lat="0.056919189999998565" lon="0.003767100000004575"/>
<node id="91" version = "3" lat="0.057096130000001466" lon="0.003794100000007461"/>
<node id="92" version = "3" lat="0.057617019999998575" lon="0.003906299999997032"/>
<node id="93" version = "3" lat="0.058179979999998466" lon="0.004078699999993773"/>
<node id="94" version = "3" lat="0.05859308999999868" lon="0.004288099999996575"/>
<node id="95" version = "3" lat="0.05885119999999944" lon="0.004444399999997017"/>
<node id="96" version = "3" lat="0.059237419999998764" lon="0.004712900000001241"/>
<node id="97" version = "3" lat="0.0598817899999986" lon="0.005163800000005381"/>
<node id="98" version = "3" lat="0.06001587000000086" lon="0.005256000000002814"/>
<node id="99" version = "3" lat="0.060455130000001134" lon="0.005567600000006223"/>
<node id="100" version = "3" lat="0.06072427999999874" lon="0.005756000000005201"/>
<way id="1" version = "3" >
<nd ref="1"/>
<nd ref="2"/>
<nd ref="3"/>
<nd ref="4"/>
<nd ref="5"/>
<nd ref="6"/>
<nd ref="7"/>
<nd ref="8"/>
<nd ref="9"/>
<nd ref="10"/>
<nd ref="11"/>
<nd ref="12"/>
<nd ref="13"/>
<nd ref="14"/>
<nd ref="15"/>
<nd ref="16"/>
<nd ref="17"/>
<nd ref="18"/>
<nd ref="19"/>
<nd ref="20"/>
<nd ref="21"/>
<nd ref="22"/>
<nd ref="23"/>
<nd ref="24"/>
<nd ref="25"/>
<nd ref="26"/>
<nd ref="27"/>
<nd ref="28"/>
<nd ref="29"/>
<nd ref="30"/>
<nd ref="31"/>
<nd ref="32"/>
<nd ref="33"/>
<nd ref="34"/>
<nd ref="35"/>
<nd ref="36"/>
<nd ref="37"/>
<nd ref="38"/>
<nd ref="39"/>
<nd ref="40"/>
<nd ref="41"/>
<nd ref="42"/>
<nd ref="43"/>
<nd ref="44"/>
<nd ref="45"/>
<nd ref="46"/>
<nd ref="47"/>
<nd ref="48"/>
<nd ref="49"/>
<nd ref="50"/>
<nd ref="51"/>
<nd ref="52"/>
<nd ref="53"/>
<nd ref="54"/>
<nd ref="55"/>
<nd ref="56"/>
<nd ref="57"/>
<nd ref="58"/>
<nd ref="59"/>
<nd ref="60"/>
<nd ref="61"/>
<nd ref="62"/>
<nd ref="63"/>
<nd ref="64"/>
<nd ref="65"/>
<nd ref="66"/>
<nd ref="67"/>
<nd ref="68"/>
<nd ref="69"/>
<nd ref="70"/>
<nd ref="71"/>
<nd ref="72"/>
<nd ref="73"/>
<nd ref="74"/>
<nd ref="75"/>
<nd ref="76"/>
<nd ref="77"/>
<nd ref="78"/>
<nd ref="79"/>
<nd ref="80"/>
<nd ref="81"/>
<nd ref="82"/>
<nd ref="83"/>
<nd ref="84"/>
<nd ref="85"/>
<nd ref="86"/>
<nd ref="87"/>
<nd ref="88"/>
<nd ref="89"/>
<nd ref="90"/>
<nd ref="91"/>
<nd ref="92"/>
<nd ref="93"/>
<nd ref="94"/>
<nd ref="95"/>
<nd ref="96"/>
<nd ref="97"/>
<nd ref="98"/>
<nd ref="99"/>
<nd ref="100"/>
<tag k="highway" v="tertiary"/>
</way>
</osm>
这里直接使用carla自带的osm转xodr程序 PythonAPI\util\osm_to_xodr.py
python osm_to_xodr.py -i x3.osm -o x3.xodr