MIRO ist eine offene Indoor-Roboterplattform. Teil 4 - Softwarekomponente: ARDUINO (AVR)

Bild

Wir bauen die Softwarekomponente der MIRO-Plattform weiter auseinander. Ich möchte die Software unter AVR genauer untersuchen. Aus diesem Grund werden wir dem Thema zwei Teile widmen. In der ersten beschreiben wir die allgemeine Struktur der Bibliothek und in der zweiten die Implementierung einiger SchlĂŒsselklassenmethoden.

Inhaltsverzeichnis: Teil 1 , Teil 2 , Teil 3 , Teil 4 , Teil 5 .

Die Software fĂŒr ARDUINO war die grĂ¶ĂŸte der selbstgeschriebenen. Im Allgemeinen liegt fast die gesamte Logik der direkten Arbeit mit Aktuatoren und Sensoren des Roboters beim AVR. Und auf dieser Ebene wird API implementiert - eine schnelle Entwicklungssoftware fĂŒr MIRO.

Die API-Struktur ist im Wiki-Abschnitt des entsprechenden Repositorys beschrieben . Bisher nur in russischer Sprache. Und jetzt werden wir den Code genauer analysieren. Ich werde nicht absichtlich vollstĂ€ndige Klassendeklarationen abgeben, sondern sie mit den Auslassungspunkten "..." abkĂŒrzen, sodass nur die im Moment wichtigen Dinge ĂŒbrig bleiben.

In unserem Softwaremodell besteht jeder MIRO-Roboter aus einem Chassis und einer Reihe verbundener GerÀte. Bei der Konstruktion wurde davon ausgegangen, dass das Roboterfahrwerk - es wird immer ein Fahrwerk auf RÀdern sein - Laufroboter oder Roboter, die andere Bewegungsprinzipien verwenden, separat betrachtet werden sollten.

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(); ... }; 

Die Miro-Klasse ist eine Klasse der obersten Ebene und beschreibt die vollstÀndige Konfiguration des Roboters. Diese Klasse ist der Nachfolger der Roboterklasse, die nur die grundlegendsten Funktionen des Roboters beschreibt.

 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; }; 

Der Designer fĂŒhrt die Ersteinrichtung der Chassis-Pins und die Anfangswerte der Roboterkonfiguration durch.

Die Sync () -Methode implementiert die erforderlichen Operationen fĂŒr das Chassis und fĂŒr alle an den Roboter angeschlossenen GerĂ€te in jedem Schritt des Hauptschleifenzyklus () der ARDUINO-Skizze. Die Miro Sync () -Methoden der Miro-Klasse rufen die entsprechenden Sync () -Methoden des GehĂ€uses und aller mit dem Roboter verbundenen GerĂ€te auf.

Die Robot-Klasse enthÀlt auch einen Zeiger auf ein Array von GerÀten, die mit dem Roboter verbunden sind, sowie Methoden zum Arbeiten mit diesem Array (ein neues GerÀt verbinden, trennen, nach Index und Name suchen). Die Roboterklasse enthÀlt auch ein Objekt der Chassis-Klasse - das Chassis.

Aber fangen wir mit etwas Einfacherem an - mit GerĂ€ten. Jedes GerĂ€t, das mit dem Roboter verbunden werden kann, sei es eine LED, ein Sensor oder ein Aktor, der nicht direkt mit dem Chassis (Wagen) zusammenhĂ€ngt, wird durch seine Nachfolgerklasse beschrieben, die fĂŒr alle GerĂ€te in der virtuellen GerĂ€teklasse gilt:

 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]; }; 

Die virtuellen Methoden setParam, getParam, getParamCount sind mit dem Zuweisen, Empfangen und Bestimmen der Anzahl der GerĂ€teparameter verbunden. Der Parameter kann eine beliebige Eigenschaft sein: die Helligkeit der LED, die Position des ServoverstĂ€rkers usw. Die Nachfolgerklasse jedes GerĂ€ts implementiert diese Methoden auf ihre eigene Weise. Der Zweck der getName- und getPinsCount-Methoden ergibt sich meines Erachtens aus dem Namen. Die neu entdeckte Sync-Methode ist eine spezielle Methode zur blockierungsfreien GerĂ€testeuerung und -automatisierung einiger GerĂ€tevorgĂ€nge, die regelmĂ€ĂŸig bei jeder Iteration der Hauptschleife ausgefĂŒhrt werden mĂŒssen.

Betrachten wir nun eine mehr oder weniger allgemeine Implementierung der abgeleiteten Klasse.

 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; }; 

Bei der Bestimmung der Klasse eines Ultraschall-Entfernungsmessers (oben) gibt es neben den Methoden des Elternteils auch Methoden:

  • Init - Initialisierung;
  • Ein und Aus - GerĂ€t einschalten (Entfernungsmesser);
  • getDist - gibt die vom Entfernungsmesser gemessene Entfernung zurĂŒck;
  • getMesCount - gibt die Anzahl der Messungen zurĂŒck, die seit dem Einschalten des GerĂ€ts durchgefĂŒhrt wurden.

Um den internen Status des GerÀts zu speichern, werden die folgenden Felder verwendet:

  • _isOn (TRUE - das GerĂ€t ist eingeschaltet, gesteuert durch die Methoden On und Off);
  • _mesCount (speichert die Anzahl der Dimensionen, die in der getMesCount-Methode verwendet werden);
  • _max_dist - maximal benötigter Abstand zur Messung *;
  • _dist ist die tatsĂ€chlich gemessene Entfernung.

Über den maximalen Messbereich
* Es ist bekannt, dass der weit verbreitete HC-SR04 laut Pass Entfernungen von bis zu 4 Metern messen kann. Das Messverfahren selbst beinhaltet jedoch das Warten auf die RĂŒckkehr des Ultraschallsignals, gefolgt von der Codierung der Dauer in dem Signal auf der Echoleitung. Und in der Tat, wenn der Benutzer definitiv keine Entfernungen im Bereich von bis zu 4 Metern messen muss, aber genug, beispielsweise 1 Meter, dann können Sie viermal weniger auf das reflektierte Signal warten. Der Entfernungsmesser selbst erzeugt ein Signal auf der Echo-Leitung, sobald er es empfĂ€ngt, und fĂŒhrt eine Modulation durch. Das heißt Dies hat möglicherweise keinen Einfluss auf die LĂ€nge des Zeitraums zwischen benachbarten Messungen, aber die Dauer einer einzelnen Messung kann auf diese Weise verringert werden.

Und nun zur ErlĂ€uterung der Sync-Methode. Wenn das GerĂ€t den Status _isOn == TRUE (on) hat, wird der Messzyklus selbst mit der Methode Sync ausgefĂŒhrt und das Messergebnis im Feld _dist aufgezeichnet. In diesem Fall gibt die Methode beim Aufruf von getDist sofort den in _dist aufgezeichneten Wert zurĂŒck, es findet kein Messzyklus statt. Wenn _isOn == FALSE (off) ist, wird der Messzyklus im Gegensatz dazu nur wĂ€hrend des Aufrufs von getDist ausgefĂŒhrt. In der Sync-Methode wird nichts gemessen. Es wird davon ausgegangen, dass der Programmierer die Sync-Methode des gesamten Roboters aufruft, die wiederum die gleichnamigen Sync-Methoden aller mit dem Roboter verbundenen GerĂ€te und eines Objekts der Chassis-Klasse (Chassis) aufruft.

Von den GerÀten in der API sind nur die Dinge implementiert, die MIRO jetzt hat: LED, Ultraschall-Entfernungsmesser, Lichtsensor mit Fotowiderstand, Servoantrieb, Zeilensensor.

Das Chassis leicht berĂŒhren. Diese Klasse implementiert den "abstrakten Wagen" des Roboters. Es enthĂ€lt Methoden, mit denen Sie Mover steuern können.

 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 }; 

Betrachten wir einen Wagen ohne Geber und in der Regel ohne RĂŒckmeldung, so gibt es hierfĂŒr einfache Steuermethoden mit einem PWM-Signal. Wenn sich Encoder im Wagen befinden, wird die Klasse viel komplizierter. Um das Leben des Benutzers zu vereinfachen, erscheinen darin Methoden wie:

  • wheelRotateAng - Raddrehung bei vorgegebenen Drehwinkeln mit vorgegebenen Winkelgeschwindigkeiten;
  • wheelGetPath - gibt die LĂ€nge des von jedem Rad zurĂŒckgelegten Pfades zurĂŒck;
  • wheelGetLinSpeed ​​- gibt die aktuelle lineare Geschwindigkeit jedes Rads zurĂŒck;
  • wheelGetAngSpeed ​​- Gibt die aktuelle Winkelgeschwindigkeit jedes Rads zurĂŒck.
  • wheelGetEncoder - gibt die Anzahl der Antworten der Encoder jedes Rads zurĂŒck.

Und eine Reihe von Hilfsmethoden. Sowie eine Antriebskalibrierungsmethode. Im Detail werden die wichtigsten Methoden der Chassis-Klasse das nÀchste Mal betrachtet.

Mit Blick auf die Zukunft ist an dieser Stelle anzumerken, dass diese gesamte Miro-Bibliothek leicht mit jedem anderen Roboter mit einem zweirĂ€drigen Differenzialbewegungsschema angepasst oder ergĂ€nzt werden kann. Und mit einem gewissen Aufwand - und zu anderen Antriebs- und Lenkkonfigurationen. Im Falle einer Differentialschaltung mĂŒssen Sie nur die Konfigurationsdatei config.h korrekt beschreiben. Und ohne RPi. Zum Beispiel haben wir in weniger als einer Stunde fĂŒr unser regionales BlackMirrorCTF-2019-Cybersicherheitsturnier an unserer UniversitĂ€t alles ĂŒber solche Kleinen ins Leben gerufen ( Link ).

Bild

Die Roboter hatten eine Schnittstelle fĂŒr den Zugriff ĂŒber TELNET und ein Befehlssystem zur Fernsteuerung. Das Dokument mit dem Befehlssystem war irgendwo versteckt oder verschlĂŒsselt. Die Teilnehmer haben die IP-Adressen gescannt und die Ports auf den Robotern selbst geöffnet. Bei einer erfolgreichen Verbindung gaben die Roboter eine Einladung heraus und die Teilnehmer verstanden, dass sie "eingetreten" waren. Nun, dann brachten die Teams die Roboter entlang der Autobahn zur Ziellinie. UrsprĂŒnglich wollten sie die gesamte Strecke mit Robotern irgendwo in einem isolierten Raum mit installierter IP-Kamera machen, aber die Organisatoren hatten einige Probleme mit der IP-Kamera und verloren etwas an Charme.


Das ist alles fĂŒr jetzt. Es ist möglich, dass sich das Programmmodell im Laufe der Entwicklung Ă€ndert. Übrigens, es wurde kĂŒrzlich leicht modifiziert, nachdem der Code etwas erfahrener aussah als OOP-shchik.

Vor dem fĂŒnften Teil - lassen Sie uns ĂŒber Encoder, Winkel und Kalibrierungen sprechen.

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


All Articles