ブログ名

2019年10月のVirtual Raceの結果 −前半−

本記事の概要

10月で2019年のDeepRacerのVirtual Raceが終了しました。
先に結果を述べると、最終的な2019年の10月のVirtual Raceの結果で最高順位52位でした。
本記事、そして次の記事では、2019年の10月のVirtual Raceで試行錯誤した結果を報告させていただきたいと思います。

10月(2019年)のVirtual Raceのコース

10月(2019年)のVirtual Raceのコースは下のようなToronto Turnpikeというコースです。

f:id:iTD_GRP:20191211021627p:plain

Virtual Raceでは、上のように学習用のコースと実際にVirtual Raceの大会でのコースが違います。
そのため、過学習には気をつける必要があります。

そして今回から最大速度が12m/sまで出せるようになっています。

DeepRacerでの結果

コースに対応したモデルの作成

Virtual Raceのコースを見る限り、急カーブが多いように見えます。

そのためまず、以下のようにストレートとカーブで場合分けをした報酬関数を作成して学習してみました。

def reward_function(params):
    import  math
    DIF = 20
    STRAIGHT = 0
    CURVE = 1
    target_waypoint = 0
    track_directions = []
    waypoints = params['waypoints']
    closest_waypoints = params['closest_waypoints']
    all_wheels_on_track = params['all_wheels_on_track']
    speed = params['speed']
    track_waypoints = [waypoints[closest_waypoints[1]]]
    steering = abs(params['steering_angle'])
    heading = params['heading']

     # ストレート、カーブで場合分け
    for i in range(1, DIF):
        if closest_waypoints[1] + i >= len(waypoints):
            next_waypoint = waypoints[closest_waypoints[1] + i - len(waypoints)]
            track_waypoints.append(waypoints[closest_waypoints[1] + i - len(waypoints)])

        else:
            next_waypoint = waypoints[closest_waypoints[1] + i]
            track_waypoints.append(waypoints[closest_waypoints[1] + i])

        if closest_waypoints[1] + i - 1 >= len(waypoints):
            prev_waypoint = waypoints[closest_waypoints[1] + i - 1 - len(waypoints)]
        else:
            prev_waypoint = waypoints[closest_waypoints[1] + i - 1]


        track_direction = math.atan2(next_waypoint[1] - prev_waypoint[1], next_waypoint[0] - prev_waypoint[0])
        track_directions.append(track_direction)
     #もっと遠くのポイントと比較
    if track_directions[15] - track_directions[0] < 0.28:
        direction_type = STRAIGHT
        reward = 1e-3
       # Set the speed threshold based your action space
        SPEED_THRESHOLD = 12.0


        if  all_wheels_on_track:
            # 12m/sよりも遅かったら
            reward = 0.7
            if speed > SPEED_THRESHOLD:
                # 12 m/s だったら
                reward = 1.0

        # ジグザグ防止
        ABS_STEERING_THRESHOLD = 15
        if steering > ABS_STEERING_THRESHOLD:
            reward *= 0.8
    else:

        reward = 1e-3

        if all_wheels_on_track:
            reward = 1.0
        #道と同じ方向じゃなかったらペナルティ
        direction_diff = abs(track_directions[0] - heading)
        if direction_diff > 180:
            direction_diff = 360 - direction_diff

        # Penalize the reward if the difference is too large
        DIRECTION_THRESHOLD = 10.0
        if direction_diff > DIRECTION_THRESHOLD:
            reward *= 0.5

    return float(reward)

そして、長いストレートからのカーブに対応できるようにアクションスペースを広めに設定しました。

f:id:iTD_GRP:20191211021653p:plain

その結果、以下のような評価結果になり、5回走っても完走できませんでした。

また、ログ等を見た結果、以下のようなことを感じたため、ジグザク走行を抑制する報酬関数で5時間学習した後、目標ステップ数より少ないステップ数で走行できた場合に報酬を与える関数で1時間学習しました。また、その際のアクションスペースでは速度の最大値を6m/sで学習しました。

  • ジグザグ走行を抑制するため、大きなステアリングにはペナルティを与えていたため、余計なカーブが少ない。
  • 速度については触れていないためよりたくさんの報酬を得るためにステップを多くしてしまい, 直線でも低速で走行してしまっている。
