MIRO est une plate-forme de robot intérieure ouverte. Partie 4 - Composant logiciel: ARDUINO (AVR)

image

Nous continuons de démonter le composant logiciel de la plateforme MIRO. Je voudrais examiner plus en détail exactement le logiciel sous AVR. Par conséquent, nous consacrerons deux parties à la question. Dans le premier, nous décrivons la structure générale de la bibliothèque, et dans le second - l'implémentation de certaines méthodes de classe clés.

Table des matières: Partie 1 , Partie 2 , Partie 3 , Partie 4 , Partie 5 .

Le logiciel sous ARDUINO s'est avéré être le plus grand des logiciels auto-écrits. En général, presque toute la logique de travailler directement avec les actionneurs et les capteurs du robot repose sur l'AVR. Et à ce niveau, l'API est implémentée - une bibliothèque de logiciels de développement rapide pour MIRO.

La structure de l'API est décrite dans la section wiki du référentiel correspondant. Jusqu'à présent, uniquement en russe. Et maintenant, nous allons analyser le code plus en détail. Je ne donnerai pas intentionnellement des déclarations de classe complètes, en l'abrégeant avec des points de suspension "...", ne laissant que des choses significatives pour le moment.

Dans notre modèle logiciel, chaque robot MIRO se compose d'un châssis et d'un ensemble d'appareils connectés. Lors de la conception, il a été supposé que le châssis du robot - ce sera toujours une sorte de châssis à roues - les robots marcheurs ou les robots qui utilisent d'autres principes de mouvement devraient être considérés séparément.

