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:
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);
O 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:
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.
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:
A tabela abaixo mostra as produções e a função de cada uma delas.
propose*wander | operador para que a criatura possa andar ou girar em caráter exploratório; |
apply*wander | se este operador for selecionado, aplica o comando na saida |
apply*wander*remove*move | remove o comando após ter executado esta ação |
propose*see*entity*with*memory*count | operador para atualizar os objetos e guardar na memória do agente; |
apply*see*entity*with*memory*count | se este operador for selecionado, aplica o comando na saida |
propose*see*entity*without*memory*count | operador 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*count | se este operador for selecionado, aplica o comando na saida |
propose*move*food | operador para propor mover-se até a comida |
apply*move*food | se este operador for selecionado, aplica o comando de na saida |
apply*moveFood*remove-move | remove o comando de mover-se até a comida da saída |
apply*moveFood*remove*food | remove a comida da memória do agente |
propose*eat*food | propõe comer a comida |
apply*eat*food | se este operador for selecionado, aplica o comando de comer a comida na saida |
apply*eatFood*remove-eat | aplica remover a comida |
propose*move*jewel | propõe que o agente vá até a jóia |
apply*move*jewel | aplica o comando de mover-se até a jóia |
apply*moveJewel*remove-move | remove o comando de mover-se até a jóia após ter concluido esta ação |
apply*moveJewel*remove*jewel | remove a jóia da memória após ter coletado ela |
propose*get*jewel | propõe pegar a jóia |
apply*get*jewel | se este operador for selecionado, aplica o comando de pegar a jóia na saida |
apply*getJewel*remove-get | remove o comando de pegar a jóia |
propose*avoidBrick | propõe para evitar as paredes |
apply*avoidBrick | aplica o comando para evitar uma parede |
apply*avoidBrick*remove*entity*memory | remover a parede da memória |
apply*avoidBrick*remove-move | remove o comando evitar a parede da memória do agente após ter se desviado |
moveJewel*seeEntity*preferences | preferência para mover-se até a jóia |
avoidBrick*seeEntityWithMemory*preferences | preferência para evitar as paredes |
seeEntity*without*memory*preferences | preferência "sem preferências" de memória |
moveJewel*getJewel*preferences | preferência para decisão entre mover ou pegar a jóia |
getJewel*avoidBrick*preferences | preferência para decisão entre pegar uma jóia ou desviar-se da parede |
moveJewel*moveJewel*less*distance | preferência para escolha de menor distância entre mover-se até a jóia |
getJewel*getJewel*preferences | preferência para escolha de qual jóia pegar |
moveFood*eatFood*preferences | preferência para escolha entre mover-se até a comida ou comer a comida |
eatFood*avoidBrick*preferences | preferência para escolha entre comer a comida ou desviar-se de uma parede |
moveFood*moveFood*preferences | preferência para escolha para mover-se até a comida |
eatFood*eatFood*preferences | preferência para escolha da comida |
moveFood*moveJewel*preferences*moveFoodWins | preferê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*moveJewelWins | preferẽ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*preferences | preferência para evitar uma parede ou evitar a preferência de jóia |
avoidBrick*moveJewel*moveFood*preferences | preferência para evitar uma parede, mover-se até uma jóia ou ir até a comida |
wander*preferences | preferência para andar em caráter exploratório |
link do WorldServer3D:
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:
Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer