Usando uma rede neural de várias camadas para evitar obstáculos nos jogos

Encontrar maneiras de evitar obstáculos nos jogos é uma tarefa clássica com a qual todos os desenvolvedores de jogos precisam lidar. Existem vários algoritmos conhecidos, com diferentes graus de eficiência. Todos eles, em um grau ou outro, analisam a posição relativa do obstáculo e do jogador e, com base nos resultados, é tomada uma ou outra decisão de se mover. Tentei usar uma rede neural treinada para resolver o problema de evitar obstáculos. Quero compartilhar minha experiência na implementação dessa abordagem no Unity3D neste pequeno artigo.


Conceito


O terreno baseado no terreno padrão é usado como espaço de jogo. Colisões com a superfície não são consideradas neste artigo. Cada modelo é equipado com um conjunto de coletores, com a maior precisão possível, descrevendo a geometria dos obstáculos. O modelo, que deve contornar obstáculos, possui quatro



sensor de colisão (na captura de tela, a localização e a distância dos sensores são indicadas por linhas turquesas). Em essência, os sensores são reykast, cada um dos quais passa a distância do objeto de colisão no algoritmo de análise. A distância varia de 0 (o objeto está localizado o mais próximo possível) a 1 (sem colisão, essa direção está livre de obstáculos).
Em geral, o trabalho do algoritmo de prevenção de obstáculos é o seguinte:


  1. Quatro valores dos sensores de colisão são alimentados nas quatro entradas de uma rede neural treinada
  2. O estado da rede neural é calculado. Na saída, obtemos três valores:
    a. A potência de rotação do modelo no sentido anti-horário (assume um valor de 0 a 1)
    b. A potência de rotação do modelo no sentido horário (assume um valor de 0 a 1)
    c. Aceleração de frenagem (assume um valor de 0 a 1)
  3. Os esforços são aplicados ao modelo com coeficientes apropriados.

Implementação


Honestamente, eu não tinha ideia se algo viria desse empreendimento. Primeiro, implementei a classe neuroNet no Unity. Não vou me debruçar sobre o código de classe, pois é um perceptron clássico de multicamadas. No processo, surgiu imediatamente a questão do número de camadas da rede. Quantos deles são necessários para fornecer a capacidade necessária, por um lado, e uma velocidade de cálculo aceitável, por outro? Após uma série de experimentos, estabeleci doze camadas (três condições básicas para quatro entradas).


Em seguida, foi necessário implementar o processo de treinamento de uma rede neural. Para fazer isso, tive que criar um aplicativo separado que usasse a mesma classe neuroNet. E agora o problema dos dados para treinamento atingiu seu auge. Inicialmente, eu queria usar valores obtidos diretamente do aplicativo do jogo. Para fazer isso, organizei o registro de dados dos sensores, para que, no futuro, para cada conjunto de valores dos quatro sensores, indique ao programa de treinamento os valores de saída corretos. Mas, olhando o resultado, fiquei desanimado. O fato é que não basta indicar um valor adequado para cada conjunto de quatro valores do sensor; esses valores precisam ser consistentes. Isso é muito importante para o treinamento bem-sucedido da rede neural. Além disso, não havia garantia de que a amostra resultante representasse todas as situações possíveis.


Uma solução alternativa foi uma tabela compilada manualmente de opções básicas para os valores dos sensores e saídas. As opções básicas foram tomadas: 0,01 - o obstáculo está próximo, 0,5 - o obstáculo está no meio do caminho, 1 - a direção está livre. Isso reduziu o tamanho da amostra de treinamento.


  Sensor 1 
  Sensor 2 
  Sensor 3 
  Sensor 4 
Rotação no sentido horárioRotação no sentido anti-horárioTravagem
0,010,010,010,010,010,010,01
0,010,010,010,50,010,010,01
0,010,010,010,9990,010,010,01
0,010,010,50,010,9990,010,01
0,010,010,50,50,9990,010,01
0,010,010,50,9990,9990,010,5
0,010,010,9990,010,9990,010,5
0,010,010,9990,50,9990,010,999
0,010,010,9990,9990,9990,010,999

A tabela mostra um pequeno fragmento da amostra de treinamento (total na tabela 81-a linha). O resultado final do programa de treinamento foi uma tabela de ponderação, que foi salva em um arquivo separado.


Resultados


