O objetivo da aula de hoje é executar um tutorial prático utilizando a arquitetura LIDA.
Faça o download com o tutorial prático do LIDA aqui. Se você fez o download da arquitetura LIDA na aula passada, é possível que esse mesmo arquivo lhe tenha sido enviado por e-mail. Nesse caso, utilize a versão mais recente do mesmo, que é a que lhe foi enviada. Abra o arquivo zip e o instale em sua máquina. Verifique a árvore de diretórios, com os arquivos instalados, para os dois exemplos que estudaremos na aula de hoje, o exercício básico e o exercício de Artificial Life. Observe principalmente o arquivo LIDA-Tutorial-Exercises.pdf, que contém o roteiro dos exercícios que seguiremos na aula de hoje.
Siga o roteiro apresentado no arquivo LIDA-Tutorial-Exercises.pdf e execute as seguintes atividades:
Relate os progresso obtidos em seu relatório.
Siga o roteiro apresentado na continuidade e execute as seguintes atividades:
Relate os progressos obtidos em seu relatório.
Após baixar o pacote LIDA, o primeiro passo foi carregar o projeto 1 desse tutorial.
Figura 1 - Pacote lida no Netbeans
Uma vez carregado, foram analisados diversos arquivos com as principais implementações conforme descritos no tutorial. Alguns arquivos de configurações também foram vistos (xml e properties).
Figura 2 - Arquivos de Configurações
Um detalhe importante é que o arquivo lidaConfig.properties faz um link com os demais arquivos de configuração, porém esse arquivo não tem nome definido estaticamente. Podemos alterar o arquivo inicial de configuração bastando apenas passar o nome do novo arquivo como parametro de execução (argumento).
Figura 3 - Arquivo inicial de configuração
Pode-se ver também alguns detalhes de execução da simulação, como iniciar/parar uma simulação, rodar em modo "passo a passo", inclusive definindo o numero de passos a serem executados por vez, e o tempo de duração de cada passo, conforme figuras abaixo.
Figura 4 - Rodando Lida
Figura 5 - Start/Pause
Figura 6 - Step Mode
Figura 7 - Tick Duration
Outro ponto interessante e muito útil para desenvolver uma aplicação é a parte de depuração. Na figura abaixo foi alterado a classe a ser logada e nível da depuração. Nota-se que mais mensagens foram mostradas uma vez mudado o nível de INFO para FINEST.
Figura 8 - Logging
Na aba Configuration Files podemos ver quais arquivos de configuração estão em uso pelo framework.
Figura 9 - Configuration files
Nota-se que essas configurações são as mesmas setadas inicialmente no arquivo lidaConfig.properties.
#Agent properties
lida.agentdata=configs/basicAgent.xml
lida.elementfactory.data=configs/factoryData.xml
#Gui properties
lida.gui.panels=configs/guiPanels.properties
lida.gui.commands=configs/guiCommands.properties
lida.gui.enable=true
#Logging properties
lida.logging.configuration=configs/logging.properties
No próximo passo alteramos o arquivo de configuração relacionado ao agente para basicAgent_ex2.xml. Ao executar a aplicação com essa configuração, pode-se notar que apenas o botão 1 é pressionado.
Figura 10 - Apenas Botão 1 na segunda execução
Podemos ver que na aba "PAM Table" apens os sensores relacionados a Vermelho e Quadrado são percebidos pelo agente (ativação vai para 1.0 e cai até o 0.0 - Durante o screenshot o valor estava em 0.96)
Na aba Perceptual buffer podemos ver a percepção do que foi sensorizado pela plataforma.
STUDY QUESTION 1.1: What module of the LIDA Model is the Perceptual Buffer in? Where do the nodes in this buffer come from? How do they differ from the nodes in other modules?
O Perceptual Buffer faz parte do Workspace. Esses nodes chegam do PAM e são identificados pelo id unico.
Figura 11 - Perceptual Buffer
Na aba Global Workspace podemos acompanhar algumas colisões e seu histórico.
Figura 12 - Global Workspace
STUDY QUESTION 1.2: Where do the coalitions in the Global Workspace come from? Where do they go? Think about how the answer to these questions depends on whether we are discussing the LIDA Model or a specific agent implemented using the Framework.
As colisões no Global Workspace são provenientes do Workspace e o resultado é distribuido entre os listeners.
A aba de CSM (Current Situation Model) foi adicionada na posição 7 alterando o arquivo de configurações guiPanels.properties.
csm = CSM,edu.memphis.ccrg.lida.framework.gui.panels.NodeStructurePanel,B,7,Y,Workspace.CurrentSituationalModel
Figura 13 - Adição da aba CSM
STUDY QUESTION 1.3: How are the contents of the Current Situational Model related to the contents of the Perceptual Buffer? to the contents of PAM? Think about how the answer to these questions depends on whether we are discussing the LIDA Model, or a specific agent implementedin the Framework.
Os nodes do CSM e do Perceptual Buffer estão relacionados com o PAM. Uma vez percebido e ativado pelo PAM, o node se torna parte da percepção atual.
No próximo passo alteramos o arquivo de configuração relacionado ao agente para basicAgent_ex3.xml. Nesse agente, algumas informações necessitam ser adicionadas para correto funcionamento. Editamos o XML adicionando a seguinte informação:
O XML gerado relacionado a essas informações ficou da seguinte forma:
<module name="Environment">
<class>myagent.modules.ButtonEnvironment</class>
<param name="height" type="int"> 10 </param>
<param name="width" type="int">10 </param>
<taskspawner>defaultTS</taskspawner>
</module>
Ao iniciar a aplicação, vemos algumas diferenças quanto a execução anterior. Em ambos os exercícios o módulo PAM detecta vermelho/quadrado porém não azul/círculo, mas no caso do exercício 3, essa informação não é passada ao Perceptual Buffer. Isso acontece por que a ligação entre o PAM e o Workspace não foi definida na configuração do agente. Isso corresponde ao Move Percept (item 2) na figura abaixo.
Figura 14 - Workspace module
Esse comportamento acontece pois o PAM Listener não esta configurado no modulo Workspace. A implementação a seguir soluciona essa situação.
O XML gerado relacionado a essas informações ficou da seguinte forma:
<listener>
<listenertype>edu.memphis.ccrg.lida.pam.PamListener</listenertype>
<modulename>PerceptualAssociativeMemory</modulename>
<listenername>Workspace</listenername>
</listener>
Com isso as informações chegam ao Workspace e são mostradas na aba Perceptual Buffer como no exemplo 2.
Para o agente detectar a cor azul e a forma circulo, adicionamos dois novos detectores no módulo PAM.
O XML gerado relacionado a essas informações ficou da seguinte forma:
<task name="BlueDetector">
<tasktype>ColorDetector</tasktype>
<ticksperrun>3</ticksperrun>
<param name="color" type="int">-16776961</param>
<param name="node" type="string">blue</param>
</task>
<task name="CircleDetector">
<tasktype>ShapeDetector</tasktype>
<ticksperrun>3</ticksperrun>
<param name="area" type="int">31</param>
<param name="backgroundColor" type="int">-1</param>
<param name="node" type="string">circle</param>
</task>
Após essa implementação, é possível ver a Percepção do Azul e do Circulo no módulo PAM, porem as ações relacionadas (apertar o Botão 2) não acontece pois essa informação não é repassada ao Global Workspace. Isso acontece por que não há nenhum attention codelet para adicionar esses nodes no Global Workspace. A implementação do attention codelet deve ser feita conforme descrito abaixo.
O XML gerado relacionado a essas informações ficou da seguinte forma:
<task name="BlueCircleCodelet">
<tasktype>BasicAttentionCodelet</tasktype>
<ticksperrun>5</ticksperrun>
<param name="nodes" type="string">blue,circle</param>
<param name="refractoryPeriod" type="int">30</param>
<param name="initialActivation" type="double">1.0</param>
</task>
Agora, ao rodar a aplicação, o comportamento de detecção de Azul, Vermelho, Quadrado e Círculo acontecem devidamente.
Uma opção para se rodar o LIDA é sem a interface gráfica. Para isso basta alterar o arquivo de configurações lidaConfig.properties setando para false o atributo lida.gui.enable.
A figura abaixo mostra um exemplo de execução sem a interface gráfica.
Figura 15 - execução do LIDA sem GUI
Após alterar os parametros de ticksPerRun do attention Codelet relacionado a detecção do Quadrado Vermelho, pode-se notar melhorias no numero de colisões e broadcasts:
As modificações consistem em multiplicar por 10 os valores de ticksperrun e refractoryPeriod
Como ultima atividade desse tutorial, foi implementada uma função de quando nenhum formato for identificado, liberar os botões. A execução e código podem ser conferidos a seguir.
Figura 17 - Execução da liberação de teclas mediante detecção de vazio
Classe de detecção de vazio:
package myagent.featuredetectors;
import java.util.HashMap;
import java.util.Map;
import edu.memphis.ccrg.lida.pam.tasks.BasicDetectionAlgorithm;
public class EmptyFeatureDetector extends BasicDetectionAlgorithm {
public static final int TOLERANCE = 5;
/*
* Size of the shape in pixels.
*/
private int soughtArea = 1000;
/*
* The white background color should not be counted in determined the number of pixels in the shape
*/
private int backgroundColor = 0xFFFFFFFF;
private Map<String, Object> smParams = new HashMap<String, Object>();
/*
* The square in the environment is 20x20 = 400 pixels. Thus it takes up ~40% of the area.
* The circle has radius = 10 so its area ~= 314 pixels. Thus it takes up ~31% of the area.
*/
@Override
public void init() {
super.init();
smParams.put("mode","all");
backgroundColor = 0xFFFFFFFF;
}
@Override
public double detect() {
int[] layer = (int[]) sensoryMemory.getSensoryContent("visual",smParams);
int area=0;
for(int i=0;i<layer.length;i++){
if(layer[i]!=backgroundColor){
area++;
}
}
area=(area*1000)/layer.length;
if(area < 1){
return 1.0;
}
return 0.0;
}
}
Mudanca no factoryData.xml
<task name="EmptyDetector">
<class>myagent.featuredetectors.EmptyFeatureDetector</class>
<ticksperrun>5</ticksperrun>
<associatedmodule>SensoryMemory</associatedmodule>
<associatedmodule>PerceptualAssociativeMemory</associatedmodule>
<param name="node" type="string">empty</param>
</task>
Mudanca no basicAgent.xml
<task name="EmptyDetector">
<tasktype>EmptyDetector</tasktype>
<ticksperrun>3</ticksperrun>
<param name="node" type="string">empty</param>
</task>
.
.
<task name="EmptyCodelet">
<tasktype>BasicAttentionCodelet</tasktype>
<ticksperrun>5</ticksperrun>
<param name="nodes" type="string">empty</param>
<param name="refractoryPeriod" type="int">30</param>
<param name="initialActivation" type="double">1.0</param>
</task>
.
.
<param name="scheme.3">if empty, release press|(empty)()|action.releasePress|()()|0.01</param>
.
.
<param name="smm.3">action.releasePress,algorithm.releasePress</param>
.
.
O Segundo tutorial traz um agente, que recebe energia comendo hamburgers, e perde energia ao passar do tempo ou quando é atacado por algum macaco. Há no ambiente algumas pedras e arvores. Todos os elementos estão distribuidos em no cenário.
A pŕimeira atividade foi bem simples, onde iniciamos o ambiente e vemos algumas informações como as dos elementos do ambiente e as informações no PAM e a fila de execução.
No segundo tutorial começamos a explorar o funcionamento do agente. Podemos notar que o agente fica "fraco" quando sem saúde, e não se movimenta mais no ambiente de forma apropriada. Para esse teste mudamos o XML para que a quantidade de comida no ambiente fosse 0, a fim de esgotar a energia do agente.
Para dar continuidade, criamos um detector de saúde baixa em nosso agente e adicionamos esse detector no framework.
A classe ficou da seguinte forma: (ela foi baseada no detector de energia alta, onde acima de 0.6, é ativada, e na energia baixa, abaixo de 0.33)
public class BadHealthDetectorSolution extends BasicDetectionAlgorithm {
private final String modality = "";
private Map<String, Object> detectorParams = new HashMap<String, Object>();
@Override
public void init() {
super.init();
detectorParams.put("mode", "health");
}
@Override
public double detect() {
double healthValue = (Double) sensoryMemory.getSensoryContent(modality, detectorParams);
double activation = 0.0;
if (healthValue <= 0.33) {
activation = 1.0;
}
return activation;
}
}
Configuramos o XML de acordo com a classe acima:
<task name="BadHealthDetector">
<tasktype>BadHealthDetector</tasktype>
<ticksperrun>3</ticksperrun>
<param name="node" type="string">badHealth</param>
</task>
Criamos também o detector do predador (macaco) quando visto a frente e configuramos o XML apropriadamente. Essa detector é baseado no ObjectDetector.
<task name="predatorFrontDetector">
<tasktype>ObjectDetector</tasktype>
<ticksperrun>3</ticksperrun>
<param name="node" type="string">predatorFront</param>
<param name="object" type="string">predator</param>
<param name="position" type="int">1</param>
</task>
Na próxima atividade, checaremos alguns impactos de attention codelets. Inicialmente criaremos uma tarefa para predadores baseados na tarefa de pedras. Para isso alteramos o XML do framework da seguinte forma:
<task name="PredatorAttentionCodelet">
<tasktype>NeighborhoodAttentionCodelet</tasktype>
<ticksperrun>5</ticksperrun>
<param name="nodes" type="string">predator</param>
<param name="refractoryPeriod" type="int">50</param>
<param name="initialActivation" type="double">1.0</param>
</task>
Seguindo as instrucoes do tutorial, o tipo de tarefa é NeighborhoodAttentionCodelet. Vemos durante a execução colisões com o nó predator.
No próximo passo alteramos o valor de ativação do Codelet de comida, de 1.0 para 0.01 e podemos notar que o agente nunca reaje a comida. Voltamos esse falor para 1.0 e o refractoryPeriod mudamos de 50 para 10 e vemos que as colisões de comida acontecem muito mais frequentes que inicialmente no mesmo intervalo.
Depois pudemos ver os exemplos relacionados a PM, PAM e ElementFactory. Carregando o exercício 4 vemos que o agente não se move até que sua energia caia a um patamar de 0.66. Ao modificar o seguinte parametro no XML, vemos o agente se movimentando normalmente:
Parametro inicial
<param name="scheme.10b">if emptyFront turn around|(emptyFront)()|action.TurnAround |()() |0.1</param>
Parametro modificado
<param name="scheme.10b">if emptyFront move|(emptyFront)()|action.moveAgent |()() |0.1</param>
Podemos ver na regra que em caso seja detectado emptFront, o agente estava girando no pŕoprio lugar. Essa modifucação faz com que a ação seja alterada para moveAgent, permitindo mobilidade ao agente quando a frente estiver desocupada.
Na próxima tarefa alteramos a inicialização de alguns elementos do PAM. Para isso modificamos a classe de inicialização de BasicPamInitializer para CustomPamInitializer.
Original: <initializerclass>edu.memphis.ccrg.lida.pam.BasicPamInitializer</initializerclass>
Modificado: <initializerclass>alifeagent.initializers.CustomPamInitializer</initializerclass>
A classe CustomPamInitializer estava parcialmente implementada. Logo adicionamos os itens referentes ao exercício, fazendo um link do nó food com object.
// Obtain the 'food' node from PAM
child = pam.getNode("food");
// Add a Link in PAM from 'food' to 'object' with link category PARENT
pam.addDefaultLink(factory.getLink(child, objectNode, PerceptualAssociativeMemoryImpl.PARENT));
Podemos ver esse link na aba PAM GUI após executar o agente.
Como último passo desse exercício, modificamos o decaimento do nó object para slowDecay (decaimento lento) com o seguinte código adicionado na classe CustomPamInitializer.
DecayStrategy decayStrategy = factory.getDecayStrategy("slowDecay");
objectNode.setDecayStrategy(decayStrategy);
Por fim adicionamos um attention codelet para quando uma arvore for avistada, o agente se mova e alteramos a classe do ActionSelection para usar uma classe customizada implementada extendendo FrameworkModuleImpl.
<param name="scheme.11">if treeFront turn around|(treeFront)()|action.moveAgent |()() |0.1</param>
<module name="ActionSelection">
<class>alifeagent.CustomActionSelection</class>
<param name="actionSelection.ticksPerStep" type="int"> 10</param>
<taskspawner>defaultTS</taskspawner>
</module>
Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer