# Robotik

Informationen und Anleitungen zu den Bauteilen und den Zusammenbau des SMARS-Roboters

# Allgemeine Hinweise

## Umgang mit Elektronikteilen

Die hier veröffentlichten Anleitungen sind nicht als alleinige Anleitung gedacht, sondern sollen immer zusammen mit dem Unterricht benutzt werden. Sie dienen als Gedächtnisstütze oder für diejenigen, die einzelne Stunden verpasst haben, als Möglichkeit, beim Aufbau aufzuholen.

Grundsätzlich gilt, dass alle verbauten elektronischen Teile sehr empfindlich gegen Verpolung und zu hohe Spannungen sind. Daher sollte man sich nach dem Hinzufügen eines Bauteils immer vergewissern, dass alles richtig angeschlossen ist. Am besten zeigt man die Anschlüsse immer noch jemand anderem.

Hier einige Beispiele für explodierende Elektronikteile:

[Kondensator](https://video.link/w/9OxGd)

[Diode](https://video.link/w/XOxGd)

## Allgemeine Tipps für die Programmierung

- Python und Micropython definieren Programmierblöcke über Einrückungen. Es ist also wichtig, dass von Anfang an darauf geachtet wird, Einrückungen immer sauber und vor allem einheitlich zu machen. Bei einer falschen Einrückung taucht in der Fehlermeldung das Wort `Indent` auf.
- Auch dürfen Blöcke nicht leer sein, da sonst die Einrückung nicht erkannt werden kann. Wenn man also Code schreibt und erst einmal etwas anderes implementieren möchte, dann schreibt man den Platzhalter `pass` in den Code.
- Bevor man Code auf den Roboter lädt, muss dieser getestet werden. Dazu muss das Programm in Thonny gestartet werden, damit man Fehlermeldungen und Debug-Nachrichten lesen kann. Wer ungetesteten Code hochlädt und dann erwartet, dass der Roboter funktioniert, ist selbst schuld.
- Eine Python-Datei wird auf dem Pico automatisch gestartet, wenn sie `main.py` heißt. Das bedeutet aber auch, dass das Programm gestartet wird, wenn man den Pico am Rechner über USB einsteckt. Das kann beim Testen zeitraubend sein, da man immer erst das Programm beenden muss. In der Testphase bietet es sich also an, einen anderen Namen für die Startdatei zu wählen.

# Elektronische Bauteile für den Roboterbau

# Die LED

[![red_LED.png](https://bookstack.jb-net.eu/uploads/images/gallery/2022-12/scaled-1680-/PB7s4moCWNsswqZN-red-led.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2022-12/PB7s4moCWNsswqZN-red-led.png)

Die LED oder Leuchtdiode hat heute die Glühlampe fast vollständig verdrängt. Und das hat auch gute Gründe. Der Wirkungsgrad einer LED liegt bei 30–40 %. Der Wirkungsgrad einer Glühlampe liegt bei ca. 5 % [¹]. Das heißt, dass eine LED 30–40 % der eingesetzten Energie in Licht umwandelt. Eine normale Glühlampe ist damit zu 95 % eine Heizung und nur zu 5 % ein Leuchtmittel. Die LED ist damit immer noch nicht perfekt, aber das Beste, das es zurzeit für die Lichterzeugung gibt. 
Außerdem lebt sie länger als die Glühlampe, die aufgrund ihrer Konstruktion schnell altert. 
## Die Polung der LED
Eine LED muss richtig gepolt werden, um zu leuchten. Wird sie verpolt, kann sie dabei kaputtgehen. Das lange Beinchen heißt Anode und ist die positive Seite. Die kurze Seite ist die Kathode und die negative Seite oder Masse.
Eine LED muss immer zusammen mit einem [Widerstand](https://bookstack.jb-net.eu/books/robotik/page/der-widerstand) geschaltet werden.

Wie eine LED mit dem Pico an- und ausgeschaltet werden kann, steht [hier](https://bookstack.jb-net.eu/books/robotik/page/leds-schalten)

[¹]: https://www.gluehbirne.de/led-ratgeber-wirkungsgrad-effizienz-led-lampen

# Der Widerstand

[![resistor.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-07/scaled-1680-/x5UvqFRaw3lUtrem-resistor.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-07/x5UvqFRaw3lUtrem-resistor.png)

Der Widerstand ist dafür da, andere elektronische Bauteile vor Überlastung zu schützen. Es gibt ihn in mit sehr vielen verschiedenen Widerstandswerten. Diese sind mit einem Farbcode angegeben. 


[![280973.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-07/scaled-1680-/NOAqLFas1Tvrtzap-280973.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-07/NOAqLFas1Tvrtzap-280973.png)

# Der Raspberry Pi Pico

Der Pico ist das Herzstück des Roboters, denn er führt den Code aus, der den Roboter steuert. Der Pico wird mit Micropython programmiert, das eine kleinere Ausgabe des großen Pythons ist. 
[![Download.jpeg](https://bookstack.jb-net.eu/uploads/images/gallery/2023-09/scaled-1680-/jaIEwGFN9WI3u8s9-download.jpeg)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-09/jaIEwGFN9WI3u8s9-download.jpeg)
Der Pico ist, wie die meisten elektronischen Teile, sehr empfindlich gegen Verpolung oder Überlastung oder Kurzschlüsse. Daher muss man immer sehr sorgfältig alle Verbindungen überprüfen, bevor eine Spannung angelegt wird. 
[![Raspberry_Pi_Pico_Pinout.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/dpnyOmPPwpq4IEza-raspberry-pi-pico-pinout.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/dpnyOmPPwpq4IEza-raspberry-pi-pico-pinout.png)


[Detaillierter Pinout-Plan des Pico zum Download](https://bookstack.jb-net.eu/attachments/12)


Er wird über einen Mikro-USB-Anschluss an den Computer angeschlossen. Als Entwicklungsumgebung benutzen wir [Thonny](https://thonny.org). 

Um die Pin-Anschlüsse ansteuern zu können, muss zunächst aus der Bibliothek *machine Pin* importiert werden: 

```from machine import Pin ```

Nicht alle Pins haben dieselben Fähigkeiten. Welcher Pin was kann, steht im detaillierten Pinout-Plan. Generell sollten für den Roboterbau die angegebenen Pins verwendet werden. Diese sind auf jeden Fall geeignet für die benötigte Funktion und die Fehlersuche wird erleichtert, wenn man nicht jedes Mal auch die Zuordnung der Pins überprüfen muss.

# Der Transistor

[![transistor.png.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/A9vJ3perZNZKO5YF-transistor-png.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/A9vJ3perZNZKO5YF-transistor-png.png)

## Das Schaltzeichen
Transistoren gibt es in vielen verschiedenen Ausfertigungen. Dies ist ein Beispiel. 


[![schaltzeichen_transistor.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/zKRQWKNXjjtELO2s-schaltzeichen-transistor.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/zKRQWKNXjjtELO2s-schaltzeichen-transistor.png)

Das heißt, mit einem Transistor kann man mit einer niedrigen Spannung eine größere Spannung regulieren. Das kann ein einfacher Schaltvorgang sein oder die relative Regelung einer Spannung. Damit ist der Transistor in seiner Grundfunktion ein Verstärker. In einer digitalen Schaltung wird der Transistor auch wieder nur an- oder ausgeschaltet. Die Regelung der Helligkeit einer LED oder der Geschwindigkeit eines Motors erfolgt genauso wie ohne Transistor. 

Für den Roboterbau werden wir nicht direkt mit einem Transistor arbeiten. Sie sind aber Teil fast aller Bauteile, die verwendet werden. 

Der Transistor ist die Grundvoraussetzung, dass es moderne Computer gibt. 

>Ein Transistor ist ein elektronisches Halbleiter-Bauelement zum Steuern oder Verstärken meistens niedriger elektrischer Spannungen und Ströme. Er ist der weitaus wichtigste „aktive“ Bestandteil elektronischer Schaltungen, der beispielsweise in der Nachrichtentechnik, der Leistungselektronik und in Computersystemen eingesetzt wird. Besondere Bedeutung haben Transistoren – zumeist als Ein/Aus-Schalter – in integrierten Schaltkreisen, was die weit verbreitete Mikroelektronik ermöglicht.
>
>Die Bezeichnung „Transistor“ ist ein Kofferwort des englischen transfer resistor, was in der Funktion einem durch eine angelegte elektrische Spannung oder einen elektrischen Strom steuerbaren elektrischen Widerstand entspricht. Die Wirkungsweise ähnelt der einer entsprechenden Elektronenröhre, nämlich der Triode.
>
> _Quelle: Wikipedia: https://de.wikipedia.org/wiki/Transistor_

# Der Motortreiber

## Aktuelle Variante

[![motortreiber.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/nPLzC2vp8pPITn9J-motortreiber.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/nPLzC2vp8pPITn9J-motortreiber.jpg)

Dieser Motortreiber beinhaltet gleichzeitig einen Spannungswandler, der genau 5V Gleichspannung liefert. 

## Vorherige Variante

[![motortreiber_alt.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/KYjI2nh4qyYQ8eQw-motortreiber-alt.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/KYjI2nh4qyYQ8eQw-motortreiber-alt.jpg)

Dieser Typ Motortreiber ist häufiger mal durchgebrannt. Daher wurde er durch ein stärkeres Modell ersetzt.

# Der Spannungswandler

Der Spannungswandler wird mittlerweile nicht mehr benötigt, da dieser im neuen Motortreiber integriert ist. 

[![spannungswandler.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/6uinEID7AwhphHjw-spannungswandler.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/6uinEID7AwhphHjw-spannungswandler.jpg)



Mit dem Spannungswandler können wir eine Gleichspannung in eine __niedrigere__ Spannung umwandeln. Da wir für die Motoren 6-9V benötigen, der Pico und die elektronischen Komponentenn aber nur 3,3V - 5 V vertragen, betreiben wir den Roboter mit einer 9V Batterie und nutzen den Spannungswandler für den Pico. Der Pico wiederum stellt 3,3V für den Betrieb der anderen Teile bereit. 

Der Spannungswandler muss __vor__ dem Anschluss an den Pico auf die richtige Spannung eingestellt werden. Dafür haben wir Spannungsmessgeräte.

# Das Wukong 2040 Board

Das Wukong 2040 Board wird für die Roboter „Walky“ und „Crawly“ benötigt. Es bietet praktische Anschlüsse für bis zu 12 Servomotoren und 4 Motoren. Außerdem bietet es zwei farbige LEDs, zwei Druckknöpfe und einen Buzzer. Die Stromversorgung läuft über einen 3,7V-Akku, der in dem Board aufladbar ist. 

Als Gehirn dient natürlich wieder ein Raspberry Pi Pico.
[![wukong2040_01.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/6BVuzMHFxEoZ3owH-wukong2040-01.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/6BVuzMHFxEoZ3owH-wukong2040-01.png)

# Der Drehgeber (rotary encoder)

[![Rotary-Encoder-For-Dummies.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2026-04/scaled-1680-/Wgr7QLCcM5Eu0O9u-rotary-encoder-for-dummies.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2026-04/Wgr7QLCcM5Eu0O9u-rotary-encoder-for-dummies.jpg)

# Grundlagen der Programmierung des Raspberry Pi Pico mit Micropython

Schritt-für-Schritt-Anleitung, um einige der vielen Möglichkeiten des Picos kennenzulernen.

# LEDs schalten

## Die interne LED
Der Pico hat eine interne LED, die folgendermaßen angesteuert werden kann: 

```
import time
from machine import Pin

#led=Pin("LED", Pin.OUT) # Für den Pico mit eingebautem WLAN
led=Pin(25, Pin.OUT) # Für den Pico ohne WLAN
led.value(1)
time.sleep(1)
led.value(0)

```
Soll die LED unabhängig vom Programmablauf blinken, dann kann man das mit einem Timer realisieren. 
```
from machine import Pin,Timer
# led=Pin("LED", Pin.OUT) # Für den Pico mit eingebautem WLAN
led=Pin(25, Pin.OUT) # Für den Pico ohne WLAN
timer = Timer()
timer.init(freq=2, mode=Timer.PERIODIC, callback=lambda t: led.toggle())

```
Soll der Timer beendet werden, so kann man das mit dem Befehl ```timer.deinit()``` erreicht werden. 

Möchte man mehr Kontrolle haben oder komplexere Funktionen einbauen, dann geht das so: 

```
import time
from machine import Pin,Timer

# led=Pin("LED", Pin.OUT) # Für den Pico mit eingebautem WLAN
led=Pin(25, Pin.OUT) # Für den Pico ohne WLAN
led.value(1)
time.sleep(1)
led.value(0)
def blink(timer):
    led.toggle()
    
timer = Timer()
Timer().init(freq=2, mode=Timer.PERIODIC, callback=blink)
``` 
Ein Timer läuft auch nach dem Ende des Programms weiter. Also nicht wundern, wenn es weiter blinkt. 

__Aufgabe:__ Die LED zum Blinken bringen.

## Eine externe LED
Das war zwar schon spannend, aber nun wollen wir eine externe LED an den Pico anschließen. Dazu benötigen wir eine LED, einen 100 Ω Widerstand sowie zwei Kabel. Stecke die Schaltung genau so zusammen, wie abgebildet. Achte vor allem darauf, dass die LED richtig herum ist. 

[![PB7s4moCWNsswqZN-red-led.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/f9XSNn25qsfWFlHf-pb7s4mocwnsswqzn-red-led.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/f9XSNn25qsfWFlHf-pb7s4mocwnsswqzn-red-led.png)

__Hier ist der dazugehörige Schaltplan:__

[![LED.png](https://bookstack.jb-net.eu/uploads/images/gallery/2022-12/scaled-1680-/HJuuTBediSqDnKuJ-led.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2022-12/HJuuTBediSqDnKuJ-led.png)

## Externe LED mit Transistor
Der Pico liefert an den Pins nur eine Spannung von 3,3 Volt und die Stromstärke ist auch nicht sehr hoch. Es ist leicht möglich, die Anschlüsse zu überlasten, zwar nicht mit nur einer LED, jedoch bleibt es dabei ja nicht. Daher müssen Transistoren verwendet werden. 
Es gibt zwei mögliche Schaltungen für die LED mit Transistor. Der Unterschied ist nur, ob die Schaltung auf Anoden- oder auf Kathodenseite geschieht. 
[![LED_mit_Transistor_alt_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-03/scaled-1680-/SXjb02PfNR2KsjXD-led-mit-transistor-alt-steckplatine.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-03/SXjb02PfNR2KsjXD-led-mit-transistor-alt-steckplatine.png)
[![LED_mit_Transistor.png](https://bookstack.jb-net.eu/uploads/images/gallery/2022-12/scaled-1680-/bH0QV7ZUrcv5ftOP-led-mit-transistor.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2022-12/bH0QV7ZUrcv5ftOP-led-mit-transistor.png)
[![LED_mit_Transistor-circuit.png](https://bookstack.jb-net.eu/uploads/images/gallery/2022-12/scaled-1680-/6nUnH7rWmR0FgAn2-led-mit-transistor-circuit.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2022-12/6nUnH7rWmR0FgAn2-led-mit-transistor-circuit.png)



## Regelung der Helligkeit
Egal, ob du die LED mit oder ohne Transistor betreibst, so ist die Helligkeit bislang immer die gleiche. Glühlampen lassen sich sehr einfach dimmen, indem  man die Spannung regelt. Bei der LED funktioniert das nicht, da die Spannung an der LED immer dieselbe ist. Die LED ist nämlich kein ohmsches Bauteil. 
Außerdem haben wir mit dem Pico die Schwierigkeit, dass er ein digitales Gerät ist und bekanntermaßen kennen digitale Geräte nur 1 und 0 oder an und aus. Wie lässt sich damit also die Helligkeit regulieren?

# Ampelschaltung mit LEDs

## Ampelschaltung
Verwende die LED-Ampel, um eine Ampelschaltung zu programmieren. Schaltet dann mehrere Ampeln zu einer Kreuzung zusammen, indem ihr die Picos miteinander kommunizieren lasst.  Dazu müsst ihr einen Pin auf dem Pico, der Befehle erhält, als Eingangspin definieren. 



[![2_Picos_communicating_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/ruTgMlZ6rOXn2f6V-2-picos-communicating-steckplatine.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/ruTgMlZ6rOXn2f6V-2-picos-communicating-steckplatine.png)

### Empfange Daten auf Pin 16 und blinke mit der internen LED
```
from machine import Pin

sensor=Pin(16, Pin.IN, Pin.PULL_UP)
led=Pin(25, Pin.OUT)
while True:
  while sensor.value():
    led.value(1)
  led.value(0)

```

### Sende Daten mit Pin 15 und blinke mit der internen LED
```
import time
from machine import Pin

#led=Pin("LED", Pin.OUT) # Für den Pico mit eingebautem WLAN
led=Pin(25, Pin.OUT) # Für den Pico ohne WLAN
sender=Pin(15,Pin.OUT)
while True:
  led.value(1)
  sender.value(1)
  time.sleep(1)
  led.value(0)
  sender.value(0)
  time.sleep(1)
```
Nur einer der beiden Picos muss über USB an Strom angeschlossen werden. Beide Programme werden unter dem Namen "main.py" auf dem Pico abgespeichert, dann laufen die Programme automatisch, sobald die Picos Strom bekommen. 

## Zwei Ampeln koordinieren
### Code für den Sender
```
from machine import Pin
from time import sleep_ms

red = Pin(10,Pin.OUT)
yellow = Pin(11,Pin.OUT)
green = Pin(12,Pin.OUT)
trigger = Pin(15, Pin.OUT)

wait = 800
phase = 3000
alarm_wait = 500

def go_red():
    red.off()
    yellow.on()
    green.off()
    sleep_ms(wait)
    red.on()
    yellow.off()
    trigger.off()
    sleep_ms(wait*2)
    
def go_green():
    trigger.on()
    sleep_ms(wait*3)
    red.on()
    yellow.on()
    green.off()
    sleep_ms(wait)
    red.off()
    yellow.off()
    green.on()

while True:
    go_red()
    sleep_ms(phase)
    go_green()
    sleep_ms(phase)
    
 

``` 

### Code für den Empfänger
```
from machine import Pin
from time import sleep_ms

red = Pin(10,Pin.OUT)
yellow = Pin(11,Pin.OUT)
green = Pin(12,Pin.OUT)
trigger = Pin(16, Pin.IN, Pin.PULL_UP)

wait = 800
phase = 3000
alarm_wait = 500

def go_red():
    red.off()
    yellow.on()
    green.off()
    sleep_ms(wait)
    red.on()
    yellow.off()
    
def go_green():
    red.on()
    yellow.on()
    green.off()
    sleep_ms(wait)
    red.off()
    yellow.off()
    green.on()
    

while True:
    if trigger.value():# Wenn trigger == True
        go_red()
        while trigger.value():
            pass
    if not trigger.value():
        go_green()
        while not trigger.value():
            pass
    

``` 

## Knopfsteuerung der Ampel
Für die Programmierung eines Ampelknopfes, muss man den Knopf *entprellen*, damit keine Geisterbewegungen registriert werden. Ein minimales Codebeispiel ist dieses: 
[![Button_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/CpObND6zddqgB5oe-button-steckplatine.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/CpObND6zddqgB5oe-button-steckplatine.png)
```
from machine import Pin
import time
button = Pin(15, Pin.IN, Pin.PULL_DOWN)
pressed = False
num_pressed = 0
last_pressed = 0
DEBOUNCE_WAIT = 100
def button_handler(pin):
    global pressed, num_pressed, last_pressed #mit dem Befehl global teilt man Python mit, dass man die Variabel verwenden möchte, die außerhalb der Funktion initialisiert wurde.
    while utime.ticks_diff(utime.ticks_ms(), last_pressed) < DEBOUNCE_WAIT: # Hier wird verhindert,dass mehrere Auslösungen hintereinander registriert werden. 
        pass
    last_pressed = utime.ticks_ms()
    if not pressed:
        while time.ticks_diff(time.ticks_ms(), last_pressed) < DEBOUNCE_WAIT:
            pass
        if pin.value() == 1:
            pressed=True #Damit kann im Programmablauf der Knopfdruck registriert werden. 
            num_pressed +=1
            print(pin.value(), "number presses: ", num_pressed)
            last_pressed = time.ticks_ms()
            pressed=False # Dies hier eher im weiteren Programmablauf verwenden.
        
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)

# Hier weiterer Programmablauf
while True:
    pass

```
[Programmiergrundkurs in Python](https://knowledgebase.jb-net.eu/books/programmiergrundkurs-in-python)

# Der Motor

Ein Motor wird genauso gesteuert wie eine LED. Man kann ihn entweder einfach an- und ausschalten, oder mithilfe der Pulsweitenmodulation die Geschwindigkeit regeln. Probiert es einfach einmal aus. Da ein Motor deutlich mehr Leistung hat als eine LED, kann man den Raspberry Pi Pico schnell überlasten. Daher betreiben wir den Motor nur über einen Transistor. Es sorgt dafür, dass das Steuersignal des Picos verstärkt von der 9V Batterie an den Motor geleitet wird. Aber Achtung: Der Motor ist für dauerhaft 6V ausgerichtet und sollte daher nicht zu lange mit 9V betrieben werden. 

Für diese Schaltung könnt ihr die Schaltung der LED mit Transistor ohne den Widerstand verwenden. 

Der Code für diese Steuerung ist genauso wie für eine LED. In dem Schaltbild wird der Pin 15 benutzt. 
## Richtungssteuerung
Wie könnte man nun die Drehrichtung des Motors ändern, ohne die Kabel umzustecken?

Überlegt erst einmal und dann schaut ihr euch das nächste Bauteil an: 

[AB Die H-Brücke](https://bookstack.jb-net.eu/attachments/76)

# Der Ultraschallsensor

[![ultraschallsensor.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/scaled-1680-/jAr3TSDB26c4ZDKe-ultraschallsensor.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-06/jAr3TSDB26c4ZDKe-ultraschallsensor.jpg)

Schließt den Ultraschallsensor an den Pico an:

[![Ultraschallsensor_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2025-06/scaled-1680-/65IPzPis7zX8BFOl-ultraschallsensor-steckplatine.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2025-06/65IPzPis7zX8BFOl-ultraschallsensor-steckplatine.png)

Zur Ansteuerung des Ultraschalsensors wird die [Roboterbibliothek](https://codeberg.org/marjaco/robotlibrary) benutzt.

# Der Infrarotsensor

[![roE8n.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/scaled-1680-/g67h1I6XOQh9fKqy-roe8n.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/g67h1I6XOQh9fKqy-roe8n.jpg)

Der IR-Sensor wird folgendermaßen an den Pico angeschlossen. Der OUT-Pin kann natürlich auch geändert werden. 

[![infrarotsensor.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/scaled-1680-/2AOAlLf6pOwTWICS-infrarotsensor.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/2AOAlLf6pOwTWICS-infrarotsensor.png)

So kann der Infrarotsensor ausgelesen werden: 
```
from machine import *
import time

# Der Pin für den Infrarotsensor wird initialisiert.  
ir=Pin(16,Pin.IN,Pin.PULL_UP)

while True:
    print(ir.value()) #Es wird einmal der Wert ausgegeben, der am Pin anliegt. 
    while ir.value() == 0: # Solange der Wert 0 bleibt, ändert sich die Anzeige nicht. 
        time.sleep_ms(50)
    print(ir.value()) # Ist/Wird der Wert 1, wird erneut auf der Konsole ausgegeben. 
    while ir.value() == 1: 
        time.sleep_ms(50) # Solange der Wert 1 bleibt, ändert sich die Anzeige nicht. 
        
    

```

Für die Benutzung in den Robotern verwenden wir die [Roboterbibliothek](https://codeberg.org/marjaco/robotlibrary).

# Zusammenbau des SMARS-Roboters

<span>In diesem Kapitel werden die mechanischen Aufbauschritte erklärt. Die ursprüngliche Variante des SMARS-Roboters kann man von </span>[Thingiverse](https://www.thingiverse.com/thing:2662828)<span> herunterladen. </span>

<span>Die meisten Roboterteile im Unterricht sind von mir verändert worden. Diese kann man auch auf dieser Seite herunterladen. </span>

# Das Steckbrett mit Raspberry Pi Pico und Abdeckplatte

Es gibt verschiedene Varianten von Platten, auf denen das Steckbrett aufgeklebt ist. 
# Aktuelle Variante
**Diese Variante ist für den „Theo III“!!** 

[![abdeckplatte.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/scaled-1680-/0se2W3Dwr5OHfA0v-abdeckplatte.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/0se2W3Dwr5OHfA0v-abdeckplatte.png)

In dieser Variante wird das Steckbrett quer aufgeklebt. Die Abdeckplatte wir von hinten in den Roboter eingeschoben. 

# Ältere Varianten
## älteste Variante
[![Breadboard01.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/8ESAcUYiLHRWnF0G-breadboard01.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/8ESAcUYiLHRWnF0G-breadboard01.png)

Die ältere Variante wird von oben auf das Gehäuse gesteckt. 
## neuere Variante
Die neuere Variante wird von hinten in das Gehäuse eingeschoben. 
[![Breadboard02.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/mQos5YsiQCyVL6qt-breadboard02.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/mQos5YsiQCyVL6qt-breadboard02.png)

# Gehäuse und Räder

Es gibt mehrere Varianten von Gehäusen für die SMARS-Roboter. Das aktuellste ist der „Theo III“.
# Theo III 

[![TheoIII.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/scaled-1680-/31QKmM8DVFKR7TpM-theoiii.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-05/31QKmM8DVFKR7TpM-theoiii.png)

Ein neu gedrucktes Gehäuse enthält noch einige Stützstrukturen um die Halterungen für die passiven Räder und in dem Schlitz für den Schalter. Diese müssen zunächst entfernt werden. 
Das Gehäuse wird mit den passiven Rädern montiert ausgeteilt. Diese sollten nicht mehr entfernt werden, da das Gehäuse dabei brechen kann. Generell ist darauf zu achten, dass die Seitenwände nicht belastet werden, da sie schnell abbrechen können. 

Es gibt bei dem Gehäuse ein vorne und hinten. Hinter erkennt man an der Einprägung „Theo III“. Dort befindet sich auch der Schalter. Beim Einsetzen der Motoren ist darauf zu achten, dass der Motor mit den längeren Kabeln nach vorne kommt. 

Für die aktiven Räder müssen zunächst die Motoren eingesetzt werden. Im Idealfall rasten die Motoren ein und sind dann schon fest. Wenn der 3D-Druck ungenau war, kann es sein, dass die Motoren nur sehr schwer einrasten oder gar nicht. Die Löcher an den Rädern, in die die Motorwellen eingesteckt werden, haben, genau wie die Motorwellen, eine abgeflachte Seite. Diese müssen zusammengebracht werden. Es kann sein, dass die Motorwelle nicht passt. In diesem Fall muss das Loch vorsichtig erweitert werden. Hier kann es schnell passieren, dass das Loch zu groß wird. Dann hilft nur noch Klebstoff. 
# Ältere Versionen
## Gehäuse für Kettenfahrzeug
Das Gehäuse kann direkt nach dem Druck noch einige Rückstände der Stützstrukturen enthalten. Das bedeutet vor allem, dass die Löcher für die Motorwellen zu sind und dass die Aufnahme für die passiven Räder noch gesäubert werden müssen. Grundsätzlich muss man beim Entfernen von Material sehr vorsichtig sein, da man schnell zu viel weggenommen hat. 

[![chassis_chain.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/YmOyFzyuGsBsc6yQ-chassis-chain.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/YmOyFzyuGsBsc6yQ-chassis-chain.png)

Als Erstes sollten die passiven Räder aufgesteckt werden. Dazu wird das Gehäuse hochkant flach auf die Tischfläche gelegt und dann werden die Räder aufgesteckt. Dies kann sehr schwer sein. Da die Gehäuse leicht brechen können, muss man darauf achten, keinen Druck auf die oberen Ränder auszuüben. Sind die Räder erst einmal aufgesteckt, sollten sie nicht mehr entfernt werden.
## Gehäuse für Gummireifenfahrzeug
Diese Gehäuse sollten sofort einsatzbereit sein. Dennoch sollte man vorher schauen, ob es noch irgendwo Überbleibsel von Stützstrukturen gibt, die vorher entfernt werden sollten. 
[![chassis_rubber.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-11/scaled-1680-/lo1Fes4bUho1kg5S-chassis-rubber.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-11/lo1Fes4bUho1kg5S-chassis-rubber.png)
Die passiven Räder haben keine Gummireifen und werden mithilfe von kurzen Stiften auf den Achsen befestigt. 

## Aktive Räder an beiden Gehäusetypen


Die 9V-Batterie wird zwischen die Motoren gelegt. Der Clip sollte auch schon befestigt werden. **Dabei ist darauf zu achten, dass sich die blanken Kabelenden nicht berühren können** (Isolierband). Die Kabel der Motoren sollten durch die Halterungen für die 9V-Batterie geführt werden. Damit kann der SMARS Roboter schon auf eigenen Rädern stehen.

# Anschluss der Motoren

Folgende Verbindungen werden benötigt, um die Motoren zum Laufen zu bringen:

[![Theo_III_Motoren_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-10/scaled-1680-/xnyb6OksGbbDuvB0-theo-iii-motoren-steckplatine.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-10/xnyb6OksGbbDuvB0-theo-iii-motoren-steckplatine.png)

Die 9V Batterie versorgt die Motoren mit Strom. Die vier Datenkabel IN1, IN2, IN3, IN4 steuern den Motor. Dabei sind IN1 und IN2 für den einen und IN3 und IN$ für den anderen Motor zuständig. Diese steuern die Brückenschaltung im Motortreiber. Die Richtungssteuerung erfolgt, indem die Signale an den Pins vertauscht werden. Ein Pin muss aus sein, der andere ist an. Wird dieser mithilfe der PWM-Modulation gesteuert, lässt sich auch die Geschwindigkeit regulieren. Hierfür kann dann die Bibliothek für die [Motorsteuerung](https://codeberg.org/marjaco/robotlibrary/src/branch/main/motor.py) in der `robotlibrary` benutzt werden.

# Der Ultraschallsensor

Der Ultraschallsensor wird von oben in die Halterung geschoben und danach wird das Gehäuse von vorne aufgesteckt. Die beiden Augen des Sensors schließen dann bündig mit dem Gehäuse ab. Die Halterung wird dann von oben auf das Gehäuse vorne oder hinten aufgeschoben und die Anschlüsse für die Kabel müssen nach oben geführt werden.
Für den Betrieb des Ultraschallsensors verwendet man eine [Bibliothek](https://github.com/marjaco/robotlibrary/blob/main/ultrasonic.py). Diese ist in der `robotlibrary` enthalten. 

Für die neueste Version des Roboters (Theo III) gibt es verschiedene Varianten von Halterungen, die ein Verstellen und alternative Befestigungen ermöglichen.

# Der komplette Roboter

Komplett zusammengebaut sollte der Roboter dann so aussehen (schematische Darstellung):
# Neueste Version (Theo III)
Grundsätzlich müssen alle Bauteile auf die richtige Polung überprüft werden. Es reicht nicht, alles so zu stecken, wie es auf dem Schaubild steht, da es beim Steckbrett und den Bauteilen Abweichungen geben kann. Beim Verpolen gehen die Bauteile schnell kaputt und die Batterie läuft leer. 

Deshalb lohnt es sich, alles gut zu überprüfen!


[![Theo_III_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/scaled-1680-/CBMSgWdeO8YEwgYo-theo-iii-steckplatine.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/CBMSgWdeO8YEwgYo-theo-iii-steckplatine.png)


# Ältere Version des Roboters (v1 und v2)
[![Roboter_v3_Steckplatine.png](https://bookstack.jb-net.eu/uploads/images/gallery/2023-02/scaled-1680-/u3xllHJ8GDLpsf7P-roboter-v3-steckplatine.png)](https://knowledgebase.jb-net.eu/uploads/images/gallery/2023-02/u3xllHJ8GDLpsf7P-roboter-v3-steckplatine.png)

# Der Kettenantrieb

Die Kette ist das mechanisch komplizierteste Element des SMARS-Roboters. Die einzelnen Kettenglieder (16 Stück pro Seite) werden mit Stücken von Filament zusammengesetzt. Dabei ist darauf zu achten, dass die Stücke nicht seitlich herausgucken und am Gehäuse hängenbleiben können. Eines könnte ein Stück heraus stehen bleiben, um die Kette einfacher aufmachen zu können. 

Die Kette darf weder zu stramm noch zu locker sein. Eine zu kurze Kette zieht die Räder zusammen und der Motor kann diese Reibung nicht überwinden. Eine zu lockere Kette spurt leicht aus und setzt sich dann auf die Rauten auf den Rädern. Dann wird die Spannung auch zu stark. Es kann eine ganze Weile dauern, bis man eine gut funktionierende Kette zusammengebaut hat. 

Außerdem werden sich die beiden Ketten nicht gleich schnell drehen, da die Motoren nicht miteinander synchronisiert sind. Hier kann man versuchen, die beiden Seiten mit unterschiedlichen Geschwindigkeiten zu betreiben, bis der Roboter möglichst geradeaus fährt.

## Unterschiedlich lange Kettenglieder
Um die Kettenlänge anzupassen, gibt es unterschiedlich lange Kettenglieder. Diese sollten eins nach dem anderen in die Kette eingefügt werden, bis die richtige Länge erreicht ist. Zuviele Kettenglieder, die nicht die Standardlänge haben, können auch zu Problemen führen, da sie nicht mehr passgenau auf den Rädern sitzen. 

### Normale Länge
Die normal langen Kettenglieder sind an der vorderen Kante rechtwinklig geschnitten. 

[![track-normal.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/scaled-1680-/b4JMK0znuY18qelQ-track-normal.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/b4JMK0znuY18qelQ-track-normal.png)

### kürzere Länge
Die etwas kürzeren Kettenglieder haben auf der linken Seite eine Fase.

[![track-short.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/scaled-1680-/K9FzNu7LlD8TNP5j-track-short.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/K9FzNu7LlD8TNP5j-track-short.png)

### längere Länge
Und die längeren Kettenglieder sind rechts gefast. 

[![track_long.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/scaled-1680-/fRZBfMLhCiWr3uPW-track-long.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/fRZBfMLhCiWr3uPW-track-long.png)

### Elastische Kette
Alternativ gibt es auch Ketten aus TPU, die elastisch sind.

[![tpu_track.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/scaled-1680-/EzGttRyggaNjAPFf-tpu-track.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-11/EzGttRyggaNjAPFf-tpu-track.png)

# Erweiterungen

## Messung der Batteriestärke

[![spannungsteiler.png](https://bookstack.jb-net.eu/uploads/images/gallery/2025-12/scaled-1680-/QgrGZE8kJAlZsB4Q-spannungsteiler.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2025-12/QgrGZE8kJAlZsB4Q-spannungsteiler.png)

Den Spannungsteiler kann man verwenden, um den Ladestand der Batterie zu messen. Dazu muss die Spannung unter 3,3V gebracht werden, um die Pins des Picos nicht zu beschädigen. Dazu werden zwei Widerstände mit den richtigen Werten in Reihe geschaltet und die Spannung zwischen den Widerständen und Masse über einen Analog-Digital-Wandler gemessen. Beim Pico sind das die Pins 26, 27 und 28.
Ein ADC-Pin wird folgendermaßen initialisiert:
```
 from machine import ADC
 batt_pin = ADC(28)
 while True:
   print(batt_pin.read_u16())
```

Nun muss man nur noch den zurückgelieferten Wert einer neuen Batterie messen und den Wert, wenn sie nicht mehr ausreichend Energie liefert. Dann kann man z. B. einen Alarm ausgeben oder die Funktionen des Roboters anpassen.

Die `robotlibrary` stellt auch hierfür eine Methode in der Klasse `Robot` bereit.

# Das Universal Robotics Board (urb)

Statt eines Steckbretts kann nun auch eine Platine, das `universal robotics board (urb)` genutzt werden. Auf der Platine finden sich geeignete Anschlüsse sowohl für den Theo III als auch für die Laufroboter. Damit gibt es weniger Kabelsalat. 
[![urb.png](https://bookstack.jb-net.eu/uploads/images/gallery/2026-05/scaled-1680-/VEE3sdH0irk7O2Nf-urb.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2026-05/VEE3sdH0irk7O2Nf-urb.png)

## Zur Beachtung
+ Die acht Steckplätze für Servomotoren P0–P7 können mit 5V oder mit 9V beschaltet werden. Normale Servomotoren laufen alle mit bis zu 6V. Daher muss darauf geachtet werden, dass der Jumper auf 5V gesteckt wird. Für Projekte, die eine höhere Spannung für Motoren erfordern, kann auf die Versorgungsspannung der Batterie umgeschaltet werden (normal 9V). 
+ Die Steckplätze für die Grundausstattung eines Roboters sind gesondert bezeichnet mit ML, MR, US, also den beiden Motoren und dem Ultraschallsensor. Natürlich können auch andere Pins benutzt werden.
+ Beim Anschluss der Stromversorgung muss unbedingt auf die richtige Polung und Spannung geachtet werden!

# Der Zusammenbau Crawlys

Crawly ist ein Roboter, der ähnlich wie eine Krabbe kriecht, aber mit nur vier statt acht Beinen.

Die Herausforderung liegt bei Crawly in der Gestaltung eines natürlichen Gangs.

# Der komplette Crawly

Komplett zusammengebaut sieht Crawly so aus. 

[![IMG_20241227_144833.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/3bERZc43CBIdV5eo-img-20241227-144833.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/3bERZc43CBIdV5eo-img-20241227-144833.jpg)
Der Prototyp des Bewegungsablaufs sieht so aus:


<video src="https://bookstack.jb-net.eu/attachments/68?open=true" controls width="480" height="270"></video>

## Zusammenbau der Beine
An den Beinen muss eventuell die Unterstützungsstruktur des 3D-Drucks noch entfernt werden. Das Bild oben zeigt, wie die Teile zusammengesetzt werden müssen. 
Bevor die Beine auf die Motorachsen gesetzt werden, müssen die Motoren noch auf einen Winkel von 90° eingestellt werden. Dazu kann die Methode `calibrate()` in der Klasse Crawly benutzt werden. Diese dreht jeden Motor auf 0°, gefolgt von 90°. Damit kann gleichzeitig überprüft werden, ob der Motor auch funktioniert. 

[![thigh.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/aZOjzE57gzu77dLN-thigh.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/aZOjzE57gzu77dLN-thigh.png)

[![lower_thigh.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/Sxu94VVKWnVSDw8v-lower-thigh.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/Sxu94VVKWnVSDw8v-lower-thigh.png)
## 2 Beine am Gehäuse befestigen
Der Schenkel muss wie eine Klammer auf den Servomotor gesetzt werden. Dabei aufpassen, dass nicht zu viel Druck ausgeübt wird. 
## 3 Anschlüsse und Software
Es ist sinnvoll, die Servomotoren in einer vernünftigen Reihenfolge anzuschließen. Ich habe mit dem Bein links hinten angefangen (Pin&nbsp;0) und bin dann im Uhrzeigersinn vorgegangen. Dabei kam der Hüftmotor immer vor dem Kniemotor. 

Bibliotheken für die verschiedenen Teile des Roboters mit Beispielcode für eine Vorwärtsbewegung sind in der `robotlibrary` integriert. Um eigene Funktionen zu implementieren, können wieder Methoden überschrieben werden, genauso wie bei den SMARS-Robotern.

# Zusammenbau von Walky

# Der komplette Walky

Komplett zusammengebaut sieht Walky so aus: 
[![IMG_20241227_141747.jpg](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/J26MRJLoBOE0m5Nb-img-20241227-141747.jpg)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/J26MRJLoBOE0m5Nb-img-20241227-141747.jpg)

Der Prototype des Bewegunsablaufs sieht so aus:

<video src="https://bookstack.jb-net.eu/attachments/69?open=true" controls width="480" height="270"></video>

Der Code für Walky in der `robotlibrary` ist in einer sehr rudimentären Version. 

## 1 Zusammenbau der Beine
[![walky_thigh.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/pu4tMzAQLJuV9Mi7-walky-thigh.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/pu4tMzAQLJuV9Mi7-walky-thigh.png)

[![walky_lower_thigh.png](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/scaled-1680-/6arfScthxMWMjUlC-walky-lower-thigh.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2024-12/6arfScthxMWMjUlC-walky-lower-thigh.png)
## 2 Beine am Gehäuse befestigen

## 3 Anschlüsse und Software

# Weitere Robotikprojekte

# Linienverfolger

Bau eines Roboters, der eine schwarze Linie auf dem Boden verfolgt. Dies kann der SMARS Roboter aber Ziel könnte es sein, einen schnelleren Roboter zu bauen. Dazu kann ein PID-Regler benutzt werden. 

Der Roboter hat zwei Motoren, die beide die hintere Achse bilden. Vorne schleift der Roboter nur auf einem Knopf. Er ist damit sehr gelenkig und vor allem die Vorderseite kann sich schnell bewegen. 

Ziel ist es, einen Algorithmus zu finden, der die Lenkung einerseits sehr schnell macht, andererseits aber auch verhindert, dass die Lenkung überkompensiert.  So ein Algorthmus könnte der PID-Controller sein. 

In der Roboterbibliothek ist ein solcher PID-Controller vorhanden, der hierfür benutzt werden kann. Die Schwierigkeit ist es, diesen sinnvoll zu konfigurieren und auf die Lenkung anzuwenden. 

[![line_follower.png](https://bookstack.jb-net.eu/uploads/images/gallery/2025-09/scaled-1680-/mDK1vRrFVSMoDnPy-line-follower.png)](https://bookstack.jb-net.eu/uploads/images/gallery/2025-09/mDK1vRrFVSMoDnPy-line-follower.png)

[PID-Controller auf Wikipedia](https://de.wikipedia.org/wiki/Regler#PID-Regler)

# Programmierung der Roboter

Micropython Software-Bibliotheken für den Betrieb der Roboter.

# Die Roboterbibliothek "robotlibrary"

Dieses Modul, das von [Codeberg](https://codeberg.org/marjaco/robotlibrary) heruntergeladen werden kann, steuert die Roboter mit allen Peripheriegeräten (Motoren, Sensoren). Dazu muss das Paket heruntergeladen und entpackt werden. Das Verzeichnis „robotlibrary“ muss dann auf den Pico hochgeladen werden. 

## SMARS Roboter "Theo III"
Um das Modul zu benutzen, muss nur folgender Import gemacht werden: `from robotlibrary.robot import Robot`. 

Ein kurzes Codebeispiel, wie der Roboter funktioniert, ist in der Quelldatei zu finden. 

Oder hier ein Beispiel für die Benutzung des Servomotors.
```
from robotlibrary.robot import Robot
from time import sleep,sleep_ms
try:
    r = Robot(False)
    r.set_angle(0)
    sleep_ms(500)
    r.set_angle(180)
    sleep_ms(500)
    r.set_angle(90)
    r.set_speed(80)
    while True:
        while r.get_dist() > 15:
            pass
        r.emergency_stop()
        sleep_ms(400)
        r.set_speed_instantly(80)
        r.spin_before_obstacle(20)
        r.set_forward(True)
        r.set_speed(80)
except:
    r.emergency_stop()
    print("Robot stopped")



```

## Crawly
Crawly ist ein Roboter, der, ähnlich wie eine Schildkröte auf vier Beinen kriechen kann. Um Crawly zu steuern, muss nur folgender Import gemacht werden: `from robotlibrary.crawly import Crawly`

Ein kurzes Codebeispiel, wie der Roboter funktioniert, ist in der Quelldatei zu finden. 
# Under construction: 
## Walky
Walky ist ein Roboter, der, ähnlich wie ein Hund, auf vier Beinen laufen kann. Um Walky zu steuern, muss nur folgender Import gemacht werden: `from robotlibrary.walky import Walky`

Ein kurzes Codebeispiel, wie der Roboter funktioniert, ist in der Quelldatei zu finden. 


Die Dokumentation für die Bibliothek ist  unter `docs` zu finden oder kann hier heruntergeladen werden: [robotlibrary.pdf](https://bookstack.jb-net.eu/attachments/63)

# Dokumentation

# Documentation for robotlibrary/robot.py 

## Robot 
This is the central class which manages and uses all the other components of the robot. The parameters are defined in config.py

## _drive 
This abstracted driving function is only called locally by the other functions with better names. 
It accelerates and decelerates to make driving more natural. Do not call directly!

## _drive_instantly 
This abstracted driving function is only called locally by the other functions with better names. 
It sets the speed immediately. Do not call directly!

## set_speed_instantly 
Sets the new speed immediately. Doesn't change the driving mode of the robot.
:param s: the speed you want to set.

## set_speed 
Sets the new speed and accelerates and decelerates. Doesn't change the driving mode of the robot.
:param s: the speed you want to set.

## set_forward 
Sets the direction of the robot. True means forward.
:param f: True for forwards and False for backwards.

## spin_right 
Spin right indefinitely. 

## spin_left 
Spin left indefinitely. 

## turn_right 
This turns the robot to the right without it spinning on the spot. Each call makes the turn steeper.

## turn_left 
This turns the robot to the right without it spinning on the spot. Each call makes the turn steeper.

## go_left 
With Meccanum wheels the robot goes sideways to the left.
        

## go_right 
With Meccanum wheels the robot goes sideways to the right.
        

## turn 
This turns the robot right or left. Is mostly used by the remote control.
:param turn: positive or negative value. Higher values mean steeper turn.

## go_straight 
Lets the robot go straight on. Usually called when a turn shall end. 

## spin_before_obstacle 
This spins until the distance to an obstacle is greater than the given parameter *distance*.
:param distance: The distance

## toggle_spin 
Toggle turn for the given duration. With each call the opposite direction(clockwise / anti-clockwise) is used.
:param d: The duration for the turn in milliseconds.

## random_spin 
Randomly turn for the given duration.
:param d: The duration for the turn in milliseconds.

## stop 
Stop the robot slowly by deceleration. 

## emergency_stop 
Stop the robot immediately.

## ir_detected 
If implemented this method is called when the IR-sensor has detected a change. Fill in your code accordingly.

## get_dist 
Get the distance from the ultrasonic sensor.

## set_angle 
If implemented, turn the servo motor with the ultrasonic sensor to the given angle.
:param a: The angle that is to be set.

## get_smallest_distance 
This returns the angle of the ultrasonic sensor where it measured the smallest distance

## get_longest_distance 
This returns the angle of the ultrasonic sensor where it measured the longest distance

# Documentation for robotlibrary/config.py 

## Module 
This defines the parameters for the motors. 

MAX_DUTY: Set to lower than the maximum not to overload the motors. Absolute maximum is 65535.
MIN_DUTY: Set this to the minimum duty cycle that the motor needs to start moving. 
MIN_SPEED: Only 0 is making sense here but if you want you can change that. Must be above 0 though.
MAX_SPEED: If you want another scale than 0-100, set the maximum here.

DEBOUNCE_WAIT: This defines the waiting time for the debouncing of the buttons. Leave as it is if 
you don't know what it means.

WHITE_DETECTED: Use these constants to check for white or black with the IR-sensor. Don't change!
BLACK_DETECTED: Use these constants to check for white or black with the IR-sensor. Don't change!

Motors and ultrasonic sensor must use consecutive pins. so, f. ex. the left motor uses pins 12 and 13. Use >None< if you don't use the device.
MLF and LRF are for four wheel drive.
ML: pin number for left motor (or left rear motor for four wheel drive).
MR: pin number for right motor (or right rear motor for four wheel drive).
MLF: pin number for left motor (or left front motor for four wheel drive). Use None if not used. 
MRF: pin number for right motor (or right front motor for four wheel drive). Use None if not used. 
US: pin number for the ultrasonic sensor. Use None if not used. 
IR: pin number for the infrared sensor. Use None if not used. 
SERVO: pin number for the servo motor. Use None if not used.

JS_X_MEDIAN 
JS_Y_MEDIAN
JS_MAX_DUTY
JS_MIN_DUTY: These define the parameters for the joystick. You need to calibrate the numbers. Look at joystick.py for details.

ROBOT_NAME: You need to set a custom name if you use a remote control.

SERVO_MIN_DUTY: Only change if the servo doesn't move the required 180°.
SERVO_MAX_DUTY: Only change if the servo doesn't move the required 180°.

# 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, indem 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)
        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:
        print("The robot was stopped by the user.")
    finally:
        robot.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. 

Dieses Beispiel vereint alle vorgestellten Techniken und sollte als Standard-Startdatei genutzt werden. 
```
from time import sleep, sleep_ms
import uasyncio as asyncio
from robotlibrary.robot import Robot
################################## Your class definition
class MyRobot(Robot):
    def __init__(self):
        super().__init__(False) # Call the original constructor.
        print("Start MyRobot")
        
    # With this method defined here, the robot will not drive as the speed is not set in this function.
    # This is to illustrate how overwriting works. 
    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():
    '''This checks the distance from the ultrasonic sensor continually.
    If the given distance is longer than the measured one, react_to_obstacle() will be called.
    '''
    global distance
    while True:
        if robot.get_dist() < distance:
            react_to_obstacle()
        await asyncio.sleep_ms(100)

def react_to_obstacle():
    '''Do whatever you want to do when an obstacle is detected.
    '''
    global distance
    robot.random_spin(300)
    robot.set_forward(True)
    robot.set_speed(80)
    
async def driving_program():
    robot.set_speed(90)
    while True:
        print("Driving program running.")
        await asyncio.sleep_ms(3000)

async def main():      
    asyncio.create_task(monitor_dist())
    await driving_program()
    
        
################################# Initialize the robot and start the program.
robot = MyRobot()
distance = 20 # Define the distance you want to have. 
if __name__ == "__main__":
    # execute only if run as a script
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("The robot was stopped by the user.")
    finally:
        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
```

### Mit einem Timer arbeiten
Diese Programmiertechnik könnte man z. B. nutzen, um nach einer bestimmten Zeit in das Programm einzuschalten, falls der Roboter zu lange dieselbe Funktion, z. B. vorwärts fahren, ausführt. Dann würde der Zähler zu gegebener Zeit eine Änderung herbeiführen können. 
```
from machine import Pin,Timer
import time

def do_something(t):
    print("do something!")
    
def main():
    timer = Timer()
    timer.init(period=1500, mode=Timer.PERIODIC, callback=do_something)
    while True:
        print("Running...")
        time.sleep_ms(700)

if __name__ == "__main__":
    # execute only if run as a script
    main()
```

# Motorsteuerung

Speichere diesen Code auf dem Pico unter dem Namen "motor.py".
## Motorbibliothek
```
from machine import Pin, PWM
import utime
MIN_DUTY = 0
MAX_DUTY = 60000
MAX_SPEED = 100
MIN_SPEED = 30
class Motor:
    '''This class manages the motor. Don't edit!'''
    def __init__(self, pinNo):
        self.gpio = pinNo
        self.speed=0
        self.forward=True
        self.pwm1=PWM(Pin(pinNo))
        self.pwm1.freq(50000)
        self.pwm1.duty_u16(0)
        self.pwm2=PWM(Pin(pinNo+1))
        self.pwm2.freq(50000)
        self.pwm2.duty_u16(0)
        self.speed_offset = 0

    def set_speed(self,s):
        '''Sets the speed of the motor. Checks for sensible input.'''
        if s + self.speed_offset <= MIN_SPEED:
            s = 0
            self.reset_offset()
        elif s + self.speed_offset >= MAX_SPEED:
            s = MAX_SPEED
        self.pwm1.duty_u16(int(MAX_DUTY*(s+self.speed_offset)/100))
        self.speed=s
    
    def change_speed(self,sc):
        '''This defines an offset to the speed in motor. It is used with the remote control to turn the robot.'''
        if self.speed + sc > MIN_SPEED and self.speed + sc < MAX_SPEED: 
            self.speed_offset += sc
            self.set_speed(self.speed)
        
    def reset_offset(self):
        self.speed_offset = 0
        
    def off(self):
        self.pwm1.duty_u16(0)
        self.speed = 0
    
    def set_forward(self,forward):
        '''Sets the motor to forward or backward without changing the speed. '''
        if self.forward==forward:
            return
        self.pwm1.duty_u16(0)
        self.pwm1,self.pwm2=self.pwm2,self.pwm1        
        self.forward=forward
        self.set_speed(self.speed)




```

## Beispiel für die Anwendung dieser Bibliothek
Kopiere diesen Code in eine andere Datei auf dem Pico, z. B. „motortest.py“.
```
from motor import Motor
from utime import sleep, sleep_ms
motor = Motor(12)

motor.set_speed(70)
motor.set_forward(True)
sleep(1)
motor.off()
```

# Ultraschallsensor

## Ultraschallbibliothek
Speichere diesen Code auf dem Pico unter dem Namen "ultrasonic.py".
```
from machine import Pin
from time import sleep
import utime

class Ultra:
    '''This class manages the ultrasonic sensor. It returns the distance to an obstacle in cm. '''
    def __init__(self, pinNo):
        self.trigger = Pin(pinNo, Pin.OUT) # to trigger a sound impulse
        self.echo = Pin(pinNo+1, Pin.IN) # records the echo of the trigger pulse      

    def get_dist(self):
        '''This returns the measured distance in cm. (float)'''
        timepassed = 0
        signalon = 0
        signaloff = 0
        self.trigger.low()
        utime.sleep_us(2)
        self.trigger.high()
        utime.sleep_us(5)
        self.trigger.low()
        while self.echo.value() == 0:
            signaloff = utime.ticks_us()
        while self.echo.value() == 1:
            signalon = utime.ticks_us()
        timepassed = signalon - signaloff
        distance = round((timepassed * 0.0343) / 2, 2)
        # print("The distance from object is ", distance, "cm.") # for debugging purposes uncomment the line.
        utime.sleep_ms(10) # Wait necessary or program halts
        return distance
```

## Beispiel für die Anwendung dieser Bibliothek
Kopiere diesen Code in eine andere Datei auf dem Pico, z. B. „ultratest.py“.

```
from ultrasonic import Ultra
from utime import sleep, sleep_ms
us = Ultra(16)

while True:
    print(f"gemessene Entfernung: {us.get_dist()} cm.")
    sleep(1)
```
Zum Fahren siehe [Motorsteuerung](https://bookstack.jb-net.eu/books/robotik/page/motorsteuerung).

# Infrarotsteuerung

Ein Infrarotsensor kann zum Erkennen von Hindernissen oder der Verfolgung einer schwarzen Linie genutzt werden. Die folgende Klasse steuert den Sensor. 
```
from machine import Pin,Timer

import micropython

micropython.alloc_emergency_exception_buf(100)

class IR:
    '''This class manages the IR-sensor. Write your code in Robot.ir_detected()'''
    def __init__(self, pinNo,callback_func):
        self.out = pinNo
        self.ir_detected = callback_func
        self.ir = Pin(pinNo, Pin.IN, Pin.PULL_UP)
        self.ir.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self.obstacle)
        self.detected=False
        self.timer = Timer()
        
    def reset_detected(self,t):
        self.detected = False
        
    def obstacle(self, pin):
        '''This is called on any change in the IR-sensor. '''
        if not self.detected:
            self.ir_detected(pin,self.out)
            self.detected = True
            self.timer.init(mode=Timer.ONE_SHOT, period=100, callback=self.reset_detected)

```
Der Code muss unter dem Dateinamen „infrared.py“ auf dem Pico gespeichert werden.

Der Infrarotsensor wird folgendermaßen im eigenen Programm eingebunden. Das Beispiel ist für zwei Infrarotsensoren. 
```
from infrared import IR
IR_PIN_LEFT=0
IR_PIN_RIGHT=1

def ir_detected(pin, pinno):
    print(f"Pin: {pin}, pin number: {pinno}")
    if pinno == IR_PIN_LEFT:
        print("links")
    elif pinno == IR_PIN_RIGHT:
        print("right")
    
ir_left = IR(0, ir_detected)
ir_right = IR(1, ir_detected)
```

# Servosteuerung

Der Ultraschallsensor kann auch mit einem Servomotor drehbar gemacht werden. Die folgende Klasse steuert den Servomotor: 
```
from machine import Pin, PWM
import utime

class Servo:
    '''This class manages the servo motor that turns the ultrasonic sensor. You need a servo motor installed to get use out of this. 
    Don't use directly or edit.'''
    def __init__(self,pin):
        self.pin=PWM(Pin(pin))
        self.pin.freq(50)
        self.min=1350
        self.max=8100
        self.angle=0
        
    def set_angle(self,a):
        '''If installed, the servor motor will set the angle of the ultrasonic sensor. 90° ist straight ahead.'''
        if a > self.angle:
            for i in range(self._get_duty(self.angle),self._get_duty(a)):
                self.pin.duty_u16(i)

        elif a < self.angle:
            for i in range(self._get_duty(self.angle), self._get_duty(a),-1):
                self.pin.duty_u16(i)
        self.angle = a
        utime.sleep_ms(4)  
        
    def _get_duty(self,angle):
        '''Internal function. Calculates the PWM duty for the given angle.'''
        return round((self.max-self.min)/180*angle+self.min)
    

```

Dieser Code muss unter dem Dateinamen „servo.py“ auf dem Pico gespeichert werden.

# Startdatei für den SMARS-Roboter

Kopiere den Code und speichere die Datei als `main.py` auf dem `Pico`.
```
from time import sleep, sleep_ms
import uasyncio as asyncio
from robotlibrary.robot import Robot
################################## Your class definition
class MyRobot(Robot):
    def __init__(self):
        super().__init__(False) # Call the original constructor.
        print("Start MyRobot")
        
    # With this method defined here, the robot will not drive as the speed is not set in this function.
    # This is to illustrate how overwriting works. 
    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():
    '''This checks the distance from the ultrasonic sensor continually.
    If the given distance is longer than the measured one, react_to_obstacle() will be called.
    '''
    global distance
    while True:
        if robot.get_dist() < distance:
            react_to_obstacle()
        await asyncio.sleep_ms(100)

def react_to_obstacle():
    '''Do whatever you want to do when an obstacle is detected.
    '''
    global distance
    robot.random_spin(300)
    robot.set_forward(True)
    robot.set_speed(80)
    
async def driving_program():
    robot.set_speed(90)
    while True:
        print("Driving program running.")
        await asyncio.sleep_ms(3000)

async def main():      
    asyncio.create_task(monitor_dist())
    await driving_program()
    
        
################################# Initialize the robot and start the program.
robot = MyRobot()
distance = 20 # Define the distance you want to have. 
if __name__ == "__main__":
    # execute only if run as a script
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("The robot was stopped by the user.")
    finally:
        robot.emergency_stop()

```

# Startdatei für Crawly

Um den Bewegungsablauf für Crawly anders als in der Bibliothek zu machen, können auch hier die entsprechenden Klassen überschrieben werden. 
```  
from time import sleep, sleep_ms
from robotlibrary.crawly import Crawly
from robotlibrary.crawly_leg import Leg
from robotlibrary.crawly_joint import Joint
from robotlibrary import config_crawly as conf
from robotlibrary.servo import Servo
################################## Your class definition
class MyCrawly(Crawly):
    def __init__(self):
        self.legs = {
            "front_right" : MyLeg(4, True, True, "front right"),
            "rear_right" : MyLeg(6, True, False, "rear right"),
            "rear_left" : MyLeg(0, False, False, "rear left"),
            "front_left" : MyLeg(2, False, True, "front left")
            }
        
    def galumph(self):
            for l in self.legs.values():
                l.leg_fully_up()
            sleep_ms(150)
            for l in self.legs.values():
                l.leg_fully_forward()
            sleep_ms(150)
            for l in self.legs.values():
                l.leg_fully_down()
            sleep_ms(150)
            for l in self.legs.values():
                l.leg_fully_backward()
            sleep_ms(150)
            
class MyLeg(Leg):
    def __init__(self, pin, right, front, name):
        if right and front: 
            self.shoulder = MyJoint(conf.SHOULDER_FRONT, name, False, False, pin)
        if right and not front:
            self.shoulder = MyJoint(conf.SHOULDER_REAR, name, False, False, pin)
        if not right and front:
            self.shoulder = MyJoint(conf.SHOULDER_FRONT, name, True, False, pin)
        if not right and not front:
            self.shoulder = MyJoint(conf.SHOULDER_REAR, name, True, False, pin)
        self.knee = MyJoint(conf.KNEE, name, False, True, pin+1)
        
class MyJoint(Joint):
    def __init__(self, j_type, name, left_side, inverted, pin):
        self.name = name
        self.j_type = j_type
        min_duty = conf.SERVO_MIN_DUTY
        max_duty = conf.SERVO_MAX_DUTY
        self.left_side = left_side
        if j_type == conf.SHOULDER_FRONT:
            self.__min_angle = conf.SHOULDER_FRONT_MIN_ANGLE
            self.__max_angle = conf.SHOULDER_FRONT_MAX_ANGLE
        elif j_type == conf.SHOULDER_REAR:
            self.__min_angle = conf.SHOULDER_REAR_MIN_ANGLE
            self.__max_angle = conf.SHOULDER_REAR_MAX_ANGLE
        elif j_type == conf.KNEE:
            self.__min_angle = conf.KNEE_MIN_ANGLE
            self.__max_angle = conf.KNEE_MAX_ANGLE
            # min_duty = conf.SERVO_MIN_DUTY_TYPE2 # Comment out if the duty cycle is different from the shoulder servo's duty cycle.
            # max_duty = conf.SERVO_MAX_DUTY_TYPE2 # Comment out if the duty cycle is different from the shoulder servo's duty cycle.
        self.servo = Servo(pin, inverted, min_duty, max_duty)
        
        
################################# End of class definition
    
def move_program():
    crawly.move_to_start_pos()
    for i in range(10):
        crawly.galumph()

def main():      
    move_program()
    
        
################################# Initialize the robot and start the program.
crawly = MyCrawly() 
if __name__ == "__main__":
    # execute only if run as a script
    try:
        main()
    except KeyboardInterrupt:
        print("The robot was stopped by the user.")
    finally:
        crawly.park()
```
Hier wurden schon alle drei Hauptklassen des Crawly-Roboters überschrieben. Die anderen Methoden in den Klassen können bei Bedarf auch überschrieben werden.

# Fehlerbehebung

# Motoren

###  Motoren drehen sich nicht oder nur sehr schwer
### Bei Kettenrobotern: Die Ketten sind zu stramm oder zu locker
Wenn die Ketten zu stramm sind, dann hat der Motor nicht genügend Kraft sich zu drehen. Ein Anzeichen dafür ist, wenn die Räder von der Ketten zusammengezogen werden, sodass die Achsen auf einer Seite nicht parallel sind. In diesem Fall sollten längere Kettenglieder eingefügt werden, bis die Kette gut sitzt. 

Sind die Ketten zu locker, kann die Kette aus der Führung springen und auf den Führungsknubbeln aufsitzen. Dann muss die Kette wieder strammer gemacht werden.

### Die Motoren drehen sich auch ohne Kette nicht
1. Drehen sich beide Motoren nicht, kann es sein, dass die Stromversorgung nicht richtig angeschlossen ist. Es ist unbedingt darauf zu achten, dass der Motortreiber nicht falsch herum gepolt wird. 
1. Dreht sich nur ein Motor nicht, ist zunächst zu überprüfen, ob die Steuerpins auch korrekt mit dem Motortreiber verbunden sind.
2. Ist dies der Fall, dann sollte überprüft werden, ob an den Anschlüssen für den Motor auch eine Spannung anliegt.
3. Ist dies nicht der Fall, ist der Motortreiber eventuell kaputt.


### Ein Motor dreht sich immer falsch herum
1. Die Steuerpins sind verpolt
2. Der Motor ist am Motortreiber verpolt.
3. Es ist ein Fehler im Code

Es ist grundsätzlich sinnvoll, die Verkabelung der Steuerpins zu vertauschen, da man hierfür nicht mit dem Schraubendreher arbeiten muss. Das Ergebnis ist dasselbe. 

### Sobald der Pico Strom bekommt, dreht sich ein Motor und hört auch nicht mehr auf
1. Der Pico ist womöglich beschädigt. Diesen mit dem Testprogramm und der Testplatine auf korrekte Funktion überprüfen.

### Testprogramm
```
from machine import Pin, PWM
import utime
# Bestimme, welche Pins getestet werden sollen. Die Platine hat sieben LEDs. Somit können 7 Pins gleichzeitig getestet werden. 
p_start = 11
p_end = 16
pins = list()
for i in range(p_start,p_end):
    pins.append(PWM(Pin(i)))
    
# Die LEDs werden initialisiert. Hier nichts ändern.
#leds=[PWM(Pin(pins[0], Pin.OUT)), PWM(Pin(pins[1], Pin.OUT)), PWM(Pin(pins[2], Pin.OUT)), PWM(Pin(pins[3], Pin.OUT)), PWM(Pin(pins[4], Pin.OUT)), PWM(Pin(pins[5], Pin.OUT)), PWM(Pin(pins[6], Pin.OUT))]
# PWM Frequenz wird eingerichtet.
print("Der Test beginnt.")
for p in pins:
    p.freq(2000)
# Zu Beginn werden alle LEDs ausgeschaltet. 
for p in pins:
    p.duty_u16(0)
# Die LEDs werden einzeln langsam aufgeblendet und abgeblendet. Gehen Sie ruckartig an und aus, dann ist PWM defekt. 
for p in pins:
    for i in range (65535):
        p.duty_u16(i)
    for i in range (65535, 0, -1):
        p.duty_u16(i)
    utime.sleep_ms(800)
# Alle LEDs blinken dreimal schnell hintereinander
for x in range(3):
    for p in pins:
        p.duty_u16(65535)
    utime.sleep_ms(100)
    for p in pins:
        p.duty_u16(0)
    utime.sleep_ms(100)
# Alle LEDs werden auf einer niedrigen Helligkeitsstufe angeschaltet und bleiben an. 
for p in pins:
        p.duty_u16(10000)
print("Der Test ist fertig.")

```

# Der Ultraschallsensor funktioniert nicht

1. Der Ultraschallsensor funktioniert am besten, wenn das Hindernis aus einem harten Material besteht, dass den Schall gut reflektieren kann. Ebenso kann es sein, dass das Hindernis sehr schräg steht und damit den Schall in eine andere Richtung reflektiert. 
1. Der SMARS reagiert nicht auf Hindernisse. In diesem Fall ist zunächst zu prüfen, ob auf Softwareseite alles in Ordnung ist. Zum Beispiel könnte der Code einer anderen Gruppe getestet werden, bei denen es funktioniert. 
2. Im nächsten Schritt sollte der Ultraschallsensor getestet werden. Dazu muss das Messprogramm von Thonny aus gestartet werden, damit die Messergebnisse angezeigt werden. Werden keine Messergebnisse gezeigt, dann zum nächsten Schritt.
3. Es muss überprüft werden, ob die Pins für Trigger und Echo auch nicht vertauscht sind. Die beiden Pins lassen sich ohne Gefahr für den Pico vertauschen, sie funktionieren aber natürlich nur, wenn sie richtig herum sind.
4. Funktioniert der Ultraschallsensor bei Start des Programms über Thonny aber nicht beim autonomen Start, dann muss überprüft werden, ob der Kondensator auch an der richtigen Stelle steckt.
5. Wird das Programm als main.py ohne Thonny ausgeführt, kann es sein, dass der Code Fehler enthält, wenn er nicht vorher gut getestet war. Dann stürzt das Programm natürlich ab und es sieht nur so aus, als ob der Ultraschallsensor nicht funktionieren würde. 
6. In einigen Fällen hat der Ultraschallsensor auch Funktionsstörungen, die nach einigen Minuten Pause wieder behoben sind.

# 3D-Konstruktionsdateien für die Roboter

Die Dateien für "Theo III" sind nicht mit der originalen Version des SMARS-Roboters kompatibel, da das Gehäuse leicht verändert wurde. Die Dateien (.FCSTD) sind mit dem Programm "Freecad" zu bearbeiten.

Die Halterungen für "Theo III" sind mit den anderen Robotern "Crawly" und "Walky" kompatibel.

STL-Dateien können direkt in einem Slicer für den Druck auf einem 3D-Drucker vorbereitet werden.

Gute Tutorials für die Benutzung des Programms Freecad gibt es z.B. hier: [https://www.youtube.com/@stolz3d](https://www.youtube.com/@stolz3d "Flowwies corner")

# SMARS Roboter

## Das Chassis (Theo III)

[Das Chassis für Theo III](https://bookstack.jb-net.eu/attachments/89)


Das Modell enthält die nötigen Stützstrukturen und muss daher __ohne__ weitere Stützstrukturen gedruckt werden. 

Diese Version des Roboters hat etwas stärkere Vorder- und Rückseiten und einen Schlitz für einen Schalter. 

Des Weiteren stehen die Räder 2 mm näher zusammen, was reichen sollte, um die Kettenspannung genügend zu reduzieren, damit sich die Motoren frei drehen können. Es könnte eher das Problem sein, dass die Ketten zu locker sind.

## Die Steckbrettplatte für den Theo III

Auf der Steckbrettplatte werden das Steckbrett und der Motortreiber befestigt. Der Druck erfolgt ohne Stützstrukturen. 

[Steckbrettplatte für Theo III](https://bookstack.jb-net.eu/attachments/90)


## Konstruktionsdatei für den Ultraschallsensor-Halter
Der Druck erfolgt ohne Stützstrukturen. 

[Gehäuse für den Ultraschallsensor (Theo III)](https://bookstack.jb-net.eu/attachments/80)

[Halterung für den Ultraschallsensor (Theo III)](https://bookstack.jb-net.eu/attachments/78) 


[Ultraschallhalter für allgemeinen Halter](https://bookstack.jb-net.eu/attachments/77)

## Konstruktionsdatei für den IR-Sensor-Halter:

Der Druck erfolgt ohne Stützstrukturen. Die beiden Teile müssen korrekt auf der Druckplatte ausgerichtet werden. Dazu wird der allgemeine Halter benötigt.

[IR sensor](https://bookstack.jb-net.eu/attachments/86)

Für diejenigen, die den IR-Halter selbst konstruieren wollen: Hier ist eine Konstruktionsskizze, die man benutzen kann:

[Konstruktionsskizze](https://bookstack.jb-net.eu/attachments/91)


## Konstruktionsdatei für den Motor- und Batteriehalter

[Motor- und Batteriehalter](https://bookstack.jb-net.eu/attachments/82)

[Bemaßungen für Motor- und Batteriehalter](https://bookstack.jb-net.eu/attachments/92)


## Allgemeine Halter
[Allgemeiner Halter](https://bookstack.jb-net.eu/attachments/81)

[Allgemeiner Halter hoch](https://bookstack.jb-net.eu/attachments/88) 

[Allgemeiner Halter hinten](https://bookstack.jb-net.eu/attachments/84)

[Halterung für Servomotor](https://bookstack.jb-net.eu/attachments/83)

[Bemaßung der Halterung für eigene Designs.](https://bookstack.jb-net.eu/attachments/41)


## Selbst konstruierte alternative Teile für den Theo III
Alternativ können auch folgende Bauteile genommen werden, die auch verändert werden können. 

[TPU-Kette](https://bookstack.jb-net.eu/attachments/93)

[Kettenglied](https://bookstack.jb-net.eu/attachments/79)

[Aktives Rad](https://bookstack.jb-net.eu/attachments/87)

[Passives Rad](https://bookstack.jb-net.eu/attachments/85)

# Crawly

## Freecad Konstruktionsdateien
[Crawly Halterung gerade](https://bookstack.jb-net.eu/attachments/70)

[Crawly Halterung schräg (druckt nicht gut)](https://bookstack.jb-net.eu/attachments/71)

[Crawly Schuhe (Drucken in TPU)](https://bookstack.jb-net.eu/attachments/72)

[Crawly Oberschenkel](https://bookstack.jb-net.eu/attachments/73)

[Crawly Chassis](https://bookstack.jb-net.eu/attachments/74)

[Crawly Unterschenkel](https://bookstack.jb-net.eu/attachments/75)