Mein zweites Programm
#####################

Motoren ansteuern
-----------------

Um die Motoren des Roboters zu steuern, müssen wir zunächst das entsprechende Python-Modul am Anfang der Datei importieren. Dann können wir Motor.power(port, power) verwenden, um den Motor zu steuern.
Dies ist auch ein guter Punkt, um sich mit der Dokumentation vertraut zu machen: Besuchen wir https://lib.comp-air.at/lib/Motor.html#compLib.Motor.Motor.power. Hier werden die beiden relevanten Parameter beschrieben.

Als Beispiel wollen wir den rechten Motor für fünf Sekunden auf volle Geschwindigkeit setzen:

.. code-block:: python
    :linenos:

    # motor.py
    import time
    from compLib.Motor import Motor

    Motor.power(0, 100)
    time.sleep(5)

Gerade fahren
-------------
Um geradeaus zu fahren, müssen wir beide Motoren auf dieselbe Geschwindigkeit einstellen.
Aber Achtung! Der rechte Motor muss umgedreht werden! Das liegt daran, dass einer nach rechts und einer nach links zeigt, sie sind also technisch gesehen gespiegelt.
Wenn wir nun diesen Code ausführen, wird der Roboter 5 Sekunden lang vorwärts fahren:

.. code-block:: python
    :linenos:

    # motor.py
    import time
    from compLib.Motor import Motor

    Motor.power(0, -100)
    Motor.power(3, 100)
    time.sleep(5)

**Erläuterung**

| In ``Zeile 2`` wird das python-Paket ``time`` importiert. Wir brauchen es später, um auf die Fahrt des Roboters zu warten. Z.B.: ``time.sleep(5)``
| In ``Zeile 3`` importieren wir die notwendigen Funktionen aus dem ``Motor``-Modul der compLib.
| In ``Zeile 5`` stellen wir den ``rechten`` Motor so ein, dass er vorwärts fährt. Da der Motor rückwärts eingebaut ist, müssen wir den Wert auf ``-100`` setzen.
| In ``Zeile 6`` stellen wir den ``linken`` Motor auf Vorwärtsfahrt ein. Hier können wir den Wert ``100`` verwenden, da der Motor in der richtigen Richtung eingebaut ist.
| In ``Zeile 7`` müssen wir warten, bis der Roboter die Fahrbefehle tatsächlich ausführt. In diesem Fall warten wir ``5`` Sekunden lang.

Danach wird das Programm beendet und der Roboter bleibt stehen.

Mehr fahren
+++++++++++

Jetzt ist es Zeit für einige komplexere Bewegungen. Um unseren Code modular und leicht lesbar zu halten, werden wir jede Aktion in eine eigene Funktion packen.

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

In ``Zeile 4`` definieren wir die Funktion ``driveForward()``, die den Roboter mit voller Geschwindigkeit zwei Sekunden vorwärts bewegt.

Jetzt werden wir eine Funktion für das Rückwärtsfahren definieren:

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    def driveBackward():
        Motor.power(0, 100)
        Motor.power(3, -100)
        time.sleep(2)

In ``Zeile 9`` haben wir die Funktion ``driveBackward()`` definiert, die den Roboter zwei Sekunden lang rückwärts fahren lässt.

Jetzt können wir diese beiden Funktionen aufrufen und vorwärts und dann wieder rückwärts fahren:

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    def driveBackward():
        Motor.power(0, 100)
        Motor.power(3, -100)
        time.sleep(2)

    driveForward()
    driveBackward()

Wenn wir diesen Code ausführen, sollte der Roboter zunächst zwei Sekunden vorwärts und dann wieder zwei Sekunden rückwärts fahren und ungefähr an der gleichen Position wie beim Start anhalten.

Zwischen den Zeilen ``14`` und ``15`` brauchen wir kein ``time.sleep(2)``, da der sleep-Befehl bereits in den Funktionen integriert ist.

Jetzt wollen wir, dass der Roboter erst vorwärts fährt, dann zwei Sekunden stillsteht und dann wieder rückwärts in seine Ausgangsposition fährt.

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    def driveBackward():
        Motor.power(0, 100)
        Motor.power(3, -100)
        time.sleep(2)

    driveForward()
    time.sleep(2)
    driveBackward()

