kumitatepazuru's blog

中学生のメモブログ。みんなの役に立ちたい。

soccerAIのプログラムレベルのお話(α0.1.0)

f:id:kumitatepazuru:20200614125955p:plain soccerai α0.1.0を公開したので解説。

注意

今回から、sexpdataとtensorflowというライブラリを使います。 実行前に

pip install -r requirement.txt

を実行してください。 sexpdataについてはこちら。

kumitatepazuru.hatenablog.com

大きな変更箇所

  • rewardの表示方法をmatplotlibから、tensorboardに変更

matplotlibが使いづらいと感じたので変更(メモリ使って少し重かったし)。 こちらのサイトを参考にしてclassを改造した。

gokids.hatenablog.com

  • hearメッセージを表示しないようにした

邪魔だったので止めました。

  • 引数を追加

いろいろ便利な引数を追加した。 説明は以下の通り。

名前 既定値 説明
special_division True Q tableのindexをボールの距離に応じて変更するかどうか。普通はキックできる場所になったときのみ変更するシステムにしている。
special_division_value 1 上記のボールの距離を設定する
damage_reset_ball_distance 5 今回から追加されたdamageシステムのリセットするボールの距離を指定。不具合あり。
num 11 登録する選手数を設定して、報酬が同じになるようにするためのもの。
actions 6 actionの数を指定。
  • goal情報を入手するようにした

メモリ使用量がかなり減ったのでgoal情報も取得するようにしてゴール報酬を設定した。

  • repeat関数を削除

前回のバージョンで首がもげる問題が解消されていたらしいのでなくした。

  • action数を大幅削減、reward関数を一から作成

今回のバージョンから、ボールが見えるようになって、前のreward関数が非効率になったため変更。不具合あり。

  • ボールキックの判定を良くした

解説

rewardの表示方法をmatplotlibから、tensorboardに変更

自分のプログラムの該当部分はこちら。

github.com

class TensorBoardLogger(object):

    def __init__(self, log_dir, session=None):
        self.log_dir = log_dir
        self.writer = tf.summary.FileWriter(self.log_dir)
        self.episode = 0

        self.session = session or tf.get_default_session() or tf.Session()

    def log(self, logs=None):
        # scalar logging
        if logs is None:
            logs = {}
        for name, value in logs.items():
            summary = tf.Summary()
            summary_value = summary.value.add()
            summary_value.simple_value = value
            summary_value.tag = name
            self.writer.add_summary(summary, self.episode)

        self.writer.flush()
        self.episode += 1

上記のサイトのhistgram部分を消して、短くした。

github.com

self.logger1 = TensorBoardLogger(log_dir='./logdir/reward')
self.logger2 = TensorBoardLogger(log_dir='./logdir/see')
self.logger3 = TensorBoardLogger(log_dir='./logdir/action')
self.logger4 = TensorBoardLogger(log_dir='./logdir/damage')

ここで初期設定をして(ログの保存先を指定)

github.com

self.logger1.log(logs=dict(reward=args[0]))
self.logger2.log(logs=dict(reward=args[1]))
self.logger3.log(logs=dict(reward=args[2]))
self.logger4.log(logs=dict(reward=args[3]))

ここで、ログを実際に書き込みしている。

tensorboardはコマンドで呼び出すのでstart.shにtensorboard --logdir=./logdirを入れている。

goal情報を入手するようにした

github.com

goal_info = self.see_analysis(rec_raw, ["g", self.enemy])

実際に使っている場所

github.com

# ゴールが見えているか
if goal_info is None:
    goal_info = [self.max_goal_distance + 1, self.max_goal_angle + 1]
else:
    goal_info = list(map(lambda n: float(n), goal_info))
    if goal_info[0] > self.max_goal_distance:
        goal_info = [self.max_goal_distance, goal_info[1]]

see_analysisの使い方は以下のwikiを参照してください。

kumitatepazuru.hatenablog.com

action数を大幅削減、reward関数を一から作成

github.com

