Buts: Simuler les mécanismes internes de prédation chez le scorpion
L'objectif de cette étape est de programmer la classe NeuronalScorpion capable de simuler les mécanismes de prédation du scorpion.
Nous considérerons qu'un NeuronalScorpion est équipé de 8 senseurs situés à l'extrémité de ses pattes. Pour simplifier, ces senseurs seront à équidistance du centre du scorpion, sur une rayon getAppConfig().scorpion_sensor_radius depuis le Vec2d servant de position au scorpion :
![]() | ← modélisation simplifiée des senseurs (cercles verts) Le CircularCollider a été dessiné en noir pour une meilleur visibilité. |
Commencez par créer une classe Sensor vide.
Codez ensuite une amorce de la classe NeuronalScorpion dérivant de Scorpion. Un NeuronalScorpion et caractérisé par:
Nous verrons un peu plus loin que le NeuronalScorpion va être dans différents états (de façon analogue au Scorpion standard). Lorsque l'un de ses senseurs est activé, le scorpion passe dans un état indiquant qu'il perçoit les signaux d'une onde et va rester dans cet état pendant un temps limité. Lorsque ce temps est écoulé il va désactiver l'ensemble de ses senseurs.
[Question Q4.5] Comment proposez-vous de représenter, l'ensemble des senseurs et leurs angles dans NeuronalScorpion ? Répondez à cette question dans votre fichier fichier REPONSES en justifiant vos choix.
Le calcul fait par NeuronalScorpion pour estimer la direction de sa cible va dépendre de la position de ses senseurs. Il est donc judicieux de doter cette classe d'une méthode getPositionOfSensor retournant la position d'un senseur donné dans le repère global.
[Question Q4.6] Quel prototype proposez-vous pour la méthode getPositionOfSensor? Répondez à cette question dans votre fichier fichier REPONSES en justifiant vos choix.
Il s'agit maintenant de modéliser plus précisément les senseurs.
Chaque senseur est caractérisé essentiellement par :
Un senseur est activé lorsque les ondes qui le touchent (ou plus précisément qui touchent sa position) cumulent une intensité dépassant un certain seuil (getAppConfig().sensor_intensity_threshold).
L'intensité des ondes de l'environnement en un point donné est calculée comme la somme des intensités de chaque onde en ce point.
L'intensité d'une onde en un point location se calcule comme suit : si location est sur le rayon de l'onde (à getAppConfig().wave_on_wave_marging près), et si location appartient à un des arcs de cette dernière, alors l'intensité se calculera tel que décrit ici, sinon ce sera la valeur zéro.
[Question Q4.7] Quelle(s) méthode(s) ajoutez-vous et à quelle(s) classe(s) pour qu'un senseur puisse connaître l'intensité cumulée des ondes qui le touchent; sans pour autant coder de getters trop intrusifs donnant accès à l'ensemble des ondes de l'environnement ? Répondez à cette question dans votre fichier fichier REPONSES et adaptez votre code en conséquence.
Dès qu'il est activé, un senseur va augmenter son score à chaque pas de simulation. Il va également commencer à inhiber un certain nombre de senseurs situés en face de lui.
![]() | ← le senseur 5 inhibe les senseurs 0, 1 et 2
|
Soit si un senseur connecté au senseur this. Sur un pas de temps dt, this va agir sur si en augmentant sont degré d'inhibition proportionellement à son propre score (plus le score de this est élevé et plus il inhibe si). Concrètement, le degré d'inhbition de si va être augmenté du score de this multiplié par getAppConfig().sensor_inhibition_factor.
Pour résumer, à chaque pas de simulation et tant qu'il est actif, un senseur augmente le degré d'inhibition des senseurs qui lui sont connectés et met à jour son propre score en l'augmentant de 2.0 * (1.0 - inhibitor).
Lorsqu'il est inactif, le senseur va être à l'écoute d'ondes potentielles. L'algorithme est simplement le suivant: si l'intensité d'une onde perçue au niveau de la position du senseur est suffisamment forte (supérieur à getAppConfig().sensor_intensity_threshold), le senseur devient actif.
[Question Q4.8] Le senseur, pour connaître sa position à donc besoin de connaître le scorpion auquel il appartient. Qu'est-ce que cela implique au niveau des dépendances entre les classes NeuronalScorpion et Sensor ? comment le gérez-vous? Répondez à ces questions dans votre fichier fichier REPONSES.
[Question Q4.9] Cela a t-il une incidence sur la façon de construire un senseur? Répondez à ces questions dans votre fichier fichier REPONSES en justifiant votre réponse.
Au vu de ce qui précède, proposez le codage complet de la classe Sensor. Cette dernière doit dériver de Updatable et mettre à jour son score et son degré d'inhibition selon le descriptif précédente. Vous ne vous préoccuperez pas de dessiner les senseurs pour le moment.
Comme évoqué précédemment, le NeuronalScorpion n'est sensible à l'activation des senseurs que pendant un temps limité. Une fois ce temps écoulé, il va désactiver l'ensemble de ses senseurs.
Maintenant que les senseurs sont codés, nous pouvons compléter les aspects comportementaux du NeuronalScorpion. Le fonctionnement de ce type de scorpion (méthode update et updateState ) ne tient compte que de la prédation.
Vous trouverez ci-dessous des indications sur :
La direction vers la cible estimée par le scorpion est simplement la résultante :
où si est le score du senseur i et v⃗i son vecteur position.
A une estimation d⃗ de la direction de la cible sera associé un score donné simplement par la norme de d⃗.
Le comportement d'un NeuronalScorpion est conditionné par des états différents de ceux du Scorpion (pour ne tenir compte que de la prédation notamment):
[Question Q4.10] Comment et où proposez-vous de modéliser les états possibles? Répondez à cette question dans votre fichier fichier REPONSES en justifiant vos choix.
[Question Q4.11] Comment proposez-vous de modéliser les horloges associées aux états? Répondez à cette question dans votre fichier fichier REPONSES en justifiant vos choix.
L'algorithme mis en oeuvre est le suivant :
[Question Q4.12] Quelles actions liées à la gestion des différentes horloges impliquées devez-vous entreprendre et dans lesquels des états du scorpion? Répondez à cette question dans votre fichier fichier REPONSES en justifiant votre réponse.
Pour être compatible avec les fichiers de tests fourni, le NeuronalScorpion aura des constructeurs aux signatures analogues à ceux codés pour Scorpion. Il aura WANDERING comme état par défaut. La direction estimée de la cible est bien sûr nulle au départ.
Le test fourni NeuronalTest.cpp que vous pouvez lancer au moyen de la cible neuronalTest permet de tester la perception des ondes par le scorpion.
Il est programmé de sorte à ce qu'un clic gauche avec la souris crée un onde. Vous devriez voir le scorpion d'orienter en fonction de la perception de cette onde tel que montré par la vidéo suivante :
[Video : Modèle neuronal du scorpion] |
Testez également le fait que les obstacles bloquent la perception des ondes par le scorpion.
Si le scorpion ne se comporte pas tel que souhaité, quelques compléments graphiques peuvent vous aider à «debugger» votre code.
Pour vérifier vos développements, complétez l'affichage en mode «debugging» de sorte à :
Vous devriez pouvoir observer un fonctionnement analogue à celui montré par la petite vidéo ci-dessous.
[Video : Affichage du modéle neuronal en mode «debugging»] |
L'affichage en mode «debugging» vous permettra notamment de vérifier que les obstacles font bien barrage à l'activation des neurones.
[Video : Obstacles empêchant l'activation des neurones] |
Le test fourni FinalApplication.cpp permet de tester la cohabitation d'un NeuronalScorpion et d'une WaveLizard dans un environnement.
Les modalités de lancement du test seront les suivantes :
Le scorpion devrait alors pouvoir repérer la lézard au moyen des ondes qu'il émet, comme dans la vidéo ci-dessous :
[Video : Scorpion repérant un lézard hors de son champs de vision grâce à la perception des ondes qu'elle émet en marchant.] |
La simulation du modèle neuronal est maintenant terminée. Bravo d'avoir atteint le résultat voulu! Lors de l'ultime étape du projet vous allez paufiner un peu l'outil de simulation et aurez le loisir de coder quelques extensions librement choisies.