首页 > 解决方案 > 自动驾驶汽车没有通过 Q-Learning 改进

问题描述

我正在做一个项目,我试图通过 Python 中的 Q-learning 教汽车如何驾驶。但是我遇到了一个问题,这辆车似乎从来没有学到任何东西(即使在 1000000 集之后)因为我真的不知道我的问题出在哪里,所以我发布了大部分代码(我认为这可能与问题)。

目前,我有一个 Car 类和一个 Game 类作为我的项目结构。该游戏是使用 PyGame 构建的,基本上是一个固定大小为 16px 的网格。为了更快地学习,我们制作了一个简单的碰撞矩阵来节省运行时间,而不是使用精灵碰撞。我还实施了各种奖励系统来鼓励汽车朝特定方向移动,如下图所示。(面包屑和奖励汽车长时间不在同一个位置)

需要注意的一点是,汽车的运动是这样实现的,它不会锁定在网格上(为了更平稳的运动)。但是,通过将位置划分为网格大小,将汽车的位置映射到网格。

游戏截图如下: 汽车游戏

我使用的 q-table 与这个网格大小相同,每个图块上可能的移动量定义如下:

for x in range(0, int(GRIDWIDTH)+1):
            for y in range(0, int(GRIDHEIGHT)+1):
                    q_table[(y,x)] = [np.random.uniform(-5, 0) for i in range(NUMBER_OF_ACTIONS)]

我已经将 NUMBER_OF_ACTIONS 更改为仅让汽车以恒定速度前进。但也让汽车作为一种行动前进。** 值得注意的是,当使用用户输入时,所有这些操作都按预期工作。Car 类中的动作函数是这样写的:

def action(self, choice):
    self.rot_speed = 0
    if choice == 0:
        #self.move(x=1, y=0)
        self.rot_speed = PLAYER_ROT_SPEED
        self.rot = (self.rot + self.rot_speed * self.game.dt)
    elif choice == 1:
        #self.move(x=-1, y=0)
        self.rot_speed = -PLAYER_ROT_SPEED
        self.rot = (self.rot + self.rot_speed * self.game.dt)
    elif choice == 2:
        self.vel = vec(PLAYER_SPEED, 0).rotate(-self.rot + ROTATE_SPRITE_DEG)

    If NUMBER_OF_ACTIONS == 2
    self.vel = vec(PLAYER_SPEED, 0).rotate(-self.rot + ROTATE_SPRITE_DEG)   

    self.pos += self.vel * self.game.dt

Q-learning 算法如下(并不是每集都在循环中调用它)。

def qLearning(self):
#Random Starting Positons on EVERY EPISODE
    StartingPositions = self.playerPositions[np.random.randint(0,len(self.playerPositions))]
    self.player = Player(self, StartingPosition[0] * TILESIZE , StartingPosition[1] * TILESIZE)
    food = self.goal

    episode_reward = 0

#RESET BREADCRUMBS FOR EVERY EPISODE
    for bread in range(len(self.breadCrumb_array)):
        self.wallPositions[self.breadCrumb_array[bread][0]][self.breadCrumb_array[bread][1]] = 3

    self.breadCrumb_array = []
    self.lastPosition = self.player.posToTile
    self.update()
    self.dt = 0.1

    for i in range(ITERATIONS):
        obs = (int(self.player.posToTile.x), int(self.player.posToTile.y))
        if np.random.random() > self.epsilon:
            action = np.argmax(self.q_table[obs])
        else:
            action = np.random.randint(0, NUMBER_OF_ACTIONS)

        self.player.action(action)
        self.update()

        if not LEARNING:
            self.draw()

        if(self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 1):
            self.player.hitWall = True
        elif(self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 2):
            self.player.hitGoal = True
        elif (self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 3):
            self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] = 0
            self.breadCrumb_array.append((int(self.player.posToTile.x),int(self.player.posToTile.y)))
            self.player.hitReward = True


        if self.player.hitWall:
            reward = -DEATH_PENALTY
        elif self.player.hitGoal:
            reward = FOOD_REWARD
        elif self.player.hitReward:
            reward = BREADCRUMB_REWARD
            self.player.hitReward = False
        else:
            reward = -MOVE_PENALTY #+ self.distanceTo((player.pos), food)


        if i % 100 == 0 and not i == 0 and not reward == -DEATH_PENALTY or reward == FOOD_REWARD:
    # Checks how far the distance is between the last position and current.
            distance = self.distanceTo(self.lastPosition, self.player.posToTile)
            self.lastPosition = self.player.posToTile
            if (distance > RADIUS):
                if (distance <= 5):
                    reward += distance
                else:
                    reward += 5


        new_obs = (int(self.player.posToTile.x), int(self.player.posToTile.y))
        max_future_q = np.max(self.q_table[new_obs])

        current_q = self.q_table[obs][action]

        if reward == FOOD_REWARD:
            new_q = FOOD_REWARD
        elif reward == -DEATH_PENALTY:
            new_q = -DEATH_PENALTY
        else:
            new_q = (1 - LEARNING_RATE) * current_q + LEARNING_RATE * (reward + DISCOUNT * max_future_q)

        self.q_table[obs][(action)] = new_q

        episode_reward += reward

        if reward == FOOD_REWARD or reward == -DEATH_PENALTY:
            break

#For plotting later        
self.episode_rewards.append(episode_reward)
self.epsilon *= EPS_DECAY

在运行 q-learning 时,我尝试将所有常量更改为不同的值以获得更好的结果,但是,结果似乎保持不变,即没有学到任何东西。整晚我尝试了以下常量,

ITERATIONS = 5000
HM_EPISODES = 1000000
MOVE_PENALTY = 1
DEATH_PENALTY = ITERATIONS * 2
FOOD_REWARD = ITERATIONS
RADIUS = 10
BREADCRUMB_REWARD = 300
EPS_DECAY = (1 - 1/(HM_EPISODES))

LEARNING_RATE = 0.8 
DISCOUNT = 0.95    
EPSILON_START = 1       

但如下图所示,即使 Epilon 衰减(几乎达到 0),平均结果也永远不会变得更好(甚至更糟)。

剧集奖励图表

到目前为止,尽管有各种奖励系统,我也尝试过使用光线投射,这取决于汽车与墙壁的距离,该迭代的奖励会受到影响,但是这种实现似乎没有任何区别。因此,由于使用精灵碰撞的大量计算时间,我不再使用该代码。

因此,既然我似乎已经尝试了一切,但无论如何都没有成功,我希望你们中的任何人都可以看到我的问题所在。

提前感谢您,我希望我提供了有关该问题的足够信息。

编辑 POST:由于发布了此问题,因此进行了解决。我为使代理按预期工作所做的工作将运动从“类似汽车”更改为块运动。这使代理能够正确学习。因此,如果其他人也遇到过同样的问题,请查看您的运动或您的环境,看看它是否过于复杂。

标签: pythonmachine-learningpygameartificial-intelligenceq-learning

解决方案


推荐阅读