def reward_function(params):
    '''
    Example of penalize steering, which helps mitigate zig-zag behaviors
    '''

    # Read input parameters
    distance_from_center = params['distance_from_center']
    track_width = params['track_width']
    steering = abs(params['steering_angle']) # Only need the absolute steering angle


    # Calculate 3 markers that are at varying distances away from the center line
    marker_1 = 0.1 * track_width
    marker_2 = 0.25 * track_width
    marker_3 = 0.5 * track_width

    # Give higher reward if the agent is closer to center line and vice versa
    if distance_from_center <= marker_1:
        reward = 1
    elif distance_from_center <= marker_2:
        reward = 0.5
    elif distance_from_center <= marker_3:
        reward = 0.1
    else:
        reward = 1e-3  # likely crashed/ close to off track

    # Steering penality threshold, change the number based on your action space setting
    ABS_STEERING_THRESHOLD = 15

    # Penalize reward if the agent is steering too much
    if steering > ABS_STEERING_THRESHOLD:
        reward *= 0.8

    return float(reward)
def reward_function(params):

    # Read input parameters
    track_width = params['track_width']
    distance_from_center = params['distance_from_center']
    steps = params['steps']
    progress = params['progress']

    print('progress: %.2f' % progress)
    print('steps: %d' % steps)

        # ここに目標のステップ数
    TOTAL_NUM_STEPS = 270



    # Calculate 3 markers that are at varying distances away from the center line
    marker_1 = 0.1 * track_width
    marker_2 = 0.25 * track_width
    marker_3 = 0.5 * track_width

    # Give higher reward if the car is closer to center line and vice versa
    if distance_from_center <= marker_1:
        reward = 1.0
    elif distance_from_center <= marker_2:
        reward = 0.5
    elif distance_from_center <= marker_3:
        reward = 0.1
    else:
        reward = 1e-3  # likely crashed/ close to off track

            # Give additional reward if the car pass every 100 steps faster than expected
    if (steps % 100) == 0 and progress > (steps / TOTAL_NUM_STEPS) * 100 :
        reward += 10.0


    print('reward: %.2f' % reward)
    return float(reward)

しかし、結果としては最大速度で走っている部分はむしろ減少していたため、学習したモデルをクローンし、今度は上述ストレートとカーブの場合分けをして報酬を与えるようにしました。

しかし、ログを出力した結果、以下のようになり、緩いカーブでもカーブと認識されてしまい、スピードが落ちてしまっていました。

f:id:iTD_GRP:20191211021719p:plain

そのため今度は、現在地と次のwaypointとの角度からカーブを予測し、カーブとストレートの場合分けを行ってみました。

その結果画像のように、現在地から2ポイント先のwaypointとの角度が0.2radのときを閾値として場合分けしたものが最も分類できているように思えました。

def reward_function(params):
    import  math
    DIF = 20
    STRAIGHT = 0
    CURVE = 1
    target_waypoint = 0
    track_directions = []
    waypoints = params['waypoints']
    closest_waypoints = params['closest_waypoints']
    all_wheels_on_track = params['all_wheels_on_track']
    speed = params['speed']
    track_waypoints = [waypoints[closest_waypoints[1]]]

    track_width = params['track_width']
    distance_from_center = params['distance_from_center']
    half_track_width = track_width * 0.5
        # Calculate 3 markers that are at varying distances away from the center line
    marker_1 = 0.1 * track_width
    marker_2 = 0.25 * track_width
    marker_3 = 0.5 * track_width
    max_speed= 12

    #ログで見れるようにprint()追加
    print('closest_waypoints1: %d' % closest_waypoints[0])
    print('closest_waypoints2: %d' % closest_waypoints[1])
     # ストレート、カーブで場合分け
    for i in range(1, DIF):
        if closest_waypoints[1] + i >= len(waypoints):
            next_waypoint = waypoints[closest_waypoints[1] + i - len(waypoints)]
            track_waypoints.append(waypoints[closest_waypoints[1] + i - len(waypoints)])

        else:
            next_waypoint = waypoints[closest_waypoints[1] + i]
            track_waypoints.append(waypoints[closest_waypoints[1] + i])

        if closest_waypoints[1] + i - 1 >= len(waypoints):
            prev_waypoint = waypoints[closest_waypoints[1] + i - 1 - len(waypoints)]
        else:
            prev_waypoint = waypoints[closest_waypoints[1] + i - 1]


        track_direction = math.atan2(next_waypoint[1] - prev_waypoint[1], next_waypoint[0] - prev_waypoint[0])
        track_directions.append(track_direction)
     #遠くのポイントと比較
    if track_directions[2] - track_directions[0] < 0.2:
        #ストレートの時
        direction_type = STRAIGHT
        #ログで見れるようprint()を追加
        print('STRAIGHT')
        reward = 1e-3

        if  all_wheels_on_track:
            #速度が速いほどいい, トラックの真ん中に近いほどいい.       
            reward = (half_track_width - distance_from_center) / half_track_width
            reward += speed / max_speed


    else:
        #ログで見れるようにprint()を追加
        print('CURVE')

                # Give higher reward if the car is closer to center line and vice versa
        if distance_from_center <= marker_1:
            reward = 1.0
        elif distance_from_center <= marker_2:
            reward = 0.5
        elif distance_from_center <= marker_3:
            reward = 0.1
        else:
            reward = 1e-3  # likely crashed/ close to off track

    return float(reward)

