update documentation

This commit is contained in:
Konstantin Lampalzer 2022-10-13 00:02:11 +02:00
parent 6245d1308a
commit e1a17808f7
24 changed files with 1749 additions and 1014 deletions

View file

@ -59,8 +59,9 @@ message IRSensorsReadAllResponse {
}
message MotorSetPowerRequest {
uint32 port = 1;
double power = 2;
Header header = 1;
uint32 port = 2;
double power = 3;
}
message MotorsSetPowerRequest {
@ -69,8 +70,9 @@ message MotorsSetPowerRequest {
}
message MotorSetSpeedRequest {
uint32 port = 1;
double speed = 2;
Header header = 1;
uint32 port = 2;
double speed = 3;
}
message MotorsSetSpeedRequest {
@ -78,6 +80,17 @@ message MotorsSetSpeedRequest {
repeated MotorSetSpeedRequest requests = 2;
}
message MotorSetPulseWidthRequest {
Header header = 1;
uint32 port = 2;
double percent = 3;
}
message MotorsSetPulseWidthRequest {
Header header = 1;
repeated MotorSetPulseWidthRequest requests = 2;
}
message OdometryReadRequest {
Header header = 1;
}

View file

@ -5,10 +5,8 @@ import compLib.CompLib_pb2 as CompLib_pb2
class CompLibClient(object):
USE_UNIX_SOCKET = False
UNIX_SOCKET_PATH = "/tmp/compLib"
USE_TCP_SOCKET = False
TCP_SOCKET_HOST = "10.20.5.1"
TCP_SOCKET_HOST = ""
TCP_SOCKET_PORT = 9090
SOCKET = None
LOCK = Lock()
@ -16,10 +14,9 @@ class CompLibClient(object):
@staticmethod
def use_unix_socket(socket_path="/tmp/compLib"):
CompLibClient.UNIX_SOCKET_PATH = socket_path
CompLibClient.USE_UNIX_SOCKET = True
CompLibClient.USE_TCP_SOCKET = False
CompLibClient.SOCKET = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
CompLibClient.SOCKET = socket.socket(
socket.AF_UNIX, socket.SOCK_STREAM)
CompLibClient.SOCKET.connect(CompLibClient.UNIX_SOCKET_PATH)
from compLib.HealthCheck import HealthUpdater
@ -29,11 +26,11 @@ class CompLibClient(object):
def use_tcp_socket(socket_host, socket_port=TCP_SOCKET_PORT):
CompLibClient.TCP_SOCKET_HOST = socket_host
CompLibClient.TCP_SOCKET_PORT = socket_port
CompLibClient.USE_UNIX_SOCKET = False
CompLibClient.USE_TCP_SOCKET = True
CompLibClient.SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
CompLibClient.SOCKET.connect((CompLibClient.TCP_SOCKET_HOST, CompLibClient.TCP_SOCKET_PORT))
CompLibClient.SOCKET = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
CompLibClient.SOCKET.connect(
(CompLibClient.TCP_SOCKET_HOST, CompLibClient.TCP_SOCKET_PORT))
from compLib.HealthCheck import HealthUpdater
HealthUpdater.start()
@ -41,22 +38,15 @@ class CompLibClient(object):
@staticmethod
def send(data: bytes, size: int) -> bytes:
with CompLibClient.LOCK:
# if CompLibClient.USE_TCP_SOCKET:
# sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# sock.connect((CompLibClient.TCP_SOCKET_HOST, CompLibClient.TCP_SOCKET_PORT))
#
# elif CompLibClient.USE_UNIX_SOCKET:
# sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# sock.connect(CompLibClient.UNIX_SOCKET_PATH)
#
# else:
# return bytes(0)
if CompLibClient.SOCKET is None:
CompLibClient.use_unix_socket()
CompLibClient.SOCKET.sendall(size.to_bytes(1, byteorder='big'))
CompLibClient.SOCKET.sendall(data)
response_size_bytes = CompLibClient.SOCKET.recv(1)
response_size = int.from_bytes(response_size_bytes, byteorder="big")
response_size = int.from_bytes(
response_size_bytes, byteorder="big")
# print(response_size)
response_bytes = CompLibClient.SOCKET.recv(response_size)

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
import time
import compLib.CompLib_pb2 as CompLib_pb2
from compLib.CompLibClient import CompLibClient
@ -17,7 +19,8 @@ class IRSensor(object):
request.header.message_type = request.DESCRIPTOR.full_name
response = CompLib_pb2.IRSensorsReadAllResponse()
response.ParseFromString(CompLibClient.send(request.SerializeToString(), request.ByteSize()))
response.ParseFromString(CompLibClient.send(
request.SerializeToString(), request.ByteSize()))
return [i for i in response.data]
@ -29,6 +32,7 @@ class IRSensor(object):
request.header.message_type = request.DESCRIPTOR.full_name
CompLibClient.send(request.SerializeToString(), request.ByteSize())
time.sleep(0.1) # IR sensor reading is async -> Wait a bit
@staticmethod
def disable():
@ -38,3 +42,4 @@ class IRSensor(object):
request.header.message_type = request.DESCRIPTOR.full_name
CompLibClient.send(request.SerializeToString(), request.ByteSize())
time.sleep(0.1) # IR sensor reading is async -> Wait a bit

View file

@ -5,15 +5,15 @@ MOTOR_COUNT = 4
class Motor(object):
"""Class used to control the motors
"""Klasse zum Ansteuern der Motoren
"""
@staticmethod
def power(port: int, percent: float):
"""Set specified motor to percentage power
"""Motor auf eine prozentuale Leistung der Höchstgeschwindigkeit einstellen
:param port: Port, which the motor is connected to. 0-3
:param percent: Percentage of max speed. between -100 and 100
:param port: Port, an welchen der Motor angestecht wird. 0-3
:param percent: Prozentsatz der Höchstgeschwindigkeit. zwischen -100 und 100
:raises: IndexError
"""
@ -21,9 +21,10 @@ class Motor(object):
raise IndexError("Invalid Motor port specified!")
if percent < -100 or percent > 100:
raise IndexError("Invalid Motor speed specified! Speed is between -100 and 100 percent!")
raise IndexError(
"Invalid Motor speed specified! Speed is between -100 and 100 percent!")
request = CompLib_pb2.MotorsSetPowerRequest()
request = CompLib_pb2.MotorSetPowerRequest()
request.header.message_type = request.DESCRIPTOR.full_name
request.port = port
request.power = percent
@ -32,9 +33,9 @@ class Motor(object):
@staticmethod
def multiple_power(*arguments: tuple[int, float]):
"""Set specified motors to percentage power
"""Mehrere Motoren auf eine prozentuale Leistung der Höchstgeschwindigkeit einstellen
:param arguments: tuples of port, percentage
:param arguments: tuple von port, percentage
:raises: IndexError
"""
request = CompLib_pb2.MotorsSetPowerRequest()
@ -45,7 +46,8 @@ class Motor(object):
raise IndexError("Invalid Motor port specified!")
if percent < -100 or percent > 100:
raise IndexError("Invalid Motor speed specified! Speed is between -100 and 100 percent!")
raise IndexError(
"Invalid Motor speed specified! Speed is between -100 and 100 percent!")
inner_request = CompLib_pb2.MotorSetPowerRequest()
inner_request.port = port
@ -57,17 +59,17 @@ class Motor(object):
@staticmethod
def speed(port: int, speed: float):
"""Set specified motor to percentage power
"""Geschwindigkeit des Motors einstellen
:param port: Port, which the motor is connected to. 0-3
:param speed: Speed at which a motor should turn in RPM
:param port: Port, an welchen der Motor angestecht wird. 0-3
:param speed: Drehzahl, mit der sich ein Motor dreht, in Radianten pro Sekunde (rad/s)
:raises: IndexError
"""
if port < 0 or port >= MOTOR_COUNT:
raise IndexError("Invalid Motor port specified!")
request = CompLib_pb2.MotorsSetSpeedRequest()
request = CompLib_pb2.MotorSetSpeedRequest()
request.header.message_type = request.DESCRIPTOR.full_name
request.port = port
request.speed = speed
@ -76,9 +78,9 @@ class Motor(object):
@staticmethod
def multiple_speed(*arguments: tuple[int, float]):
"""Set specified motor to percentage power
"""Geschwindigkeit mehrerer Motoren einstellen
:param arguments: tuples of port, speed in rpm
:param arguments: tuple von port, Geschwindigkeit in Radianten pro Sekunde (rad/s)
:raises: IndexError
"""
@ -97,21 +99,44 @@ class Motor(object):
CompLibClient.send(request.SerializeToString(), request.ByteSize())
# @staticmethod
# def all_off():
# """
# Turns of all motors
# """
# Logging.get_logger().debug(f"Motor.all_off")
#
# for i in range(1, MOTOR_COUNT + 1):
# Motor.active_break(i)
@staticmethod
def pulse_width(port: int, percent: float):
"""Setzen den Pulsbreite eines Motors in Prozent der Periode
# @staticmethod
# def active_break(port: int):
# """
# Actively break with a specific motor
#
# :param port: Port, which the motor is connected to. 1-4
# """
# Motor.pwm(port, 0, MotorMode.BREAK)
:param port: Port, an welchen der Motor angestecht wird. 0-3
:param percent: Prozent der Periode zwischen -100 und 100
:raises: IndexError
"""
if port < 0 or port >= MOTOR_COUNT:
raise IndexError("Invalid Motor port specified!")
request = CompLib_pb2.MotorSetPulseWidthRequest()
request.header.message_type = request.DESCRIPTOR.full_name
request.port = port
request.percent = percent
CompLibClient.send(request.SerializeToString(), request.ByteSize())
@staticmethod
def multiple_pulse_width(*arguments: tuple[int, float]):
"""Setzen den Pulsbreite mehreer Motoren in Prozent der Periode
:param arguments: tuple von port, prozent
:raises: IndexError
"""
request = CompLib_pb2.MotorsSetPulseWidthRequest()
request.header.message_type = request.DESCRIPTOR.full_name
for port, percent in arguments:
if port < 0 or port >= MOTOR_COUNT:
raise IndexError("Invalid Motor port specified!")
inner_request = CompLib_pb2.MotorSetPulseWidthRequest()
inner_request.port = port
inner_request.percent = percent
request.requests.append(inner_request)
CompLibClient.send(request.SerializeToString(), request.ByteSize())

View file

@ -0,0 +1,12 @@
FAQ
###
Was ist das Passwort für die Entwicklungsumgebung?
--------------------------------------------------
``compair``
Wie verbinde ich mich zur Entwicklungsumgebung?
-----------------------------------------------
See :ref:`gettingstarted_codeserver`

View file

@ -0,0 +1,11 @@
.. _gettingstarted_codeserver:
Programmierumgebung
###################
Als Umgebung zur Programmierung des Roboters wird `code-server <https://github.com/coder/code-server>`_ eingesetzt, welche bereits am Roboter vorinstalliert ist.
Verbindung zur Entwicklungsumgebung herstellen
----------------------------------------------
Am Roboter wird die IP-Adresse des Raspberry Pi angezeigt. Um nun die Verbindung herzustellen, muss man in einem Web-Browser einfach ``<roboter_ip>:8080`` eingeben.
Das Passwort für Visual Studio Code im Browser ist ``compair``!

View file

@ -0,0 +1,26 @@
Mein erstes Programm
####################
Um mit der Programmierung zu beginnen, müssen wir zunächst einen neuen Ordner erstellen, in dem alle unsere Python-Dateien gespeichert werden.
|codeServerFolder|
Sie können diesen Ordner nennen, wie Sie wollen, für dieses Beispiel heißt er ``compAIR``.
Im nächsten Schritt erstellen wir unsere Datei ``main.py``.
|codeServerFile|
Dann können wir beginnen, unseren Code in diese Datei zu schreiben.
.. code-block:: python
print("Hallo Welt")
Praktischerweise können wir die Datei auch über die VS-Code-Plattform ausführen.
|codeServerRun|
Dann öffnet sich ein Terminal, der die Ausgabe unseres Programms anzeigt.
|codeServerTerminal|
.. |codeServerFolder| image:: images/06_codeServerFolder.png
.. |codeServerFile| image:: images/03_codeServerFile.png
.. |codeServerRun| image:: images/04_codeServerRun.png
.. |codeServerTerminal| image:: images/05_codeServerTerminal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -0,0 +1,11 @@
Erste Schritte
##############
.. toctree::
:maxdepth: 5
wifi.rst
codeServer.rst
firstProgram.rst
secondProgram.rst
thridProgram.rst

View file

@ -0,0 +1,313 @@
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.

View file

@ -0,0 +1,70 @@
Mein drittes Programm
#####################
Der offizielle compAIR-Bot ist mit einer Reihe von Sensoren ausgestattet. Die wichtigsten sind die Infrarotsensoren und -sender, die an der Vorderseite des Roboters angebracht sind. Insgesamt gibt es fünf IR-Sensoren.
Um loszulegen, muss man zunächst das entsprechende Modul wie folgt importieren:
.. code-block:: python
:linenos:
from compLib.IRSensor import IRSensor
|irSensor|
Wie im obigen Diagramm zu sehen ist, verfügt jeder Sensor auch über einen entsprechenden IR-Sender / Emitter. Dieser Sender kann mit ``IRSensor.set(port, enable)`` aktiviert werden.
Schalten wir nun alle fünf Sender ein:
.. code-block:: python
:linenos:
from compLib.IRSensor import IRSensor
IRSensor.enable()
Diese fünf verschiedenen Sensoren befinden sich an der Vorderseite des Roboters und sind wichtig, um schwarze Linien zu erkennen.
Es ist sehr einfach, den Wert der Sensoren abzulesen:
.. code-block:: python
:linenos:
from compLib.IRSensor import IRSensor
IRSensor.enable()
if IRSensor.read_all()[0] > 500:
print("high")
else:
print("low")
**Erkennen einer schwarzen Linie**
Um den IR-Sensor zu testen, kannst du deinen Roboter auf eine schwarze Linie stellen. Der Sensor in der Mitte sollte auf der schwarzen Linie liegen.
.. code-block:: python
:linenos:
from compLib.IRSensor import IRSensor
IRSensor.enable()
COLOR_BREAK = 900
if IRSensor.read_all()[2] > COLOR_BREAK:
print("Robot is standing on a black line")
else:
print("Robot is NOT standing on a black line")
Wenn das Programm ausgeführt wird, zeigt es an, dass der Roboter auf einer schwarzen Linie steht, wenn sich der mittlere IR-Sensor des Roboters über einer schwarzen Linie befindet, und es zeigt an, dass der Roboter NICHT auf einer schwarzen Linie steht, wenn sich der mittlere IR-Sensor nicht über einer Linie befindet.
| In ``Zeile 1`` importieren wir das ``IRSensor``-Modul, das zur Kommunikation mit dem IR-Sensor-Board verwendet werden kann.
| In ``Zeile 3`` wird der Sensor mit der Nummer ``3`` aktiviert. Wenn wir einen Sensor nicht aktivieren, können wir ihn nicht in unserem Programm verwenden.
| In ``Zeile 4`` stellen wir einen Farbschwellenwert von ``900`` ein, mit dem wir später prüfen werden, ob der Sensorwert unter oder über diesem Schwellenwert liegt. Unterhalb bedeutet, dass sich eine helle Farbe unter dem IR-Sensor befindet und ein höherer Wert als ``900`` bedeutet, dass sich eine dunkle Farbe unter dem IR-Sensor befindet.
| In ``Zeile 6`` lesen wir den Sensor Nummer ``2`` aus und prüfen, ob der Wert über dem von uns definierten Schwellenwert von ``900`` liegt. Wenn das der Fall ist, hat der IR-Sensor eine schwarze Linie erkannt.
Wir werden nun das Programm so ändern, dass es alle ``0.1`` Sekunden prüft, ob sich eine schwarze Linie unter dem Roboter befindet, und wenn dies der Fall ist, eine Meldung ausgibt.
.. |irSensor| image:: images/07_irSensor.webp

View file

@ -0,0 +1,77 @@
.. _gettingStarted_wifi:
WLAN-Verbindung herstellen
##########################
Schritt für Schritt - macOS
---------------------------
1. SD-Karte aus dem Raspberry Pi bzw. Roboter entnehmen.
2. Einstecken der SD-Karte in den Computer
3. Öffnen der SD-Karte mit dem Namen "boot" |bootImage|
4. Generieren des PSK auf `https://www.wireshark.org/tools/wpa-psk.html <https://www.wireshark.org/tools/wpa-psk.html>`_ |pskImage|
5. Öffnen der Datei "wpa_supplicant.conf" auf der SD-Karte
6. Einfügen der Konfiguration. Dabei muss die SSID und der vorher generierte PSK eingesetzt werden ::
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=AT
network={
ssid="EinTollerNameFürDasWlan"
psk="98117b165a48f25cbe36f288ddf597729a40feeea93054c19bfa8e5eab238541"
}
7. Speichern, Auswerfen und wieder in den Raspberry Pi einbauen
8. Starten des Roboters
9. Die IP-Adresse sollte nun am Roboter angezeigt werden
.. |bootImage| image:: images/01_boot.png
.. |pskImage| image:: images/02_psk.png
Weitere Informationen
---------------------
Die "wpa_supplicant.conf" Datei wird beim Start des Rpasberry Pi automatisch an den richtigen Ort kopiert, damit sich der Roboter zum Wlan verbindet.
Eine genauere Anleitung wird vom Hersteller des Raspberry Pi `hier <https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-networking-2>`_ bereitgestellt.
Fehlerbehandlung
----------------
Sollte es dazu kommen, dass der Roboter nicht automatisch die Verbindung mit dem Netzwerk herstellt, kann eine Kabelgebundene Verbindung zur Diagnose von Fehlern genutzt werden.
Dabei wird automatisch die IP-Adresse der Verbindung "eth" am Roboter angezeigt. Nach der erfolgreichen Verbindung zum Roboter mittels SSH kann die "wpa_cli" zur Fehlerbehandlung verwendet werden:
::
> wpa_cli
wpa_cli v2.9
Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors
This software may be distributed under the terms of the BSD license.
See README for more details.
Selected interface 'p2p-dev-wlan0'
Interactive mode
> interface wlan0
Connected to interface 'wlan0.
> scan
OK
<3>CTRL-EVENT-SCAN-STARTED
<3>CTRL-EVENT-SCAN-RESULTS
> scan_result
bssid / frequency / signal level / flags / ssid
68:02:b8:0c:d7:47 2462 -66 [WPA2-PSK-CCMP][ESS] WG
68:02:b8:0c:d7:40 5220 -63 [WPA2-PSK-CCMP][ESS] WG
34:2c:c4:da:dd:b9 5200 -65 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] WLAN10573403
98:da:c4:e5:21:d0 2437 -57 [WPA2-PSK-CCMP][ESS] WG
34:2c:c4:da:dd:c6 2412 -52 [WPA-PSK-][WPA2-PSK-CCMP+TKIP][WPS][ESS] WLAN10573403
20:83:f8:07:5b:90 2467 -67 [WPA2-PSK-CCMP][WPS][ESS] A1-075b8c
7c:39:53:94:49:82 5280 -77 [WPA2-PSK-CCMP][WPS][ESS] A1-944980-5G
7c:39:53:94:49:81 2427 -68 [WPA2-PSK-CCMP][WPS][ESS] A1-944980
90:fd:73:ac:d3:27 2452 -72 [WPA2-PSK-CCMP][WPS][ESS] Drei_H288A_24G_eKy5
50:e0:39:3c:e5:80 5180 -82 [WPA2-PSK-CCMP][WPS][ESS] A1-393CE57F
90:fd:73:ac:d3:28 5500 -83 [WPA2-PSK-CCMP][WPS][ESS] Drei_H288A_5G_eKy5
68:02:b8:41:42:f9 5180 -84 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] WLAN18792472
34:2c:c4:30:3c:65 5180 -89 [WPA-PSK-TKIP][WPA2-PSK-CCMP][WPS][ESS] witt
fa:0d:ac:d3:40:d4 2467 -80 [WPA2-PSK-CCMP][WPS][ESS][P2P] DIRECT-d4-HP M28 LaserJet
0e:84:dc:14:ac:27 2467 -85 [WPA2-PSK-CCMP][WPS][ESS][P2P] DIRECT-wo-BRAVIA
>

View file

@ -11,8 +11,11 @@ Contents
*********
.. toctree::
:maxdepth: 5
:maxdepth: 1
:glob:
gettingStarted/index.rst
software/installation.rst
faq.rst
other/usage
lib/index.rst

View file

@ -12,11 +12,14 @@ Dokumentation der Klasse
Genauere Informationen
======================
Power vs Speed
--------------
Zur ansteuerung der Motoren kann entweder ``Motor.power(...)`` oder ``Motor.speed(...)`` verwendet werden.
Der Unterschied der 2 Funktionen liegt dabei in der Einheit des 2. Parameters. Bei ``Motor.power()`` wird dabei ein Wert zwischen -100% und 100% der maximalen Geschwindigkeit angegeben.
Im Gegensatz dazu erlaubt einem ``Motor.speed()`` eine Geschwindigkeit in Umdrehungen pro Minute.
Power vs Speed vs PulseWidth
-----------------------------
Zur ansteuerung der Motoren kann entweder ``Motor.power(...)``, ``Motor.speed(...)`` oder ``Motor.pulse_width(...)``` verwendet werden.
Der Unterschied der 3 Funktionen liegt dabei in der Einheit des 2. Parameters.
| Bei ``Motor.power()`` wird dabei ein Wert zwischen -100% und 100% der maximalen Geschwindigkeit angegeben.
| ``Motor.speed()`` verwendet die Encoder um die Geschwindigkeit der Motoren mittels closed-loop zu steuern. Diese Funktion sollte nur verwendet werden, wenn ``Motor.power()`` nicht zur Ansteuerung ausreicht.
| ``Motor.pulse_width()`` stellt die Geschwindigkeit des Motors mittels der Pulsbreite der PWM-Steuerung des Motors ein. Diese Funktion ist so nah an der Hardware wie möglich und sollte auch nur verwendet werden, wenn es einen expliziten Grund dafür gibt.
Normal vs Multiple
------------------
@ -28,7 +31,7 @@ Beispiele
=========
Vorwärts fahren
-------------
---------------
Mit folgenden Programm drehen sich beide Motoren mit 50% ihrer maximalen Geschwindigkeit.
Dabei ist zu beachten, dass ein Motor in die entgegengesetzte Richtung zum aneren Motor gedreht werden muss, da diese spiegelverkehrt montiert sind.

View file

@ -0,0 +1,12 @@
.. _software_installation:
Installationsanweisungen
########################
Diese Anleitung dient dazu die Software auf dem Roboter neu aufzusetzen. Im normalen Gebraucht sollte dies jedoch nicht notwendig sein.
Betriebssystem aufsetzen
========================
Als Basis wird für den Roboter Raspberry Pi OS (64-bit) verwendet. Das 32-Bit Betriebssystem wird nicht unterstützt, da die Software-Komponenten nur für aarch64 bzw. arm64/v8 kompiliert werden.
Genauere Informationen sind `hier <https://www.raspberrypi.com/software/operating-systems/>`_ zu finden.

View file

@ -1,109 +1,96 @@
import time
from compLib.CompLibClient import CompLibClient
from compLib.IRSensor import IRSensor
from compLib.Motor import Motor
from compLib.IRSensor import IRSensor
from compLib.CompLibClient import CompLibClient
DRIVE_SPEED = 5.0
COLOR_BREAK = 1500.0
KP = 2.0
import time
import math
DRIVE_SPEED = 20.0
COLOR_BREAK = 850
KP = 7.5
KD = 0.0
SAMPLE_TIME_S = 0.001
CUTOFF_FREQ_HZ = 50.0
def drive(left_speed, right_speed):
print(left_speed, right_speed)
RC = 1.0 / (2.0 * math.pi * CUTOFF_FREQ_HZ)
right_speed *= -1.0
FIRST_COEFF = SAMPLE_TIME_S / (SAMPLE_TIME_S + RC)
SECOND_COEFF = RC / (SAMPLE_TIME_S + RC)
Motor.speed(0, right_speed)
Motor.speed(3, left_speed)
out_old = 0.0
start_time = time.time()
def follow(sleep_time=0.1):
last_error = 0
sensors_black = 0
while sensors_black <= 3:
sensor_values = IRSensor.read_all()
def drive(leftSpeed, rightSpeed):
rightSpeed *= -0.906
sensors_black = 0
for sensor in sensor_values:
if sensor > COLOR_BREAK:
sensors_black += 1
Motor.multiple_power((0, min(max(-100, rightSpeed), 100)), (3, min(max(-100, leftSpeed), 100)))
error = last_error
if sensor_values[2] > COLOR_BREAK:
def follow(sleepTime = 0.3):
global out_old
lastError = 0
sensorsBlack = 0
while sensorsBlack < 3:
sensor_data = IRSensor.read_all()
sensorsBlack = 0
for i in range(0, 5):
if sensor_data[i] > COLOR_BREAK:
sensorsBlack += 1
middle_sensor = sensor_data[2]
filtered_sensor = FIRST_COEFF * middle_sensor + SECOND_COEFF * out_old
out_old = filtered_sensor
sample_time = str(time.time() - start_time).replace(".", ",")
print(f"{sample_time} {middle_sensor} {int(filtered_sensor)}")
error = lastError
if sensor_data[2] > COLOR_BREAK:
error = 0
elif sensor_values[0] > COLOR_BREAK:
elif sensor_data[0] > COLOR_BREAK:
error = -1.5
elif sensor_values[4] > COLOR_BREAK:
elif sensor_data[4] > COLOR_BREAK:
error = 1.5
elif sensor_values[1] > COLOR_BREAK:
elif sensor_data[1] > COLOR_BREAK:
error = -1
elif sensor_values[3] > COLOR_BREAK:
elif sensor_data[3] > COLOR_BREAK:
error = 1
elif error == 1.5:
error = 3.5
error = 3
elif error == -1.5:
error = -3.5
error = -3
last_error = error
lastError = error
adjustment = KP * error + KD * (error - last_error)
left_speed = DRIVE_SPEED + adjustment
right_speed = DRIVE_SPEED - adjustment
adjustment = KP * error + KD * (error - lastError)
leftSpeed = DRIVE_SPEED + adjustment
rightSpeed = DRIVE_SPEED - adjustment
print(sensor_values)
print(f"{left_speed} {right_speed} {adjustment} {error}")
drive(left_speed, right_speed)
# print(f"{leftSpeed} {rightSpeed} {adjustment} {error}")
drive(leftSpeed, rightSpeed)
time.sleep(SAMPLE_TIME_S)
drive(0, 0)
time.sleep(sleep_time)
def follow_simple():
left_speed = DRIVE_SPEED
right_speed = DRIVE_SPEED
sensor_values = IRSensor.read_all()
while True:
sensor_values = IRSensor.read_all()
# for i in range(len(sensor_values)):
# sensor_values[i] = (sensor_values[i] + new_sensor_values[i]) / 2.0
print(sensor_values)
if sensor_values[0] > COLOR_BREAK and sensor_values[4] > COLOR_BREAK:
break
if sensor_values[0] > COLOR_BREAK:
left_speed = -DRIVE_SPEED / 2
right_speed = DRIVE_SPEED
elif sensor_values[4] > COLOR_BREAK:
left_speed = DRIVE_SPEED
right_speed = -DRIVE_SPEED / 2
elif sensor_values[2] > COLOR_BREAK:
left_speed = DRIVE_SPEED
right_speed = DRIVE_SPEED
drive(left_speed, right_speed)
time.sleep(sleepTime)
def main():
CompLibClient.use_unix_socket()
IRSensor.enable()
time.sleep(0.1)
time.sleep(0.5)
# while True:
# print(IRSensor.read_all())
# time.sleep(0.01)
# follow_simple()
# drive(5, 5)
# time.sleep(5)
# follow()
# follow()
# follow()
# follow()
# follow(0.2)
follow()
follow()
follow()
follow()
follow(0.2)
main()
main()

View file

@ -0,0 +1,34 @@
from compLib.Motor import Motor
from compLib.Encoder import Encoder
from compLib.IRSensor import IRSensor
from compLib.CompLibClient import CompLibClient
import time
import math
CompLibClient.use_unix_socket()
sign = 1
for pwr in (18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 35, 40, 45, 50, 60, 70, 80 , 90, 100):
# Motor.multiple_pulse_width((0, pwr * sign), (3, -pwr * sign))
Motor.multiple_power((0, pwr * sign), (3, -pwr * sign))
start_time = time.time()
while time.time() - start_time < 3:
Encoder.read_all_velocities()
time.sleep(0.01)
avg = 0.0
for i in range(0, 20):
vels = Encoder.read_all_velocities()
avg += ((abs(vels[0]) + abs(vels[3])) / 2.0)
time.sleep(0.01)
avg = avg / 10
vel = str(avg).replace(".", ",")
print(f"{pwr} {vel}")
sign *= -1