class Miro : public Robot { public: Miro(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Miro(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Miro(); ... }; 

La classe Miro est une classe de haut niveau et décrit la configuration complète du robot. Cette classe est la descendante de la classe Robot, qui décrit uniquement les fonctionnalités les plus élémentaires du robot.

 class Robot { public: Robot(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Robot(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Robot(); Chassis chassis; void Sync(); int attachDevice(Device *dev); int dettachDevice(Device *dev); ... protected: Device* _devices[ROBOT_MAX_DEVICES]; byte _device_count; }; 

Le concepteur effectue la configuration initiale des broches du châssis et les valeurs initiales de la configuration du robot.

La méthode Sync () implémente les opérations nécessaires pour le châssis et pour tous les appareils connectés au robot, à chaque étape du cycle de boucle principale () du croquis ARDUINO. Les méthodes Miro Sync () de la classe Miro invoquent les méthodes Sync () correspondantes du châssis et de tous les périphériques connectés au robot.

La classe Robot contient également un pointeur vers un tableau de périphériques connectés au robot, des méthodes pour travailler avec ce tableau (connecter un nouveau périphérique, déconnecter, rechercher par index et nom). La classe Robot contient également un objet de la classe Chassis - le châssis.

Mais commençons par quelque chose de plus simple - avec les appareils. Chaque appareil qui peut être connecté au robot, que ce soit une LED, un capteur ou un actionneur qui ne se rapporte pas directement au châssis (chariot), est décrit par sa classe successeur, qui est commune à tous les appareils de la classe des appareils virtuels:

 class Device { public: virtual void Sync(); virtual void setParam(byte pnum, byte *pvalue); virtual void getParam(byte pnum, byte *pvalue); virtual byte getPinsCount(); virtual char* getName(); virtual byte getParamCount(); protected: byte *pins[2]; }; 

Les méthodes virtuelles setParam, getParam, getParamCount sont associées à l'attribution, à la réception et à la détermination du nombre de paramètres de périphérique. Le paramètre peut être n'importe quelle propriété: la luminosité de la LED, la position du servo variateur, etc. La classe successeur de chaque appareil implémente ces méthodes à sa manière. Le but des méthodes getName, getPinsCount, je pense, est clair d'après le nom. La méthode Sync nouvellement rencontrée est une méthode spéciale pour le contrôle de périphérique non bloquant et l'automatisation de certaines opérations avec le périphérique qui doivent être effectuées régulièrement, à chaque itération de la boucle principale.

Voyons maintenant une implémentation plus ou moins générale de la classe descendante.

 class MIROUsonic : virtual public Device { public: void Sync(); void setParam(byte bnum, byte *pvalue); void getParam(byte bnum, byte *pvalue); byte getPinsCount(); char* getName(); byte getParamCount(); void Init(byte trig_pin, byte echo_pin); void On(unsigned int max_dist); void On(); void Off(); int getDist(unsigned int max_dist); unsigned int getMesCount(); private: bool _isOn; unsigned int _mesCount; unsigned int _dist; unsigned int _max_dist; }; 

Pour déterminer la classe d'un télémètre à ultrasons (ci-dessus), en plus des méthodes du parent, il existe également des méthodes:

  • Init - initialisation;
  • On et Off - allumez l'appareil (tĂ©lĂ©mètre);
  • getDist - renvoie la distance mesurĂ©e par le tĂ©lĂ©mètre;
  • getMesCount - renvoie le nombre de mesures prises depuis la mise sous tension de l'appareil.

Pour stocker l'état interne de l'appareil, les champs suivants sont utilisés:

  • _isOn (TRUE - l'appareil est allumĂ©, contrĂ´lĂ© par les mĂ©thodes On et Off);
  • _mesCount (stocke le nombre de dimensions utilisĂ©es dans la mĂ©thode getMesCount);
  • _max_dist - distance maximale requise pour la mesure *;
  • _dist est la distance rĂ©elle mesurĂ©e.

Ă€ propos de la plage de mesure maximale
* Il est connu que le HC-SR04 répandu selon le passeport est capable de mesurer des distances allant jusqu'à 4 mètres. Cependant, la méthode de mesure elle-même consiste à attendre le retour du signal ultrasonore, puis à coder la durée du signal sur la ligne Echo. Et en fait, si l'utilisateur n'a certainement pas besoin de mesurer des distances allant jusqu'à 4 mètres, mais assez, la plage, disons 1 mètre, alors vous pouvez attendre 4 fois moins pour le signal réfléchi. Le télémètre lui-même génère un signal sur la ligne d'écho dès qu'il le reçoit et effectue une modulation. C'est-à-dire cela peut ne pas affecter la longueur de la période entre les mesures adjacentes, mais la durée d'une seule mesure de cette manière peut être réduite.

Et maintenant pour l'explication de la méthode Sync. Si l'appareil a l'état _isOn == TRUE (activé), le cycle de mesure lui-même sera effectué dans la méthode Sync et le résultat de la mesure sera enregistré dans le champ _dist. Dans ce cas, lorsque vous appelez getDist, la méthode retournera immédiatement la valeur enregistrée dans _dist, il n'y aura pas de cycle de mesure. Si _isOn == FALSE (off), le cycle de mesure, au contraire, est effectué uniquement pendant l'appel à getDist, rien ne sera mesuré dans la méthode Sync. Il est supposé que le programmeur appellera la méthode Sync de l'ensemble du robot, qui à son tour appellera les méthodes Sync du même nom de tous les appareils connectés au robot et un objet de la classe Châssis (châssis).

Parmi les appareils de l'API, seules les choses que MIRO a maintenant sont implémentées: LED, télémètre à ultrasons, capteur de lumière photo-résistif, servo-entraînement, capteur de ligne.

Touchez légèrement le châssis. Cette classe implémente le "chariot abstrait" du robot. Il contient des méthodes qui vous permettent de contrôler les déménageurs.

 class Chassis { public: Chassis(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Chassis(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Chassis(); void Sync(); float getVoltage(); int wheelRotatePWMTime(int *speedPWM, unsigned long time); int wheelRotatePWM(int *speedPWM); bool wheelIsMoving(byte wheel) {return this->_wheel_move[wheel];} byte getWheelCount() { return WHEEL_COUNT; } #if defined(ENCODERS_ON) int wheelRotateAng(float *speed, float *ang, bool en_break); unsigned long wheelGetEncoder(byte wheel); ... #endif //ENCODERS_ON private: float _vbat; //Battery volgage bool _wheel_move[WHEEL_COUNT]; char _wheelDir[WHEEL_COUNT]; byte _wheel_PWM_pins[WHEEL_COUNT]; byte _wheel_DIR_pins[WHEEL_COUNT]; void _init(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) byte _wheel_ENCODER_pins[WHEEL_COUNT]; bool _wheel_sync_move; float _wheelAngSpeed[WHEEL_COUNT]; float _wheelSetAng[WHEEL_COUNT]; float _wheelSetAngSpeed[WHEEL_COUNT]; ... #endif //ENCODERS_ON }; 

Si nous considérons un chariot sans encodeurs et généralement sans rétroaction, il existe des méthodes de contrôle simples pour cela en utilisant un signal PWM. S'il y a des encodeurs dans le chariot, la classe devient beaucoup plus compliquée. Pour simplifier la vie de l'utilisateur, des méthodes comme celles-ci y apparaissent:

  • wheelRotateAng - rotation de la roue Ă  des angles de rotation prĂ©dĂ©terminĂ©s avec des vitesses angulaires donnĂ©es;
  • wheelGetPath - retourne la longueur du chemin parcouru par chaque roue;
  • wheelGetLinSpeed ​​- renvoie la vitesse linĂ©aire actuelle de chaque roue;
  • wheelGetAngSpeed ​​- renvoie la vitesse angulaire actuelle de chaque roue;
  • wheelGetEncoder - renvoie le nombre de rĂ©ponses des encodeurs de chaque roue.

Et un certain nombre de méthodes auxiliaires. Ainsi qu'une méthode d'étalonnage de la propulsion. Mais plus en détail, les méthodes clés de la classe Châssis seront examinées la prochaine fois.

Un peu en avance, c'est à cet endroit qu'il conviendra de remarquer que toute cette bibliothèque Miro peut être facilement adaptée ou complétée avec tout autre robot avec un schéma de mouvement différentiel à deux roues. Et avec un certain effort - et à d'autres configurations de propulsion et de direction. Dans le cas d'un circuit différentiel, il suffit de décrire correctement le fichier de configuration config.h. Et sans RPi. Par exemple, en moins d'une heure, nous avons tout lancé sur de si petits enfants pour notre tournoi régional de cybersécurité BlackMirrorCTF-2019 dans notre université ( lien ).

image

Les robots avaient une interface d'accès via TELNET et un système de commande pour la télécommande. Le document avec le système de commande était quelque part caché ou encodé. Les participants ont scanné les adresses IP et ouvert les ports sur les robots eux-mêmes. Avec une connexion réussie, les robots ont lancé une invitation et les participants ont compris qu'ils étaient «entrés». Eh bien, les équipes ont amené les robots le long de l'autoroute jusqu'à la ligne d'arrivée. Initialement, ils voulaient faire la piste entière avec des robots quelque part dans une pièce isolée avec une caméra IP installée, mais les organisateurs ont eu quelques problèmes avec la caméra IP et ont perdu une partie du charme.


C'est tout pour l'instant. Il est possible qu'au cours du développement, le modèle de programme subisse des modifications. Soit dit en passant, il a récemment été légèrement modifié, après que le code ait semblé un peu plus expérimenté OOP-shchik.

Avant la cinquième partie - parlons des encodeurs, des angles et des étalonnages.

Source: https://habr.com/ru/post/fr475512/


All Articles