Schiefe Ebene mit Laserlichtschranken

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

Hallo,

ich bin neu hier und ein Anfänger in Python. In meiner Projektarbeit muss ich eine schiefe Ebene bauen, die mit Laserlichtschranken versehen ist. Die Ebene ist bereits gebaut. Wenn die Kugel/Quader nach unter rollt möchte ich bei jeder Lichtschranke die Zeit ermitteln und auf einem Display ausgeben. Das Display funktioniert bereits über den I2C-Bus. Das ganze wird über den Raspberry-Pi gesteuert und in Python programmiert. Jedoch hackt es noch ein wenig mit der Programmierung. Ich lade das Programm einfach mal mit hoch.

Mein Problem ist: Wenn ich das Programm starte, wird mir die Zeit 1 ausgegeben. Jedoch wird mir nicht immer die Zeit 2/3 ausgegeben.


Mfg Simon


#Projektarbeit_Schiefe_Ebene_2017

#!/usr/bin/python
import time
import RPi.GPIO as GPIO
import lcddriver

lcd = lcddriver.lcd()
lcd.lcd_clear()

GPIO_SENSOR1_PIN = 22
GPIO_SENSOR2_PIN = 23
GPIO_SENSOR3_PIN = 24
GPIO_SENSOR4_PIN = 25

DISTANCE_1 = 10.0 # (in cm) Anpassen, falls notwendig
DISTANCE_2 = 40.0 # (in cm) Anpassen, falls notwendig
DISTANCE_3 = 90.0 # (in cm) Anpassen, falls notwendig

TIMEOUT_1 = 5 # sek.
TIMEOUT_2 = 10 # sek.
TIMEOUT_3 = 15 # sek.

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# Programm starten und Sensoren initialisieren:

if __name__ == '__main__':
GPIO.setup(GPIO_SENSOR1_PIN, GPIO.IN)
GPIO.setup(GPIO_SENSOR2_PIN, GPIO.IN)
GPIO.setup(GPIO_SENSOR3_PIN, GPIO.IN)
GPIO.setup(GPIO_SENSOR4_PIN, GPIO.IN)

start_time1, end_time1 = 0, 0
end_time2 = 0, 0

# Sensor 1 und 2:

while GPIO.input(GPIO_SENSOR1_PIN) == GPIO.LOW:
time.sleep(0.1)
start_time1 = time.time()
end_time1 = 0

while GPIO.input(GPIO_SENSOR2_PIN) == GPIO.LOW and time.time()-start_time1 < TIMEOUT_1:
time.sleep(0.1)
else:
if time.time()-start_time1 >= TIMEOUT_1:
print "Timeout_1"
lcd.lcd_display_string("Timeout_1", 1)
exit()
else:
end_time1 = time.time()
speed_1 = DISTANCE_1 / (end_time1 - start_time1)
print "1. Geschwindigkeit: %.2f cm/s" % speed_1
print "1. Zeit: %.2f s" % (end_time1 - start_time1)
lcd.lcd_display_string("v: %.2f cm/s" % speed_1, 1)
lcd.lcd_display_string("t: %.2f s" % (end_time1 - start_time1), 2)

# Sensor 2 und 3:

while GPIO.input(GPIO_SENSOR2_PIN) == GPIO.LOW:
time.sleep(0.1)
start_time1 = time.time()
end_time2 = 0

while GPIO.input(GPIO_SENSOR3_PIN) == GPIO.LOW and time.time()-start_time1 < TIMEOUT_2:
time.sleep(0.1)
else:
if time.time()-start_time1 >= TIMEOUT_2:
print "Timeout_2"
lcd.lcd_clear()
lcd.lcd_display_string("Timeout_2", 1)
exit()
else:
end_time2 = time.time()
speed_2 = DISTANCE_2 / (end_time2 - start_time1)
print "2. Geschwindigkeit: %.2f cm/s" % speed_2
print "2. Zeit: %.2f s" % (end_time2 - start_time1)
lcd.lcd_clear()
lcd.lcd_display_string("v: %.2f cm/s" % speed_2, 1)
lcd.lcd_display_string("t: %.2f s" % (end_time2 - start_time1), 2)

# Sensor 3 und 4:

while GPIO.input(GPIO_SENSOR3_PIN) == GPIO.LOW:
time.sleep(0.1)
start_time1 = time.time()
end_time3 = 0


while GPIO.input(GPIO_SENSOR4_PIN) == GPIO.LOW and time.time()-start_time1 < TIMEOUT_3:
time.sleep(0.1)
else:
if time.time()-start_time1 >= TIMEOUT_3:
print "Timeout_3"
lcd.lcd_clear()
lcd.lcd_display_string("Timeout_3", 1)
exit()
else:
end_time3 = time.time()
speed_3 = DISTANCE_3 / (end_time3 - start_time1)
print "3. Geschwindigkeit: %.2f cm/s" % speed_3
print "3. Zeit: %.2f s" % (end_time3 - start_time1)
lcd.lcd_clear()
lcd.lcd_display_string("v: %.2f cm/s" % speed_3, 1)
lcd.lcd_display_string("t: %.2f s" % (end_time3 - start_time1), 2)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@simideluxe: In Python sind Einrückungen wichtig. Daher füge hier im Forum Code in einer CodeBox ein. So wie es aussieht, sind da ein paar Zeilen falsch Eingerückt, so dass Dein Programm gar nicht laufen dürfte.

