You are here

Aula 7 - SOAR: Controlando o WorldServer3D

Introdução

O objetivo desta aula é utilizar o SOAR para controlar uma aplicação externa por meio da interface SML. O sistema que será desenvolvido atuará como a mente artificial de um agente no ambiente do WorldServer3D utilizado na primeira aula.

Para isso, será utilizado os seguintes códigos fontes que foram disponibilizados no site para esta atividade:

  • WorldServer3D;
  • DemoSOAR;
  • WS3DProxy;

Como primeiro passo, é necessário executar o WorldServer3D. A figura abaixo mostra a tela inicial do WorldServer3D.

O DemoSOAR irá conectar-se ao WorldServer3D e será utilizado para fazer o controle da criatura neste ambiente. Abaixo é mostrado a estrutura do seu código fonte.

O DemoSOAR contém dois packages principais: Simulation e SoarBridge. O SoarBridge é responsável por fazer a comunicação com o SOAR e o Simulation contém as classes para controle da criatura e aquisição dos dados do ambiente via sensores, conforme será detalhado abaixo.

Código Main

O SimulationSOAR.java é onde está contido o código main do software.

public class SimulationSOAR
{
    static Logger logger = Logger.getLogger(SimulationSOAR.class.getName());
    public static void main(String[] args)
    {

A primeira parte dentro do main é responsável por detectar o sistema operacional com sua respectiva arquitetura em que está executando e carregar as bibliotecas relacionadas.

 NativeUtils.setLibraryPath(".");
            System.out.println("OS:"+System.getProperties().getProperty("os.name")+" Architecture:"+System.getProperties().getProperty("os.arch"));
            
        String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
            String osArch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
                
            try {
           if(osName.contains("win")){
                  if(osArch.contains("64")) {
                    System.out.println("Windows 64 bits");
                    NativeUtils.loadFileFromJar("/win64/Soar.lib");
                    NativeUtils.loadFileFromJar("/win64/Soar.dll");
                    NativeUtils.loadFileFromJar("/win64/Java_sml_ClientInterface.dll");
                  }
                  else {
                    System.out.println("Windows 32 bits");
                    NativeUtils.loadFileFromJar("/win32/Soar.lib");
                    NativeUtils.loadFileFromJar("/win32/Soar.dll");
                    NativeUtils.loadFileFromJar("/win32/Java_sml_ClientInterface.dll");
                  }   
              } else if(osName.contains("mac")){
            System.out.println("MacOSX");
                    NativeUtils.loadFileFromJar("/macos/libSoar.dylib");
                    NativeUtils.loadFileFromJar("/macos/libJava_sml_ClientInterface.jnilib");
              } else if(osName.contains("nix") || osName.contains("nux")){
                  if(osArch.contains("64")) {
                      System.out.println("Linux 64 bits");
                      NativeUtils.loadFileFromJar("/linux64/libSoar.so");
                      NativeUtils.loadFileFromJar("/linux64/libJava_sml_ClientInterface.so");
                  }
                 else {
                      System.out.println("Linux 32 bits");
                      NativeUtils.loadFileFromJar("/linux32/libSoar.so");
                      NativeUtils.loadFileFromJar("/linux32/libJava_sml_ClientInterface.so");
                }

Na sequência, o DemoSOAR realiza o setup para carregar as produções do SOAR na memória da criatura e seta a porta para fazer a conexão com o SoarJava Debugger.

NativeUtils.loadFileFromJar("/soar-rules.soar");
            String soarRulesPath = "soar-rules.soar";
            String soarDebuggerPath = "";
            Integer soarDebuggerPort = 12121;

Depois disso, o seguinte trecho de código é executado.

//Start enviroment data
            SimulationTask simulationTask = new SimulationTask();
            simulationTask.initializeEnviroment(Boolean.FALSE); //cria paredes para delimitar o ambiente
      simulationTask.initializeCreatureAndSOAR(soarRulesPath,true,soarDebuggerPath,soarDebuggerPort);

simulationTask.initializeCreatureAndSOAR() é responsável por inicializar o ambiente, criar uma conexão com o SOAR e inserir a criatura no ambiente.

Abaixo é detalhado os principais métodos que estão dentro desta classe.

 c = proxy.createCreature(100,100,0);

        w = proxy.getWorld();
        c.start();
        w.grow(1);

Ao executar este trecho, o DemoSOAR insere uma criatura nas coordenadas passadas (100,100,0) e inicializa a mente da criatura, conforme figura abaixo.

Abaixo é possivel ainda visualizar a mente da criatura neste estado inicial com a sua área de visão delimitada.

Na figura abaixo ele adiciona de forma aleatória os objetos em que a criatura irá interagir. Na parte superior direita, é possivel visualizar o que está no campo de visão da criatura.

Depois disso, é executado o seguinte trecho de código.

// SOAR Enviroment
        soarBridge = new SoarBridge("agentSimulationSOAR", new File(rulesPath), runDebugger,soarDebuggerPath, soarDebuggerPort);

Este trecho de código é a parte responsável por inicializar e conectar-se com o SoarJavaDebugger. Ele irá então chamar o SoarBridge. Abaixo é mostrado algumas principais partes do SoarBridge e sua função.

// create Soar kernel And Agent
            kernel = Kernel.CreateKernelInNewThread();
            checkForKernelOrAgentError();
            agent = kernel.CreateAgent(agentName);
            checkForKernelOrAgentError();

Este trecho é responsável por criar um agente no SOAR, através do SoarBridge e tratar possiveis erros.

Na sequencia, o SoarJavaDebugger é inicializado e também são carregadas as produções iniciais que foram setadas ("soar-rules.soar"). A Figura abaixo mostra a tela inicial do SoarJavaDebugger.

// Load some productions
            agent.LoadProductions(productionPath.getAbsolutePath());
            checkForKernelOrAgentError();

// Debugger line
            if (startSOARDebugger)
            {
                agent.SpawnDebugger(soarDebuggerPort,"lib/debugger-linux64/SoarJavaDebugger.jar");
            }

 

Depois deste estágio, o DemoSOAR entra no seguinte looping.

 while(true)
            {
                simulationTask.runSimulation();
                Thread.sleep(100);
            }

Abaixo é mostrado os principais pontos para entender o funcionamento da simulationTask.runsimulation.

       if (soarBridge != null)
        {
            c.updateState();
            //Status currentStatus = c.getState();
            List<Thing> v = c.getThingsInVision();
            logger.info("Objetos no Ambiente: "+v.size());
            System.out.println("Objetos no Ambiente: "+v.size());
            for (Thing t : v) {
                logger.info(t.toString());
            }

Este método é o responsável por detectar os objetos que existem no ambiente e atualizar a mente da criatura. A função getThingsInVision é responsável por detectar os objetos que estão dentro do campo de visão da criatura e imprime na tela de saída do Java o número de objetos que está no campo de visão da criatura.

A criatura pode detectar se existe um dos seguintes objetos na sua área de visão:

  • Food;
  • Jewel;
  • PerishableFood;
  • Brick;

Depois desta fase, o DemoSOAR prepara a criatura para a simulação, conforme explicado abaixo.

if (c != null)
            {
                // Prepare Creature To Simulation
                prepareAndSetupCreatureToSimulation(c, v);

                // Run simulation
                soarBridge.runSimulation();

                // Process Responde Commands
                processResponseCommands();
            }

 

// Prepare Creature To Simulation
A classe prepareAndSetupCreatureToSimulation(c, v) instancia o sensor visual da criatura, habilita o sensor visual para iniciar a leitura do ambiente, seta o nível de combustível da criatura para nível 1000 e adicionar os sensores visual e de combustível na criatura.

private void prepareAndSetupCreatureToSimulation(Creature creature, List<Thing> things) throws SoarBridgeException, IllegalAccessException
    {
        if (creature != null)
        {
            // Create Visual Sensor
            VisualSensor visualSensor = new VisualSensor();
            visualSensor.setSensorReadings(things);

// Create Fuel Sensor
            FuelSensor fuelSensor = new FuelSensor();
            FuelSensorReadings fuelSensorReadings = new FuelSensorReadings();
            fuelSensorReadings.setCurrentFuel(creature.getFuel());
            fuelSensor.setSensorReadings(fuelSensorReadings);

//adiciona os sensores visual e de combustível na criatura

// Put things in SOAR output interface to start simulation
            SimulationRobot simulationCreature = new SimulationRobot(creature);
            simulationCreature.addSensorsAvailable(visualSensor);
            simulationCreature.addSensorsAvailable(fuelSensor);

Após isso, é iniciado efetivamente a simulação. Caso o SoarJavaDebugger esteja habilitado, é possível acompanhar a proposição e execução das produções, conforme mostrado na figura abaixo.

No processo de simulação, o SOAR, através dos dados lidos através do input-link , irá propor e executar as seguintes ações.

  • Mover ('MOVE');
  • Pegar ('GET');
  • Comer ('EAT);

A medida em que a criatura se desloca pelo ambiente ela gasta energia e recarrega se comer alguma fruta do ambiente.

Regras do SOAR (soar-rules.soar)

As regras do SOAR para controle da criatura estão localizadas no arquivo soar-rules.soar. As regras seguem o padrão do SOAR:

  • Propor um operador (Propose);
  • Aplicar o operador selecionado (Apply);
  • Remover o operador (remove);

A tabela abaixo mostra as produções e a função de cada uma delas.

propose*wanderoperador para que a criatura possa andar ou girar em caráter exploratório;
apply*wanderse este operador for selecionado, aplica o comando na saida
apply*wander*remove*moveremove o comando após ter executado esta ação
propose*see*entity*with*memory*countoperador para atualizar os objetos e guardar na memória do agente;
apply*see*entity*with*memory*countse este operador for selecionado, aplica o comando na saida
propose*see*entity*without*memory*countoperador para atualizar os objetos que estão na memória do agente. Este operador não guarda na memória episódica;
apply*see*entity*without*memory*countse este operador for selecionado, aplica o comando na saida
propose*move*foodoperador para propor mover-se até a comida
apply*move*foodse este operador for selecionado, aplica o comando de na saida
apply*moveFood*remove-moveremove o comando de mover-se até a comida da saída
apply*moveFood*remove*foodremove a comida da memória do agente
propose*eat*foodpropõe comer a comida
apply*eat*foodse este operador for selecionado, aplica o comando de comer a comida na saida
apply*eatFood*remove-eataplica remover a comida
propose*move*jewelpropõe que o agente vá até a jóia
apply*move*jewelaplica o comando de mover-se até a jóia
apply*moveJewel*remove-moveremove o comando de mover-se até a jóia após ter concluido esta ação
apply*moveJewel*remove*jewelremove a jóia da memória após ter coletado ela
propose*get*jewelpropõe pegar a jóia
apply*get*jewelse este operador for selecionado, aplica o comando de pegar a jóia na saida
apply*getJewel*remove-getremove o comando de pegar a jóia
propose*avoidBrickpropõe para evitar as paredes
apply*avoidBrickaplica o comando para evitar uma parede
apply*avoidBrick*remove*entity*memoryremover a parede da memória
apply*avoidBrick*remove-moveremove o comando evitar a parede da memória do agente após ter se desviado
moveJewel*seeEntity*preferencespreferência para mover-se até a jóia
avoidBrick*seeEntityWithMemory*preferencespreferência para evitar as paredes
seeEntity*without*memory*preferencespreferência "sem preferências" de memória
moveJewel*getJewel*preferencespreferência para decisão entre mover ou pegar a jóia
getJewel*avoidBrick*preferencespreferência para decisão entre pegar uma jóia ou desviar-se da parede
moveJewel*moveJewel*less*distancepreferência para escolha de menor distância entre mover-se até a jóia
getJewel*getJewel*preferencespreferência para escolha de qual jóia pegar
moveFood*eatFood*preferencespreferência para escolha entre mover-se até a comida ou comer a comida
eatFood*avoidBrick*preferencespreferência para escolha entre comer a comida ou desviar-se de uma parede
moveFood*moveFood*preferencespreferência para escolha para mover-se até a comida
eatFood*eatFood*preferencespreferência para escolha da comida
moveFood*moveJewel*preferences*moveFoodWinspreferência para escolha entre mover-se para a comida ou até uma jóia (mover-se até a comida tem maior prioridade)
moveFood*moveJewel*preferences*moveJewelWinspreferẽncia para escolha entre mover-se até a comida ou até a jóia (mover-se até a jóia tem maior prioridade)
avoidBrick*avoidBrick*without*move*jewel*preferencespreferência para evitar uma parede ou evitar a preferência de jóia
avoidBrick*moveJewel*moveFood*preferencespreferência para evitar uma parede, mover-se até uma jóia ou ir até a comida
wander*preferencespreferência para andar em caráter exploratório

 

link do WorldServer3D:

http://faculty.dca.fee.unicamp.br/gudwin/sites/faculty.dca.fee.unicamp.br.gudwin/files/ws3d/launch.jnlp

O executável com a implementação (ainda parcial) está disponível no link abaixo.

/gudwin/sites/faculty.dca.fee.unicamp.br.gudwin/files/users/2015-8/7aula/Demosoar/launch.jnlp

O código fonte está disponivel no link:

gudwin/sites/faculty.dca.fee.unicamp.br.gudwin/files/users/2015-8/7aula/ver1-Linux64.zip

link do vídeo:

https://youtu.be/AVKVsU_6IUE

 

 

 

 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer