通过carla.map可以获得当前地图的信息,包括道路的信息和航向点的管理的类函数。
# 创建一个空图
G = nx.Graph()
for i in range(len(topology)):
waypoint1 = topology[i][0]
waypoint2 = topology[i][1]
waypoint1_info = "%s-%s-%s" % (waypoint1.road_id, waypoint1.section_id, waypoint1.lane_id)
waypoint2_info = "%s-%s-%s" % (waypoint2.road_id, waypoint2.section_id, waypoint2.lane_id)
G.add_edge(waypoint1_info, waypoint2_info)
# nx.draw_networkx(G)
# plt.show()
建立有向图带权重
G = nx.DiGraph()
for i in range(len(topology)):
waypoint1 = topology[i][0]
waypoint2 = topology[i][1]
waypoint1_info = "%s-%s-%s" % (waypoint1.road_id, waypoint1.section_id, waypoint1.lane_id)
waypoint2_info = "%s-%s-%s" % (waypoint2.road_id, waypoint2.section_id, waypoint2.lane_id)
distance = math.sqrt(math.pow((waypoint2.transform.location.x - waypoint1.transform.location.x), 2) +
math.pow((waypoint2.transform.location.y - waypoint1.transform.location.y), 2))
# G.add_edge(waypoint1_info, waypoint2_info)
G.add_weighted_edges_from([(waypoint1_info, waypoint2_info, distance)])
之后使用A*算法即可得到从起点到终点的路径。
path = nx.astar_path(G, "8-0--2", "483-0-4")
通过地图给出的拓扑结构,以及networkx的最短路径规划功能可以实现一个简单的路径规划,给定两点的Transform,上述所获得的拓扑图G,键值对信息{key(道路名称string):velue(航向点carla.Waypoint)},以及地图信息即可获得从起始点到终点的路径规划。注:没有验证是最短路径规划
# 输入起始点和终点的transform,拓扑图,航向点键值对,以及地图,输出从起始点到终点的路径规划
def generate_routelist(start, end, G, waypoints_info, _map):
start_waypoint = _map.get_waypoint(start.location)
start_info = "%s-%s-%s" % (start_waypoint.road_id, start_waypoint.section_id, start_waypoint.lane_id)
end_waypoint = _map.get_waypoint(end.location)
end_info = "%s-%s-%s" % (end_waypoint.road_id, end_waypoint.section_id, end_waypoint.lane_id)
path = nx.astar_path(G, start_info, end_info)
print(path)
# print(path)
RouteList = []
list_to_end = start_waypoint.next_until_lane_end(0.25)
for j in range(len(list_to_end)):
temp_waypoint = list_to_end[j]
temp_x = temp_waypoint.transform.location.x
temp_y = temp_waypoint.transform.location.y
RouteList.append([temp_x, temp_y])
for i in range(1, len(path)):
_waypoint = waypoints_info[path[i]]
list_to_end = _waypoint.next_until_lane_end(0.25)
for j in range(len(list_to_end)):
temp_waypoint = list_to_end[j]
temp_x = temp_waypoint.transform.location.x
temp_y = temp_waypoint.transform.location.y
RouteList.append([temp_x, temp_y])
return RouteList
carla中的航向点,为一个3D的坐标,其中包括carla.Transform具有表示车帘位置的Location信息和表示车辆方向的Rotation信息,同时存储该点与其有关车道的道路信息。
十分方便的一个工具包,里面包括了在地图中描点,画箭头、线段,方框的函数
在获得世界信息后使用
debug = world.debug
即可得到实例化类型
通过此类,可以获得一个世界对象,world中包括我们要使用的carla.map和carla.DebugHelper。
此外我们可以对该对象进行设置,包括同步或者异步的属性,通过carla.WorldSettings对象可以对world对象进行属性设置。
如果我们要在carla中使用强化学习,那么将世界设置为同步模式是一个十分方便的设置,在同步模式中client与sever的通信使用tick来校准,因此整个过程便成为收集一次world场景中的信息做一次操作,而在此操作的过程中world不会有变化。
同步模式中需要使用carla.World.tick() 向服务器发送确认信息,并且从服务器返回一个新的帧的ID信息,通过此ID信息我们可以对获得信息进行校准。
在执行world.tick()时会自动调用world.on_tick()函数,world.on_tick()的参数包括callback,在初始化时可以将回调函数传入,在每次tick()时便会自动执行回调函数中的操作,on_tick()将一个carla.WorldSnapshot 传入此回调函数。通过回调函数我们可以获得此帧下世界的具体信息,更新所有的参数。例如下面的一个carla官方给的类函数
class CarlaSyncMode(object):
def __init__(self, world, *sensors, **kwargs):
self.world = world
self.sensors = sensors
self.frame = None
self.delta_seconds = 1.0 / kwargs.get('fps', 20)
self._queues = []
self._settings = None
def __enter__(self):
self._settings = self.world.get_settings()
self.frame = self.world.apply_settings(carla.WorldSettings(
no_rendering_mode=False,
synchronous_mode=True,
fixed_delta_seconds=self.delta_seconds
))
def make_queue(register_event):
q = queue.Queue()
register_event(q.put)
# register_event(push_snapshot)
self._queues.append(q)
make_queue(self.world.on_tick)
for sensor in self.sensors:
make_queue(sensor.listen)
return self
def tick(self, timeout):
self.frame = self.world.tick()
data = [self._retrieve_data(q, timeout) for q in self._queues]
assert all(x.frame == self.frame for x in data)
return data
def __exit__(self, *args, **kwargs):
self.world.apply_settings(self._settings)
def _retrieve_data(self, sensor_queue, timeout):
while True:
data = sensor_queue.get(timeout=timeout)
if data.frame == self.frame:
return data
当在程序的某一处使用同步模式时便会执行__enter__中的初始化,将world.on_tick()作为事件传入make_queue中,申请一个队列q,将q.put作为回调函数放入on_tick()中,以后每次执行tick()都会执行该操作。
在该类函数的tick()函数中,每次执行world.tick()函数返回的帧ID都被保存在类变量self.frame中,并且以此从队列中取出carla.WorldSnapshot这是该某一帧下这个世界中所有信息的快照即保存此时刻所有物体的状态。之后通过_retrieve_data中判断该数据的frame是否和此时刻的frame相同,并将此时刻的信息返回。通过返回的信息我们可以获得车辆的当前状态即其他参与者的信息。
参考链接:知乎-暨华-CARLA: An Open Urban Driving Simulator
参考论文 > CARLA: An Open Urban Driving Simulator
链接: arxiv
此文章中使用CARLA研究了自动驾驶的三种方法,第一种是经典的modular pipeline,第二种是imitation learning, 第三种是reinforcement learning。
表中的数值为成功率,可以看到三个方法中modular pipeline总体表现更好,RL则更差
参考链接:知乎-暨华-End-to-end Driving via Conditional IL
参考论文 > End-to-end Driving via Conditional Imitation Learning
链接: arxiv
4. 作者在居民区里进行了数据采样。在测试的时候,允许agent错过一次十字路口,不计算missed。上图是测试结果。可以看到不使用data augmentation的话,训练结果非常糟糕。作者在没见过的环境上进行了泛化性测试。效果不错。