Wenn wir den obigen Code ausführen, bleibt der Roboter nicht zwei Sekunden lang stehen, sondern fährt nach der Funktion ``driveForward()`` noch zwei Sekunden lang weiter. Warum passiert das? Um das zu verstehen, müssen wir wie der Roboter denken!

**Erläuterung**

| 1. (``Zeile 14``) Die Funktion Vorwärtsfahrt wird aufgerufen
|   (``Zeile 5``) Motor 1 wird auf -100 gesetzt
|   (``Zeile 6``) Motor 4 wird auf 100 gesetzt
|   (``Zeile 7``) Zwei Sekunden warten und Motor 1 mit der Geschwindigkeit -100 und Motor 4 mit der Geschwindigkeit 100 bewegen (z.B. vorwärts fahren)

| 2. (``Zeile 15``) Zwei Sekunden warten, die Motoren sind immer noch auf -100 und 100 eingestellt, also fahren wir weiter vorwärts

| 3. (``Zeile 16``) Die Funktion Rückwärtsfahren wird aufgerufen
|   (``Zeile 5``) Motor 1 wird auf 100 gesetzt
|   (``Zeile 6``) Motor 4 wird auf -100 gesetzt
|   (``Zeile 7``) Warte zwei Sekunden und bewege Motor 1 mit der Geschwindigkeit 100 und Motor 4 mit der Geschwindigkeit -100 (z.B. Rückwärtsfahren)

| 4. Das Programm ist beendet, und alle Motordrehzahlen werden auf 0 gesetzt.

Wir sehen also, dass wir die Motoren nach der Vorwärts- oder Rückwärtsfunktion wieder auf Geschwindigkeit ``0`` setzen müssen, wenn wir den Roboter anhalten wollen. Für diesen Anwendungsfall können wir eine neue Funktion ``stopMotors()`` schreiben, die die Geschwindigkeit für Motor ``0`` und ``3`` auf ``0`` setzt:

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    def driveBackward():
        Motor.power(0, 100)
        Motor.power(3, -100)
        time.sleep(2)

    def stopMotors():
        Motor.power(0, 0)
        Motor.power(3, 0)

Wenn wir nun vorwärts fahren, dann zwei Sekunden warten und dann wieder rückwärts fahren wollen, können wir die Funktionen wie folgt aufrufen:

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    def driveBackward():
        Motor.power(0, 100)
        Motor.power(3, -100)
        time.sleep(2)

    def stopMotors():
        Motor.power(0, 0)
        Motor.power(3, 0)

    driveForward()
    stopMotors()
    time.sleep(2)
    driveBackward()

Und endlich bekommen wir die Bewegung, die wir uns wünschen.

**More Optimizations**

Während der Code für sehr einfache Bewegungen funktioniert, wollen wir normalerweise nicht, dass unsere Funktionen entscheiden, wie lange wir vorwärts fahren. Vielleicht müssen wir manchmal vier Sekunden vorwärts fahren, und manchmal nur eine Sekunde.

Nehmen wir an, wir wollen vier Sekunden vorwärts fahren. Wir wissen, dass ``driveForward()`` den Roboter zwei Sekunden vorwärts bewegen wird. Also können wir die Funktion einfach zwei Mal aufrufen!

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward():
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(2)

    driveForward()
    driveForward()

Was aber, wenn wir uns nur eine Sekunde vorwärts bewegen wollen? Oder vielleicht drei Sekunden? Mit der Funktion ``driveForward()`` können wir das im Moment nicht machen.

Stattdessen werden wir die Funktion so umschreiben, dass sie einen Parameter akzeptiert, der die Zeit angibt.

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward(seconds):
        Motor.power(0, -100)
        Motor.power(3, 100)
        time.sleep(seconds)

    driveForward(3)

Und mit dieser neuen Funktion können wir drei Sekunden lang vorwärts fahren.
Wie funktioniert das nun?

In ``Zeile 4`` definieren wir die Funktion ``driveForward`` und sagen, dass sie einen Parameter ``seconds`` benötigt. Dieser Parameter ist im Grunde eine Variable, die wir uns zum Zeitpunkt der Definition wie einen Platzhalter vorstellen können. Wenn wir die Funktion definieren, wissen wir noch nicht, welchen Wert ``seconds`` haben wird.

Später in ``Zeile 9``, wenn wir die Funktion aufrufen, übergeben wir den Wert ``3`` an die Funktion und unser Platzhalter ``seconds`` wird den Wert ``3`` haben. Der Roboter wird also drei Sekunden vorwärts fahren.

Vielleicht wollen wir auch, dass der Roboter mit verschiedenen Geschwindigkeiten fahren kann. Wir können also einen weiteren Parameter mit dem Namen ``speed`` anlegen. Dann werden wir ein Programm schreiben, das den Roboter drei Sekunden mit voller Geschwindigkeit und dann fünf Sekunden mit halber Geschwindigkeit fahren lässt.

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward(seconds, speed):
        Motor.power(0, -speed)
        Motor.power(3, speed)
        time.sleep(seconds)

    driveForward(3, 100)
    driveForward(5, 50)

In ``Zeile 9`` wird der Platzhalter ``seconds`` auf ``3`` und die ``Geschwindigkeit`` auf ``100`` gesetzt.
In ``Zeile 10`` wird der Platzhalter ``seconds`` auf ``5`` und die ``Geschwindigkeit`` auf ``50`` gesetzt.

**Bewährte Praktiken**
Nun werden wir uns einige weitere Optimierungen und bewährte Verfahren ansehen.

**1. Wir sollten den Schlafbefehl nicht in die Fahrfunktion einbauen.**

Wir haben das bis jetzt getan, um ein Gefühl dafür zu bekommen, wie Funktionen funktionieren, und der Einfachheit halber. Später, wenn Sie anfangen, komplexere Programme zu schreiben, sollten Sie dies vermeiden.

Das Beispiel von oben, in dem wir vorwärts und rückwärts gefahren sind und zwei Sekunden gewartet haben, sollte also wie folgt aussehen:

.. code-block:: python
    :linenos:

    import time
    from compLib.Motor import Motor

    def driveForward(speed):
        Motor.power(0, -speed)
        Motor.power(3, speed)

    def driveBackward(speed):
        Motor.power(0, speed)
        Motor.power(3, -speed)

    def stopMotors():
        Motor.power(0, 0)
        Motor.power(3, 0)

    driveForward(100)   # Set the motors to forward
    time.sleep(2)       # Let the robot drive for 2 seconds
    stopMotors()        # Now stop the robot

    time.sleep(2)       # Wait another 2 seconds, robot is not moving

    driveBackward(100)  # Now set the motors to a backwards speed
    time.sleep(2)       # Let the robot continue driving for 2 seconds
    stopMotors()        # And finally stop it again

**Warum ist das so wichtig?**

Normalerweise schlafen wir nicht sehr viel und führen in dieser Zeit andere Verarbeitungen durch. Zum Beispiel könnten wir ein Bild von der Kamera verarbeiten oder die IR-Sensoren auslesen. Wenn wir also eine Funktion wie ``driveForward()`` aufrufen, können wir davon ausgehen, dass sie im Hintergrund abläuft und wir andere Aufgaben erledigen, während sich der Roboter bewegt, anstatt nur darauf zu warten, dass er fertig wird.

**2. Fahren Sie nicht zu langsam.**

Wenn du die Fahrgeschwindigkeit auf eine sehr kleine Zahl einstellst, kann es sein, dass sich der Roboter gar nicht mehr bewegt, weil die Motoren eine bestimmte Menge an Energie benötigen, um den Roboter überhaupt zu bewegen.

**3. Fahren Sie nicht zu schnell.**

Wenn du die Fahrgeschwindigkeit auf eine sehr hohe Zahl einstellst (z. B. ``100``), könnte dein Roboter zu schnell für seine Sensoren sein. Dies wird später wichtig sein, wenn wir versuchen, eine schwarze Linie zu erkennen, aber zu schnell über sie fahren.