def step(self, action):
        # actionから移動をして報酬を計算するプログラム
        # self.goal_damage += 0.01
        reward = 0
        reward_see = 0
        reward_action = 0
        tmp = False
        self.ball_damage += 11 / self.num * 0.1
        if random.random() < self.noise:
            end_action = action
        else:
            action = random.randrange(0, self.actions)
            end_action = action
        if action == 0:
            self.send_dash(100)
        elif action == 1:
            self.send_turn(10)
        elif action == 2:
            self.send_turn(-10)
        elif action == 3:
            self.send_turn(30)
        elif action == 4:
            self.send_turn(-30)
        elif action == 5:
            self.send_kick(100, 0)

        """==========reward setting=========="""
        # 見えているものを確認する
        rec = {"type": None}
        rec_raw = []
        while rec["type"] != "see":
            rec_raw = self.recieve_msg()
            rec = self.msg_analysis(rec_raw)
            if rec["type"] == "hear":
                # ゴールしたときの報酬(できたらめっちゃ褒める)
                if rec["contents"][:6] == "goal_" + self.team:
                    self.send_move(-10, -10)
                    reward_action += 1000 * int(rec["contents"][7])
                    self.up_reward += 100
                    print("GOAL!", reward_action, "time:", rec["time"])
                    tmp = True
            elif rec["type"] == "sense_body":
                if self.kickcount < int(rec["value"][4][1]):
                    print("kick!!!!", "time:", rec["time"])
                    reward_action += 100
                    self.kickcount = int(rec["value"][4][1])

        ball_info = self.see_analysis(rec_raw, "b")
        goal_info = self.see_analysis(rec_raw, ["g", self.enemy])

        # ボールが見えてるか
        if ball_info is None:
            ball_info = [self.max_ball_distance + 1, self.max_ball_angle + 1]
        else:
            ball_info = list(map(lambda n: float(n), ball_info))
            if ball_info[0] > self.max_ball_distance:
                ball_info = [self.max_ball_distance, ball_info[1]]
            elif ball_info[0] < self.damage_reset_ball_distance:
                self.ball_damage = 0
            # 距離に応じた報酬をもらう
            reward_see += 80 - (ball_info[0] * 2)

            if ball_info[0] < self.special_division_value:
                ball_info = [self.special_division, ball_info[1]]
                # reward_see += 25

        if action == 5:
            if ball_info[0] < self.special_division_value:
                reward_action += 100
            else:
                reward_action -= 100

        # ゴールが見えているか
        if goal_info is None:
            goal_info = [self.max_goal_distance + 1, self.max_goal_angle + 1]
        else:
            goal_info = list(map(lambda n: float(n), goal_info))
            if goal_info[0] > self.max_goal_distance:
                goal_info = [self.max_goal_distance, goal_info[1]]

        # ログの追加
        reward_see += self.up_reward
        reward_action += self.up_reward
        reward += reward_see + reward_action - self.ball_damage
        self._update(reward, reward_see, reward_action, self.ball_damage * -1)
        """=========ここまで==========="""
        return (int(ball_info[0] / self.ball_distance_interval), int(ball_info[1] / self.ball_angle_interval),
                int(goal_info[0] / self.goal_distance_interval), int(goal_info[1] / self.goal_angle_interval)
                ), reward, end_action

詳しい説明は割愛。

ボールキックの判定を良くした

github.com

elif rec["type"] == "sense_body":
                if self.kickcount < int(rec["value"][4][1]):
                    print("kick!!!!", "time:", rec["time"])
                    reward_action += 100
                    self.kickcount = int(rec["value"][4][1])

sense_bodyメッセージの中のkickcountが前回と変わっていたらキックしたということにするようにした。

最後に

今回は報酬設定関係がメイン。


個人的な質問等はこちらまで。

https://forms.gle/V6NRhoTooFw15hJdA

また、自分が参加しているRobocup soccer シミュレーションリーグのチームでは参加者募集中です!活動の見学、活動に参加したい方、ご連絡お待ちしております!

詳しくはこちら

kumitatepazuru.github.io