Python Script langsam - schlechter Algorithmus!?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Kaito90
User
Beiträge: 15
Registriert: Donnerstag 4. Juli 2013, 09:37

Hallo zusammen,

ich bin noch ziemlich neu im Bereich Python und beschäftige mich momentan mit einem Script, dass sich via PySerial mit einem Arduino verbindet und fragt ab, welche Taster gedrückt wurden. Die Kommunikation funktioniert einwandfrei, nur habe ich leider ziemliche performance Probleme.

Mithilfe von ein paar clock() Befehlen konnte ich dann die übeltäter in meinem Script finden, wüsste jetzt aber nicht wie ich es besser machen sollte :?

Hier mal ein Ausschnitt aus meinem Code:

Code: Alles auswählen

arduinoDigital = "1"
arduinoAnalog = "2"
arduinoLED = "10"
arduinoDisplay = "11"
arduinoPWM = "12"
arduinoSystem = "20"

LEDon = "1"
LEDoff = "0"


def converter( thelist ):
    result =""
    a= clock()
    for element in thelist:
        str(element)
        if result != "":
            result = result +"," + element
        if result == "":
            result = element
    b=clock()
    print(b-a)
    return result

def ComInterface():
    ser = Verbindungsaufbau("off")
    while(threadFlag == 0):

        
        for i in range(0,15): #Joystickabfrage
            a = clock()
            ser.write(converter( [arduinoAnalog, format(i)] ).encode())
            b = clock()
            print("senden: ")
            print(b-a)

            x = clock()
            command = str(ser.readline().decode().strip('\r\n'))
            y= clock()
            print("empfangen: ")
            print(y-x)
            joystickNR = i
            ergebnis = joystickControl(command, joystickNR)

Code: Alles auswählen

in def ComInterface 1. Schleife
Converter:
1.3687216311453199e-05
senden: 
0.007375698689621402
empfangen: 
1.0011197853844482
Converter:
1.454266733169618e-05
senden: 
0.007338914295782928
empfangen: 
0.9980050882226639

ab der 2. Schleife
Converter:
5.132706114352459e-06
Converter:
8.554510195324383e-06
Converter:
5.98815713459544e-06
Converter:
9.409961208461937e-06
Bin für jede Hilfe dankbar. Ich denke ich habe beides zu umständlich programmiert oder ist hier Python zu langsam? Meiner Meinung nach liegt es an mir :mrgreen:
Gedacht war, dass so eine Abfrage innerhalb weniger Millisekunden durch wäre, damit ich den Arduino und die Taster als Controller benutzen kann. Habt ihr vielleicht noch bessere Ideen?

Vielen Dank

und

einen schönen Abend noch
Kaito
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Probier mal folgendes für den Converter-Code:

Code: Alles auswählen

result = ','.join(str(elem) for elem in thelist)
Falls die Listenelemente übrigens bereits Strings sind, dann kann man sich das `str()` natürlich auch sparen.

Im Übrigen wird bei dem von dir gezeigten Code das Ergebnis von `str(element)` gar nicht verwendet. Falls das trotzdem funktioniert, dann ist entweder die Umwandlung unnötig und kann entfallen oder aber der gezeigte Code entspricht nicht exakt dem tatsächlich von dir eingesetzten Code.

Vielleicht zur Erklärung: Du erzeugst haufenweise neue Strings als Zwischenergebnisse. Das Generieren von Strings verbraucht in der Masse irgendwann spürbare Rechenzeit. Die "join"-Variante hingegen hat einen speziellen Mechanismus, um beim Hinarbeiten auf das Endresultat möglichst effizient vorzugehen.

Und wie gesagt: Guck mal, ob du das `str()` auch weglassen kannst. Das erzeugt nämlich für jedes Element einen möglicherweise unnötigen neuen String.
Sirius3
User
Beiträge: 18332
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: wenn element schon ein String ist, macht `str` zum Glück gar nichts.

@Kaito90: das Senden über eine serielle Schnittstelle erfolgt mit einer bestimmten Taktrate. Da kann Dein Rechnerchen noch so schnell sein, mehr als eine feste Anzahl an Zeichen pro Sekunde sind da nicht drin. Beim Empfangen kommt noch hinzu, dass die Gegenseite ja auch eine bestimmte Zeit braucht, um die Anfrage zu verarbeiten. Also von der 1 Sekunde auf die Antwort warten sind wohl 0.99 Sekunden Antwort generieren und <0.01 Sekunden Antwort senden/empfangen.

Wenn Du genau zwei Elemente einer Liste hast, nimmt man nicht join oder Dein convert sondern direkt Stringformatierung:

Code: Alles auswählen

ser.write("{},{}".format(arduinoAnalog, i).encode("ascii"))
Kaito90
User
Beiträge: 15
Registriert: Donnerstag 4. Juli 2013, 09:37

Morgen,

danke euch beiden für die Tipps.

@snafu: Ich meine, ich musste str(element) hinschreiben, weil er sonst rumgemeckert hat, dass es sich um ein INT handelt und das nicht in den String reingeht.

@Sirius: Wenn ich zu Hause bin, werde ich es sofort mal ausprobieren.

Ich werde dann berichten :)

Edit: Ich denke die Denksekunde beim empfangen liegt nicht beim Arduino, sondern eher an der Umwandlung:

Code: Alles auswählen

command = str(ser.readline().decode().strip('\r\n'))
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein, wie Sirius3 schon geschrieben hat ist die serielle Verbindung das Bottleneck. Die Umwandlung ist dagegen kein grosses Problem.
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Kaito90 hat geschrieben:Edit: Ich denke die Denksekunde beim empfangen liegt nicht beim Arduino, sondern eher an der Umwandlung:

Code: Alles auswählen

command = str(ser.readline().decode().strip('\r\n'))
Da steckt `.readline()` drin. Das gehört zu den Operationen, die auf Ein- bzw Ausgabegeräten stattfinden (Stichwort: "IO"). Diese Operationen sind im allgemeinen eher langsam, weil das angesprochene Gerät eine Weile braucht, bis es die Operation verarbeitet hat und entsprechende Rückmeldung gegeben hat. Damit muss man leider leben.

Vielleicht kannst du ja Einstellungen am Gerät oder an der Verbindung vornehmen, um hier eine schnellere Verarbeitung zu ermöglichen.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Zeig doch mal den Code vom Arduino.
Kaito90
User
Beiträge: 15
Registriert: Donnerstag 4. Juli 2013, 09:37

Beim Arduino blinken die beiden LED's (RX und TX) gleichzeitig. Der gibt die Daten sofort zurück.

Code: Alles auswählen

#include <Wire.h>


void setup() {
  for (int pin = 30; pin < 54; pin++){
   //Digitale Eingänge 30 bis 53  
   pinMode(pin, INPUT); //als eingang
   digitalWrite(pin, HIGH); //pullup ein
   }
  for (int pin = 14; pin < 20; pin++){
   //Digitale Ausgänge 14 bis 19
   pinMode(pin, OUTPUT); //als ausgang
   digitalWrite(pin, LOW); //ausgang low
   }
  for (int pin = 22; pin < 30; pin++){
   //Digitale Ausgänge 22 bis 29
   pinMode(pin, OUTPUT); //als ausgang
   digitalWrite(pin, LOW); //ausgang low
   }
  for (int pin = 2; pin < 14; pin++){
   //Digitale PWM Ausgänge 2 bis 13
   pinMode(pin, OUTPUT); //als ausgang
   digitalWrite(pin, LOW); //ausgang low
   }
  Serial.begin(19200);
}





//Hauptschleife

void loop() {
 checkserial();
 
 }







/*
#####################################################

                  Funktionen

#####################################################
*/