Antecipando esfregar minhas mãos, organizei o carregamento das probabilidades em um jogo de demonstração e iniciei o processo. Mas, como se viu, claramente não fiz o suficiente para o caso. Desde o início, o modelo testado girou em todos os obstáculos seguidos, como um gatinho cego. Em geral, o resultado foi muito mais ou menos. Eu tive que me aprofundar no estudo do problema. Uma fonte de comportamento desamparado foi descoberta rapidamente. Com as respostas geralmente corretas da rede neural às leituras dos sensores, as ações de controle transmitidas se mostraram muito fortes.


Tendo resolvido esse problema, encontrei uma nova dificuldade - a distância do sensor reykast. Com uma longa distância de detecção de interferência, o modelo realizou manobras prematuras que resultaram em distorção significativa da rota (e mesmo em colisões imprevistas em obstáculos aparentemente já ultrapassados). Uma pequena distância levou a uma coisa - uma "impotência" impotente do modelo em todos os obstáculos, com uma clara falta de tempo para resposta.


Quanto mais eu brincava com o modelo de jogo de demonstração, tentando ensiná-lo a evitar obstáculos, mais me parecia que eu não estava programando, mas tentando ensinar meu filho a andar. E foi uma sensação incomum! Foi ainda mais alegre ver que meus esforços trouxeram resultados tangíveis. No final, o infeliz barco flutuando acima da superfície começou a contornar as estruturas que surgiam na rota. Os testes reais para o algoritmo começaram quando eu conscientemente tentei empurrar o modelo para um beco sem saída. Aqui foi necessário alterar a lógica do trabalho com aceleração de frenagem, para fazer algumas correções na amostra de treinamento. Vejamos exemplos práticos do que aconteceu como resultado.


1. Simples desvio de um obstáculo



Como você pode ver, o desvio não causou nenhuma dificuldade.


2. Dois obstáculos (opção 1)



O modelo encontrou facilmente uma passagem entre os dois edifícios. Tarefa fácil.


3. Dois obstáculos (opção 2)



Os edifícios estão mais próximos, mas o modelo encontra uma passagem.


4. Dois obstáculos (opção 3)



A opção é mais complicada, mas ainda está resolvida.


5. Três obstáculos



O problema foi resolvido rapidamente.


6. Beco sem saída



Aqui o modelo teve problemas. Os primeiros 30 segundos do vídeo mostram que o modelo tropeça impotente em uma configuração simples de construção. O problema aqui provavelmente não está tanto no modelo de rede neural como no algoritmo principal para se mover ao longo da rota - ele está constantemente tentando recuperar o navio de rumo, apesar das tentativas desesperadas de evitar uma colisão.


Depois de várias execuções sem êxito dessa situação com parâmetros diferentes, consegui obter um resultado positivo. A partir do trigésimo segundo do vídeo, é possível observar como um modelo com uma distância maior de sensores e uma força de frenagem mais poderosa é selecionado a partir do beco sem saída. Para isso, ela precisou de quase cinco minutos de tempo (cortei o tormento e deixei apenas os últimos 30 segundos do vídeo). É improvável que, em um jogo real, isso seja considerado um bom resultado, portanto, obviamente, há espaço para melhorias no algoritmo.


Conclusão


Em geral, o problema foi resolvido. A eficácia dessa solução é uma questão em aberto e são necessárias mais pesquisas. Por exemplo, não se sabe como o modelo se comportará quando obstáculos dinâmicos (outros objetos em movimento) aparecerem. Outro problema é a falta de sensores de colisão apontando para trás, o que leva a dificuldades em evitar obstáculos complexos.


O desenvolvimento óbvio da idéia de um algoritmo para evitar obstáculos na rede neural é visto na introdução do treinamento. Para fazer isso, uma avaliação do resultado da decisão tomada deve ser introduzida e, com as correções subsequentes sem alterações significativas na posição do objeto, a avaliação deve se deteriorar. Ao atingir um determinado valor, o modelo deve entrar no modo de treinamento e, digamos, alterar aleatoriamente as decisões tomadas para encontrar uma saída.


Outra característica do modelo me parece a variabilidade do treinamento inicial. Isso possibilita, por exemplo, ter vários comportamentos para modelos diferentes sem a necessidade de programar cada um deles separadamente. Em outras palavras, se tivermos, digamos, um tanque pesado e um reconhecimento leve, sua maneira de evitar obstáculos pode variar significativamente. Para alcançar esse efeito, usamos o mesmo perceptron, mas treinado em amostras diferentes.

Source: https://habr.com/ru/post/pt419265/


All Articles