Alles unter `if __name__ == '__main__':` sollte in einer Funktion stehen und dort nur der Funktionsaufruf. Außerdem hast Du alles drei mal kopiert, statt für die Zeitmessung eine Funktion auszulagern und diese 3mal aufzurufen. Warnings sollte man nicht auf False setzen, sondern die Ursache der Warnung beheben. Ein `else` bei einer Schleife ohne `break` macht keinen Sinn, weil der else-Zweig immer durchlaufen wird. Eine zehntel Sekunde ist ein relativ langer Zeitraum. Während dieser Zeit kann eine Kugel unbemerkt durch die Lichtschranke rollen. Variablen mit werden zu belegen, die man dann nicht benutzt ist unsinnig. Das Verhindert, dass man Fehler findet, weil man irgendwo vergessen hat, den richtigen Wert zu setzen. Die Shebang-Zeile (#!) muß die erste im Programm sein. `exit` sollte man in normalen Programmen nicht benutzen.

Code: Alles auswählen

#!/usr/bin/python
#Projektarbeit_Schiefe_Ebene_2017
import time
import RPi.GPIO as gpio
import lcddriver

GPIO_SENSOR_PINS = [22, 23, 24, 25]

SETUP = [
    {'pin': 23, 'distance': 10.0, 'timeout': 5},
    {'pin': 24, 'distance': 40.0, 'timeout': 10},
    {'pin': 25, 'distance': 90.0, 'timeout': 15},
]

class TimeoutError(Exception):
    pass

def get_lowtime(pin, timeout=1e30):
    start_time = current_time = time.time()
    while gpio.input(pin) == gpio.LOW:
        current_time = time.time()
        if current_time - start_time > timeout:
            raise TimeoutError()
    return current_time - start_time

def main():
    # Programm starten und Sensoren initialisieren:
    gpio.setmode(gpio.BCM)
    gpio.setup(GPIO_SENSOR_PINS, gpio.IN)
    lcd = lcddriver.lcd()
    lcd.lcd_clear()
    
    _ = get_lowtime(GPIO_SENSOR1_PIN) # Warten auf den Start

    for nr, setup in enumerate(SETUP, 1):
        try:
            timedelta = get_lowtime(setup['pin'], setup['timeout'])
        except TimeoutError:
            lcd.lcd_clear()
            lcd.lcd_display_string("Timeout %d" % nr, 1)
            return
        speed = setup['distance'] / timedelta
        print "%d. Geschwindigkeit: %.2f cm/s" % (nr, speed)
        print "%d. Zeit: %.2f s" % (nr, timedelta)
        lcd.lcd_display_string("v: %.2f cm/s" % speed, 1)
        lcd.lcd_display_string("t: %.2f s" % timedelta, 2)
    
if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirius3: Der busy-wait ist nicht gut. Dafuer gibt es wait_for_edge:

Code: Alles auswählen

def get_lowtime(pin, timeout=1e30):
    start = time.time()
    if GPIO.wait_for_edge(pin, GPIO_RISING, timeout=timeout * 1000) is None:
        raise TimeoutError
    return time.time() - start
simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

Vielen Dank für Eure schnelle und professionelle Hilfe!! Die ersten, die mir helfen :D @Sirius3: Das Programm hatte Einrückungen und ist auch ausführbar aber funktioniert wie gesagt nicht richtig.

Ich probiere Euren Quellcode und melde mich dann nochmal!
simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

@ Alle! Habe noch Fehlermeldungen:
- In Zeile 47 das main()
- In Zeile 31 ist der globale Name "GPIO_SENSOR1_PIN" nicht definiert

Code: Alles auswählen

#!/usr/bin/python
#Projektarbeit_Schiefe_Ebene_2017
import time
import RPi.GPIO as GPIO
import lcddriver

GPIO_SENSOR_PINS = [22, 23, 24, 25]

SETUP = [
        {'pin': 23, 'distance': 10.0, 'timeout': 5},
        {'pin': 24, 'distance': 40.0, 'timeout': 10},
        {'pin': 25, 'distance': 90.0, 'timeout': 15},
]

class TimeoutError(Exception):
    pass

def get_lowtime(pin, timeout=1e30):
    start = time.time()
    if GPIO.wait_for_edge(pin, GPIO_RISING, timeout=timeout * 1000) is None:
        raise TimeoutError
    return time.time() - start

def main():
    # Programm starten und Sensoren initialisieren:
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_SENSOR_PINS, GPIO.IN)
    lcd = lcddriver.lcd()
    lcd.lcd_clear()

    _ = get_lowtime(GPIO_SENSOR1_PIN) # Warten auf den Start

    for nr, setup in enumerate(SETUP, 1):
        try:
            timedelta = get_lowtime(setup['pin'], setup['timeout'])
        except TimeoutError:
            lcd.lcd_clear()
            lcd.lcd_display_string("Timeout %d" % nr, 1)
            return
        speed = setup['distance'] / timedelta
        print "%d. Geschwindigkeit: %.2f cm/s" % (nr, speed)
        print "%d. Zeit: %.2f s" % (nr, timedelta)
        lcd.lcd_display_string("v: %.2f cm/s" % speed, 1)
        lcd.lcd_display_string("t: %.2f s" % timedelta, 2)

