Matomo/interfaces club
Objectifs du module info
modifierLa carte PC a accès à deux ressources indépendantes : la carte électronique et la caméra firewire. Son but est donc de les utiliser au mieux afin de jouer le plus intelligemment (ou plutôt efficacement) possible. Le programme doit aussi être facilement configurable et pratique à déboguer, notamment en vue d'un changement tactique de dernière minute.
Unités
modifierCette partie pourrait presque faire partie du guide de style, elle concerne les unités utilisés par les interfaces.
- On utilisera pour désigner des longueurs, positions ou distances le millimètre.
- Les angles seront mesurés en radian.
- Les vitesses s'expriment en millimètre/seconde et les accélérations en millimètre/seconde².
- Les vitesses angulaires en radian/seconde et les accélérations en radian/seconde².
- Les fréquences en Hertz.
- Les temps en seconde.
Si pour une raison ou une autre une autre unité est utilisée, il faudra préciser clairement dans le commentaire et dans le nom de la fonction ou de la variable quelle unité est utilisée.
Numérotation des éléments du terrain
modifierVoir les schémas annexes... On considère que les éléments sont fixes et que c'est le robot qui démarre d'un cote ou de l'autre.
Les éléments importants du terrain sont numérotés de la façon suivante :
- Tx.y pour la position des totems, x représentant le numéro de camp et y la position dans le camp.
- Hx.y pour la position des trous (Holes), x représentant le numéro de camp et y la position dans le camp en partant du haut du terrain. La numérotation des trous suit la logique du terrain (les trous sont placés par symétrie axiale), mais le terrain risquant d'être modifié, il est probable que la numérotation change.
- Bx.y pour la position des balles, x représentant la couleur de la balle et y la position de la balle en partant en haut à gauche du terrain.
- Beaconx.y pour la position des balises, x représentant le numéro de camp et y le numéro de balise.
Description des couches et modules
modifierComme le montre le diagramme UML, le programme est divisé en couche représentant différents niveaux d'abstraction permettant de passer du protocole de communication avec le matériel, au niveau le plus abstrait qui permet de faire jouer un match complet en quelques lignes de script.
Légende des priorités
modifierIl existe un système de priorité de développement pour organiser le développement. 3 niveaux de priorités :
- *** : Urgent / indispensable. À faire en priorité, doit être parfaitement stable et fonctionnel
- ** : Intéressant. Indispensable pour faire une intelligence évoluée. Dans un 1er temps, facultatif.
- * : Facultatif. Le must du must pour de l'IA poussée... si on a fini en avance alors on gère ce genre de choses, mais on ne perds pas une minute dessus jusqu'à Avril.
Couche "Auxiliaires Globaux"
modifierCette couche regroupe l'ensemble des modules pouvant servir à plusieurs niveaux, on y retrouve typiquement des modules de débogages, de configurations, des définitions de types globaux ou des modules facilitant le codage (plus spécifique au C++).
Types Globaux
modifier- Description
Ce sont tous les types qui ne sont pas spécifiques à un module, et qui pourront donc être employés par plusieurs couches différentes. On a notamment des types permettant de spécifier des positions.
- Ébauche d'interface
Priorité globale : ***
typedef int64;
typedef uint64;
typedef int32;
typedef uint32;
typedef int16;
typedef uint16;
typedef int8;
typedef uint8;
class cRange
{
bool IsInRange(float value);
float GetNearestValueFrom(float value);
}
class cPos3D
{
cVector4D GetCoordInBase(const cBase &);
void SetCoordInBase(const cBase &, const cVector4D &);
};
class cBase
{
cBase(cBase *Parent);
SetPosition(const cVector3D&), Rotate();
};
- Variables globales
cBase RepereAbsolu; cBase RepereRobot;
On utilise deux repères globaux à l'application :
- Le premier est absolu, son origine se situe dans le coin a droite de la zone de départ du robot. L'axe des X est parallèle à la grande longueur du terrain, l'axe des Y est parallèle à la largeur du terrain et l'axe Z est perpendiculaire au terrain, orienté vers le haut.
- Le second est relatif, son origine se situe entre les deux roues motrices du robot. L'axe des X est dirigé vers l'avant du robot, l'axe des Y est dirigé vers la gauche du robot et l'axe Z est identique à l'axe Z du repère absolu. Enfin on définit l'angle thêta comme l'angle direct (sens trigo) entre l'axe X absolue et l'axe X relatif.
Math
modifier- Description
Primitives mathématiques diverses qui servent en particulier au positionnement (matrice4x4, vecteur3d)
- Ébauche d'interface
Priorité globale : ***
class cMatrix4x4
{
//opérations basiques sur les matrices :
//addition, multiplication... (avec une autre matrice ou un scalaire)
}
class cVector4D
{
//opérations basiques sur les vecteurs:
//addition, multiplication... (avec un autre vecteur, un scalaire ou une matrice)
}
+ fonctions permettant de créer des matrices de rotation, translation...
Constantes Globales
modifier- Description
Constantes généralement en relation avec le règlement, le terrain ou la mécanique du robot.
- Ébauche d'Interface
FIELD_WIDTH FIELD_HEIGHT HOLE_RADIUS BALL_RADIUS TOTEM_RADIUS BALL_NB HOLE_NB
Voir la section sur la numérotation des éléments du terrain.
HOLE_POSITION[x][y] TOTEM_POSITION[x][y] BALL_START_POSITION[x][y] BEACON_POSITION[x][y]
Helpers divers et Abstraction OS
modifier- Description
Couche d'abstraction avec le système d'exploitation.
En particulier il s'agit d'objets simplifiant :
- le multitâche (création de thread et synchronisation)
- le réseau (création de client/serveur TCP/IP)
- le chronométrage (utilisé uniquement par le module Timer)
- les interfaces graphiques (utilisé uniquement par le module IHM)
- Ébauche d'interface
Priorité globale : ***
class cMutex
{
Lock();
Release();
};
class cEvent
{
Wait();
Set();
};
+Gestionnaire d’événement/message avec des fonctions du style :
LinkEventToCallback(event, callback);
// Problème de durée de vie des événements ?
class cThread
{
virtual int32 threadMain();
};
//Automatise la protection de variable avec un mutex
template
class cProtectedData<typename T>
{
T Get();
void Set(const &T);
//Classe permettant de modifier l'objet directement
//(sans en faire de copie)
class Accessor;
};
// Idem cProtectedData, mais stocke la variable en
// plusieurs exemplaires (technique du backbuffering)
template
class cBufferedData<typename T>
{};
template class cSingleton<typename>;
uint64 GetCurrentTick();
uint64 GetTickFrequency();
date GetTodayDate();
Priorité : ** //Classes mères de client/serveur TCP/IP class cTCPIPServer; class cTCPIPClient;
Voir doc spécifique OSAL.
Timer
modifier- Description
Le timer peut envoyer des évènements à certains moments clefs du match. On peut aussi lui demander de lever un évènement toutes les x secondes. Il permet en plus de chronométrer, et de profiler facilement certaines parties du code.
- Ébauche d'interface
Priorité globale : ** (seule sont indispensables les méthodes permettant de connaître la fin du match, et de calculer un temps simplement et précisément)
uint64 GetCurrentTick(); uint64 GetTickFreq(); void MatchBeginNow(); uint32 GetMsAfterBeginning(); uint32 GetMsBeforeEnd();
[IT]: EndOfMatch(); [IT]: TimeBeforeEnd(int sec); [IT]: TimeAfterBeginning(int sec); [IT]: WarnMeIn(int ms); [IT]: WarnMeEvery(int ms);
class cProfiler - Priorité : * { SegmentStart(); SegmentEnd(); GetAveragePeriod(); GetAverageFrequency(); GetCallNumber(); }
IHM/Debug
modifier- Description
Le but de ce module est de faciliter le debuggage (éventuellement à distance). On y retrouve des classes permettant de logguer du texte, de tracer des courbes, d'afficher des images à l'écran assez facilement ou encore de lire le clavier de manière asynchrone.
- Ébauche d'interface
//Classe de log, fournit des ostream (type cout)
//l'affichage se fait dans des consoles graphiques
class cLog - Priorité : '''***'''
{
static std::ostream &Debug();
static std::ostream &Error();
static std::ostream &Info();
};
//type de figure pouvant etre dessine sur le terrain2D
class cPattern - Priorité : '''**'''
{
Move(const cPos2D &);
Hide(); / Show();
Blink(hz);
};
//Fenetre d'affichage de terrain vue du dessus
class cFieldDisplayer - Priorité : '''**'''
{
cPattern *AddPattern(Dot/Pixel/Line/Cross/Circle/Square + color);
[IT]: OnMouseClick() + GetMousePos()
};
//Fenêtre d'affichage vidéo
class cVideoDisplayer - Priorité : '''***'''
{
DrawPicture(rgb);
}
//Fenetre d'affichage de courbe
class cPlotDisplayer - Priorité : '''*'''
{
AddPoint(x, y);
SetScale(minX, maxX, minY, maxY);
}
//La fonction callback prend en paramètre l'id de la touche pressée
Priorité : ** [IT] OnKeyPressed(); [IT] OnSpecKeyPressed(keyid);
Config Manager
modifier- Description
Le Config Manager permet de configurer les constantes de chaque module au démarrage du programme. Il pourra par exemple lire ces constantes depuis un fichier de script (type .ini ou .xml).
- Ébauche d'interface
class Config - Priorité : ** { void Load(file); int32 GetVar(string); };
Couche IA (Intelligence Artificielle)
modifierIl s'agit de la couche décisionnelle de plus haut niveau : à terme la tactique devrait être programmé uniquement dans ces modules.
IA/Script
modifier- Description
Priorité : *
Il s'agit du module le plus haut : c'est lui qui prend les décisions, il peut éventuellement se servir d'un fichier de script (type Python ou Lua) pour lire les actions qu'il doit exécuter.
Il n'y a pas réellement d'interface pour ce module étant donné que c'est à lui d'utiliser les autres modules. Par contre si on décide d'utiliser un fichier de script, il faudra définir des fonctions "glues" permettant d’appeler les fonctions haut niveau du programme C++ depuis le script.
- Exemple de script simple du type homologation
On récupère une balle a une position sure et on va la déposer dans le trou le plus simple.
onStarter()
{
cParallelLinePath path1;
cBallPickupWP wp1(BALL_START_POSITION(B1.1));
path1.push(wp1);
Controler::Run(path1);
}
~ Approximatif : ~
onBallPickupReached(-400)
{
Automatisme::ActivateRollerDuring(800);
}
~
onNewBallInEjector()
{
Controler::Stop();
cParallelLinePath path2;
if(Automatisme::GetBallColor(LEFT_EJECTOR) == WHITE_BALL)
{
cBallDropping wp2(LEFT_EJECTOR, HOLE_POSITION(H2.1));
}
else if(Automatisme::GetBallColor(RIGHT_EJECTOR) == WHITE_BALL)
{
cBallDropping wp2(RIGHT_EJECTOR, HOLE_POSITION(H2.1));
}
path2.push(wp2);
Controler::Run(path2);
}
onBallDroppingReached()
{
if(Automatisme::GetBallColor(LEFT_EJECTOR) == WHITE_BALL)
{
Automatisme::DropBall(LEFT_EJECTOR);
}
else if(Automatisme::GetBallColor(RIGHT_EJECTOR) == WHITE_BALL)
{
Automatisme::DropBall(RIGHT_EJECTOR);
}
}
onEndOfGame()
{
cControler::EmergencyStop();
Automatisme::StopAll();
}
onCollisionDetected()
{
cControler::EmergencyStop();
Automatisme::StopAll();
}
Exemple de script en pseudo langage :
//On suppose que le script a déjà été lancé par l'IHM elec avec les fonctions suivantes :
//Automatisme->ActivateIA().Wait();
//nb = Automatisme->GetIHMStratSwitch();
//LaunchScript(nb);
Pos->SetTeam(Auto->GetTeamSwitch();
Auto->Starter().Wait();
ClothoidPath path;
pos = posaux->GetNearestWhiteBall();
wp = WaypointPickup(pos);
LinkEventToCallback(wp.Approaching(500), Auto->ActivateRollerDuring(1000));
path.push(wp);
pos = posaux->GetNearestWhiteBall(pos);
wp = WaypointPickup(pos);
LinkEventToCallback(wp.Approaching(500), Auto->ActivateRollerDuring(1000));
path.push(wp);
pos = posaux->GetNearestAlliedFreeHole(pos);
wp = Waypoint(pos);
LinkEventToCallback(wp.Approaching(1000), RecomputePathWithEjecteur());
path.push(wp);
path.go();
function RecomputePathWithEjecteur
path.clear();
if(Auto->GetBallColor(LEFT) == WHITE_BALL)
wp = WaypointDropping(pos, LEFT);
else
wp = WaypointDropping(pos, RIGHT);
path.push(wp);
path.recompute();
LinkEventToCallback(path.GoalReached(), Auto->DropBall(LEFT/RIGHT);
Couche Auxiliaire IA
modifierLes auxiliaires de l'IA sont la pour simplifier certains calculs et algorithmes et permettent donc à la couche d'IA d'utiliser des scripts assez simples. Cela permet aussi de factoriser certaines taches qui pourrait revenir systématiquement dans chaque script.
Positionnement auxiliaire
modifier- Description
Fournit à l'IA des fonctions simples pour obtenir des informations sur des éléments particuliers du jeu (comme la balle la plus proche ou le trou le plus accessible). Basiquement il s'agit d'algorithme de tri ou de recherche de min/max.
- Interface
Les fonctions retournent un booléen indiquant l'existence de l'élément recherché et modifie l'objet position le cas échéant. Le terme 'nearest' ici signifie proche en temps, pas forcément en distance. Il s'agit donc d'obtenir l'élément le plus accessible.
bool GetNearestWhiteBallPos(Pos &, posRobot = currentPos); (priorité : ***) bool GetNearestBlackBallPos(Pos &, posRobot = currentPos); (priorité : *)
bool GetNearestUntriggeredTotemPos(Pos &, posRobot = currentPos); (priorité : *)
bool GetNearestFreeAlliedHolePos(Pos &, posRobot = currentPos); (priorité : ***) bool GetNearestBlackAlliedHolePos(Pos &, posRobot = currentPos); (priorité : *)
bool GetNearestFreeOpponentHolePos(Pos &, posRobot = currentPos); (priorité : **) bool GetNearestWhiteOpponentHolePos(Pos &, posRobot = currentPos); (priorité : *)
//retourne le point le plus proche de la bordure void GetNearestHorizontalBorderPos(Pos &, posRobot = currentPos); (priorité : **) void GetNearestVerticalBorderPos(Pos &, posRobot = currentPos); (priorité : **)
//Fonction de retour d'info IA → Positionnement Priorité : *** void FrontTouchedDownHEdge(); void FrontTouchedUpHEdge(); void FrontTouchedLeftVEdge(); void FrontTouchedRightVEdge(); void BackTouchedDownHEdge(); void BackTouchedUpHEdge(); void BackTouchedLeftVEdge(); void BackTouchedRightVEdge();
Pathfinder
modifier- Description
Le pathfinder permet de déterminer les trajectoires optimales permettant d'aller d'un point à un autre.
- Ébauche d'interface
class cPath //- Priorité : '''***'''
{
public:
virtual void Compute();
virtual cmd TranslateToMotorCmd();
virtual float EstimateDeviation(const cPos &Current);
virtual float EstimateRemainingTime();
void Push(const cWaypoint &);
void Push(const cFixedWaypoint &);
static float EstimateTime(pos From, pos To);
}
Des classe spécialisées peuvent hériter de cPath afin d'avoir plusieurs style de trajectoires, par exemple on peut avoir un cPath version clothoide, un cPath version ligne droite et rotation sur place, ou encore un cPath ligne droite horizontale ou verticale uniquement.
class cClothoidPath : public cPath; Priorité : '''*'''
class cStraightLinePath : public cPath; Priorité : '''**'''
class cParallelLinePath : public cPath; Priorité : '''***'''
class cControler : public cSingleton - Priorité : '''***'''
{
void Run(const cPath &); == TransitionToPathAt (path, 0)
void Stop();
void EmergencyStop();
void TransitionToPathAt(const cPath &, ~uint32 Tick~);
cWaypoint GetInitWaypointAt(uint32 Tick);
cWaypoint GetNextWaypoint();
[IT]:
GoalReachedIn(uint32 ms = 0);
WaypointReachedIn(uint32 ~waypnb~, uint32 ms = 0); (dans Waypoint ?)
DeviationDetected(float difference);
ObstacleOnPath();
};
class cWaypoint - Priorité : '''***'''
{
virtual cRange GetAllowedSpeedRange();
virtual cRange GetAllowedAngularSpeedRange();
virtual cRange GetAllowedThetaRange();
virtual enum GetAllowedDirection();
virtual cPos GetPos(float theta);
//GenerateFixedWaypoint(...);
};
class cFixedWaypoint
{
float GetLeftSpeed();
float GetRightSpeed();
cPos GetPos();
float GetTheta();
};
Des classes spécialisées peuvent hériter de cWaypoint, pour définir par exemple des Waypoint de recallage sur bordure, d'attrapage de balle ou encore de dépose de balle. Ceci permet d'ajuster plus précisément la position du robot : pour déposer une balle dans un trou, il faut aligner l'éjecteur de balle avec le trou (et non pas le centre du robot).
class cEdgeFrontAdjustement : public cWaypoint; //(priorité : '''**''')
class cEdgeBackAdjustement : public cWaypoint; //(priorité : '''**''')
class cBallPickup : public cWaypoint; //(priorité : '''***''')
class cBallPickupAgainstBorder : public cWaypoint; //(priorité : '''**''')
class cBallDropping : public cWaypoint; //(priorité : '''***''')
class cBallAspiration : public cWaypoint; //(priorité : '''*''')
class cTotemTriggering : public cWaypoint; //(priorité : '''*''')
Synthèse
modifierCette couche permet de regrouper les informations par catégorie sans distinctions de leur source (Vidéo ou HAL). Par exemple, on peut contrôler et connaître l'état d'un capteur par la même interface même si l'état est connu par le biais de la vidéo et que le contrôle se fait par la HAL.
Positionnement
modifier- Description
Le module de positionnement synthétise toutes les informations relatives aux positions des éléments du jeu (balles, robots, totems, éléments du terrain), il peut aussi donner l'incertitude sur ces valeurs. L'intérêt est notamment de faire la synthèse des informations redondantes (particulièrement pour la position du robot : odométrie, caméra, balise).
- Ébauche d'interface
(priorité globale : ***)
struct sBall
{
enum color;
pos; delta;
trustyness;
bool isInHole;
};
struct sOppBot
{
pos; delta;
trustyness;
};
struct sRobot
{
pos; delta;
angle; delta;
vitesse;
};
struct sTotem
{
id;
bool Triggered;
enum side;
pos;
};
struct sHole
{
id;
enum side;
pos;
enum filled;
};
vector<sBall> GetKnownWhiteBall(); //(priorité : '''***''')
vector<sBall> GetKnownBlackBall(); //(priorité : '''*''')
vector<sTotem> GetKnownTotem(); //(priorité : '''*''')
sOppBot GetOpponentBot(); //(priorité : '''*''')
sRobot GetRobot(); //(priorité : '''***''')
sHole[28] GetHole(); //(priorité : '''***''')
//fonctions de retour d'infos IA -> Positionnement
void SetTeamColor(enum); //(priorité : '''***''')
void SetPosRobotX(); //(priorité : '''***''')
void SetPosRobotY(); //(priorité : '''***''')
void SetWhiteBallInHole(id); //(priorité : '''**''')
void SetBlackBallInHole(id); //(priorité : '''*''')
void SetTotemTriggered(id); //(priorité : '''*''')
Automatisme
modifier- Description
Le module d'automatisme gère les différents actionneurs et capteurs (comme la tirette de démarrage). Il permet donc de connaître l'état des actionneurs et de les contrôler assez facilement. On peut par exemple savoir la couleur de la balle actuellement dans le système d'éjection (vidéo) et commander son éjection (HAL).
- Ébauche d'interface
(priorité globale : ***)
La définition des interfaces du driver HAL générique dépasse le cadre de ce document, voir la doc spécifique.
void ActivateRoller(bool); void ActivateFan(bool);
void ActivateRollerDuring(int ms); void ActivateFanDuring(int ms);
void EjectBall(enum); void DropBall(enum); enum GetBallColor(enum);
enum GetIHMTeamSwitch(); int GetIHMStratSwitch(); void StopAll(); CommandeAsservBasNiveau.... (Vitesse moteur + distance à parcourir) pour chaque roue.
[IT] CollisionDetected(); [IT] NewBallInEjector(); [IT] ActivateIA(); [IT] Starter();
Drivers (spécifiques et génériques)
modifierLa couche de drivers spécifiques dépend entièrement de la mécanique du robot utilisé et du règlement du jeu. Son interface explicite donc les noms des divers éléments. Cette couche est directement relié à la couche de drivers génériques, elle effectue une simple traduction. La couche générique est plus universelle et devrait pouvoir être réutilisée pour plusieurs règlement/application.
Vidéo et vidéo lib
modifier- Description
Le module de vidéo utilise la librairie vidéo afin de détecter les éléments du jeu. La librairie vidéo est quant à elle beaucoup plus générique et devrait pouvoir être réutilisée telle quelle d'année en année.
- Ébauche d'interface
La définition des interfaces du driver vidéo générique dépasse le cadre de ce document, voir la doc spécifique.
vector<sBall> GetExternalBall(); - Priorité : *** vector<sBall> GetInternalBall(); - Priorité : *
sRobot GetRobotPos(); - Priorité : **
vector<sTotem> GetTotem(); - Priorité : ** enum GuessTeam(); - Priorité : ½*
HAL (Hardware Abstraction Layer) et Driver Elek
modifier- Description
De la même façon que la vidéo, l'HAL fournit une implémentation spécifique du driver elek. L'HAL fournit donc une interface explicitant le nom des actionneurs, tandis que le driver elek reste totalement générique et ne dépend que de l'électronique utilisée.
- Ébauche d'interface
(priorité globale : ***)
[A définir plus précisément avec la méca et l'asservissement]
Pos GetOdometriePos(); angle-time GetGonioTick();
+ Idem Automatisme
Simulateur
modifier- Description
Le simulateur permet de coder les modules haut niveau (au dessus de la couche de HAL) sans avoir implémenté les couches inférieures. Dans notre cas, le simulateur va nous aider à coder :
- Dans un premier temps : les modules de recherche et de génération de chemin.
- Ensuite : les modules de synthèses de position et de positionnement auxiliaire.
- Enfin indirectement, le module d'intelligence artificielle.
- Interface
L'interface possède donc deux niveaux (mutuellement exclusive, on utilise qu'une des deux):
- Un niveau qui simule la couche de synthèse avec notamment les fonctions :
sRobot GetRobot(); //infos diverses sur l'etat du robot (cf struct sRobot) cEvent Starter(); //tirette de demarrage
//Commande Asserv PushCmd(v1,v2, d1,d2); //Envoie une commande a l'asservissement ClearCmd(); //Efface les commandes courantes de l'asservissement
//+Eventuellement les fonctions de positionnement des autres objets: vector<sBall> GetKnownWhiteBall(); vector<sBall> GetKnownBlackBall(); vector<sTotem>GetKnownTotem(); sOppBot GetOpponentBot(); sHole[28] GetHole();
- Un second niveau simulant la couche de HAL :
//Vidéo : vector<sBall> GetExternalBall(); sRobot GetRobotPos(); //camera
//Elek : sRobot GetRobotPos(); //odometrie cEvent CollisionDetected();
À ces deux niveaux d'interfaces, vient se rajouter le module de constantes.
Le simulateur doit aussi être configurable, on doit pouvoir notamment régler les erreurs/incertitudes sur les capteurs, un decallage entre la commande et la mise en action (simulation de la latence de la transmission usb), ...