Atividade 1
Na primeira parte desta atividade se executarão os programas do WorldServer3D (que foi modificado) e o programa DemoSOAR que é um cliente que opera com regras.
Depois foram descargados os arquivos-fonte dos dois programas e da biblioteca WS3DProxy que é uma libraria de apoio que faz a conexão como o WorldServer3d.
O código foi compilado e executado. Na seguinte imagem é apresentada o código executado no entorno NetBeans
Apos abrir o código da classe SimulationSOAR.java e localizar o método main a primeira coisa que o programa faz é estabelecer a pasta atual na classe NativeUtils depois são utilizados os seguintes métodos:
System.getProperties().getProperty("os.name")
e
System.getProperties().getProperty("os.arch")
O primeiro é para obter o tipo de sistema operacional que se está usando. O segundo comando obtém o tipo de arquitetura do computador.
No meu caso as saídas são:
OS:Linux e Architecture: amd64
Depois são armazenadas o nome do sistema operativo e a arquitetura em duas variáveis: String osName e String osArch. As duas variáveis serão usadas para carregas as bibliotecas que correspondem ao sistema operativo e à arquitetura do computador que está usando o programa, assim é utilizada a estrutura if-else para determinar que arquivos são carregados.
Por exemplo, o primeiro if pergunta se a variável osName contem a palavra win (de windows), se a arquitetura atual é Windows, então o programa entra no segundo if e pergunta o tipo de arquitetura, se a variável osArch contém o número 64, então é determinado que o sistema operacional e a arquitetura é Windows de 64 bits e serão carregadas os arquivos chamados: /win64/Soar.lib , /win64/Soar.dll e /win64/Java_sml_ClientInterface.dll.
No meu casso, o meu sistema operacional é Linux e tenho uma arquitetura amd64 portanto serão carregados os arquivos: /linux64/libSoar.so e /linux64/libJava_sml_ClientInterface.so
Finalmente é carregado o arquivo /soar-rules.soar que contem as regras do Soar.
Após é estabelecida a variável String soarRulesPath = "soar-rules.soar" e depois a porta para o Debugger do soar:
Integer soarDebuggerPort = 12121;
Após carregar os arquivos e inicializar algumas variáveis o programa entra no loop principal:
while(true)
{
simulationTask.runSimulation();
Thread.sleep(100);
}
O método runSimulation() da classe simulationTask é executado indefinidamente enquanto o programa esteja em execução.
Toda vez que é executado o método runSimulation() são realizadas as seguintes tarefas:
Atualiza o estado da criatura
São obtidos os objetos “vistos” pela criatura.
A informação de posição, pitch etc. da criatura e a informação dos objetos que ela consegui “ver” é traduzida na linguagem do SOAR, criando Working Memory Elements (WME)
É enviada a estrutura de informação gerada no passo anterior ao SOAR. No SOAR são executadas as regras do arquivo soar-rules.soar e é obtida uma resposta, em forma de comando de saída que será enviada novamente ao programa cliente.
O programa cliente recebe a saída do SOAR e processa a informação executando os comandos de resposta do SOAR na criatura, este processo de executar os comandos muda o estado atual da criatura e o processa começa de novo.
Em resumo, a criatura obtêm informação da sua posição e dos objetos que ela consegue ver, sintetiza a informação numa estrutura de dados que o SOAR consiga entender e são enviados para seu processamento. Em SOAR as regras do arquivo soar-rules.soar são executadas, é escolhido um operador e a regra de aplicação desse operador cria comandos no elemento output-link do estado.Depois da seleção dos comandos a serem enviado para o cliente, o programa cliente recebe os comandos de saída, provenientes do SOAR, e executa as ações modificando o estado da criatura.
O método que prepara a informação para ser enviada a SOAR é
prepareAndSetupCreatureToSimulation(...)
Aqui é criado o sensor visual e o sensor de constituível. Após de agregar ao objeto simulationCreature os sensores disponíveis.
Depois é chamado o método setupStackHolder(...) da classe soarBridge que é o encargado de criar a estrutura de dados para o SOAR a partir da informação da criatura é seu entorno.
No método setupStackHolder(...) é perguntado se o tipo de entidade é uma criatura, se assim for, então se gera a seguente estrutura de Working Memory Elements
Existem tantas ^ENTITY como elementos sejam vistos pela criatura no mundo virtual
A posição da criatura é obtida a partir de:
creatureParameter.getPosition().getX()
creatureParameter.getPosition().getY()
Uma lista dos sensores disponíveis é obtida a partir da função:
creatureParameter.getSensorsEnabled()
Para obter o valor do combustível e os elementos que são vistos pela criatura no mundo virtual é usada a função:
creatureParameter.getSensorHandler(<param>).getSensorReadings();
Onde <param> pode ser SensorType.FUEL ou SensorType.VISUAL, no primeiro caso é retornado o valor atual do combustível; no segundo casso é retornada uma lista com os objetos vistos pela criatura. Existem três tipos de elementos no mundo virtual: os tijolos (BRICK), joias (JEWEL) e comida (FOOD)
A execução da simulação é feita através do método runSimulation() da classe soarBridge.
Após a execução das regras no SOAR é completada, o método processResponseCommands() obtêm e processa a informação enviada desde SOAR.
A resposta do SOAR está constituída por uma lista de comandos a serem executados no robô. Existem três tipos de comandos: mover (MOVE), obter (GET) e comer (EAT). Os comandos são processados pelas funções:
-
processMoveCommand(...)
-
processGetCommand(...)
-
processEatCommand(...)
cada comando leva os argumentos necessários para processar a ordem, como direção de movimento, velocidade etc.
É possível executar o SoarDebbuger enquanto se está executado o programa cliente e o WorldServer3d:
A seguinte imagem apresenta a saída do SoarDebbuger enquanto se está executando a simulação:
Programa de regras de SOAR
No programa de regras do SOAR o primeiro operador a ser proposto é chamado de WANDER
Este operador permite à criatura se movimentar pelo entorno com uma velocidade VelR igual a 2
A seguir a regra de proposição do operador wander
# This operator will make the agent to walk ahead at the enviroment
# Propose*wander:
sp {propose*wander
(state <s> ^attribute state
^impasse no-change
^superstate <ss>)
(<ss> ^io.input-link <il>)
(<ss> ^superstate nil)
(<il> ^CREATURE <creature>)
(<creature> ^SENSOR.VISUAL <visual>)
-->
(<ss> ^operator <o> +)
(<o> ^name wander)}
Esta é a regra de aplicação do operador wander
# Apply*wander:
# If the wander operator is selected, then generate an output command to it
sp {apply*wander
(state <s> ^operator <o>
^io <io>)
(<io> ^output-link <ol>)
(<o> ^name wander)
-->
(<ol> ^MOVE <command>)
(<command> ^Vel 0)
(<command> ^VelR 2)
(<command> ^VelL 0)}
A seguinte regra elimina o comando se ele já esta completo
# If the wander operator is selected,
# and there is a completed move command on the output link,
# then remove that command.
sp {apply*wander*remove*move
(state <s> ^operator.name wander
^io.output-link <out>)
(<out> ^MOVE <move>)
(<move> ^status complete)
-->
(<out> ^MOVE <move> -)}
O operador seeEntityWithMemoryCount permite armazenar na memória entidades que a criatura pode ver no mundo virtual, este operador é proposto se o agente já possui alguma entidade na memória.
O operador seeEntityWithoutMemoryCount faz o mesmo trabalho do que o operador seeEntityWithMemoryCount mas é aplicado quando não existe entidade nenhuma no agente.
Depois são declarados os operadores de movimento em direção na comida (moveFood), o operador de pegar a comida (eatFood), o operador de ir na direção das joias (moveJewel), o operador de pegar as joias (getJewel) e o operador de evitar os tijolos (avoidBrick).
Finalmente são criadas as regras de controle de preferências e as regras que dão solução aos impasses.