Si no quieres conducir solo, Python puede ayudarte con la conducción automática

1. Entorno de instalación

gym es un conjunto de herramientas para desarrollar y comparar algoritmos de aprendizaje por refuerzo. Es fácil instalar la biblioteca gym y sus subescenarios en python.

Instalar gimnasio:

pip install gym

Instale el módulo de piloto automático utilizando el paquete highway-env publicado en github por Edouard Leurent (enlace: https://github.com/eleurent/highway-env):

pip install --user git+https://github.com/eleurent/highway-env

Contiene 6 escenarios:

  • Autopista - "autopista-v0"

  • Importar - "combinar-v0"

  • Rotonda - "rotonda-v0"

  • Estacionamiento - "estacionamiento-v0"

  • intersección - "intersección-v0"

  • Pista de carreras - "pista de carreras-v0"

La documentación detallada se puede encontrar aquí: https://highway-env.readthedocs.io/en/latest/

En segundo lugar, el entorno de configuración.

Después de la instalación, puede experimentar con el código (tome la escena de la carretera como ejemplo):

import gym
import highway_env
%matplotlib inline

env = gym.make('highway-v0')
env.reset()
for _ in range(3):
    action = env.action_type.actions_indexes["IDLE"]
    obs, reward, done, info = env.step(action)
    env.render()

Después de correr, se generará la siguiente escena en el simulador:

El verde es la clase env del vehículo ego. Hay muchos parámetros que se pueden configurar. Para obtener más información, consulte el documento original.

En tercer lugar, el modelo de formación.

1. Tratamiento de datos

(1) estado

No hay sensores definidos en el paquete Highway-Env, y todos los estados del vehículo (observaciones) se leen del código subyacente, lo que ahorra mucho trabajo inicial. De acuerdo con la documentación, state (ovservations) tiene tres métodos de salida: cinemática, imagen en escala de grises y cuadrícula de ocupación.

Cinemática

Genere una matriz de V*F, donde V representa la cantidad de vehículos que se observarán (incluido el propio vehículo del ego) y F representa la cantidad de características que se contarán. ejemplo:

Cuando se generan los datos, se normalizarán de manera predeterminada. El rango de valores es: [100, 100, 20, 20]. También puede configurar los atributos del vehículo que no sean el vehículo ego para que sean las coordenadas absolutas del mapa o el coordenadas relativas al vehículo del ego.

Al definir el entorno, debe establecer los parámetros de la función:

config = \
    {
    "observation": 
         {
        "type": "Kinematics",
        #选取5辆车进行观察(包括ego vehicle)
        "vehicles_count": 5,  
        #共7个特征
        "features": ["presence", "x", "y", "vx", "vy", "cos_h", "sin_h"], 
        "features_range": 
            {
            "x": [-100, 100],
            "y": [-100, 100],
            "vx": [-20, 20],
            "vy": [-20, 20]
            },
        "absolute": False,
        "order": "sorted"
        },
    "simulation_frequency": 8,  # [Hz]
    "policy_frequency": 2,  # [Hz]
    }

Imagen en escala de grises

Genere una imagen en escala de grises de W*H, donde W representa el ancho de la imagen y H representa la altura de la imagen

Cuadrícula de ocupación

Genere una matriz tridimensional de WHF y use una tabla W*H para representar la situación del vehículo alrededor del vehículo ego, y cada cuadrícula contiene características F.

(2) acción

Las acciones del paquete Highway-Env se dividen en dos tipos: continuas y discretas. Las acciones continuas pueden definir directamente los valores de aceleración y ángulo de dirección, y las acciones discretas incluyen 5 meta acciones:

ACTIONS_ALL = {
        0: 'LANE_LEFT',
        1: 'IDLE',
        2: 'LANE_RIGHT',
        3: 'FASTER',
        4: 'SLOWER'
    }

(3) recompensa

La misma función de recompensa se usa en el paquete Highway-env excepto para la escena de estacionamiento:

Esta función solo se puede cambiar en su código fuente, y solo los pesos se pueden ajustar en la capa exterior. (La función de recompensa de la escena del estacionamiento está en el documento original, demasiado perezoso para escribir la fórmula...)

2. Construye el modelo

La estructura y el proceso de construcción de la red DQN se han discutido en otro artículo mío, por lo que no lo explicaré en detalle aquí. Utilizo la primera representación de estado - Cinemática para la demostración.

Debido a la pequeña cantidad de datos de estado (5 vehículos * 7 características), puede convertir directamente el tamaño [5,7] de los datos bidimensionales a [1,35] sin considerar el uso de CNN y la entrada de el modelo es 35, la salida es el número de acciones discretas, 5 en total.

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim
import torchvision.transforms as T
from torch import FloatTensor, LongTensor, ByteTensor
from collections import namedtuple
import random 
Tensor = FloatTensor

EPSILON = 0    # epsilon used for epsilon greedy approach
GAMMA = 0.9
TARGET_NETWORK_REPLACE_FREQ = 40       # How frequently target netowrk updates
MEMORY_CAPACITY = 100
BATCH_SIZE = 80
LR = 0.01         # learning rate

class DQNNet(nn.Module):
    def __init__(self):
        super(DQNNet,self).__init__()                  
        self.linear1 = nn.Linear(35,35)
        self.linear2 = nn.Linear(35,5)               
    def forward(self,s):
        s=torch.FloatTensor(s)        
        s = s.view(s.size(0),1,35)        
        s = self.linear1(s)
        s = self.linear2(s)
        return s           
                         
class DQN(object):
    def __init__(self):
        self.net,self.target_net = DQNNet(),DQNNet()        
        self.learn_step_counter = 0      
        self.memory = []
        self.position = 0 
        self.capacity = MEMORY_CAPACITY       
        self.optimizer = torch.optim.Adam(self.net.parameters(), lr=LR)
        self.loss_func = nn.MSELoss()

    def choose_action(self,s,e):
        x=np.expand_dims(s, axis=0)
        if np.random.uniform() < 1-e:  
            actions_value = self.net.forward(x)            
            action = torch.max(actions_value,-1)[1].data.numpy()
            action = action.max()           
        else: 
            action = np.random.randint(0, 5)
        return action

    def push_memory(self, s, a, r, s_):
        if len(self.memory) < self.capacity:
            self.memory.append(None)
        self.memory[self.position] = Transition(torch.unsqueeze(torch.FloatTensor(s), 0),torch.unsqueeze(torch.FloatTensor(s_), 0),\
                                                torch.from_numpy(np.array([a])),torch.from_numpy(np.array([r],dtype='float32')))#
        self.position = (self.position + 1) % self.capacity
       
    def get_sample(self,batch_size):
        sample = random.sample(self.memory,batch_size)
        return sample
      
    def learn(self):
        if self.learn_step_counter % TARGET_NETWORK_REPLACE_FREQ == 0:
            self.target_net.load_state_dict(self.net.state_dict())
        self.learn_step_counter += 1
        
        transitions = self.get_sample(BATCH_SIZE)
        batch = Transition(*zip(*transitions))

        b_s = Variable(torch.cat(batch.state))
        b_s_ = Variable(torch.cat(batch.next_state))
        b_a = Variable(torch.cat(batch.action))
        b_r = Variable(torch.cat(batch.reward))    
             
        q_eval = self.net.forward(b_s).squeeze(1).gather(1,b_a.unsqueeze(1).to(torch.int64)) 
        q_next = self.target_net.forward(b_s_).detach() #
        q_target = b_r + GAMMA * q_next.squeeze(1).max(1)[0].view(BATCH_SIZE, 1).t()           
        loss = self.loss_func(q_eval, q_target.t())        
        self.optimizer.zero_grad() # reset the gradient to zero        
        loss.backward()
        self.optimizer.step() # execute back propagation for one step       
        return loss
Transition = namedtuple('Transition',('state', 'next_state','action', 'reward'))

3. Ejecución de resultados

Después de completar cada parte, el modelo se puede combinar para entrenar el modelo. El proceso es similar al uso de CARLA, por lo que no entraré en detalles.

Inicialice el entorno (simplemente agréguele la clase DQN):

import gym
import highway_env
from matplotlib import pyplot as plt
import numpy as np
import time
config = \
    {
    "observation": 
         {
        "type": "Kinematics",
        "vehicles_count": 5,
        "features": ["presence", "x", "y", "vx", "vy", "cos_h", "sin_h"],
        "features_range": 
            {
            "x": [-100, 100],
            "y": [-100, 100],
            "vx": [-20, 20],
            "vy": [-20, 20]
            },
        "absolute": False,
        "order": "sorted"
        },
    "simulation_frequency": 8,  # [Hz]
    "policy_frequency": 2,  # [Hz]
    }
    
env = gym.make("highway-v0")
env.configure(config)

Entrena el modelo:

dqn=DQN()
count=0

reward=[]
avg_reward=0
all_reward=[]

time_=[]
all_time=[]

collision_his=[]
all_collision=[]
while True:
    done = False    
    start_time=time.time()
    s = env.reset()
    
    while not done:
        e = np.exp(-count/300)  #随机选择action的概率,随着训练次数增多逐渐降低
        a = dqn.choose_action(s,e)
        s_, r, done, info = env.step(a)
        env.render()
        
        dqn.push_memory(s, a, r, s_)
        
        if ((dqn.position !=0)&(dqn.position % 99==0)):
            loss_=dqn.learn()
            count+=1
            print('trained times:',count)
            if (count%40==0):
                avg_reward=np.mean(reward)
                avg_time=np.mean(time_)
                collision_rate=np.mean(collision_his)
                                
                all_reward.append(avg_reward)
                all_time.append(avg_time)
                all_collision.append(collision_rate)
                                
                plt.plot(all_reward)
                plt.show()
                plt.plot(all_time)
                plt.show()
                plt.plot(all_collision)
                plt.show()
                
                reward=[]
                time_=[]
                collision_his=[]
                
        s = s_
        reward.append(r)      
    
    end_time=time.time()
    episode_time=end_time-start_time
    time_.append(episode_time)
        
    is_collision=1 if info['crashed']==True else 0
    collision_his.append(is_collision)

Agregué algunas funciones de dibujo al código y puedo dominar algunos indicadores clave durante el proceso de ejecución y contar el promedio cada 40 veces de entrenamiento.

Tasa de colisión promedio:

Duración promedio de época (s):

Recompensa promedio:

Se puede ver que la tasa de colisión promedio disminuirá gradualmente a medida que aumenta el número de entrenamientos, y la duración de cada época aumentará gradualmente (si ocurre una colisión, la época terminará inmediatamente)

-FINAL-

Escanee el código para agregar, tenga en cuenta: python

 

Supongo que te gusta

Origin blog.csdn.net/qiqi1220/article/details/125797443
Recomendado
Clasificación