Parallel Abtasten + Rechnen

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
piratos
User
Beiträge: 12
Registriert: Dienstag 26. Oktober 2021, 11:03

Hallo,

zu meiner Masterarbeit in der Elektrotechnik/Antriebstechnik soll ich zusätzlich ein kleines Programm zum Abtasten und Auswerten von Messwerten schreiben. Die paralleln Aufgaben bereiten mir dabei Probleme. Mit komplexeren Programmen habe ich leider noch wenig Erfahrung und suche nach der elegantesten Lösung für folgende Ansprüche.

- Über die serielle Schnittstelle werden Daten eingelesen (pyserial, baudrate 921600)
- Parallel dazu sollen die Daten live ausgewertet und geplottet werden.
- Die Messwerte wären bestenfalls in zwei Numpy Arrays gespeichert, wobei ein Thread/Prozess das Array beschreibt und die anderen beiden Threads/Prozesse die Daten live auslesen sollen.
- Die Abtastrate soll möglichst hoch und konstant gehalten werden. (1kHz, 60-120 Sekunden Laufzeit)
- GUI: Tkinter
- Raspberry Pi

Da die Zeit zu Beginn drängte, habe ich eine erste Version am Laufen. Ich nutze Multiprocessing und teile die Messwertdaten in einer Liste. Der Auswerteprozess muss die Daten dann allerdings erst ein np.array umwandeln, was natürlich nicht sehr effektiv ist. Die Abtastrate ist hier sehr inkonstant und abhängig von den parallelen Berechnungen. Das möchte ich vermeiden.

Könntet Ihr mir evtl. Frameworks oder Module empfehlen, die das Einlesen bzw. Auswerten effektiver gestalten?

Vielen Dank im Voraus!
Jeff
Benutzeravatar
pillmuncher
User
Beiträge: 1485
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Ohne den Code zu sehen, kann man nur ganz allgemeine Tipps geben.

Ich würde vermutlich eine multiprocessing.Queue verwenden um die Daten von einem zum anderen Prozess zu schicken. Ein Prozess macht dabei nichts anderes, als die Daten von der seriellen Schnitstelle einzulesen und in die Queue zu schaufeln. Der Prozess am anderen Ende der Queue ist dann dafür verantwortlich, diese Daten in eine weiterverarbeitbare Form zu bringen, zum Beispiel in ein numpy-Array. Seit Python 3.8 gibt es auch das Modul multiprocessing.shared_memory. Da gibt es ein Beispiel, wie man das mit numpy verwenden kann. Zusätzliche Prozesse könnten dann die Daten in dem Array lesen und darstellen. Generell sollte immer nur ein einziger Prozess die Daten schreiben und alle anderen nur lesen. Ich persönlich habe damit noch nicht herumgespielt, aber ich denke, einen Versuch wäre es wert.
In specifications, Murphy's Law supersedes Ohm's.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das mit den 1KHz koennte schwierig werden. Und den Mehrprozess-Ansatz auch fuer ueberkandidelt. Wie sieht denn das serielle Protokoll aus? Musst du mit 1KHz etwas senden, oder liefert das angeschlossene Geraet mit 1KHz Daten? Wenn letzteres, dann ist es eh nicht so schlimm, weil die serielle Schnittstelle buffert. Wenn ersteres, dann wird es eh schwierig. Wir haben gerade einen Kernel-Patch eingebaut, um ziemlich genau das zu erreichen. In unserem Fall geht es um MIDI, und ohne diesen Patch versacken die seriellen Daten einfach in der TTY-Infrastruktur des Kernels, und da kann ein Event auch schon mal 100ms zu spaet kommen.

Da du aber an anderer Stelle ueber USB redest: handelt es sich hier um einen USB-Serial-Adapter? Dann wird es noch gruseliger. Denn da kann es auch schon mal zu einer Sekunde Latenz kommen.

Man kann dem PI so ein Echtzeitverhalten abtrotzen, aber das erfordert einiges an Aufwand. Einen PREEMPT_RT-Kernel zB (oder mindestens threaded IRQs), sowie Programmiersprache, die deterministisch sind. Python ist das nicht. C, C++ oder Rust funktionieren zB.

Die einfache Alternative ist ein Arduino, der die Messwertaufnahme sehr stabil hinbekommt, und dann einfach buffert. Und der Buffer verkraftet dann auch, wenn die Daten mal nicht so regelmaessig abgefragt werden.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Um dem "ueberkandidelt" noch mal etwas mehr Kontext zu geben: du musst hier keine krasse memory-sharing-super-effiziente Interprozesskommunikation aufbauen. Wir reden hier ueber 1MBit/s. Also ~125KB/s. Maximal. Wahrscheinlich viel weniger, weil deine 1000 Messpunkte ja nicht pro Stueck ~125 Byte haben, nehme ich an. Sondern eher so 4-20. Das ist etwas, dass der PI mit seinen mehreren hundert MB/s an Speicherperformance locker via multiprocessing oder auch innerhalb eines Prozesses einfach per Bufferung abarbeitet. Da muss nicht sonders effizient in numpy-arrays gewandelt werden, oder sowas. Wenn es hakt, dann aus den schon angefuehrten Gruenden, oder weil du dich irgendwie anders verprogrammiert hast.
piratos
User
Beiträge: 12
Registriert: Dienstag 26. Oktober 2021, 11:03

Vielen Dank für die schnellen Antworten!

@pillmuncher: Ich habe den Code noch nicht gepostet, weil es für mich noch ein bisschen am Verständnis hakt mit den multiplen Prozessen. Queue ist ein sehr gutes Stichwort, danke!

@__deets__: Ich habe eine RS485 Schnittstelle mit einem USB-Serial-Adapter mit FTDI Chip. Leider muss ich jedes Mal den Auslesebefehl erneut an den Sensor senden, um die Temperatur in HEX zu erhalten.
Ich habe den Latency Timer auf 0ms gestellt. Bei nur einem ausgeführten Prozess komme ich so auf etwas über 90000 Messwerte pro Minute oder 1.5kHz. Diese Zahl sinkt mit den paralleln Prozessen allerdings sehr schnell.

Der Datenaustausch erfolgt im ASCII-Format mit folgenden Übertragungsparametern:
8 Bit, 1 Stoppbit, gerade Parität (8,1,e)

Kommandoreihenfolge zum Schreiben von Parametern:
2-stellige Geräteadresse +Befehl + Parameter + Carriage Return

Bsp. Befehl - Antwort

Code: Alles auswählen

>19mw1\r  
>015E\r
In meiner ersten Version nutze ich den mp.Manager() zum teilen der Messwertliste. Ich habe allerdings gelesen, dass der mp.Manager() besonders langsam sein soll.

Code: Alles auswählen

        manager = mp.Manager()
        param= manager.list()
        t= manager.list()
        temp = manager.list()
        p1 = mp.Process(target=self.abtasten, args=[param,t,temp])
        p2 = mp.Process(target=self.berechnen, args=[param,t,temp])
        p3 = mp.Process(target=self.liveplot, args=[param,t,temp])
 
Die Daten kommen von verschiedenen Sensoren und sollen in Matrizen gespeichert werden (Spalte pro Sensor). In der parallelen Berechnung müssen die Matrizen weiter manipuliert und invertiert werden, deswegen wäre es mir wichtig, dass ich die Messwerte gar nicht erst in einer Liste speichere, sondern nur einem np.array kontinuierlich hinzufüge.

Vom Raspberrry kann ich leider nicht umsteigen. Die Linearität der Messwertaufnahme hat keine Priorität, solange die Gesamtmenge der Messwerte mit steigender Berechnungskomplexität nicht zu stark schwankt.

Mir stellen sich noch folgende Fragen:
- Kann ich mit queue den gemeinsamen Speicher für np.arrays nutzen?
- Habe ich mit Multiprocessing die richtige Herangehensweise gewählt oder hat Treading doch Vorteile für mich?



Sorry für die lange Antwort, ich hoffe das macht das Problem deutlich.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe nicht gesagt, dass du den PI nicht benutzen sollst. Ich habe gesagt, dass du ihm Hardware beistellst, die den Job besser macht. Wie schon gesagt, USB ist nicht so dolle, vor allem, wenn das System anfaengt, viel CPU-Zeit zu verbraten. Wuerde ich aendern. Der PI hat einen UART, und natuerlich gibt es auch Adapter von UART auf RS485. Oder eben noch besser einen Arduino oder etwas vergleichbares, der kann auch gerne per USB angeschlossen sein. Aber er buffert eben.

Bei welcher CPU-Last bewegt sich denn dein System? Und wer ist dafuer verantwortlich? Kannst du zB mit htop rausfinden.

Was deine multiprocessing-Geschichte angeht: das ist viel zu viel. Man kann rechtfertigen, zwei Prozesse zu haben - einen zur Messwertaufnahme, den du auch zB im System hoeher priorisieren kannst, so dass er garantiert dran kommt. Aber die Trennung von Berechnung und Plotting ist ueberfluessig. Und dann kommt noch dazu, dass du ja schon einen Prozess *hast*. Der startet jetzt drei Prozesse, und ist selbst ebenfalls ueberfluessig.

Stattdessen wuerde man einen Prozess machen, mit GUI, der dann einen Unterprozess startet fuer die Datenakquise. Und zwischen denen eine Queue, in die du einfach jeden Wert stopfst. Das wars. numpy dann im Hauptprozess zur Verrechnung der Werte.

Den shared memory wuerde ich ignorieren. Das ist nicht das richtige vorgehen fuer dich, damit faengst du dir mehr Komplexitaet ein, als du handhaben kannst.

Threading ist zur Aufteilung der Datenakquise und Darstellung nicht sinnvoll, aber innerhalb der Darstellung kann es ggf. von Vorteil sein.
Sirius3
User
Beiträge: 17777
Registriert: Sonntag 21. Oktober 2012, 17:20

@piratos: was für ein Temperatursensor ist das? Was soll gemessen werden?
Temperaturen im kHz-Bereich zu sampeln ist wenig sinnvoll, da Temperatur etwas sehr träges ist. Da ist 1Hz im Normalfall schon sehr schnell. Man sollte keine Genauigkeit vorgaukeln, die von der Physik gar nicht gegeben ist.

@__deets__: wenn Hardwaregebuffert wird, dann reicht für die Datenakquise auch ein normaler Thread. Timing ist egal und Rechenpower irrelevant.
piratos
User
Beiträge: 12
Registriert: Dienstag 26. Oktober 2021, 11:03

@__deets__: Ich komme auf eine maximale CPU-Auslastung von 50%, wobei sich die Prozesse ziemlich gleichmäßig aufteilen.

Die Umsetzung mit Queue liefert gerade schon ein besseres Ergebnis.
q.get() ist nur ziemlich langsam, bzw. stoppt zwischendurch. Es passiert eine Weile nichts und dann läuft die Werteübergabe weiter. Woran kann das liegen?

@Sirius3:
An einer leistungsstarken Synchronmaschine soll die Oberflächentemperatur beim Anlauf mit 2 oder 3 Highspeed Pyrometern gemessen und an ein thermisches Modell übergeben werden. Die Sensoren liefern Daten über den gleichen seriellen Bus. Die Pyrometer sind fest angebracht, während die Maschine auf 1500 U/min - 25U/s beschleunigt. Mit der Umdrehung der Maschine und einer relativ hohen Abtastrate soll so die Temperatur über den Gesamtumfang eingefangen werden können, wobei der Temperaturanstieg bei niedrigerer Drehzahl steiler ist. Eine Erwärmung von 300K in der ersten Minute wird erwartet.


In etwa so sieht das gerade aus:

Code: Alles auswählen

        # Abtasten
        # .....
        while timer < int(self.entry_abtast.get()):
            
            db_t = []
            db_temp = []
            
            for datenpakte in range(100):
                for pyro in self.pyrols:
                    recv = com.Read.mw1(pyro.adr) # Werte auslesen
                    timer = time.time()-t0
                    t_buff.append(timer)
                    temp_buff.append(config.hexToInt(recv)/10)
                
                db_t.append(t_buff)
                db_temp.append(temp_buff)
                temp_buff = []
                t_buff = []
                
            sh_t.put(db_t)
            sh_temp.put(db_temp)
# .....

Code: Alles auswählen

#berechnen
# ......
        while exitFlag is False:
            t.extend(sh_t.get())
            temp.extend(sh_temp.get())
# .....
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

50% CPU Last ist schon signifikant und klingt danach, dass du da etwas falsch machst, wenn die sich so gleichmaessig verteilen. In dem Bereich wirst du ggf. auch schon Scheduling-Probleme (also Verzoegerungen in der Abtastung) bekommen. Und solltest versuchen, das aufzuklaeren, und zu druecken.

Ich habe gerade mal auf einem PI3 ein kleines Testprogramm laufen lassen, dass mit 1KHz Ausgaben ueber die serielle Schnittstelle taetigt. Das kommt auf ~5% CPU Last. Das sollte bei dir nicht signifikant mehr sein fuer den Prozess, der die Datenakuise macht. Wenn es das ist, dann hast du da etwas falsch programmiert.

Du solltest eh dein Problem in Teile zerlegen, und ein reinen Akquise-Prozess schreiben, der die Daten nur wegspeichert. Der sollte eben nicht mehr als 5-10% CPU brauchen, denn der macht im Grunde nix. IPC ist so teuer nicht. Dann diesen Prozess (bzw. den Code, den man ja einfach wiederverwenden kann) via multiprocessing nutzen, um die Daten via Queue rueberzuschaufeln, und der wertet die dann aus.
Sirius3
User
Beiträge: 17777
Registriert: Sonntag 21. Oktober 2012, 17:20

@piratos: dann beleibt es dabei, was __deets__ geschrieben hatte: wenn die Hardware an sich nicht Daten mit einer stabilen Frequenz liefern kann, dann braucht es dezidierte Hardware (z.B. in Form eines Arduino), das diese Aufgabe übernimmt. Wenn alle Sensoren am selben seriellen Bus hängen, kann es noch zusätzlich zu Synchronisationsproblemen auf dem BUS kommen, was Dir die Frequenz versaut.

Oft gibt es verschiedene Betriebsmodi für solche Sensoren, deshalb die Frage nach dem genauen Sensor den Ihr einsetzt.
piratos
User
Beiträge: 12
Registriert: Dienstag 26. Oktober 2021, 11:03

Danke euch beiden für die Antworten. Mir wird klar, dass ich ein paar Schritte zurück gehen und das Ganze geduldiger aufbauen muss.

Könntet ihr mir nur bitte nochmal erklären, warum sich der Arduino besser eignet? Um die Hardware zu wechseln, muss ich mich rechtfertigen können.
- Ist der Arduino auch die bessere Alternative, wenn man im Anschluss komplexere Berechnungen durchführen möchte?
- Welches Modell erfüllt die Ansprüche?

@Sirius3: Wir benutzen Sensoren von Sensortherm - Metis H318
Benutzeravatar
__blackjack__
User
Beiträge: 13152
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@piratos: Die Hardware soll nicht gewechselt werden, sondern ergänzt. Ein Arduino, oder ein anderer geeigneter Mikrocontroller kann die Messwerte mit einer stabileren Frequenz erfassen, weil da nur dieses eine Programm direkt läuft, ohne ein Mehrbenutzerbetriebssystem was sehr wenig Garantien geben kann, wann welcher Code von Dir genau ausgeführt wird, und was da alles dazwischen kommen und Dich/Dein Programm blockieren kann. Für Berechnungen, Visualisierung, und so weiter ist weiterhin der Raspi sinnvoll.

Arduinos haben dabei eine lange Tradition, sind sehr einsteigerfreundlich, zum Beispiel gegenüber sich selbst einen ”nackten” Mikrocontroller auf ein Board zu löten + was man sonst noch so braucht. Es gibt Unmengen an Informationsmaterial im Netz und eine grosse Community. Und eine IDE + viele Bibliotheken, beispielsweise für verschiedene Sensoren/Protokolle. Alternativen wären zum Beispiel ESPs oder der Raspberry Pico.

Anmerkungen zu dem gezeigten Quelltext: Es ist nicht so ganz klar welche Namen Du vergeben hast und welche von externem Code kommen und wo Du nichts ändern kannst, aber Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Wenn das Abtasten in einem Thread läuft, dann gehört das Abfragen eines Wertes aus einem Eingabeelement da nicht rein, denn das ist nicht threadsicher. Wenn das während der Laufzeit geändert werden können soll, dann wird man das auch irgendwie threadsicher übertragen müssen.

`time` würde deutlicher `elapsed_time` heissen und statt `t0` würde ich `start_time` verwenden.

Die Zeit in der ``while``-Schleife würde neu berechnen, statt auf das letzte `elapsed_time` in der Schleife zu setzen, denn da muss man sich ja sehr sicher sein, dass die Queue nicht irgendwann mal blockiert und da substantiell Zeit vergeht. Und dann muss man `elapsed_time` auch nicht vor der Schleife initialisieren.

Insgesamt keine kryptischen Abkürzungen und kryptische Präfixe. Was soll `db_*` und `sh_*`? Ich finde das extrem undurchsichtig/unübersichtlich was da mit diesen Listen gemacht wird. Einmal weil das aus der Namensgebung nicht klar wird, und dann weil die sehr ungünstig an mehr als einer Stelle neu gesetzt werden. Die beiden `*_buff` müssen ja *vor* diesem ganzen Code schon mal an leere Listen gebunden worden sein‽ Das sollte da weg und die sollten am Anfang der Schleife *einmal* an leere Listen gebunden werden, statt einmal vor der Schleife und dann am Ende der Schleife.

Die Einzahl von `pyrols` ist `pyro`? Einer der beiden Namen ist falsch.

Bei `datenpakte` fehlt ein `e`. Aber da dieser Wert sowieso nirgends verwendet wird, kann man den auch einfach `_` nennen. Das ist per Konvention ein Name für Werte die nicht interessieren.

`time.time()` ist nicht wirklich geeignet weil das auch mal ”stehen bleiben” oder ”rückwärts laufen” kann. `monotonic()` oder `perf_counter()` haben dieses Problem nicht.

`recv` ist ein sehr komischer Name für eine Temperatur. Zudem auch traditionell der Name einer Funktion/Methode in der BSD-Socket-API.

Was macht denn `config.hex_to_int()`? Mehr als man mit `int()` machen kann?

Code: Alles auswählen

In [309]: int(b'015E\r', 16)                                                    
Out[309]: 350
Und was hat das in einem `config`-Objekt zu suchen? Da würde man eher Konfigurationswerte erwarten und keine Umwandlungsfunktionen.

Zusammengehörende Werte sollte man nicht in ”parallelen” Datenstrukturen halten. Du hast alles zweimal, jeweils für die Zeit und für die Messwerte.

Dann kann man plötzlich in der innersten Schleife auf Namen verzichten und das bietet sich an es als „list comprehension“ umzuschreiben. Und das kann man dann auch gleich mit der Zählschleife wiederholen. Da landet man dann hier:

Code: Alles auswählen

        # Abtasten
        # .....
        start_time = time.monotonic()
        while time.monotonic() - start_time < end_time:
            measurements_queue.put(
                [
                    [
                        (
                            time.monotonic() - start_time,
                            config.hex_to_int(com.read.mw1(pyro.address)) / 10,
                        )
                        for pyro in self.pyros
                    ]
                    for _ in range(100)
                ]
            )
Ein bisschen schöner/lesbarer würde es so aussehen:

Code: Alles auswählen

        # Abtasten
        # .....
        clock = Clock()
        while clock.elapsed_time < end_time:
            measurements_queue.put(
                [
                    [
                        (clock.elapsed_time, pyro.read_temperature())
                        for pyro in self.pyros
                    ]
                    for _ in range(100)
                ]
            )
Dazu müsste man die `pyro`-Objekte entsprechend implementieren und einen `Clock`-Typ.

Zur Empfängerseite: Wahrheitswerte vergleicht man nicht mit ``is``. Eigentlich vergleich man die gar nicht, denn bei einem Vergleich kommt ja wieder nur ein Wahrheitswert heraus. Entweder den, den man sowieso schon hatte, dann kann man den auch gleich verwenden. Oder dessen Gegenteil. Dafür gibt es ``not``. Also ``while not exit_flag:``.

