TigerJython xx
für Gymnasien

Roboter-Kommumikation über TCP/IP


Zwei Pi2Go-Roboter können miteinander über das WLAN kommunizieren, indem sie einander Strings zusenden. Dabei läuft auf einem Raspibrick ein TCP-Server- und auf dem anderen ein TCP-Client-Programm. Unter der Verwendung des Moduls tcpcon aus der TigerJython-Distribution, können mit wenig Aufwand interessante Anwendungen programmiert werden.
 

In der Regel spielt es keine Rolle, welcher der beiden Robotern Server und welcher Client ist. Das Server-Programm muss aber immer zuerst gestartet werden. Der Client erstellt danach unter der Verwendung der IP-Adresse des Servers und der Portnummer, welche im Serverprogramm festgelegt ist, eine Verbindung zum Server. Die Verbindung kann über das öffentliche Internet, einen lokalen WLAN-Router oder einen mobiler WLAN-Hotspot, der auf einem Smartphone aktiviert ist, erfolgen. Mehr zur Einrichtung der Kommunikation findet man hier.

Das Modul tcpcom ist eventgesteuert aufgebaut. Der Status der Verbindung wird mit der Callbackfunktion onStateChanged(state, msg) überwacht. Diese wird bei der Erzeugung von TCPServers bzw. TCPClients registriert.

Der Parameter state des Servers kennt folgende Zustände:
  TCP.Server.LISTENING
TCP.Server.CONNECTED
TCP.Server.MESSAGE
TCP.Server.TERMINATE
Server wartet auf eine neue Verbindung
Die Verbindung zum Client wurde aufgebaut
Der Server hat eine Message msg empfangen
Der Server ist beendet

Der Client kennt folgende Zustände:
  TCPClient.CONNECTED
TCPClient.MESSAGE
TCPClient.DISSCONNECTED
Verbindung erfolgreich erstellt
Der Client hat eine Message msg empfangen
Verbindung unterbrochen

Mit der Funktion sendMessage() können Messages in Form eines Strings vom Server zum Client und umgekehrt gesendet werden. Mehr über die TCP/IP-Programmierung findet man unter www.tcpcom.ch.

Beispiel 1: Die Roboter stellen Züge dar, die sich auf einer runden Bahn bewegen. Ein Teil der Strecke ist nur einspurig befahrbar. Die Roboter teilen einander via TCP mit, wenn sie am Bahnhof angekommen sind und die Bahn frei ist. Das Serverprogramm wird zuerst gestartet. Nach der Aufbau der Verbindung fährt der Client zuerst seine Runde und hält beim beweglichen weissen Streifen an, den er mit seinem vorderen Infrarotsensor erfasst. Dabei sendet er die Message "go" an den Server. Dann fährt der Server seine Runde und sendet eine Message an Client, sobald er angehalten hat und die Bahn frei ist.


 

# TrainServer.py

from raspibrick import *
from tcpcom import TCPServer

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPServer.LISTENING:
        ledLeft.setColor("red")
        display.showText("stop")
        gear.stop()
        isWaiting = True
    if state == TCPServer.CONNECTED:
        ledLeft.setColor("green")
    if state == TCPServer.MESSAGE:
        isWaiting = False
        start = time.time()
        display.showText("run")

robot = Robot()
display = Display()
ledLeft = Led(LED_LEFT)
ledLeft.setColor("red")
gear = Gear()
gear.setSpeed(25)
irLeft = InfraredSensor(IR_LINE_LEFT)
irRight = InfraredSensor(IR_LINE_RIGHT)
irCenter = InfraredSensor(IR_CENTER)
r = 0.1
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
start = time.time()
isWaiting = True
while not isEscapeHit():
    if isWaiting:
        continue
    vL = irLeft.getValue()
    vR = irRight.getValue()
    vC = irCenter.getValue()
    if vL == 1 and vR == 0:
        gear.forward()
    elif vL == 0 and vR == 0:   
        gear.leftArc(r)
    elif vL == 1 and vR == 1:
        gear.rightArc(r)
    if vC == 1 and time.time() - start > 5:
        server.sendMessage("go")
        display.showText("stop")
        gear.stop()
        isWaiting = True
                
server.terminate()        
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

 

# TrainClient.py

from raspibrick import *
from tcpcom import TCPClient
import time

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPClient.CONNECTED:
        ledLeft.setColor("green")
    if state == TCPClient.DISCONNECTED:
        ledLeft.setColor("red")
        gear.stop()
    if state == TCPClient.MESSAGE:
        isWaiting = False
        start = time.time()
        display.showText("run")

robot = Robot()
display = Display()
ledLeft = Led(LED_LEFT)
ledLeft.setColor("red")
gear = Gear()
gear.setSpeed(25)
irLeft = InfraredSensor(IR_LINE_LEFT)
irRight = InfraredSensor(IR_LINE_RIGHT)
irCenter = InfraredSensor(IR_CENTER)
r = 0.1
host = "192.168.0.4"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
isWaiting = False
display.showText("try")
if client.connect():
    display.showText("run")
    start = time.time()
    while not isEscapeHit():
        if isWaiting:
            continue
        vL = irLeft.getValue()
        vR = irRight.getValue()
        vC = irCenter.getValue()
        if vL == 1 and vR == 0:
            gear.forward()
        elif vL == 0 and vR == 0:   
            gear.leftArc(r)
        elif vL == 1 and vR == 1:
            gear.rightArc(r)
        if vC == 1 and time.time() - start > 5:
            client.sendMessage("go")
            display.showText("stop")
            gear.stop()
            isWaiting = True
    client.disconnect()                
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

Erklärungen zum Programmcode:

host = "192.168.0.4": IP-Adresse des Roboters, auf welchen das Serverprogramm läuft
if vC == 1 and time.time() - start > 5: Der vordere Infrarotsensor hat den weissen Streifen erfasst und die Zeit seit dem letzten Start grösser als 5 Sekunden. Damit wird verhinder, dass der Roboter nach dem Anfahren sofort wieder stopt
client.sendMessage("go"): Mit dieser Message wird die Bahn für den zweiten Roboter frei gegeben

 

Beispiel 2: Die TCP-Kommunikation funktioniert auch im Simulationsmodus. Zum Testen kann man das Server- und das Client-Programm auf dem gleichen Computer ausführen, indem man die TigerJython-IDE zweimal startet und die IP-Adresse localhost verwendet. In diesem Beispiel werden die Züge jeweils mit einer Lichtquelle angehalten. Diese wird oberhalb der Geleise positioniert. Die Roboter erfassen die Lichtquelle mit den vorderen Lichtsensoren. Im realen Modus kann man als Lichtquelle eine Taschenlampe verwendet. Das Hintergrundbild für die Simulation (train.zip) muss in das Unterverzeichnis sprites des Programmverzeichnisses kopiert werden.

  Server:
Client

 

# SimTrainServer.py

from raspisim import *
from tcpcom import TCPServer
import time

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPServer.LISTENING:
        gear.stop()
        isWaiting = True
    if state == TCPServer.CONNECTED:
        robot.playTone(264, 100)
    if state == TCPServer.MESSAGE:
        if msg == "go":
            isWaiting = False
            start = time.time()
            print "run"

RobotContext.useBackground("sprites/train.gif")
RobotContext.setStartPosition(225, 400)
RobotContext.setStartDirection(190)
RobotContext.useTorch(1, 250, 400, 100)

robot = Robot()
gear = Gear()
irL = InfraredSensor(IR_LINE_LEFT)
irR = InfraredSensor(IR_LINE_RIGHT)
ls1 = LightSensor(LS_FRONT_LEFT)
ls2 = LightSensor(LS_FRONT_RIGHT)
r = 0.1
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
start = time.time()
isWaiting = True
while not isEscapeHit():
    if isWaiting:
        continue
    vL = irL.getValue()
    vR = irR.getValue()    
    v1 = ls1.getValue()
    v2 = ls2.getValue()
    if vL == 1 and vR == 1:
        gear.rightArc(r)
    if vL == 1 and vR == 0:
        gear.forward()
    elif vL == 0 and vR == 0:   
        gear.leftArc(r)   
    elif vL == 1 and vR == 0:
        gear.forward()
    if (v1 > 900 or v2 > 900) and time.time() - start > 5:
        server.sendMessage("go")
        gear.stop()
        Tools.delay(2000) 
        isWaiting = True                 
server.terminate()        
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

 

# SimTrainClient.py

from raspisim import *
from tcpcom import TCPClient
import time

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPClient.CONNECTED:
        robot.playTone(440, 100)
    if state == TCPClient.DISCONNECTED:
        gear.stop()
    if state == TCPClient.MESSAGE:
        if msg == "go":
            isWaiting = False       
            start = time.time()
            print "run"    
            
RobotContext.useBackground("sprites/train.gif")
RobotContext.useTorch(1, 250, 350, 100)
RobotContext.setStartPosition(285, 350)
RobotContext.setStartDirection(-20)

robot = Robot()
gear = Gear()
irL = InfraredSensor(IR_LINE_LEFT)
irR = InfraredSensor(IR_LINE_RIGHT)
ls1 = LightSensor(LS_FRONT_LEFT)
ls2 = LightSensor(LS_FRONT_RIGHT)
gear.forward()
r = 0.1
host = "localhost"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
isWaiting = False
if client.connect():
    start = time.time()
    while not isEscapeHit():
        if isWaiting:
            continue
        vL = irL.getValue()
        vR = irR.getValue()
        v1 = ls1.getValue()
        v2 = ls2.getValue()
        if vL == 0 and vR == 0:   
            gear.leftArc(r)
        elif vL == 1 and vR == 1:
            gear.rightArc(r)
        elif vL == 1 and vR == 0:
            gear.forward()              
        if (v1 > 900 or v2 > 900) and time.time() - start > 5:
            client.sendMessage("go")
            gear.stop()
            Tools.delay(2000)  
            isWaiting = True      
    client.disconnect()                
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

Erklärungen zum Programmcode:

host = "localhost": Anstelle der IP-Adresse des Servers kann localhost verwendet werden
if (v1 > 900 or v2 > 900) and time.time() - start > 5: Falls der Roboter unter der Lichquelle ist und die Zeit seit dem letzten Start grösser als 5 Sekunden ist, hält er an
client.sendMessage("go"): Mit dieser Message wird die Bahn für den zweiten Roboter frei gegeben

 


Aufgaben Serie10:

1)

Synchron fahren und stoppen.
Zwei Roboter bewegen sich auf einer Bahn. Nach zufällig gewählten Anzahl Millisekunden hält der Client an und bleibt kurze Zeit stehen und fährt weiter. Seine "stop" und "go" sendet er dem Server, der dann ebenfalls anhalten oder losfahren muss.