Wenn man hinreichend leistungsstarke Prozessorboards hat, reicht durchaus ein einziges Board.
Und letztlich muss ja 1 die Zügel in der Hand haben, um die Daten aus den untergeordneten "Modulen" zu sichten, zu ordnen und auszuwerten, und das sollte dann eigentlich schon auch eines der leitungsfähigsten sein (Takt, Speicher, Multithreading-fähig).
Man kann ntl jederzeit überall mit weiteren Boards oder ICs über verschiedenste Bussysteme (UART, i2c, SPI, CAN) kommunizieren:
aber z.B. einem Programm-"Modul" in einem pthread oder subsumption-Behaviour ist es egal, ob es die Sensordaten direkt von onboard-Sensoren oder von vorprogrammierten remote-Clients erhält.
Aber je mehr Clients (Multi-Boards), mit denen kommuniziert werden muss, desto komplizierter wird es im Vergleich zum Single-Board.
Ich sehe für modulare Architekturen 3 praktikable Wege, Multithreading ist dabei essentiell ("Modul" verstanden als funktionelle Einheit, z.B. ein Sensor-Modul, ein Kalkulationsmodul oder ein Motorsteuerungsmodul):
a) Subsumption-Architektur: hier verdrängen Funktionen ("Behaviours") mit hoher Prio solche mit hierarchisch niedriger Prio (googeln!). Prinzipiell beruht es auf (zumindest kooperativem) Multithreading.
Vorteile: es können jederzeit neue Sensor- oder Behaviour-"Module" eingefügt werden, und es läuft auch auf sehr einfachen, langsamen Systemen wie Interpreter-Frameworks (zB. NQC oder Java etc. auf Lego Mindstorms RCX oder NXT). Auch auf dem Arduino Due mit dem kooperativen Scheduler MT geht das natürlich.
Nachteil: läuft auf langsamen Systemen ein wenig holprig und ist so nicht echtzeitfähig.
Beispiel: https://github.com/dsyleixa/Mindstor...enmaeher-6.nqc
https://www.youtube.com/watch?v=lNLPejeS-_Y
http://www.youtube.com/watch?v=z7mqnaU_9A8
hier sieht man, welche Behaviours (z.B. "Ausweichen" oder "Wenden") welche anderen (z.B."Cruise" oder "Ausweichen") hierarchisch verdrängen:
Code:
int MotorCommand;
task arbitrate ()
{ // Hier werden die Behaviour-Prioritäten festgelegt!
while(true) {
if (vCruise != CommNone) MotorCommand = vCruise; // niedrigste Prio
if (vAusweichen != CommNone) MotorCommand = vAusweichen;
if (vWenden != CommNone) MotorCommand = vWenden;
if (vRueckAbbr != CommNone) MotorCommand = vRueckAbbr;
if (vNotStop != CommNone) MotorCommand = vNotStop; // höchste Prio
MotorControl (); // MotorCommand erhält seinen Wert Event-gesteuert vom entsprechenden Task
} // MotorControl übernimmt dann die einfachsten Motorbefehle
}
b) preemptives (!) Multithreading auf schnellen single- (SAMD51, Rpi Zero) - oder multicores (ESP32, Rpi ab 2B), mit pthreads, thread prios und mutexes.
Wie bei der Subsumption mit kooperativem MT können dann aus den parallel ermittelten Daten optional die hierarchisch organisierten Verhaltensweisen generiert werden, wieder in einer unabhängig laufenden pthread Funktion. Anderes als bei kooperativem MT können jetzt einzelne threads keine anderen blockieren, dadurch kann das Programm in Echtzeit reagieren.
Es können auch hier jederzeit neue "Module" in Form zusätzlicher pthreads hinzugefügt werden.
Vorteile: schnell, parallelisierbar, priorisierbar und skalierbar, kompatibel auch zu C++ GUI Frameworks wie z.B. qt oder openCV.
c) modulare Systeme z.B. wie ROS auf rechenstarken Computern (Windows-PC, Rpi 3B+, 4). Hier übernimmt das ROS die Verwaltung und das threading aller Module.
Vorteile: viele fertige Module verfügbar.
Nachteil: IMO großer Installations- und Programmier-Overhead.
Lesezeichen