Der Name `exit_flag` ist nicht gut, weil man erst nachschauen muss wie der Verwendet wird, weil alleine am Namen kann man nicht erkennen ob denn nun `True` oder `False` für den Ausstieg stehen soll.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, der Arduino ist nicht besser fuer die komplexeren Berechnungen. Aber er hat kein Betriebssystem, das gleichzeitig eine unueberschaubare Anzahl an Aufgaben erledigen muss. Unter Linux stabile Echtzeit-Performance fuer bestimmte Aufgaben zu bekommen ist eine hochspezialisiert und komplexe Aufgabe. Es ist auch meine Aufgabe, und ich behaupte von mir noch nicht mal, dass ich das gut kann - sondern bei Problemen rufe ich bei Mama an (Mama ist in diesem Fall der "Erfinder" von PREEMPT_RT, Linutronix). Das kostet am Tag schlappe 1000 Euro, wenn das bei euch im Budget drin ist, kannst du das auch tun. Ich habe aber das Gefuehl, dass ist eher nicht der Fall.

Und bestimmte Dinge bekommt man auch beim besten Willen (und allen Kenntnissen der Welt) nicht eingefangen. Wir haben - wie schon erwaehnt - gerade ein Test-Setup fuer USB-Midi hergestellt, um bei ebenfalls 1KHz MIDI-Daten zu schicken, und die dann gleich wieder zu konsumieren. Roundtrip-Latency nennt sich das. Und entspricht sehr genau dem was du machst: etwas senden, und dann auf die Antwort warten.

Bei Systemlasten um 60% oder so bekommen wir dabei gelegentlich Ausreisser im *Sekundenbereich*. Das ist voellig unakzeptabel fuer uns (wer eine Kickdrum abfeuern will, der moechte, dass die sofort kommt (mit vielleicht 10ms Latenz oder so), nach einer Sekunde ist schon der halbe Takt vorbei.) Der Grund dafuer ist, dass USB eine sehr komplexe Architektur ist, die zusaetlich auch noch auf dem PI mit einem abgrundtief schlechten Controller abgehandelt wird, der fuer eine solche Aufgabe zehntausenden Interrupts pro Sekunde abarbeiten muss.

Damit ist also eine stabile Messwertaufnahme nicht wirklich drin in meinen Augen. Was der PI aber *gut* kann, ist viel und schnell rechnen. Und auch halbwegs hohen Datendurchsatz erreichen. Das ist ein grosser Unterschied zur Echtzeit! Ein LKW braucht lange, bis er aus der Toreinfahrt raus ist, aber dann hat man gleich 40 Tonnen auf dem Weg.

Und darum benutzt man eben einen Microcontroller wie den Arduino, und der tastet die RS485 Schnittstelle mit hohem Gleichtakt ab. Schreibt die Daten dann in einen Buffer, und liefert diesen Buffer auf Anfrage (bzw streamt den einfach) ueber die serielle Schnittstelle aus. Auch da verwendest du besser die eingebaute des UART (die ist eben nicht so komplex wie der USB-2-Serial-Weg, aus genannten Gruenden).

In diesem Fall muss der PI dann nur noch *im Schnitt* ausreichend Performance liefern. Wenn deine Daten zb sagen wir mal 10KB/Sekunde sind, dann kann ein Arduino Due (das ist der mit dem meisten Speicher, den ich gerade auf die Schnelle finden konnte), dann kann der mit seinen 64 + 32 KB sagen wir mal 60 KB fuer einen grossen Buffer "opfern", und damit auch mal 6 Sekunden Zeit ueberbruecken. Und da sollte der PI zwischenzeitlich schon mal nach Daten fragen.

Und zusaetlich hat es eben auch noch den Vorteil, dass du auf dem PI von einem Frage/Antwort-Spielchen mal X Sensoren zu einem kontinuierlichen Stream aus Daten wechseln kannst, und auch das reduziert die Last auf der Seite des Pi.
piratos
User
Beiträge: 12
Registriert: Dienstag 26. Oktober 2021, 11:03

@__blackjack__: Ja, das Test-Programm wurde jetzt durch das viele Rumprobieren sehr chaotisch. Ich werde mir in Zukunft mehr Mühe geben Konventionen einzuhalten. Das Ziel dieses Beitrags war es allerdings eher, ein vernünftiges und solides Grundgerüst für meine Anwendung zu finden.

@__deets__: Ich verstehe, das ergibt auf jeden Fall Sinn. Vielen Dank für die gute Erklärung und deine Mühe. Ich werde die Messwertaufnahme auslagern und komme dem Ziel hoffentlich ein Stück näher.
Antworten