Skip to main content

Codebeispiele

Theo III

Best Practise

Ausschalten der Motoren bei Unterbrechung des Programms

Anfangs wird man sehr viel an dem Roboterprogramm testen müssen. Dabei wird das Programm dann häufig abstürzen. Damit die Motoren nicht weiter in dem Zustand laufen, in dem sie dabei geschaltet waren, kann man mit einem try/except arbeiten:

try:
  # Hier läuft das Programm
except Exception as err:
    print(err) # Nötig, um Fehlermeldungen angezeigt zu bekommen.
    r.emergency_stop() # Roboter anhalten, hier ein Beispiel mit der robotlibrary.
    print("Robot stopped") # Damit es ganz deutlich ist.
except KeyboardInterrupt:
    r.emergency_stop()
    print("Keyboard interrupt")

Effizienter Code

Auch wenn die Rechenleistung der Picos ausreichen sollte, ergibt es Sinn, sich über Effizienz Gedanken zu machen, da schwer zu erkennen ist, ob manche Probleme durch Überlastung des Prozessors hervorgerufen werden.

while True:
    robot.drive()
    if us.get_dist() > min_distance:
        # stop or turn
        robot.stop()

In diesem Beispiel wird in der Schleife der Befehl drive() mit jedem Durchlauf aufgerufen, was nicht sonderlich effizient ist, da die Motoren weiterfahren, auch wenn das Programm gerade andere Befehle ausführt. Eine bessere Variante wäre diese:

robot.drive()
while us.get_dist() > min_distance:
    pass
robot.turn()

Hier wird nur die Entfernung zum nächsten Hindernis überprüft. Sobald der Roboter zu nahe gekommen ist, wird die Schleife beendet und der Code wird weiter ausgeführt.

Fehlertoleranter Code

Die Sensoren, die wir benutzen, liefern nicht immer zuverlässige und korrekte Ergebnisse. Daher kann man sich nicht darauf verlassen, dass eine Messung ausreicht. Ist man auf genauere Ergebnisse angewiesen, kann es sinnvoll sein, die Ergebnisse von Sensormessungen (insbesondere des Ultraschallsensors) zu filtern. Dazu kann gehören, Extremwerte, die im vorliegenden Fall unwahrscheinlich sind, zu ignorieren oder Mittelwerte von mehreren Messungen zu bilden. Ein Beispiel für die Glättung der Entfernungswerte aus dem Ultraschallsensor:

us = Ultra(16)
dist_values = deque([0,0,0,0,0],5)
while True:
    d = us.get_dist()
    dist_values.append(d)
    d = sum(dist_values)/len(dist_values)
    print(f"Entfernung: {d} cm")

Eine andere Methode muss für das Auslesen des Infrarotsensors gefunden werden. Hier könnte man z.B. eine kurze Wartezeit einbauen und dann abfragen, ob der Sensor immer noch denselben Wert liefert wie bei Auslösung der Reaktion.

Überschreiben von Methoden aus der Bibliothek

Möchte man die Funktion einer Methode aus der Roboterbibliothek (robotlibrary) verändern, kann man die Methode überschreiben (Fachbegriff aus der objektorientierten Programmierung). Dafür wird die Klasse Robot vererbt, wie in dem Codebeispiel angegeben. Möchte man nun z.B. die Methode set_speed() verändern, dann definiert man sie einfach neu. Ist eine Methode nicht in der abgeleiteten Klasse (in diesem Fall MyRobot definiert, dann wird die Methode der Elternklasse (Robot) genommen. Probiere es aus, in dem du das vorliegende Programm einmal mit und einmal ohne die Definition von set_speed() aufrufst.

from robotlibrary.robot import Robot
from time import sleep, sleep_ms

class MyRobot(Robot):
    def __init__(self):
        super().__init__(False,None)
        print("start")
        
    def set_speed(self,x):
        print(f"Child method set_speed. Value: {x}")
        
def main():
    try:
        robot = MyRobot()
        robot.set_speed(90)
        while True:
            sleep(1)
    except KeyboardInterrupt:
        robot.emergency_stop()
     
if __name__ == "__main__":
    # execute only if run as a script
    main()

Verändern der Funktion der Fernbedienung

Eine Ableitung funktioniert nicht, wenn man die Methode read() verändern möchte, da von der Bibliothek immer die Elternklasse aufgerufen würde. Man kann aber vor der Initialisierung der Klasse Robot eine neue Methode definieren und diese als Parameter übergeben. Hier ist ein Beispiel, wie es geht.

# Example for how to override the read() method that gets input from the remote control.
# Define your own method. 
def my_read(buffer: memoryview):
    print("my_read called.")
    
def main():
    try:
        # Use the name of your method as a parameter when initialising the robot object. 
        r = Robot(True,my_read)
        r.set_speed(100)
        while True:
            sleep(1)
    except KeyboardInterrupt:
        r.emergency_stop()
    
if __name__ == "__main__":
    # execute only if run as a script
    main()

Den Ultraschallsensor im Hintergrund laufen lassen

In komplexeren Programmen kann es lästig werden, immer wieder die Entfernung zum nächsten Hindernis zu überpüfen. Dieses Beispiel erläutert, wie man das Problem löst, indem man die Entfernungsmessung in den Hintergrund verlegt.

from robotlibrary.robot import Robot
from time import sleep, sleep_ms
import uasyncio as asyncio

################################## Your class definition
class MyRobot(Robot):
    def __init__(self):
        super().__init__(False,None) # Call the original constructor.
        print("start")
        
    # With this method defined here, the robot will not drive as the speed is not set in this function.     
    def set_speed(self,x):
        print(f"Child method set_speed. Value: {x}")
        
################################# End of class definition
# Define functions for your program
async def monitor_dist():
    global distance
    while True:
        if robot.get_dist() < distance:
            react_to_obstacle()
        await asyncio.sleep_ms(100)

def react_to_obstacle():
    global distance
    print("danger")
    robot.random_spin(300)
    robot.set_forward(True)
    robot.set_speed(80)
    
async def my_program():
    robot.set_speed(90)
    while True:
        print("driving")
        await asyncio.sleep_ms(100)

async def main():      
    asyncio.create_task(monitor_dist())
    await my_program()
    
        
################################# Initialize the robot and start the program.
robot = MyRobot()
distance = 20
if __name__ == "__main__":
    # execute only if run as a script
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Emergency stop")
        robot.emergency_stop()


Beschleunigung mit Entfernungsmessung

Diese Methode ist nur nötig, wenn man nicht asyncio benutzt. Beschleunigt man den Roboter langsam mit der Methode set_speed, dann kann er in der Zeit bis zum Erreichen der Geschwindigkeit keine Entfernungsmessung durchführen. Dies ist ein Beispiel, wie man beides erreichen kann:

obstacle_detected = False
new_speed = 100
speed_now = 0
min_distance = 15
while speed_now <= new_speed and not obstacle_detected:
    #Set the speed for the motors, f. ex. motor.set_speed(speed_now)
    utime.sleep_ms(10+int(speed_now/2))
    speed_now += 1
    if us.get_dist() < min_distance: # Adjust the code to your needs. 
        obstacle_detected = True
if obstacle_detected:
    # Stop or turn or whatever
    obstacle_detected = False
else:
    # keep going
    pass