int checkserial()
{
  int indata = 0;
  int argument1 = 0;
  int argument2 = 0;
  if (Serial.available() > 0) {
    indata = Serial.parseInt();
   
    switch(indata){
    case 1:
      argument1 = Serial.parseInt();
      checktaster(argument1);
      break;
    case 2:
      argument1 = Serial.parseInt();
      checkanalog(argument1);
      break;
    case 10:
      argument1 = Serial.parseInt();
      argument2 = Serial.parseInt();
      setled(argument1, argument2);
      break;
    case 12:
      argument1 = Serial.parseInt();
      argument2 = Serial.parseInt();
      setpwm(argument1, argument2);
      break;
    case 20:
      report();
    }
   }
  }

int report(){Serial.println("1");}

int checktaster(int pin){
  int zustand = digitalRead(pin);
  Serial.println(zustand);
}

int setled(int pin, int zustand){
  digitalWrite(pin, zustand);
}

int setpwm(int pin, int cycle){
  analogWrite(pin, cycle);}

int checkanalog(int kanal){
  switch(kanal){
    case 0:
      Serial.println(analogRead(A0));
      break;
    case 1:
      Serial.println(analogRead(A1));
      break;
    case 2:
      Serial.println(analogRead(A2));
      break;
    case 3:
      Serial.println(analogRead(A3));
      break;
    case 4:
      Serial.println(analogRead(A4));
      break;
    case 5:
      Serial.println(analogRead(A5));
      break;
    case 6:
      Serial.println(analogRead(A6));
      break;
    case 7:
      Serial.println(analogRead(A7));
      break;
    case 8:
      Serial.println(analogRead(A8));
      break;
    case 9:
      Serial.println(analogRead(A9));
      break;
    case 10:
      Serial.println(analogRead(A10));
      break;
    case 11:
      Serial.println(analogRead(A11));
      break;
    case 12:
      Serial.println(analogRead(A12));
      break;
    case 13:
      Serial.println(analogRead(A13));
      break;
    case 14:
      Serial.println(analogRead(A14));
      break;
    case 15:
      Serial.println(analogRead(A15));
      break;
  }
}











//Parkbereich
/*


//Methode 1
int checktaster()
{
   for (int pin = 30; pin < 54; pin++){
   //Digitale Eingänge 30 bis 53  
   int zustand = digitalRead(pin);
   if (zustand == LOW) {
   Serial.println(pin);
   Serial.println(zustand);}
   }
}

Kaito90
User
Beiträge: 15
Registriert: Donnerstag 4. Juli 2013, 09:37

habe mich jetzt noch was eingelesen. Es scheint wohl ein Windows zu liegen. Windows wartet 40bit Times (ca. Sekunde), bevor es die bytes an die CPU gibt. Habe jetzt mal unter Windows/Anschlüsse/COM die FIFO abgestellt und den Eingangsbuffer von 14bytes auf 1 byte gestellt.

Brachte aber auch keine Abhilfe. Jedoch scheint die Software Hterm 2000 Bytes in einer Sekunde zu schicken, bekommt auch entsprechend Antwort. 180KiB/s und wieso ist das mit Python nicht möglich.
Sirius3
User
Beiträge: 18332
Registriert: Sonntag 21. Oktober 2012, 17:20

Dein Arduino wartet auf einen Timeout, weil er das Ende Deiner Eingabe nicht anders ermitteln kann.
Korrekt müßte es also heißen:

Code: Alles auswählen

ser.write("{},{}\n".format(arduinoAnalog, i).encode("ascii"))
Kaito90
User
Beiträge: 15
Registriert: Donnerstag 4. Juli 2013, 09:37

Sirius3 hat geschrieben:Dein Arduino wartet auf einen Timeout, weil er das Ende Deiner Eingabe nicht anders ermitteln kann.
Korrekt müßte es also heißen:

Code: Alles auswählen

ser.write("{},{}\n".format(arduinoAnalog, i).encode("ascii"))
peinlich :oops:

Ich hatte auch damit gerechnet, das macht PySerial für mich :] Habe jetzt eine Zeit von 0,01

Vielen Dank
Antworten