DAI2020  SMARTS自动驾驶挑战赛(强化学习)总结+前三名思路

早在半个多月前,华为诺亚方舟举办的DAI2020 SMARTS自动驾驶挑战赛已经结束了,一直没写总结,一方面是想等前五名的汇报ppt发出来,可是官方一直没有公布ppt,我也就拿当时参加DAI会议的录制视频作参考写了总结。

虽然比赛结果很可惜,没有拿到前五获奖,只取得了第七,但好歹也花费了2个月时间,不写点总结都对不起自己2个月的努力。

比赛介绍

比赛背景

为了推动自动驾驶的前沿研究,激发对驾驶中多智能体交互的研究,在今年的分布式人工智能大会(DAI 2020)上,华为诺亚方舟实验室和上海交通大学APEX数据与知识管理实验室联合举办自动驾驶挑战赛(DAI-2020 Autonomous Driving Competition),旨在以自动驾驶中的交互难题,激励人工智能算法和模型创新,助力强化学习(RL)在自动驾驶领域的应用,提升自动驾驶的决策性能。

比赛环境

基于华为自主研发的SMARTS (Scalable Multi-agent Reinforcement Learning Training School) 自动驾驶仿真平台。SMARTS作为首个支持MARL的自动驾驶仿真平台,将提供Simulator Core(快速且灵活地创建RL模拟环境)、Algorithm Library(集成主流的强化学习算法)、Multi-Agent Trainer(支持大多数多智能体训练范式)、Policy Zoo(支持对社会车辆的实例化)和 Scenario Studio(支持灵活的场景设置),方便参赛者在比赛过程中实现对车辆动力学行为的真实建模,并利用丰富的交通场景进行研究和应用。

华为诺亚方舟实验室在比赛后也开源了环境代码,有兴趣的可以去查看他们的官方

以及他们的论文SMARTS: Scalable Multi-Agent Reinforcement Learning Training School for Autonomous Driving

比赛内容

我参加的是单智能体赛道,在单智能体赛道中,聚焦单智能体多车道巡航,参赛者需要训练模型控制一辆车完成复杂城市道路和车流下的智能驾驶。场景中包含直行道、路口、匝道、环岛等。车辆需要遵循预设的路线,在保证安全的前提下,尽可能快的从起点出发达到终点。比赛总共给出了9张地图,作为训练集,最后的提交结果还会有不同的测试地图,每张地图用4个不同的随机种子进行测试。

以下是官方demo给的状态空间和动作空间设计

状态空间

# To make car follow the waypoints
# distance from lane center
"distance_from_center": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# relative heading angle from 10 waypoints in 50 forehead waypoints
"heading_errors": gym.spaces.Box(low=-1.0, high=1.0, shape=(10,)),
# Car attributes
# ego speed
"speed": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# ego steering
"steering": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# To make car learn to slow down, overtake or dodge
# distance to the closest car in each lane
"lane_dist": gym.spaces.Box(low=-1e10, high=1e10, shape=(5,)),
# time to collide to the closest car in each lane
"lane_ttc": gym.spaces.Box(low=-1e10, high=1e10, shape=(5,)),
# ego lane closest social vehicle relative speed
"closest_lane_nv_rel_speed": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# distance to the closest car in possible intersection direction
"intersection_ttc": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# time to collide to the closest car in possible intersection direction
"intersection_distance": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# intersection closest social vehicle relative speed
"closest_its_nv_rel_speed": gym.spaces.Box(low=-1e10, high=1e10, shape=(1,)),
# intersection closest social vehicle relative position in vehicle heading coordinate
"closest_its_nv_rel_pos": gym.spaces.Box(low=-1e10, high=1e10, shape=(2,)),

共有11个状态,第一个状态表示自己距离最近的车道中心点距离,第二个状态表示车前50个waypoints中选取10个,计算他们的朝向与自己朝向的差,第3个状态和第4个状态分别为当前速度和方向盘状态,第5,6个状态前方车道信息,最大为5条车道,分别计算该车道最近车辆离自己的距离以及发生碰撞所需的时间,共10个量,如果小于5条车道,对应2个指标分别取0,第7个状态量为离自己最近车辆的相对速度,第8-11状态分别是交叉路口信息,分别为可能的交叉方向上最近车辆发生碰撞需要的时间,距离,相对速度和相对位置。

动作空间

ACTION_SPACE = gym.spaces.Box(
    low=np.array([0.0, 0.0, -1.0]), high=np.array([1.0, 1.0, 1.0]), dtype=np.float32
)

原始的连续动作空间有三个量,包含有油门[0, 1],刹车[0, 1],方向盘[-1, 1]

还有别的动作空间供使用,具体可以看安装包里的文档

下面写一些我做这个问题时的想法

在做的过程中为了减少无用的探索空间,我们考虑到不应该同时踩刹车和油门,于是将刹车和油门合并为一个量,只需要写相应的动作适配器

def action_adapter(model_action):
    assert len(model_action) == 2
    throttle = np.clip(model_action[0], 0, 1)
    brake = np.abs(np.clip(model_action[0], -1, 0))
    return np.asarray([throttle, brake, model_action[1]])

动作空间改为

ACTION_SPACE = gym.spaces.Box(
    low=np.array([-1.0, -1.0]), high=np.array([1.0, 1.0]), dtype=np.float32
)

在比赛中我考虑过以不同的车道数作为分支条件,训练时切换不同的分支进行训练,最后训练结果上看虽然reward看上去还不错,但提交的结果都不太好。

后来也经过了不同的尝试,发现gamma设置为0.999时的结果比原始的0.99稳定些,虽然速度整体上慢一些,但更容易避免碰撞,提交的结果也好些。

以及使用dual clip 的效果还不错,考虑到可能是太多即将碰撞时的惩罚奖励干扰了reward,对负优势值的舍去有一定作用。

pos1 = torch.where(logp_ratio<0)
    surrogate_loss1 = torch.max(torch.min(
        advantages[pos1] * logp_ratio[pos1],
        advantages[pos1] * torch.clamp(logp_ratio[pos1], 1 - clip_param,
                                 1 + clip_param)),
        advantages[pos1] * (1 + clip_param))
    pos2 = torch.where(logp_ratio>=0)                    
    surrogate_loss2 = torch.min(
        advantages[pos2] * logp_ratio[pos2],
        advantages[pos2] * torch.clamp(logp_ratio[pos2], 1 - clip_param,
                                 1 + clip_param))                                    
    surrogate_loss = torch.cat([surrogate_loss1, surrogate_loss2], dim=0)

以及一些其他的尝试。

这里有一些我最后训练比较不错的

https://video.zhihu.com/video/1309097440550260736?player=%7B%22shouldShowPageFullScreenButton%22%3Atrue%7D

DAI2020 smarts 自动驾驶单智能体
启人zhr 的视频
 · 991 播放

值得一提的是,官方给出的demo中计算交叉口的状态量出现了错误,在比赛后我也在

中指出了,看来这篇baseline论文的准确性有待商榷。(⊙o⊙)

以下是比赛前五名汇报时的做法记录

1.CASIA DRL Team (中科院自动化所)

  • Observation

  • Road Recognition

首先通过lane heading errors(=waypoint_headingi-waypoint_heading0) 和 wp_paths首先划分5种道路种类。

  • Reawrd Design

  • Continuous Training

在所有的地图上训练一个元策略,然后根据不同的奖励比重继续训练出三种不同的子策略

  • Policy Switch

根据一些规则判断以及前面定义的道路标示切换到不同的子策略,他们发现这样切换不同的子策略比单独的策略表现优异

  • Action Revision

最后再对一些不同条件下定义一些特殊的条件切换策略

总结一下,首先使用使用所有的地图训练出一个元策略,然后根据不同的奖励设置训练出不同的子策略,然后通过定义的规则根据前面50个waypoints判断出道路形状以及前后周围密度决定切换的子策略,最后还有一些紧急制动的规则作为应急处理。

有意思的是,在比赛结束前几天第一名加了我好友,我没太在意,以为哪个比我分数差的,后来最后几天随口问了一下对方,哪知道是第一名,现在人都这么扮猪吃虎了么(⊙o⊙)

2. alombard 法国utbm

第二名是一个法国人,口音发音使我我不太能理解他的方法,不过大概是一种model-based 的策略

  • General architecture

  • Lateral control

  • Longitudinal control

  • Decision making -- lane change

  • Decision making -- Intersection

3. Wait a Minute (南大LAMDA+清华THICV)

  • Observation

  • Reward

  • Time-variant Training Strategy

训练的不同迭代数使用不同的奖励比重

  • Knowledge-based Guarantee

最后根据不同的危险条件限制最大刹车和油门

第四和第五名的算法感觉上大多在调参上,就没有放出来了,想看的可以去看我录的视频

演讲视频我传到了