SOAR: Controlando o WorldServer3D
LeafletSOAR
O programa DemoSOAR, visto anteriormente, usava a arquitetura do SOAR para criar a mente do agente que controlava o robô do ambiente WorldServer3D. Basicamente, esse programa comandava o robô em busca de jóias e/ou comida espalhados pelo ambiente e visualizados pela câmera (e memorizados pelo agente SOAR). O programa LeafletSOAR, discutido neste tópico, foi construído com base nas classes Java e nas regras SOAR usados no programa DemoSOAR. Neste caso, a tarefa do agente SOAR será mover o robô pelo ambiente do WorldServer3D em busca de apenas uma certa quantidade de tipos específicos (cores) de jóias disponíveis no ambiente. Essas jóias específicas estão descritas numa estrutura do robô, disponibilizada pelo WorldServer3D, chamada leaflet.
O Leaflet do robô contém informações sobre a cor da jóia (color) que deve ser coletada, o número de jóias (number) que devem ser coletadas, além de também armazenar o número de jóias coletadas (collected) e o valor do pagamento efetuado ao robô quando este entregar (delivery) as jóias coletadas. O comando "leaflet" do WorldServer3D gera os Leaflet's do robô. Cada robô possui três Leaflet's (conforme pode ser observado em uma das figuras a seguir).
Para implementar esta tarefa do robô, de apenas coletar as jóias descritas nos seus Leaflet's, será necessário tratar dois problemas não analisados no programa DemoSOAR: receber os Leaflet's do robô, e, gerar a lógica responsável pela tarefa do agente. A primeira parte será solucionada por modificações nas seguintes classes Java: SimulationCreature, SimulationRobot, SimulationTask e SoarBridge (além de SoarCommand e a nova classe SoarCommandDelivery). A segunda parte do problema será tratada por modificações nas regras escritas no arquivo soar-rules.soar. É importante ressaltar que o robô deve coletar as jóias dos seus Leaflet's o mais rápido possível, ignorando as demais jóias e comida disponíveis em seu ambiente. No entanto, nesta solução, as jóias ou comida que estejam no caminho do robô também serão coletadas.
Recebendo os Leaflet's do Robô
O programa-exemplo DemoSOAR já trata da geração, aleatória, dos Leaflet's do robô. No entanto, esse programa não disponibiliza métodos para se trabalhar com os Leaflet's gerados. O primeiro passo para solucionar isto é alterar a classe SimulationTask. O método prepareAndSetupCreatureToSimulation() recebe o robô (Creature) criado como um de seus parâmetros. As linhas de código abaixo foram adicionadas a este método:
//Get Creature Leaflets
Vector<Leaflet> vLeaflets = creature.getLeaflets();
simulationCreature.SetLeafletList(vLeaflets);
O comando getLeaflets() recebe os Leaflet's do robô instanciado no ambiente do WorldServer3D. Os Leaflet's são passados para um objeto da classe SimulationRobot. Essa classe possui métodos herdados da classe SimulationCreature. Foram criados novos métodos, em ambas as classes, para tratar os Leaflet's recebidos do robô. A classe SimulationCreature possui o método que recebe os Leaflet's do robô (SetLeafletList()). O próximo passo será configurar o número de jóias que devem ser coletadas de acordo com suas respectivas cores, cuja informação está disponível nos Leaflet's recebidos; além de também efetuar a verificação do número de jóias já coletadas, dos leaflet's, pelo robô. O método SetColors() é responsável por essas tarefas. Uma vez configuradas essas informações, outros métodos são disponibilizados para ler o número de jóias que devem ser coletadas (GetColorRED(), ...) de cada cor possível (RED, GREEN, BLUE, YELLOW, MAGENTA, WHITE) e o número de jóias que já foram coletadas (GetCollectedRED(), ...) dos leaflet's do robô.
Para disponibilizar as informações dos Leaflet's do robô para o agente SOAR criado, é necessário modificar a classe SoarBridge. O método setupStackHolder() desta classe é responsável por criar alguns WME's usados pelo agente SOAR. As linhas de código abaixo devem ser acrescentadas, neste método, para criar os WME's que armazenarão as informações dos Leaflet's do robô:
// Set creature Leaflets
if (creatureLeaflets == null)
{
creatureLeaflets = agent.CreateIdWME(creature, "LEAFLETS");
agent.CreateIntWME(creatureLeaflets, "Red", parameter.GetColorRED());
agent.CreateIntWME(creatureLeaflets, "Green", parameter.GetColorGREEN());
agent.CreateIntWME(creatureLeaflets, "Blue", parameter.GetColorBLUE());
agent.CreateIntWME(creatureLeaflets, "Yellow", parameter.GetColorYELLOW());
agent.CreateIntWME(creatureLeaflets, "Magenta", parameter.GetColorMAGENTA());
agent.CreateIntWME(creatureLeaflets, "White", parameter.GetColorWHITE());
}
creatureJewels = agent.CreateIdWME(creature, "JEWELS");
agent.CreateIntWME(creatureJewels, "Red", parameter.GetCollectedRED());
agent.CreateIntWME(creatureJewels, "Green", parameter.GetCollectedGREEN());
agent.CreateIntWME(creatureJewels, "Blue", parameter.GetCollectedBLUE());
agent.CreateIntWME(creatureJewels, "Yellow", parameter.GetCollectedYELLOW());
agent.CreateIntWME(creatureJewels, "Magenta", parameter.GetCollectedMAGENTA());
agent.CreateIntWME(creatureJewels, "White", parameter.GetCollectedWHITE());
Será criado um WME chamado LEAFLETS que possuirá outros elementos com o número de jóias que devem ser coletadas de acordo com cada cor de jóia disponível no ambiente do robô. Observa-se que são usados métodos da classe SimulationRobot (que herda de SimulationCreature) para ler o número de jóias de cada cor, segundo as informações recebidas dos Leaflet's do robô.
Um outro WME chamado JEWELS armazenará o número de jóias que já foram coletadas, dos leaflet's, pelo robô. Métodos da classe SimulationRobot (que herda de SimulationCreature) são usados para obter essas informações. Deve-se observar que este elemento deverá ser sempre destruído, e então recriado, para que seus atributos possam ser atualizados (agent.DestroyWME(creatureJewels)).
Outra modificação necessária na classe SoarBridge, para implementar a execução das tarefas do robô, foi a criação de um novo comando: DELIVERY. Esse comando refere-se ao WME ^DELIVERY cuja ação faz o robô parar de se mover e suspende (halt) o agente. O ideal seria fazer o robô se mover até o DeliverySpot, criado no seu ambiente, e efetuar a entrega das jóias coletadas para receber o seu devido pagamento. A classe SoarCommand também foi modificada e uma nova classe, SoarCommandDelivery, foi criada. A execução dessa tarefa está descrita na classe SimulationTask (processDeliveryCommand()).
Implementando a Tarefa do Robô
O programa DemoSOAR possui essencialmente a seguinte lógica de operação: coletar apenas os objetos (jóias ou comida) que estão na memória do agente, ou seja, os objetos que foram captados pela câmera e gravados na memória. Portanto, para implementar a tarefa do programa LeafletSOAR, isto é, fazer o robô coletar apenas as jóias descritas nos seus Leaflet's e apenas um certo número delas, será feita a seguinte modificação nas regras responsáveis pela gravação das informações visuais do agente:
Regras: Propose seeEntityWithoutMemoryCount e Propose seeEntityWithMemoryCount
(<il> ^CREATURE <creature>)
(<creature> ^SENSOR.VISUAL.ENTITY <entity>)
(<creature> ^LEAFLETS.<color> <numberColor>)
(<creature> ^JEWELS.<color> <collected> < <numberColor>)
(<entity> ^TYPE <type> << JEWEL >>)
(<entity> ^COLOR <color> )
As linhas acima ilustram parte das condições das regras citadas. As regras testam se existe um objeto (<entity>) no campo visual do agente que seja do tipo JEWEL e cuja cor seja igual a de alguma jóia requisitada pelos Leaflet's do robô. O elemento ^LEAFLETS armazena o número de jóias que devem ser coletadas de cada cor. Um segundo elemento, ^JEWELS, armazena o número de jóias que já foram coletadas para cada cor. As regras testam se o número de jóias coletadas é menor que o número a ser coletado (<collected> < <numberColor>). Enquanto o robô não tiver coletado o número de jóias requisitadas, para uma determinada cor, as jóias dessa cor captadas pela câmera ainda serão gravadas na memória do agente.
Conforme descrito anteriormente, quando um objeto (jóia) é gravado na memória do agente, as regras que propõem operadores para mover e coletar tais objetos serão disparadas. Os operadores que movem o robô na direção dos objetos memorizados farão o robô se mover apenas na direção das jóias gravadas na memória do agente, para coletá-las. Assim, o robô não disperdiça tempo se movendo na direção de objetos que não estão nos seus Leaflet's. No entanto, ao se aproximar de um objeto (jóia ou comida) que esteja no seu caminho, o robô deverá coletá-los. Portanto, é necessário realizar duas modificações: criar uma segunda regra para coletar as jóias que não constam nos Leaflet's do robô, e, modificar a regra que propõe a coleta de comida. Ambas as regras devem propor operadores de coleta (eatFood e getJewel) quando um desses objetos estiver próximo do robô. As condições dessas regras estão descritas abaixo:
Regra Propose eatFood:
(state <s> ^io.input-link <il>)
(<il> ^CREATURE <creature>)
(<creature> ^SENSOR.VISUAL.ENTITY <entity>)
(<entity> ^TYPE JEWEL)
(<entity> ^DISTANCE <jewelDistance> < 30)
(<entity> ^NAME <jewelName>)
Regra Propose getJewel:
(state <s> ^io.input-link <il>)
(<il> ^CREATURE <creature>)
(<creature> ^SENSOR.VISUAL.ENTITY <entity>)
(<entity> ^TYPE FOOD)
(<entity> ^DISTANCE <foodDistance> < 30)
(<entity> ^NAME <foodName>)
As condições das regras acima apenas testam a ocorrência de algum objeto próximo (no caminho) do robô. Caso esses objetos, jóias e comida que não constam nos Leaflet's do robô, não fossem coletados, o robô colidiria com estes e conseqüentemente pararia de se mover. Outra modificação necessária foi alterar as preferências de proposição dos operadores. Novas regras foram escritas para propor e aplicar operadores que realizam a coleta das jóias dos Leaflet's (getJewelLeaflet). As regras de preferências de operadores tiveram que ser modificadas em relação a isso: nessas regras, as ocorrências do operador getJewel foram substituídas por getJewelLeaflet. Também foi necessário escrever uma nova regra para dar uma maior preferência para a proposição do operador getJewelLeaflet em relação ao operador getJewel.
O operador getJewelLeaflet será proposto e aplicado para coletar as jóias que constam nos Leaflet's do robô. Após ser aplicado, ou seja, após coletar mais uma jóia requisitada, há a necessidade de atualizar os elementos que armazenam a jóia que deve ser coletada (^ENTITY) e o número de objetos (^COUNT) gravados na memória (^MEMORY) das jóias que o agente deve coletar. A regra que aplica o operador, a regra apply*get*jewel*leaflet, se encarrega de realizar a atualização da estrutura ^MEMORY. Abaixo estão descritas partes das condições e das ações desta regra:
(<o> ^name getJewelLeaflet)
(<creature> ^MEMORY <memory>)
(<memory> ^COUNT <quantity>)
(<memory> ^ENTITY <memoryEntity>)
-->
(<ol> ^GET <command>)
(<command> ^Name <jewelName>)
(<memory> ^COUNT <quantity> -
^COUNT (- <quantity> 1))
(<memory> ^ENTITY <memoryEntity> -)}
Assim, após aplicar o operador getJewelLeaflet, o objeto coletado (jóia de certa cor) não será mais perseguido pelo robô. O programa DemoSOAR também impunha um limite para o número de objetos armazenáveis na memória (no máximo sete objetos).
Após coletar todas as jóias descritas nos seus Leaflet's, o robô pára de se mover pelo ambiente do WorldServer3D. A regra propose*delivery*jewels testa se o número de jóias coletadas (collected) é igual ao número de jóias que devem ser coletadas (number), para cada cor, e dispara o operador deliveryJewels. A ação desse operador é criar o comando ^DELIVERY, que gera a ação de parar o movimento do robô, e suspender o agente (halt). Seria interessante alterar esta regra para propor a ação de entregar (delivery) as jóias coletadas pelo robô para que este recebesse o pagamento (payment) devido.
Resultados da Simulação
Para executar adequadamente este programa, foi necessário usar a versão do WorldServer3D (modificada) disponibilizada pelo aluno Eduardo F. Jucá de Castro. As versões anteriores não computavam, nos leaflet's do robô, as jóias que foram coletadas. O código e executável desta versão estão disponibilizados no final deste relatório.
A figura a seguir ilustra o início da simulação:
O robô navega pelo seu ambiente à procura das jóias contidas nos seus leaflet's. Essas jóias estão descritas na janela (Leaflets) no canto superior esquerdo da próxima figura que exibe o final da simulação:
Pode-se observar, na figura acima, os Leaflet's do robô e as jóias que foram coletadas. O número de jóias coletadas, para algumas cores, foi maior do que o requisitado devido ao fato do robô também coletar as jóias que estão no seu caminho. A figura seguinte exibe a tela do SOARDebugger no final da simulação deste agente:
O agente foi suspenso (This Agent halted) e o comando de parada (stop()) foi enviado para o robô, fazendo-o parar de se mover pelo seu ambiente.
Arquivos Disponibilizados:
Projeto Completo: LeafletSOAR.rar
Arquivos Modificados: Aula 07 - SOAR Controlando o WorldServer3D.rar
Programa LeafletSOAR: launch.jnlp
WorldServer3D (executável): launch.jnlp