fischertechnik 30402 und Python

fischertechnik 30402 und Python
verrahtetes Interface 30402 von fischertechnik

Beim aufräumen habe ich ein altes fischertechnik intelligent Interface 30402 von etwa 1998 gefunden. Dazu gab es auch mal einen Roboter-Bausatz. Leider nur Win95 Software.

Seit 1996 bin ich privat Microsoft-frei und so lag der Kasten rum. Die letzten Tage habe ich dann das ft-Python-Projekt in Angriff genommen. Im Internet habe ich ein PDF gefunden, das beschreibt, wie mit dem Interface seriell kommuniziert werden kann. 
Super, hab ich mich gefreut und das Projekt mächtig unterschätzt.
Es geht um dieses Interface

verdrahtetes Interface 30402 von fischertechnik

Die serielle Ansteuerung

Zur Ansteuerung des Interface sind grundsätzlich zwei Bytes an das serielle Interface zu schicken. Das erste Byte ist der Interfacebefehl (s.u.) und das zweite Byte enthält den Motorzustand. Der Motorzustand beschreibt, welche Motoren in welcher Richtung gestartet werden sollen. Je nach Interfacebefehl antwortet das Interface mit einem oder drei Bytes.

Was ich bisher kann:

  • Motoren ansteuern
  • Eingänge abfragen
  • seriell kommunizieren
  • die max 300ms je Telegram einhalten
  • serielle bytes in binäre Register abbilden und maskieren
  • gezielt einen Eingang abfragen
  • bestimmte Wegstrecken fahren

Was ich noch nicht kann:

  • das serielle Kabel vom Laptop weglassen.

jetzt erstmal ein kleines Video vom Fahrzeug, das beim Anstossen ausweicht.

Was macht es genau? Nach dem Auslösen des Sensors fährt das Modell kurz mit beiden Rädern rückwärts und danach mit dem gegenüberliegenden Rad einzeln ein kleines Stück.

Dazu braucht es:

  • Feststellen, dass ein Sensor ausgelöst wurde
  • Vorwärtsfahrt beider Motoren unterbrechen
  • Rückwärtsfahrt mit beiden Motoren einleiten
    • bestimmte Wegstrecke rückwärts fahren
  • einzelnen Motor ansteuern und Wegstrecke rückwärts fahren
  • Ausweichmanöver beenden
  • Vorwärtsfahrt wieder einleiten.
  • Notaus-Sensor zum Abschalten ständig prüfen.

In der IDLE Ausgabe sieht das so aus:

Jede Menge Einzelaufgaben die ich wie folgt über Python gelöst habe:

#E4 linker bumper
if int(neu,2) & int(E4n,2):
fahrt=('ausweichen links <---------')
#für eine Sekunde Rückwärts fahren
wegstrecke_fahren(rückwärts,2)
wegstrecke_fahren(linkskurve_rü,3)

 
#E3 rechter bumper
elif int(neu,2) & int(E3n,2):
fahrt=('ausweichen rechts <---------')
#für eine Sekunde Rückwärts fahren
wegstrecke_fahren(rückwärts,2)
wegstrecke_fahren(rechtskurve_rü,3)

Ihr seht, es gibt offensichtlich eine Funktion def wegstrecke_fahren(a,b)

  • es wird ein Parameter für den Fahrbefehl übergeben
  • es wird ein Parameter für die Anzahl Impulse übergeben

davor steht aber noch ein if int(neu,2) & int(E4n,2):

  • neu ist das byte, das von der seriellen Schnittstelle zurück kommt
  • es wir hier vom binär ins dezimal-Format gewandelt
  • und mit dem dezimal-Format von E4n maskiert.
  • E4n hat was mit Eingang 4 zu tun.

Ich mache also etwas in dieser Art:

