TigerJython xx
für Gymnasien

18. Roboter-Kommumikation über TCP/IP


Zwei EV3-Roboter können miteinander über das WLAN kommunizieren, indem sie einander Strings zusenden. Dabei läuft auf einem EV3-Brick 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 tcpcom.

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 blauen Streifen an, der mit einem zweiten Lichtsensor erkannt wird. Dabei sendet er die Message "go" an den Server. Dann fährt der Server eine Runde und sendet die Message "go" an den Client, sobald er angehalten hat und die Bahn frei ist.



In den folgenden Programmen sind die Importzeilen "from tcpcom import TCPServer" bzw. "from tcpcom import TCPClient" nur für den direkten Modus notwendig. Für den realen Modus muss man diese Zeilen deaktivieren oder löschen. Auf der SDCard ist das Modul tcpcom im Modul ev3robot integriert und muss daher nicht explizit importiert werden.

#TrainServer.py

from ev3robot import *
from tcpcom import TCPServer 
import time

def onStateChanged(state, msg):
    global isWaiting, isStoped, start
    if state == TCPServer.LISTENING:
        gear.stop()
        isWaiting = True
    if state == TCPServer.CONNECTED:
        robot.drawString("Connected", 0, 1);
        playTone(260, 100) 
    if state == TCPServer.MESSAGE:
        if msg == "go":
            isWaiting = False
            start = time.time()
            print "run"
    
robot = LegoRobot()
gear = Gear()
robot.addPart(gear)
ls1 = LightSensor(SensorPort.S3)
robot.addPart(ls1)
ls2 = LightSensor(SensorPort.S1)
robot.addPart(ls2)
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
start = time.time()
isWaiting = True

while not robot.isEscapeHit():
    if isWaiting:
        continue
    v1 = ls1.getValue()
    v2 = ls2.getValue()
    if v1 > 500:  
        gear.rightArc(0.2)
    else: 
        gear.leftArc(0.2)
    if v2 > 300 and v2 < 600 and time.time() - start > 5: 
        server.sendMessage("go")
        print "ok"
        gear.stop()
        isWaiting = True           
    Tools.delay(200)                    
server.terminate()        
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

 

# TrainClient.py

from ev3robot import *
from tcpcom import TCPClient
import time

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPClient.CONNECTED:
        print "connected"
    if state == TCPClient.DISCONNECTED:
        gear.stop()
    if state == TCPClient.MESSAGE:
        if msg == "go":
            isWaiting = False 
            start = time.time() 
            print "run"     
            
robot = LegoRobot()
gear = Gear()
robot.addPart(gear)
ls1 = LightSensor(SensorPort.S3)
robot.addPart(ls1)
ls2 = LightSensor(SensorPort.S1)
robot.addPart(ls2)
gear.forward()
host = "192.168.0.17"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
isWaiting = False
if client.connect():
    start = time.time()
    while not robot.isEscapeHit():
        if isWaiting:
            continue
        v1 = ls1.getValue()
        v2 = ls2.getValue()
        if v1 > 500:  
            gear.rightArc(0.2)
        else: 
            gear.leftArc(0.2) 
        if v2 > 300 and v2 < 600 and time.time() - start > 5: 
            print "ok"
            gear.stop()
            client.sendMessage("go")
            isWaiting = True   
        Tools.delay(200)            
    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 v2 > 500 and v2 < 750 and time.time() - start > 5: Falls der Roboter mit seinem Lichtsensor den blauen Streifen erfasst hat 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 Server-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 einem blauen Streifen gestoppt. Die Roboter registrieren den blauen Streifen mit einem Lichtsensor.

Das Hintergrundbild für die Simulation train.png können Sie hier herunterladen. Klicken Sie mit der rechten Maustaste auf das nebenstehende Bild, wählen Sie Grafik speichern unter und speichern Sie das Bild im Unterverzeichnis sprites des Verzeichnisses, in dem sich ihr Programm befindet.

 

 
# SimTrainServer.py

from simrobot import *
from tcpcom import TCPServer 
import time

def onStateChanged(state, msg):
    global isWaiting, isStoped, start
    if state == TCPServer.LISTENING:
        gear.stop()
        isWaiting = True
    if state == TCPServer.CONNECTED:
        robot.drawString("Connected", 0, 1);
        playTone(260, 100) 
    if state == TCPServer.MESSAGE:
        if msg == "go":
            isWaiting = False
            start = time.time()
            print "run"
    
RobotContext.useBackground("sprites/train.png")
RobotContext.setStartPosition(230, 415)
RobotContext.setStartDirection(180)

robot = LegoRobot()
gear = Gear()
robot.addPart(gear)
ls1 = LightSensor(SensorPort.S3)
robot.addPart(ls1)
ls2 = LightSensor(SensorPort.S1)
robot.addPart(ls2)
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
start = time.time()
isWaiting = True

while not robot.isEscapeHit():
    if isWaiting:
        continue
    v1 = ls1.getValue()
    v2 = ls2.getValue()
    if v1 > 800:  
        gear.rightArc(0.2)
    else: 
        gear.leftArc(0.2)
    if v2 > 500 and v2 < 750 and time.time() - start > 5: 
        server.sendMessage("go")
        print "ok"
        gear.stop()
        isWaiting = True           
    Tools.delay(200)                    
server.terminate()        
robot.exit()
Programmcode markieren (Ctrl+C kopieren)

 

# SimTrainClient.py

from simrobot import *
from tcpcom import TCPClient
import time

def onStateChanged(state, msg):
    global isWaiting, start
    if state == TCPClient.CONNECTED:
        print "connected"
    if state == TCPClient.DISCONNECTED:
        gear.stop()
    if state == TCPClient.MESSAGE:
        if msg == "go":
            isWaiting = False 
            start = time.time() 
            print "run"     
            
RobotContext.useBackground("sprites/train.png")
RobotContext.setStartPosition(285, 350)
RobotContext.setStartDirection(-20)

robot = LegoRobot()
gear = Gear()
robot.addPart(gear)
ls1 = LightSensor(SensorPort.S3)
robot.addPart(ls1)
ls2 = LightSensor(SensorPort.S1)
robot.addPart(ls2)
gear.forward()
host = "localhost"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
isWaiting = False
if client.connect():
    start = time.time()
    while not robot.isEscapeHit():
        if isWaiting:
            continue
        v1 = ls1.getValue()
        v2 = ls2.getValue()
        if v1 > 800:  
            gear.rightArc(0.2)
        else: 
            gear.leftArc(0.2) 
        if v2 > 500 and v2 < 750 and time.time() - start > 5: 
            print "ok"
            gear.stop()
            client.sendMessage("go")
            isWaiting = True   
        Tools.delay(200)            
    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 v2 > 500 and v2 < 750 and time.time() - start > 5: Falls der Roboter mit seinem Lichtsensor den blauen Streifen erfasst hat 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 Server-Roboter frei gegeben

 


Aufgaben Serie18:

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.