if __name__ == '__main__':
    main()

__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na, versuch doch mal selbst rauszufinden, was damit gemeint sein koennte. Ein bisschen Muehe zu verstehen, was da passiert, darfst du dir schon geben :roll:
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@simideluxe: schön, dass Du Fehler gefunden hast. Da ist auch noch ein von Dir unentdeckter dabei. Ich bin mir sicher, dass Du beide beseitigen kannst.

Module werden klein geschrieben. Irgendwie ist GPIO wieder groß geworden.
simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

Gut "GPIO_SENSOR1_PIN" kann nicht gehen, weil es nicht deklariert ist. Ich würde jetzt auf die Liste zugreifen: "GPIO_SENSOR_PINS, 0" für den GPIO-PIN 22. Das mit main() verstehe ich nicht ganz. :?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

Vorherige Programmfehler sind nun behoben. Beim Ausführen des Programms stoße ich nun auf ein weiteres Problem:

line 41 wird aufgerufen: TypeError: not enough arguments for format string

Aber wieso fehlen Argumente? Kann mir wieder jemand nenn Tipp geben? Danke!

Code: Alles auswählen

#!/usr/bin/python
# Projektarbeit_Schiefe_Ebene_2017
import time
import RPi.GPIO as gpio # Einbinden der RPi.GPIO Bibliothek
import lcddriver	# Einbinden des LC-Display Treibers

GPIO_SENSOR_PINS = [22, 23, 24, 25] # Erstellen einer Liste mit den GPIO_SENSOR_PINS

SETUP = [
        {'pin': 23, 'distance': 10.0, 'timeout': 5},
        {'pin': 24, 'distance': 40.0, 'timeout': 10},
        {'pin': 25, 'distance': 90.0, 'timeout': 15},]

class TimeoutError(Exception):
    pass

def get_lowtime(pin, timeout=36):
    start = time.time()
    if gpio.wait_for_edge(pin, gpio.RISING, timeout=timeout*1000) is None:
        raise TimeoutError()
    return time.time() - start

def main():
    # Programm starten und Sensoren initialisieren:
    gpio.setmode(gpio.BCM)
    gpio.setup(GPIO_SENSOR_PINS, gpio.IN)
    lcd = lcddriver.lcd()
    lcd.lcd_clear()

    _ = get_lowtime(GPIO_SENSOR_PINS[0]) # Warten auf den Start

    for nr, setup in enumerate(SETUP, 1):
        try:
            timedelta = get_lowtime(setup['pin'], setup['timeout'])
        except TimeoutError:
            lcd.lcd_clear()
            lcd.lcd_display_string("Timeout %d" % nr, 1)
            return
        speed = setup['distance'] / timedelta
        print("%d. Geschwindigkeit" % nr, speed)
        print("%d. Zeit: %.2f s" % nr, timedelta)
        lcd.lcd_display_string("v: %.2f cm/s" % speed, 1)
        lcd.lcd_display_string("t: %.2f s" % timedelta, 2)


        result = speed
        with open('schiefe_ebene.csv','a') as out:
            for i in result:
                out.write("{};{};{}\n".format(testname,i,result[i]))


if __name__ == '__main__':		# Idiom erstellen

    main()

Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@simideluxe: zähl mal, wie viele Ersetzungstellen Du hast und wie viele Argument Du angibst. Welchen Typ hat `result`? Was enthält dann gegebenenfalls `i` in der for-Schleife und was ist dann `result`?

Damit die Messung nicht zu ungenau ist, sollte in der Schleife nichts als die Messung gemacht werden und die Ausgabe erst nachträglich.
simideluxe
User
Beiträge: 8
Registriert: Donnerstag 7. Dezember 2017, 08:59

Das komische ist ja, dass "speed" ausgegeben wird und timedelta nicht :? Die zwei Argumente müssten eigentlich schon stimmen. Mit result = speed usw... wollte ich die Werte sammeln und in eine Datei schreiben/speichern (Daran tüftel ich noch).
Antworten