f:id:iTD_GRP:20191211021737p:plain

この閾値を用いて、以下のような報酬関数、アクションスペースで実行したところ、6時間の学習で16.317s出すことができました。

しかし、トップは7秒台なため、さらにタイムの向上が必要になります。

アクションスペースを少なくしたモデルの作成

次に、アクションスペースを少なくしてみました。

今月のコースはストレートと大きなカーブがメインなため、アクションスペースを小さくして、直線か曲がるかの2択にすることで、カーブではしっかり曲がり、それ以外ではできるだけ直線で走れるようなモデルを目指そうと考えました。

そのため、報酬関数とアクションスペースは以下のように設定しました。

import math
def reward_function(params):
    # Read input parameters
    is_left = params['is_left_of_center']
    track_width = params['track_width']
    quarter_track_width = track_width / 4
    distance_from_center = params['distance_from_center']
    progress = params["progress"]
    steps = params["steps"]
    all_wheels_on_track = params['all_wheels_on_track']
    steer = params["steering_angle"]
    # waypointsの取得
    waypoints = params['waypoints']
    closest_waypoints = params['closest_waypoints']
    heading = params['heading']

    # 前のwaypointsと次のwaypointsを取得
    next_point = waypoints[closest_waypoints[1]]
    prev_point = waypoints[closest_waypoints[0]]

    reward = 0

    if all_wheels_on_track and distance_from_center > 0 and steps > 0:
        reward += 1/distance_from_center
        reward += progress / steps

        # 次の角度を計算        
        # 前のwaypointから次のwaypointに向かう角度(radian)を計算する
        track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0])
        # degreeに変換
        track_direction = math.degrees(track_direction)
        # コース上の基準軸に対する車体の向きと直近のwaypointを繋ぐ向きの差分を取る
        direction_diff = track_direction - heading

        # 30度ある→曲がれるのでステアリングさせる
        if (direction_diff > 30 and not is_left) and steer > 0:
            reward += 20
        elif (direction_diff < -30 and is_left) and steer < 0:
            reward += 20
        # 30度未満→曲がれない可能性あるので、中心線沿っているものに報酬
        elif abs(direction_diff) < 30 and distance_from_center < quarter_track_width:
            reward += 20

    else :
        reward += 1e-5

    return float(reward)

f:id:iTD_GRP:20191211021818p:plain

この報酬関数では、基本的にタイヤがトラックの上にあるときに報酬を与えるようにして、車体が直前に通過したwaypointと次のwaypointとの角度(現在走行しているトラックの角度)と車体の角度を計算し、その角度とコースの車体の位置によって報酬を調整するように設定しています。

この関数で6時間学習したところ、学習結果と評価結果は以下のようになりました。

f:id:iTD_GRP:20191211021829p:plain

f:id:iTD_GRP:20191211021837p:plain

学習の結果のグラフのピンク色のグラフ(完走率)は上昇しているので、学習は進んでいるように見えるのですが、結果は3回走って完走できませんでした。

その後さらに4時間学習させたのですが、完走率はさらに下がってしまい、少し速度を意識しすぎたのと、報酬関数に偏りが大きすぎた可能性があるかなと思いました。

まとめ

今回は2019年10月のVirtual Raceで試行錯誤した結果を報告させていただきました。

次回は52位になったモデルも含めてこの続きを報告させていただきたいと思います。


次の記事へ

前の記事へ 目次へ戻る