Initial push
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import RPi.GPIO as GPIO
|
||||
import time
|
||||
|
||||
class AlphaBot2(object):
|
||||
|
||||
def __init__(self,ain1=12,ain2=13,ena=6,bin1=20,bin2=21,enb=26):
|
||||
self.AIN1 = ain1
|
||||
self.AIN2 = ain2
|
||||
self.BIN1 = bin1
|
||||
self.BIN2 = bin2
|
||||
self.ENA = ena
|
||||
self.ENB = enb
|
||||
self.PA = 50
|
||||
self.PB = 50
|
||||
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(self.AIN1,GPIO.OUT)
|
||||
GPIO.setup(self.AIN2,GPIO.OUT)
|
||||
GPIO.setup(self.BIN1,GPIO.OUT)
|
||||
GPIO.setup(self.BIN2,GPIO.OUT)
|
||||
GPIO.setup(self.ENA,GPIO.OUT)
|
||||
GPIO.setup(self.ENB,GPIO.OUT)
|
||||
self.PWMA = GPIO.PWM(self.ENA,500)
|
||||
self.PWMB = GPIO.PWM(self.ENB,500)
|
||||
self.PWMA.start(self.PA)
|
||||
self.PWMB.start(self.PB)
|
||||
self.stop()
|
||||
|
||||
def forward(self):
|
||||
self.PWMA.ChangeDutyCycle(self.PA)
|
||||
self.PWMB.ChangeDutyCycle(self.PB)
|
||||
GPIO.output(self.AIN1,GPIO.LOW)
|
||||
GPIO.output(self.AIN2,GPIO.HIGH)
|
||||
GPIO.output(self.BIN1,GPIO.LOW)
|
||||
GPIO.output(self.BIN2,GPIO.HIGH)
|
||||
|
||||
|
||||
def stop(self):
|
||||
self.PWMA.ChangeDutyCycle(0)
|
||||
self.PWMB.ChangeDutyCycle(0)
|
||||
GPIO.output(self.AIN1,GPIO.LOW)
|
||||
GPIO.output(self.AIN2,GPIO.LOW)
|
||||
GPIO.output(self.BIN1,GPIO.LOW)
|
||||
GPIO.output(self.BIN2,GPIO.LOW)
|
||||
|
||||
def backward(self):
|
||||
self.PWMA.ChangeDutyCycle(self.PA)
|
||||
self.PWMB.ChangeDutyCycle(self.PB)
|
||||
GPIO.output(self.AIN1,GPIO.HIGH)
|
||||
GPIO.output(self.AIN2,GPIO.LOW)
|
||||
GPIO.output(self.BIN1,GPIO.HIGH)
|
||||
GPIO.output(self.BIN2,GPIO.LOW)
|
||||
|
||||
|
||||
def left(self):
|
||||
self.PWMA.ChangeDutyCycle(30)
|
||||
self.PWMB.ChangeDutyCycle(30)
|
||||
GPIO.output(self.AIN1,GPIO.HIGH)
|
||||
GPIO.output(self.AIN2,GPIO.LOW)
|
||||
GPIO.output(self.BIN1,GPIO.LOW)
|
||||
GPIO.output(self.BIN2,GPIO.HIGH)
|
||||
|
||||
|
||||
def right(self):
|
||||
self.PWMA.ChangeDutyCycle(30)
|
||||
self.PWMB.ChangeDutyCycle(30)
|
||||
GPIO.output(self.AIN1,GPIO.LOW)
|
||||
GPIO.output(self.AIN2,GPIO.HIGH)
|
||||
GPIO.output(self.BIN1,GPIO.HIGH)
|
||||
GPIO.output(self.BIN2,GPIO.LOW)
|
||||
|
||||
def setPWMA(self,value):
|
||||
self.PA = value
|
||||
self.PWMA.ChangeDutyCycle(self.PA)
|
||||
|
||||
def setPWMB(self,value):
|
||||
self.PB = value
|
||||
self.PWMB.ChangeDutyCycle(self.PB)
|
||||
|
||||
def setMotor(self, left, right):
|
||||
if((right >= 0) and (right <= 100)):
|
||||
GPIO.output(self.AIN1,GPIO.HIGH)
|
||||
GPIO.output(self.AIN2,GPIO.LOW)
|
||||
self.PWMA.ChangeDutyCycle(right)
|
||||
elif((right < 0) and (right >= -100)):
|
||||
GPIO.output(self.AIN1,GPIO.LOW)
|
||||
GPIO.output(self.AIN2,GPIO.HIGH)
|
||||
self.PWMA.ChangeDutyCycle(0 - right)
|
||||
if((left >= 0) and (left <= 100)):
|
||||
GPIO.output(self.BIN1,GPIO.HIGH)
|
||||
GPIO.output(self.BIN2,GPIO.LOW)
|
||||
self.PWMB.ChangeDutyCycle(left)
|
||||
elif((left < 0) and (left >= -100)):
|
||||
GPIO.output(self.BIN1,GPIO.LOW)
|
||||
GPIO.output(self.BIN2,GPIO.HIGH)
|
||||
self.PWMB.ChangeDutyCycle(0 - left)
|
||||
|
||||
if __name__=='__main__':
|
||||
|
||||
Ab = AlphaBot2()
|
||||
Ab.forward()
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
GPIO.cleanup()
|
||||
@@ -0,0 +1,166 @@
|
||||
import RPi.GPIO as GPIO
|
||||
import threading
|
||||
import time
|
||||
import subprocess
|
||||
import os, signal, sys
|
||||
from pid_line_follow1 import *
|
||||
from AlphaBot2 import AlphaBot2
|
||||
from TRSensors import TRSensor
|
||||
|
||||
|
||||
Ab = AlphaBot2()
|
||||
|
||||
IR = 17
|
||||
PWM = 50
|
||||
|
||||
'''def move_and_stop(func, duration=0.5):
|
||||
func()
|
||||
time.sleep(duration)
|
||||
Ab.stop()'''
|
||||
def setup_gpio():
|
||||
"""Initialisiert die GPIO-Pins."""
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(IR, GPIO.IN)
|
||||
|
||||
def getkey():
|
||||
if GPIO.input(IR) == 0:
|
||||
count = 0
|
||||
while GPIO.input(IR) == 0 and count < 200: #9ms
|
||||
count += 1
|
||||
time.sleep(0.00006)
|
||||
|
||||
if(count < 10):
|
||||
#print("None returned")
|
||||
return None
|
||||
count = 0
|
||||
while GPIO.input(IR) == 1 and count < 80: #4.5ms
|
||||
count += 1
|
||||
time.sleep(0.00006)
|
||||
#print("High-Burst-Length:", count)
|
||||
if (count < 20):
|
||||
#print("repeat returned")
|
||||
return "repeat"
|
||||
|
||||
idx = 0
|
||||
cnt = 0
|
||||
data = [0,0,0,0]
|
||||
for i in range(0,32):
|
||||
count = 0
|
||||
while GPIO.input(IR) == 0 and count < 15: #0.56ms
|
||||
count += 1
|
||||
time.sleep(0.00006)
|
||||
|
||||
count = 0
|
||||
while GPIO.input(IR) == 1 and count < 40: #0: 0.56mx
|
||||
count += 1 #1: 1.69ms
|
||||
time.sleep(0.00006)
|
||||
|
||||
if count > 7:
|
||||
data[idx] |= 1<<cnt
|
||||
if cnt == 7:
|
||||
cnt = 0
|
||||
idx += 1
|
||||
else:
|
||||
cnt += 1
|
||||
# print data
|
||||
if data[0]+data[1] == 0xFF and data[2]+data[3] == 0xFF: #check
|
||||
print("OK")
|
||||
return data[2]
|
||||
return None
|
||||
|
||||
def stop_listener():
|
||||
"""
|
||||
Deine Remote‑API muss hier so lange blocken, bis
|
||||
ein Key‑Event eintrifft und dir den Code 0x43 liefert.
|
||||
"""
|
||||
while True:
|
||||
code = getkey()
|
||||
if code == 0x43:
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
return
|
||||
|
||||
setup_gpio()
|
||||
print('IRremote Test Start ...')
|
||||
Ab.stop()
|
||||
last_key = 0
|
||||
last_key_press_time = 0
|
||||
current_action = None
|
||||
try:
|
||||
while True:
|
||||
key = getkey()
|
||||
# 0x43 == line_follow
|
||||
|
||||
if key == "repeat":
|
||||
key = last_key
|
||||
last_key_press_time = time.time()
|
||||
elif key is not None:
|
||||
print("getkey:")
|
||||
print(hex(key))
|
||||
last_key = key
|
||||
last_key_press_time = time.time()
|
||||
|
||||
if key is not None:
|
||||
last_key_press_time = time.time()
|
||||
|
||||
if key == 0x18 and current_action != "forward":
|
||||
Ab.forward()
|
||||
print("forward")
|
||||
current_action = "forward"
|
||||
elif key == 0x08 and current_action != "left":
|
||||
Ab.left()
|
||||
print("left")
|
||||
current_action = "left"
|
||||
elif key == 0x1c and current_action != "stop":
|
||||
Ab.stop()
|
||||
print("stop")
|
||||
current_action = "stop"
|
||||
elif key == 0x5a and current_action != "right":
|
||||
Ab.right()
|
||||
print("right")
|
||||
current_action = "right"
|
||||
elif key == 0x52 and current_action != "backward":
|
||||
Ab.backward()
|
||||
print("backward")
|
||||
current_action = "backward"
|
||||
elif key == 0x15:
|
||||
if(PWM + 10 < 101):
|
||||
PWM += 10
|
||||
Ab.setPWMA(PWM)
|
||||
Ab.setPWMB(PWM)
|
||||
print("PWM:", PWM)
|
||||
elif key == 0x07:
|
||||
if(PWM - 10 > -1):
|
||||
PWM -= 10
|
||||
Ab.setPWMA(PWM)
|
||||
Ab.setPWMB(PWM)
|
||||
print("PWM:", PWM)
|
||||
elif key == 0x43:
|
||||
tr = TRSensor()
|
||||
running = threading.Event()
|
||||
process = subprocess.Popen(
|
||||
["python", "pid_line_follow1.py"],
|
||||
)
|
||||
# follow_thread = threading.Thread(target=follow, args=(tr, Ab, running), daemon=True)
|
||||
# follow_thread.start()
|
||||
key = getkey()
|
||||
while(key != 0x43):
|
||||
key = getkey()
|
||||
|
||||
# running.set()
|
||||
process.terminate()
|
||||
|
||||
Ab.setPWMA(PWM)
|
||||
Ab.setPWMB(PWM)
|
||||
|
||||
time.sleep(0.05)
|
||||
|
||||
else:
|
||||
if time.time() - last_key_press_time > 0.1 and current_action != "stop":
|
||||
Ab.stop()
|
||||
print("stopping bc no key is being pressed")
|
||||
current_action = "stop"
|
||||
|
||||
except KeyboardInterrupt:
|
||||
GPIO.cleanup()
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
# Robot
|
||||
|
||||
## Cam-stream
|
||||
|
||||
Files:
|
||||
- cam_stream.py
|
||||
|
||||
### Ziel
|
||||
|
||||
Kamera läuft und schickt Bilder via seriell an den ESP.
|
||||
|
||||
### Voraussetzung
|
||||
|
||||
- Die Kamera 🤡
|
||||
- ESP am Serial-Port `/dev/ttyACM1` (115200 Baud)
|
||||
|
||||
### Umsetzung
|
||||
|
||||
In Endlosschleife wird ein 640x480-Graustufen-Frame von der Kamera gelesen und als JPEG komprimiert bis die Größe unter 1200 Bytes liegt.\
|
||||
Danach wird der Frame in JSON-Format über seriell an den ESP gesendet.\
|
||||
Sollte es zu einem Verbindungsabbruch zwischen Pi und ESP kommen wird automatisch so lange eine Neuverbindung versucht, bis diese Verbindung wieder steht.
|
||||
|
||||
### Probleme während der Bearbeitung und ihre Lösungen
|
||||
|
||||
Die Kamera wurde nicht konstant erkannt (beim Suchen der Kamera wurde sie manchmal gefunden und manchmal nicht, meistens nicht).\
|
||||
Eine Neuinstallation von Raspbian hat das Problem gelöst, allerdings wurde diesmal die full-version installiert.
|
||||
|
||||
-----------------------
|
||||
|
||||
## Line follow
|
||||
|
||||
Files:
|
||||
- AlphaBot2.py
|
||||
- TRSensors.py
|
||||
- line_follow.py
|
||||
|
||||
### Ziel
|
||||
|
||||
Roboter soll in der Lage sein einer Linie auf dem Boden zu folgen,
|
||||
ohne diesen zu verlieren
|
||||
|
||||
### Voraussetzung
|
||||
|
||||
Linienbreite deckt 3 der 5 Sensoren ab (die mittleren), äußeren Sensoren müssen den boden sehen.
|
||||
|
||||
### Umsetzung
|
||||
|
||||
#### erste Idee: (Verworfen)
|
||||
|
||||
Von 5 Sensoren müssen die mittleren 3 immer die Linie sehen.\
|
||||
Mithilfe der äußeren Sensoren werden Korrekturen durchgeführt, damit der Roboter auf der Linie bleibt.
|
||||
|
||||
#### zweite Idee:
|
||||
|
||||
Es müssen wieder die mittleren 3 Sensoren auf der Linie sein.\
|
||||
Diesmal wird die Korrektur aber von Sensor 1 und Sensor 3 durchgeführt (Index beginnt bei 0).\
|
||||
Mithilfe der äußeren Sensoren, soll entschieden werden, ob eine Abzweigung existiert und falls sie existiert wird eine stärkere Korrektur durchgeführt damit der Roboter sich richtig dreht.
|
||||
|
||||
### Probleme während der Bearbeitung und ihre Lösungen
|
||||
|
||||
#### erste Idee:
|
||||
|
||||
Der Roboter hat zu sehr gezappelt und es war schwer zu unterscheiden ob nur leicht korrigiert werden musste oder ob die Linie die Richtung gewechselt hat.
|
||||
Das Problem sollte durch die zweite Idee gelöst werden.
|
||||
|
||||
#### zweite Idee: (Idee wurde vereinfacht auf nur starke Kurven = Roboter muss eine 90° Drehung machen um zu folgen)
|
||||
|
||||
Der Roboter interpretiert, je nach Untergrund, Kurven falsch.\
|
||||
Obwohl keine Kurve vorhanden ist biegt er doch ab.\
|
||||
Wenn eine Kurve vorhanden ist nimmt er diese nicht korrekt war und bleibt am Ende der Linie stehen.
|
||||
|
||||
-------------------
|
||||
|
||||
## remote steering
|
||||
|
||||
Files:
|
||||
- AlphaBot2.py
|
||||
- IRremote.py
|
||||
|
||||
### Ziel
|
||||
|
||||
Steuerung des kompletten Roboters über Fernbedienung.
|
||||
Möglich machen:
|
||||
- Roboter bewegen
|
||||
- Linienfolgen starten und stoppen + Kalibrierung manuell starten
|
||||
- (Kameraausrichtung ermöglichen)
|
||||
|
||||
### Voraussetzung
|
||||
|
||||
Die Fernbedienung 🤡
|
||||
|
||||
### Umsetzung Buttons
|
||||
|
||||
- play/pause = starten bzw. stoppen des line follows
|
||||
- 2 = geradeaus fahren
|
||||
- 4 = nach links drehen um die eigene Achse
|
||||
- 6 = nach rechts drehen um die eigene Achse
|
||||
- 8 = rückwärts fahren
|
||||
|
||||
### Probleme während der Bearbeitung und ihre Lösungen
|
||||
Keine Probleme
|
||||
|
||||
-------------------
|
||||
|
||||
## Daemon
|
||||
|
||||
Files: Kein
|
||||
|
||||
### Ziel
|
||||
|
||||
Automatisches starten des Kamerastreams,
|
||||
sowie die Steuerung über eine Fernbedienung.
|
||||
|
||||
### Umsetzung
|
||||
|
||||
1. **Service-Datei anlegen**
|
||||
|
||||
Erstelle unter `/etc/systemd/system/` eine Datei namens `<service_name>.service` mit folgendem Inhalt:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=<Kurzbeschreibung des Dienstes>
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=<system_user>
|
||||
WorkingDirectory=<Arbeitsverzeichnis>
|
||||
ExecStart=<Pfad zu Interpreter> <Pfad zum Skript>
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
2. **Systemd neu einlesen**
|
||||
|
||||
``sudo systemctl daemon-reload``
|
||||
3. **Dienst aktivierten (Boot-Start)**
|
||||
|
||||
``sudo systemctl enable <service_name>.service``
|
||||
4. **Dienst sofort starten**
|
||||
|
||||
``sudo systemctl start <service_name>.service``
|
||||
5. **Status und Logs prüfen**
|
||||
- Status anzeigen:
|
||||
|
||||
``sudo systemctl status <service_name>.service``
|
||||
- Live-Logs:
|
||||
|
||||
``sudo journalctl -u <service_name>.service -f``
|
||||
|
||||
### Probleme während der Bearbeitung und ihre Lösungen
|
||||
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding:utf-8 -*-
|
||||
import RPi.GPIO as GPIO
|
||||
import time
|
||||
|
||||
CS = 5
|
||||
Clock = 25
|
||||
Address = 24
|
||||
DataOut = 23
|
||||
Button = 7
|
||||
|
||||
class TRSensor(object):
|
||||
def __init__(self,numSensors = 5):
|
||||
self.numSensors = numSensors
|
||||
self.calibratedMin = [0] * self.numSensors
|
||||
self.calibratedMax = [1023] * self.numSensors
|
||||
self.last_value = 0
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(Clock,GPIO.OUT)
|
||||
GPIO.setup(Address,GPIO.OUT)
|
||||
GPIO.setup(CS,GPIO.OUT)
|
||||
GPIO.setup(DataOut,GPIO.IN,GPIO.PUD_UP)
|
||||
GPIO.setup(Button,GPIO.IN,GPIO.PUD_UP)
|
||||
|
||||
"""
|
||||
Reads the sensor values into an array. There *MUST* be space
|
||||
for as many values as there were sensors specified in the constructor.
|
||||
Example usage:
|
||||
unsigned int sensor_values[8];
|
||||
sensors.read(sensor_values);
|
||||
The values returned are a measure of the reflectance in abstract units,
|
||||
with higher values corresponding to lower reflectance (e.g. a black
|
||||
surface or a void).
|
||||
"""
|
||||
def AnalogRead(self):
|
||||
value = [0]*(self.numSensors+1)
|
||||
#Read Channel0~channel6 AD value
|
||||
for j in range(0,self.numSensors+1):
|
||||
GPIO.output(CS, GPIO.LOW)
|
||||
for i in range(0,8):
|
||||
#sent 8-bit Address
|
||||
if i<4:
|
||||
if(((j) >> (3 - i)) & 0x01):
|
||||
GPIO.output(Address,GPIO.HIGH)
|
||||
else:
|
||||
GPIO.output(Address,GPIO.LOW)
|
||||
else:
|
||||
GPIO.output(Address,GPIO.LOW)
|
||||
#read MSB 4-bit data
|
||||
value[j] <<= 1
|
||||
if(GPIO.input(DataOut)):
|
||||
value[j] |= 0x01
|
||||
GPIO.output(Clock,GPIO.HIGH)
|
||||
GPIO.output(Clock,GPIO.LOW)
|
||||
for i in range(0,4):
|
||||
#read LSB 8-bit data
|
||||
value[j] <<= 1
|
||||
if(GPIO.input(DataOut)):
|
||||
value[j] |= 0x01
|
||||
GPIO.output(Clock,GPIO.HIGH)
|
||||
GPIO.output(Clock,GPIO.LOW)
|
||||
#no mean ,just delay
|
||||
# for i in range(0,6):
|
||||
# GPIO.output(Clock,GPIO.HIGH)
|
||||
# GPIO.output(Clock,GPIO.LOW)
|
||||
time.sleep(0.0001)
|
||||
GPIO.output(CS,GPIO.HIGH)
|
||||
for i in range(0,6):
|
||||
value[i] >>= 2
|
||||
# print (value[1:])
|
||||
return value[1:]
|
||||
|
||||
"""
|
||||
Reads the sensors 10 times and uses the results for
|
||||
calibration. The sensor values are not returned; instead, the
|
||||
maximum and minimum values found over time are stored internally
|
||||
and used for the readCalibrated() method.
|
||||
"""
|
||||
def calibrate(self):
|
||||
max_sensor_values = [0]*self.numSensors
|
||||
min_sensor_values = [0]*self.numSensors
|
||||
for j in range(0,10):
|
||||
|
||||
sensor_values = self.AnalogRead()
|
||||
|
||||
for i in range(0,self.numSensors):
|
||||
|
||||
# set the max we found THIS time
|
||||
if((j == 0) or max_sensor_values[i] < sensor_values[i]):
|
||||
max_sensor_values[i] = sensor_values[i]
|
||||
|
||||
# set the min we found THIS time
|
||||
if((j == 0) or min_sensor_values[i] > sensor_values[i]):
|
||||
min_sensor_values[i] = sensor_values[i]
|
||||
|
||||
# record the min and max calibration values
|
||||
for i in range(0,self.numSensors):
|
||||
if(min_sensor_values[i] > self.calibratedMin[i]):
|
||||
self.calibratedMin[i] = min_sensor_values[i]
|
||||
if(max_sensor_values[i] < self.calibratedMax[i]):
|
||||
self.calibratedMax[i] = max_sensor_values[i]
|
||||
|
||||
"""
|
||||
Returns values calibrated to a value between 0 and 1000, where
|
||||
0 corresponds to the minimum value read by calibrate() and 1000
|
||||
corresponds to the maximum value. Calibration values are
|
||||
stored separately for each sensor, so that differences in the
|
||||
sensors are accounted for automatically.
|
||||
"""
|
||||
def readCalibrated(self):
|
||||
value = 0
|
||||
#read the needed values
|
||||
sensor_values = self.AnalogRead()
|
||||
|
||||
for i in range (0,self.numSensors):
|
||||
|
||||
denominator = self.calibratedMax[i] - self.calibratedMin[i]
|
||||
|
||||
if(denominator != 0):
|
||||
value = (sensor_values[i] - self.calibratedMin[i])* 1000 / denominator
|
||||
|
||||
if(value < 0):
|
||||
value = 0
|
||||
elif(value > 1000):
|
||||
value = 1000
|
||||
|
||||
sensor_values[i] = value
|
||||
|
||||
#print("readCalibrated",sensor_values)
|
||||
return sensor_values
|
||||
|
||||
"""
|
||||
Operates the same as read calibrated, but also returns an
|
||||
estimated position of the robot with respect to a line. The
|
||||
estimate is made using a weighted average of the sensor indices
|
||||
multiplied by 1000, so that a return value of 0 indicates that
|
||||
the line is directly below sensor 0, a return value of 1000
|
||||
indicates that the line is directly below sensor 1, 2000
|
||||
indicates that it's below sensor 2000, etc. Intermediate
|
||||
values indicate that the line is between two sensors. The
|
||||
formula is:
|
||||
|
||||
0*value0 + 1000*value1 + 2000*value2 + ...
|
||||
--------------------------------------------
|
||||
value0 + value1 + value2 + ...
|
||||
|
||||
By default, this function assumes a dark line (high values)
|
||||
surrounded by white (low values). If your line is light on
|
||||
black, set the optional second argument white_line to true. In
|
||||
this case, each sensor value will be replaced by (1000-value)
|
||||
before the averaging.
|
||||
"""
|
||||
def readLine(self, white_line = 0):
|
||||
|
||||
sensor_values = self.readCalibrated()
|
||||
avg = 0
|
||||
sum = 0
|
||||
on_line = 0
|
||||
for i in range(0,self.numSensors):
|
||||
value = sensor_values[i]
|
||||
if(white_line):
|
||||
value = 1000-value
|
||||
# keep track of whether we see the line at all
|
||||
if(value > 200):
|
||||
on_line = 1
|
||||
|
||||
# only average in values that are above a noise threshold
|
||||
if(value > 50):
|
||||
avg += value * (i * 1000); # this is for the weighted total,
|
||||
sum += value; #this is for the denominator
|
||||
|
||||
if(on_line != 1):
|
||||
# If it last read to the left of center, return 0.
|
||||
if(self.last_value < (self.numSensors - 1)*1000/2):
|
||||
#print("left")
|
||||
self.last_value = 0
|
||||
|
||||
# If it last read to the right of center, return the max.
|
||||
else:
|
||||
#print("right")
|
||||
self.last_value = (self.numSensors - 1)*1000
|
||||
else:
|
||||
self.last_value = avg/sum
|
||||
|
||||
return self.last_value,sensor_values
|
||||
|
||||
|
||||
|
||||
# Simple example prints accel/mag data once per second:
|
||||
if __name__ == '__main__':
|
||||
TR = TRSensor()
|
||||
print("TRSensor Example")
|
||||
while True:
|
||||
try:
|
||||
print(TR.AnalogRead())
|
||||
time.sleep(0.2)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
Requirements Step by Step:
|
||||
sudo apt update
|
||||
sudo apt install libjpeg62-turbo-dev python3-prctl python3-picamera2 python3-libcamera libcamera-apps libcamera-dev
|
||||
python3 -m venv --system-site-packages env
|
||||
source env/bin/activate
|
||||
pip install flask pillow
|
||||
"""
|
||||
|
||||
from flask import Flask, Response
|
||||
from picamera2 import Picamera2
|
||||
from PIL import Image
|
||||
import io
|
||||
import base64
|
||||
import serial
|
||||
import time
|
||||
import cv2
|
||||
|
||||
s = serial.Serial('/dev/ttyACM1', 115200, timeout=1) # serielle verbindung zum ESP aufbauen
|
||||
time.sleep(2)
|
||||
|
||||
'''
|
||||
Kamera Konfigurieren mit einem Format von 640x480
|
||||
'''
|
||||
picam2 = Picamera2()
|
||||
config = picam2.create_preview_configuration(main={"size": (640, 480)})
|
||||
picam2.configure(config)
|
||||
picam2.start()
|
||||
|
||||
MAX_RAW = 1200
|
||||
MAX_B64 = 1200
|
||||
|
||||
# TODO: map image to MAX_B64
|
||||
|
||||
def encode_image_to_b64_with_limit(img: Image.Image, max_b64: int = 1024, scale_step: float = 0.9, initial_quality: int = 30, min_quality: int = 5) -> str:
|
||||
quality = initial_quality
|
||||
working_img = img.copy()
|
||||
|
||||
while True:
|
||||
buf = io.BytesIO()
|
||||
working_img.save(buf, format="JPEG", quality=quality, optimize=True)
|
||||
jpeg_bytes = buf.getvalue()
|
||||
|
||||
b64 = base64.b64encode(jpeg_bytes).decode("utf-8")
|
||||
|
||||
if len(b64) <= max_b64 or (working_img.width < 2 or working_img.height < 2):
|
||||
return b64
|
||||
|
||||
|
||||
# if len(jpeg_bytes) <= MAX_RAW:
|
||||
# return jpeg_bytes
|
||||
|
||||
new_w = max(1, int(working_img.width * scale_step))
|
||||
new_h = max(1, int(working_img.height * scale_step))
|
||||
working_img = working_img.resize((new_w, new_h), Image.LANCZOS)
|
||||
|
||||
if quality > min_quality:
|
||||
quality = max(min_quality, int(quality * scale_step))
|
||||
|
||||
def init_serial():
|
||||
global s
|
||||
s = None
|
||||
while s is None:
|
||||
try:
|
||||
s = serial.Serial('/dev/ttyACM1', 115200, timeout=1)
|
||||
except Exception:
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
def gen():
|
||||
global s
|
||||
while True:
|
||||
arr = picam2.capture_array("main") # Einen frame holen
|
||||
img = Image.fromarray(arr).convert("L") # Bild in Graustufen umwandeln
|
||||
# TODO: skip config with 640x480
|
||||
img = img.resize((640, 480), Image.LANCZOS)
|
||||
|
||||
b64 = encode_image_to_b64_with_limit(img, MAX_B64) # Bildqualität reduzieren bis es eine Größe von maximal MAX_B64 (1200) hat
|
||||
print("Image_b64 size: " + str(len(b64)))
|
||||
|
||||
try:
|
||||
s.write(("{\"type\":\"image\",\"data\":\"" + b64 + "\"}\n").encode("utf-8")) # Frame als JSON-String an den ESP verschicken
|
||||
# s.write((b64 + "\n").encode("utf-8"))
|
||||
except Exception:
|
||||
'''
|
||||
War das schreiben über seriell nicht möglich wird ein erneuter Verbindungsaufbau versucht bis die Verbindung wieder steht.
|
||||
Es wird davon ausgegangen das wir die Verbindung verloren haben und deswegen das schreiben nicht möglich war.
|
||||
'''
|
||||
init_serial()
|
||||
time.sleep(0.1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# app.run(host="0.0.0.0", port=1234, threaded=True)
|
||||
try:
|
||||
gen()
|
||||
except KeyboardInterrupt:
|
||||
serial.close()
|
||||
print("Uebertragung beenden")
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Line following robot for Raspberry Pi 4 using AlphaBot2 and TRSensor
|
||||
Ohne LED-Visualisierung
|
||||
"""
|
||||
import time
|
||||
import RPi.GPIO as GPIO
|
||||
from AlphaBot2 import AlphaBot2
|
||||
from TRSensors import TRSensor
|
||||
|
||||
MAX_SPEED = 25
|
||||
CORRECTION_STRENGTH = 8
|
||||
EXTRA_CURVE_CORRECTION = 2
|
||||
LINE_DETECTED_THRESHOLD = 650
|
||||
|
||||
# Liste für gleitenden Durchschnitt
|
||||
last_outer = []
|
||||
|
||||
def setup_gpio():
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
|
||||
def calibrate_sensors(tr, robot):
|
||||
print("Auto-calibrating sensors...")
|
||||
|
||||
for i in range(5):
|
||||
tr.calibratedMin[i] = 1023
|
||||
tr.calibratedMax[i] = 0
|
||||
|
||||
for _ in range(3):
|
||||
robot.setPWMA(20)
|
||||
robot.setPWMB(15)
|
||||
robot.forward()
|
||||
for _ in range(30):
|
||||
raw = tr.AnalogRead()
|
||||
for i in range(5):
|
||||
tr.calibratedMin[i] = min(tr.calibratedMin[i], raw[i])
|
||||
tr.calibratedMax[i] = max(tr.calibratedMax[i], raw[i])
|
||||
time.sleep(0.02)
|
||||
|
||||
robot.backward()
|
||||
for _ in range(30):
|
||||
raw = tr.AnalogRead()
|
||||
for i in range(5):
|
||||
tr.calibratedMin[i] = min(tr.calibratedMin[i], raw[i])
|
||||
tr.calibratedMax[i] = max(tr.calibratedMax[i], raw[i])
|
||||
time.sleep(0.02)
|
||||
|
||||
robot.stop()
|
||||
print("Calibration complete.")
|
||||
print(" Min:", tr.calibratedMin)
|
||||
print(" Max:", tr.calibratedMax)
|
||||
|
||||
def detect_line_type(tr):
|
||||
sensor_values = tr.readCalibrated()
|
||||
center_avg = sum(sensor_values[1:4]) / 3
|
||||
outer_avg = (sensor_values[0] + sensor_values[4]) / 2
|
||||
white_line = outer_avg < center_avg
|
||||
print("Detected line type:", "WHITE on BLACK" if white_line else "BLACK on WHITE")
|
||||
return white_line
|
||||
|
||||
def compute_correction(sensors):
|
||||
left = sensors[1]
|
||||
right = sensors[3]
|
||||
correction = 0
|
||||
|
||||
if abs(left - right) < 50:
|
||||
correction = 0
|
||||
elif left < LINE_DETECTED_THRESHOLD and right > LINE_DETECTED_THRESHOLD:
|
||||
correction = -CORRECTION_STRENGTH
|
||||
elif right < LINE_DETECTED_THRESHOLD and left > LINE_DETECTED_THRESHOLD:
|
||||
correction = CORRECTION_STRENGTH
|
||||
|
||||
if sensors[0] < LINE_DETECTED_THRESHOLD:
|
||||
correction -= EXTRA_CURVE_CORRECTION
|
||||
elif sensors[4] < LINE_DETECTED_THRESHOLD:
|
||||
correction += EXTRA_CURVE_CORRECTION
|
||||
|
||||
return correction
|
||||
|
||||
def perform_turn_if_needed(sensors, robot, position):
|
||||
global last_outer
|
||||
l = sensors[0]
|
||||
r = sensors[4]
|
||||
last_outer.append((l, r))
|
||||
if len(last_outer) > 5:
|
||||
last_outer.pop(0)
|
||||
|
||||
avg_l = sum(x for x, _ in last_outer) / len(last_outer)
|
||||
avg_r = sum(y for _, y in last_outer) / len(last_outer)
|
||||
delta = avg_r - avg_l
|
||||
|
||||
print(f"Turn-Check: AvgL={avg_l:.0f}, AvgR={avg_r:.0f}, Δ={delta:.0f}, pos={position:.1f}")
|
||||
|
||||
if not (1500 <= position <= 2500):
|
||||
return False
|
||||
if avg_l > 800 and avg_r > 800:
|
||||
return False
|
||||
|
||||
if avg_r > 800 and avg_l < 500 and delta > 400:
|
||||
print("→ 90° right-curve detected")
|
||||
robot.stop()
|
||||
time.sleep(0.1)
|
||||
robot.right()
|
||||
time.sleep(0.3)
|
||||
robot.forward()
|
||||
time.sleep(0.35)
|
||||
return True
|
||||
elif avg_l > 800 and avg_r < 500 and delta < -600 and sensors[2] < LINE_DETECTED_THRESHOLD:
|
||||
print("← 90° left-curve detected")
|
||||
robot.stop()
|
||||
time.sleep(0.1)
|
||||
robot.left()
|
||||
time.sleep(0.3)
|
||||
robot.forward()
|
||||
time.sleep(0.35)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def line_follow_loop(tr, robot, white_line):
|
||||
robot.forward()
|
||||
|
||||
while True:
|
||||
position, sensors = tr.readLine(white_line=white_line)
|
||||
|
||||
print(f"Sensors: {sensors}, pos={position:.1f}")
|
||||
|
||||
if perform_turn_if_needed(sensors, robot, position):
|
||||
robot.forward()
|
||||
time.sleep(0.35)
|
||||
robot.stop()
|
||||
break
|
||||
|
||||
if all(s > LINE_DETECTED_THRESHOLD for s in sensors):
|
||||
time.sleep(0.05)
|
||||
robot.backward()
|
||||
time.sleep(0.35)
|
||||
robot.stop()
|
||||
continue
|
||||
|
||||
correction = compute_correction(sensors)
|
||||
left_speed = min(MAX_SPEED, max(0, MAX_SPEED + correction))
|
||||
right_speed = min(MAX_SPEED, max(0, MAX_SPEED - correction))
|
||||
|
||||
robot.setPWMA(int(left_speed))
|
||||
robot.setPWMB(int(right_speed))
|
||||
|
||||
time.sleep(0.02)
|
||||
|
||||
def line_follow(tr=None, robot=None):
|
||||
if tr is None or robot is None:
|
||||
setup_gpio()
|
||||
robot = AlphaBot2()
|
||||
tr = TRSensor()
|
||||
robot.stop()
|
||||
calibrate_sensors(tr, robot)
|
||||
|
||||
white_line = detect_line_type(tr)
|
||||
|
||||
print("Starting line follow...")
|
||||
try:
|
||||
line_follow_loop(tr, robot, white_line)
|
||||
except KeyboardInterrupt:
|
||||
print("Stopping and cleaning up...")
|
||||
robot.stop()
|
||||
GPIO.cleanup()
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_gpio()
|
||||
robot = AlphaBot2()
|
||||
tr = TRSensor()
|
||||
robot.stop()
|
||||
|
||||
calibrate_sensors(tr, robot)
|
||||
input("Press Enter to continue...")
|
||||
line_follow(tr, robot)
|
||||
@@ -0,0 +1,119 @@
|
||||
from enum import Enum
|
||||
import enum
|
||||
import time
|
||||
import RPi.GPIO as GPIO
|
||||
from AlphaBot2 import AlphaBot2
|
||||
from TRSensors import TRSensor
|
||||
import datetime
|
||||
|
||||
class LINE_MODE(Enum):
|
||||
UNSET = -1
|
||||
WHITE_LINE_MODE = 0
|
||||
BLACK_LINE_MODE = 1
|
||||
|
||||
class STATE(Enum):
|
||||
INIT_SUCCESS = 0
|
||||
INIT_FAIL = -1
|
||||
|
||||
BTN_PIN = 7
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
MAX_SPEED = 40
|
||||
OVERDRIVE_SPEED = 50
|
||||
MAX_REVERSE_SPEED = 20
|
||||
MAX_CORRECTION_STRENGTH = 5
|
||||
|
||||
LineColorDescriptor: LINE_MODE = LINE_MODE.UNSET
|
||||
CurrentCorrectionStrength: int = 1
|
||||
logging: bool = True
|
||||
LineSearchingDirection: int = 0 # -1 -> left, 1 -> right
|
||||
|
||||
TR = TRSensor()
|
||||
Ab = AlphaBot2()
|
||||
Ab.stop()
|
||||
|
||||
def log(msg: str) -> None:
|
||||
if not logging: return;
|
||||
print("-> Logger")
|
||||
print(f"Timestamp: {datetime.datetime.now()}")
|
||||
print(f"Message: {msg}")
|
||||
print("<- Logger")
|
||||
|
||||
def LineModeBoolConverter(mode: LINE_MODE) -> bool:
|
||||
return True if mode == LINE_MODE.WHITE_LINE_MODE else False
|
||||
|
||||
def setup() -> int:
|
||||
log("Initializing TR!")
|
||||
for i in range(1, 4):
|
||||
TR.calibratedMin[i] = 1023
|
||||
TR.calibratedMax[i] = 0
|
||||
|
||||
log("Calibration sensors!")
|
||||
for _ in range(100):
|
||||
values = TR.AnalogRead()
|
||||
for i in range(1, 5): # Index 1 bis 3 → Sensor 2–4
|
||||
TR.calibratedMin[i] = min(TR.calibratedMin[i], values[i])
|
||||
TR.calibratedMax[i] = max(TR.calibratedMax[i], values[i])
|
||||
Ab.stop()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Determine line color
|
||||
sensor_values = TR.readCalibrated()
|
||||
center_avg = sum(sensor_values[1:4]) / 3
|
||||
outer_avg = (sensor_values[0] + sensor_values[4]) / 2
|
||||
LineColorDescriptor = LINE_MODE.WHITE_LINE_MODE if outer_avg < center_avg else LINE_MODE.BLACK_LINE_MODE # If outer sensors are dark → white line on dark
|
||||
log("Detected line type: " + "WHITE on BLACK" if LineColorDescriptor == LINE_MODE.WHITE_LINE_MODE else "BLACK on WHITE")
|
||||
|
||||
# Wait for button press to start
|
||||
print("Press the button to start line following")
|
||||
while GPIO.input(BTN_PIN):
|
||||
time.sleep(0.1)
|
||||
|
||||
LineSearchingDirection = 0
|
||||
|
||||
log("Setup done!")
|
||||
return 0;
|
||||
|
||||
def loop() -> None:
|
||||
Ab.forward()
|
||||
while True:
|
||||
position, sensors = TR.readLine(white_line=LineModeBoolConverter(LineColorDescriptor))
|
||||
background_level = (sensors[0] + sensors[1]) / 2
|
||||
|
||||
correction_strength = (background_level / 1023.0) * 2 * MAX_CORRECTION_STRENGTH - MAX_CORRECTION_STRENGTH
|
||||
|
||||
if sensors[2] < background_level:
|
||||
Ab.backward()
|
||||
Ab.setPWMA(MAX_REVERSE_SPEED)
|
||||
Ab.setPWMB(MAX_REVERSE_SPEED)
|
||||
while True:
|
||||
position, sensors = TR.readLine(white_line=LineModeBoolConverter(LineColorDescriptor))
|
||||
if sensors[2] >= background_level:
|
||||
break
|
||||
|
||||
Ab.forward()
|
||||
continue
|
||||
|
||||
if sensors[1] < background_level:
|
||||
Ab.setPWMA(OVERDRIVE_SPEED)
|
||||
Ab.setPWMB(MAX_SPEED)
|
||||
|
||||
elif sensors[3] < background_level:
|
||||
Ab.setPWMB(OVERDRIVE_SPEED)
|
||||
Ab.setPWMA(MAX_SPEED)
|
||||
|
||||
else:
|
||||
if correction_strength < 0:
|
||||
Ab.setPWMB(int(MAX_SPEED + correction_strength)) # decrease power from right motor if sensor indicates missing line left
|
||||
elif correction_strength > 0:
|
||||
Ab.setPWMA(int(MAX_SPEED - correction_strength))
|
||||
|
||||
# time.sleep(0.01); # we drive fast so handle steering as fast as possible
|
||||
|
||||
if __name__ == '__main__':
|
||||
if setup():
|
||||
loop()
|
||||
else:
|
||||
log("Setup failed!")
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Linienfolgeroboter für Raspberry Pi 4 mit AlphaBot2 und TRSensor
|
||||
Verwendet einen PID-Regler und eine Suchfunktion bei Linienverlust.
|
||||
(Version 3: Korrektur für Stopp nach Liniensuche)
|
||||
"""
|
||||
import time
|
||||
import RPi.GPIO as GPIO
|
||||
from AlphaBot2 import AlphaBot2
|
||||
from TRSensors import TRSensor
|
||||
|
||||
# Geschwindigkeits- und PID-Einstellungen
|
||||
MAX_SPEED = 40 # 50
|
||||
BASE_SPEED = 30 # 40
|
||||
SEARCH_SPEED = 30 # Geschwindigkeit während der Liniensuche
|
||||
|
||||
# PID-Konstanten - DIESE MÜSSEN EVENTUELL ANGEPASST WERDEN!
|
||||
Kp = 0.045 # 0.025
|
||||
Ki = 0.0001 # 0.0005
|
||||
Kd = 0.3
|
||||
|
||||
# Zielwert der Sensoren (Mitte der Linie)
|
||||
TARGET_POSITION = 2000
|
||||
|
||||
# Schwelle, ab der ein Sensor die Linie als "verloren" betrachtet
|
||||
LINE_LOST_THRESHOLD = 950
|
||||
# Zeitlimit für die Suche in Sekunden
|
||||
SEARCH_TIMEOUT = 3.0
|
||||
|
||||
IR = 17
|
||||
|
||||
def setup_gpio():
|
||||
"""Initialisiert die GPIO-Pins."""
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(IR, GPIO.IN)
|
||||
|
||||
|
||||
def calibrate_sensors(tr, robot):
|
||||
"""Führt eine automatische Kalibrierung der Sensoren durch."""
|
||||
print("Starte Sensor-Kalibrierung...")
|
||||
robot.setPWMA(20)
|
||||
robot.setPWMB(20)
|
||||
# Drehung zur Kalibrierung auf weißem und schwarzem Untergrund
|
||||
for _ in range(2):
|
||||
robot.right()
|
||||
for _ in range(50):
|
||||
tr.calibrate()
|
||||
time.sleep(0.02)
|
||||
robot.left()
|
||||
for _ in range(100):
|
||||
tr.calibrate()
|
||||
time.sleep(0.02)
|
||||
robot.right()
|
||||
for _ in range(50):
|
||||
tr.calibrate()
|
||||
time.sleep(0.02)
|
||||
robot.stop()
|
||||
print("Kalibrierung abgeschlossen.")
|
||||
print(" Min:", tr.calibratedMin)
|
||||
print(" Max:", tr.calibratedMax)
|
||||
|
||||
|
||||
def search_for_line(robot, tr, last_error):
|
||||
"""
|
||||
Sucht die Linie, indem der Roboter sich in die Richtung dreht,
|
||||
in der die Linie zuletzt war.
|
||||
"""
|
||||
print("Linie verloren! Suche...")
|
||||
robot.stop()
|
||||
time.sleep(0.1)
|
||||
|
||||
# Setze zuerst die Geschwindigkeit für beide Motoren
|
||||
robot.setPWMA(SEARCH_SPEED)
|
||||
robot.setPWMB(SEARCH_SPEED)
|
||||
|
||||
# Bestimme dann die Suchrichtung
|
||||
if last_error > 0:
|
||||
print("Suche nach links...")
|
||||
robot.left()
|
||||
else:
|
||||
print("Suche nach rechts...")
|
||||
robot.right()
|
||||
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < SEARCH_TIMEOUT:
|
||||
# Lies kontinuierlich die Sensoren, während du dich drehst
|
||||
_, sensors = tr.readLine(white_line=False)
|
||||
|
||||
# Prüfe, ob einer der Sensoren die Linie wieder sieht
|
||||
if any(s < LINE_LOST_THRESHOLD for s in sensors):
|
||||
print("Linie wiedergefunden!")
|
||||
robot.stop()
|
||||
time.sleep(0.1)
|
||||
return True # Suche war erfolgreich
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
# Schleife beendet, ohne die Linie zu finden (Timeout)
|
||||
print(f"Suche nach {SEARCH_TIMEOUT}s erfolglos. Roboter stoppt.")
|
||||
robot.stop()
|
||||
return False # Suche war erfolglos
|
||||
|
||||
|
||||
def line_follow_loop(tr, robot):
|
||||
"""Hauptschleife für die Linienverfolgung mittels PID-Regelung."""
|
||||
# Setzt die anfängliche Richtung auf vorwärts
|
||||
robot.forward()
|
||||
|
||||
last_error = 0
|
||||
integral = 0
|
||||
|
||||
while True:
|
||||
# Lese die Position der Linie. False für schwarze Linie auf weißem Grund.
|
||||
position, sensors = tr.readLine(white_line=False)
|
||||
|
||||
# Wenn alle Sensoren weiß sehen, starte die Liniensuche
|
||||
if all(s > LINE_LOST_THRESHOLD for s in sensors):
|
||||
search_successful = search_for_line(robot, tr, last_error)
|
||||
if search_successful:
|
||||
# Setze PID-Werte zurück, um einen Sprung zu vermeiden
|
||||
last_error = 0
|
||||
integral = 0
|
||||
# WICHTIG: Setze die Richtung wieder auf vorwärts, bevor die Schleife weitergeht
|
||||
robot.forward()
|
||||
continue
|
||||
else:
|
||||
# Beende die Hauptschleife, wenn die Suche fehlschlägt
|
||||
break
|
||||
|
||||
# --- PID-Berechnung ---
|
||||
error = position - TARGET_POSITION
|
||||
integral += error
|
||||
derivative = error - last_error
|
||||
last_error = error
|
||||
|
||||
correction = (Kp * error) + (Ki * integral) + (Kd * derivative)
|
||||
|
||||
# Anpassung der Motor-Geschwindigkeiten
|
||||
left_speed = BASE_SPEED + correction
|
||||
right_speed = BASE_SPEED - correction
|
||||
|
||||
# Geschwindigkeiten auf den gültigen Bereich begrenzen
|
||||
left_speed = max(0, min(MAX_SPEED, left_speed))
|
||||
right_speed = max(0, min(MAX_SPEED, right_speed))
|
||||
|
||||
# print(f"Pos: {position:.0f} | Err: {error:.0f} | Corr: {correction:.2f} | L: {left_speed:.0f} | R: {right_speed:.0f}")
|
||||
|
||||
robot.setPWMA(int(left_speed))
|
||||
robot.setPWMB(int(right_speed))
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
def follow(tr , robot):
|
||||
"""Hauptfunktion des Programms."""
|
||||
#robot = AlphaBot2()
|
||||
#tr = TRSensor()
|
||||
#robot.stop()
|
||||
|
||||
try:
|
||||
calibrate_sensors(tr, robot)
|
||||
print("Kalibrierung abgeschlossen")
|
||||
time.sleep(5.0)
|
||||
#input("Kalibrierung abgeschlossen. Drücke Enter zum Starten...")
|
||||
line_follow_loop(tr, robot)
|
||||
except KeyboardInterrupt:
|
||||
print("\nProgramm gestoppt.")
|
||||
finally:
|
||||
robot.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_gpio()
|
||||
robot = AlphaBot2()
|
||||
tr = TRSensor()
|
||||
robot.stop()
|
||||
follow(tr, robot)
|
||||
print("Räume GPIO auf.")
|
||||
GPIO.cleanup()
|
||||
Reference in New Issue
Block a user