neu:  00001101 (eingelesen seriell)
E4n:  00000100 (Maske für Eingang 4 Signal)
Erg:  00000100 (Ergebnis von UND ist vereinfacht True
  

Da ich die Register ‚00011011‘ als String führe, kann ich die Strings nicht wie oben miteinander behandeln. Deshalb gehe ich zurück auf das Dezimal-Integer-Format und habe es einfacher. Somit kann ich gezielt abfragen, ob ein bestimmter Eingang gerade aktiv ist. Mehr Umstände muss ich nicht machen, da keine negativen Werte oder Floats über die Schnittstelle kommen.
Schön, wenn man ganz einfach die beiden ‚Register‘ verarbeiten kann.

Wenn nur der Eingang 4 gesetzt ist, kommt über die Schnittstelle dieses byte
E4 = b'\x08'
im Hex-Format.
Damit fange ich nicht soviel an. Deshalb wir aus E4 ein E4n:
E4n = (‚{:08b}‘.format(int.from_bytes(E4, ‚big‘, signed=False)))

Das byte wird in binär umformatiert und mit führenden Nullen auf 8 Stellen angelegt.
Im Python-Script habe ich für mich definiert:

# Tastersignale
E1 = b'\x01'
E1n = ('{:08b}'.format(int.from_bytes(E1, 'big', signed=False)))
E2 = b'\x02'
E2n = ('{:08b}'.format(int.from_bytes(E2, 'big', signed=False)))
E3 = b'\x04'
E3n = ('{:08b}'.format(int.from_bytes(E3, 'big', signed=False)))
E4 = b'\x08'
E4n = ('{:08b}'.format(int.from_bytes(E4, 'big', signed=False)))
E5 = b'\x10' # 1*16
E5n = ('{:08b}'.format(int.from_bytes(E5, 'big', signed=False)))
E6 = b'\x20' # 2*16
E6n = ('{:08b}'.format(int.from_bytes(E6, 'big', signed=False)))
E7 = b'\x40' # 4*16
E7n = ('{:08b}'.format(int.from_bytes(E7, 'big', signed=False)))
E8 = b'\x80' # 8*16
E8n = ('{:08b}'.format(int.from_bytes(E8, 'big', signed=False)))

Nun zu den Motoren.

innerhalb von 300ms muss über die Schnittstelle ein Status für die Motoren geschickt werden. Erfolgt das nicht, werden die Motoren vom Interface abgeschaltet. Die Motoren werden wie folgt angesprochen:

Für die beiden verwendeten Motoren habe ich mir das zu Beginn so dokumentiert:

# Ansteuerung der Motoren und Eingänge am intelligent Interface 30402
# motorzustand= b'00001010' #beide Motoren vorwärts
# motorzustand= b'00000101' #beide Motoren rückwärts
# motorzustand= b'00000001' #M1 rechter Motor rückwärts
# motorzustand= b'00000010' #M1 rechter Motor vorwärts
# motorzustand= b'00001000' #M2 linker Motor vorwärts
# motorzustand= b'00000100' #M2 linker Motor rückwärts

Recht unhandlich und in keiner Weise flexibel. Es ist ja folgendes klar, immer 2 bit sind für einen Motor zuständig. Damit wir der Motor in der jeweiligen Richtung eingeschaltet.
Somit gilt 01 ist Motor links und 10 ist Motor rechts. Nicht vergessen, 00 ist Motor aus.
Daraus lässt sich in Python ein wunderschönes Dict anlegen:

#Vorbelegungen Motorbewegungen über ein dict
#mv=MotorVorwärt, mr=MotorRückwärts, maus=MotorAUS
mot = {'mv':'10','mr':'01','maus':'00'}

Typische Fahrbefehle habe ich mir dann so angelegt:

#Zusammenstellungen von typischen FahrBefehlen
m1m2v=(mot['maus']+mot['maus']+mot['mv']+mot['mv'])
m1m2r=(mot['maus']+mot['maus']+mot['mr']+mot['mr'])
nurm1v=(mot['maus']+mot['maus']+mot['maus']+mot['mv'])
nurm2v=(mot['maus']+mot['maus']+mot['mv']+mot['maus'])
nurm1r=(mot['maus']+mot['maus']+mot['maus']+mot['mr'])
nurm2r=(mot['maus']+mot['maus']+mot['mr']+mot['maus'])

Das war immer noch unhandlich, daraus wurde dann:

#Kurzbefehle der Fahrten für die Programmierung
vorwärts = m1m2v
rückwärts = m1m2r
linkskurve = nurm1v
rechtskurve = nurm2v
linkskurve_rü=nurm1r
rechtskurve_rü=nurm2r
#default Motorsituation
alleMotorenAus=(mot['maus']+mot['maus']+mot['maus']+mot['maus'])

Jetzt sind wir wieder am Anfang bei unserer Funktion fürs Ausweichen:

wegstrecke_fahren(rückwärts,2)
wegstrecke_fahren(linkskurve_rü,3)

Dort wurden als Parameter die Kurzbefehle und die Anzahl Impulse übergeben.

Die Impulse

Die Impulse für die Wegstreckenzählung funktionieren so:

Tut, hat aber einen Nachteil. Bei einer Zyklenzeit von max. 300ms bekomme ich das ‚Gedrückt‘-Signal mehrmals hintereinander und das ‚Nicht-Gedrückt‘-Signal natürlich auch. Wie finde ich jetzt raus wie ich das sauber zählen kann. Also, der Nocken wurde einmal ganz reingedrückt und einmal ganz losgelassen ?

if motorzustand==rechtskurve_rü:
  #print('Rechtskurve rückwärts')
  #Eingänge abfragen und reagieren
  #E2 Impulszähler entprellen
  if frisch:
    if (int(neu,2) & int(E2n,2)): # Impuls da, könnte mehrmals sein
    #print('Impuls erkannt',n)
    frisch = False
  if frisch==False:
    if (int(neu,2) & int(E2n,2))==False: #bitweises exclusives Oder
    #print('Impuls wieder weg',n)
    frisch = True
    n=n+1 #Impuls
 
Über den Fahrbefehl weiss ich, welcher Motor läuft und welchen Taster ich prüfen muss. Die Aufgabe ist in 2 Teile zerlegt.
  1. feststellen, ob der Nocken einmal ganz durchgedrückt war
  2. feststellen, ob der Nocken einmal ganz ausgerückt war

Das war dann ein ganzer Impuls. Also einmal rein und raus vom Nocken. Die if-Bedingungen prüfen zuerst auf True und dann auf False. Also, ob der Nocken des Sensors ein Signal am Eingang liefert und danach dass kein Signal mehr geliefert wird. Das geht über die Maskierung mit & (UND).

Der Eingang 2 ist binär 0000 0010 und die Maske arbeitet wie folgt:

neu:  00001110 (eingelesen seriell)
E2n:  00000010 (Maske für Eingang 2 Signal)
Erg:  00000010 (Ergebnis von UND ist vereinfacht True
 

jetzt ist das Signal nicht mehr ganz ‚frisch‘ und ich setze frisch auf false. Somit greift die zweite IF-Bedingung und ich prüfe mit derselben Maske, werte aber False aus:
 

neu:  00001100 (eingelesen seriell)
E2n:  00000010 (Maske für Eingang 2 Signal)
Erg:  00000000 (Ergebnis von UND ist vereinfacht False
 

Da nicht beide Bits auf 0 sitzen, wird ist das Ergebnis False. Beim ersten sicheren Erkennen von False suche ich wieder ein ‚frisches‘ Signal. Mit diesem Trick habe ich die Signale an der Radachse entprellt und zähle ganze Hübe des Nockens.
Nun noch ein kleiner Zähler in der Funktion und ich kann die Wegstrecke im Verhältnis zu den Nockenhüben festlegen:
 
def wegstrecke_fahren(motorzustand,impulse):
  n=0
  frisch=True
  #print(motorzustand)
  while n <= impulse:
    ...
       frisch = True
       n=n+1 #Impuls
 
So sieht das dann aus. Achtet darauf, nach dem Auslösen eines Nockens wird kurz mit beiden Rädern rückwärts gefahren und danach fährt nur noch ein Rad rückwärts. Nach kurzer Zeit fahren wieder beide Räder vorwärts.
 

Demnächst geht’s weiter.

 
  • RaspberryPi zero mit RS232-Hat
  • Fahrbefehle mit Blynk

0 Kommentare zu „fischertechnik 30402 und Python

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.