Bemerkung
Hallo, willkommen in der SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasten-Community auf Facebook! Tauchen Sie mit anderen Enthusiasten tiefer in Raspberry Pi, Arduino und ESP32 ein.
Warum beitreten?
Expertenunterstützung: Lösen Sie Probleme nach dem Kauf und technische Herausforderungen mit Hilfe unserer Community und unseres Teams.
Lernen & Teilen: Tauschen Sie Tipps und Tutorials aus, um Ihre Fähigkeiten zu verbessern.
Exklusive Vorschauen: Erhalten Sie frühzeitigen Zugang zu neuen Produktankündigungen und Sneak Peeks.
Sonderrabatte: Genießen Sie exklusive Rabatte auf unsere neuesten Produkte.
Festliche Aktionen und Gewinnspiele: Nehmen Sie an Gewinnspielen und Feiertagsaktionen teil.
👉 Bereit, mit uns zu entdecken und zu gestalten? Klicken Sie auf [here] und treten Sie noch heute bei!
4.11 Servo-Winkelmesser
Einführung
In dieser Lektion bauen Sie einen Servo-Winkelmesser – eine visuelle Servo-Winkelanzeige, die ein Potentiometer verwendet, um einen Servomotor zu steuern, während der aktuelle Winkel auf einem OLED-Bildschirm angezeigt wird. Das Potentiometer liefert eine analoge Spannung über die ADC-Schnittstelle des Fusion HAT+. Der Servo erhält auf Grundlage dieses Messwerts Steuerbefehle, und ein 128×64-I2C-OLED-Bildschirm zeigt den numerischen Servowinkel sowie einen grafischen Balken an, der sich flüssig über das Display bewegt.
Wenn Sie das Potentiometer drehen, bewegt sich der Servo ungefähr zwischen -90° und +90°, und das OLED wird in Echtzeit aktualisiert.
Was Sie benötigen
Für dieses Projekt werden die folgenden Komponenten benötigt:
KOMPONENTENBESCHREIBUNG |
KAUFLINK |
|---|---|
- |
|
- |
|
Raspberry Pi |
- |
Verdrahtungsdiagramm
Verwenden Sie das folgende Verdrahtungsdiagramm, um die Komponenten korrekt zu verbinden:
Einrichtungsschritte
Installieren Sie die erforderlichen Bibliotheken:
sudo pip3 install adafruit-circuitpython-ssd1306 --break
Der gesamte in diesem Tutorial verwendete Beispielcode befindet sich im Verzeichnis
ai-lab-kit:cd ~/ai-lab-kit/python/ sudo python3 4.11_ServoAngleMeter.py
Wenn das Skript ausgeführt wird:
Durch Drehen des Potentiometers bewegt sich der Servo zwischen -90° und +90°.
Das OLED zeigt den numerischen Winkel und einen beweglichen Balkenzeiger an.
Mit Ctrl+C wird das Programm beendet, der Servo auf 0° zurückgesetzt und das Display gelöscht.
Code
Hier ist das Python-Skript für den Servo-Winkelmesser:
from fusion_hat.adc import ADC
from fusion_hat.servo import Servo
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
import board, time
# ==== OLED setup ====
WIDTH, HEIGHT = 128, 64
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)
oled.fill(0)
oled.show()
# Framebuffer for drawing
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
def text_size(font, text):
l, t, r, b = font.getbbox(text)
return (r - l, b - t)
# ==== Servo & potentiometer ====
servo = Servo('P0') # servo on port P0
pot = ADC('A0') # potentiometer on A0 (0..4095)
def linear_map(x, in_min, in_max, out_min, out_max):
"""Map x from one range to another."""
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# ---- bar layout ----
BAR_TOP = 40
BAR_HEIGHT = 10
BAR_MARGINX = 6
BAR_WIDTH = WIDTH - BAR_MARGINX * 2
BAR_CENTERX = BAR_MARGINX + BAR_WIDTH // 2
def draw_bar(angle_deg):
"""Draw a centered horizontal bar and pointer for -90..90 degrees."""
draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
# Title
title = "Servo Angle"
tw, th = text_size(font, title)
draw.text(((WIDTH - tw) // 2, 4), title, font=font, fill=255)
# Numeric angle
txt = f"{angle_deg:>4} deg"
nw, nh = text_size(font, txt)
draw.text(((WIDTH - nw) // 2, 20), txt, font=font, fill=255)
# Bar outline
draw.rectangle(
(BAR_MARGINX, BAR_TOP, BAR_MARGINX + BAR_WIDTH - 1, BAR_TOP + BAR_HEIGHT),
outline=255, fill=0
)
# Ticks
for x in (BAR_MARGINX, BAR_CENTERX, BAR_MARGINX + BAR_WIDTH - 1):
draw.line((x, BAR_TOP - 3, x, BAR_TOP + BAR_HEIGHT + 3), fill=255)
# Map angle to pixel position
pos = int(linear_map(angle_deg, -90, 90, BAR_MARGINX, BAR_MARGINX + BAR_WIDTH - 1))
draw.line((pos, BAR_TOP - 2, pos, BAR_TOP + BAR_HEIGHT + 2), fill=255)
# Fill direction highlight
if pos >= BAR_CENTERX:
draw.rectangle((BAR_CENTERX, BAR_TOP + 1, pos, BAR_TOP + BAR_HEIGHT - 1), fill=255)
else:
draw.rectangle((pos, BAR_TOP + 1, BAR_CENTERX, BAR_TOP + BAR_HEIGHT - 1), fill=255)
try:
while True:
raw = pot.read()
angle = int(linear_map(raw, 0, 4095, -90, 90))
servo.angle(angle)
draw_bar(angle)
oled.image(image)
oled.show()
time.sleep(0.05)
except KeyboardInterrupt:
servo.angle(0)
oled.fill(0)
oled.show()
print("\nExited.")
Understanding the Code
Imports
ADCreads analog values from the potentiometerServocontrols servo rotationPILhandles all OLED graphicsadafruit_ssd1306drives the I2C OLED displayboardprovides hardware I/Otimecontrols loop speed
OLED Setup
A 128×64 SSD1306 OLED is initialized and cleared. An off-screen framebuffer holds the graphics for each frame before being pushed to the display.
# ==== OLED setup ==== WIDTH, HEIGHT = 128, 64 i2c = board.I2C() oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C) oled.fill(0) oled.show() # Framebuffer for drawing image = Image.new("1", (WIDTH, HEIGHT)) draw = ImageDraw.Draw(image) font = ImageFont.load_default()
Servo & Potentiometer
Servo connected to port
P0Potentiometer connected to analog input
A0ADC range:
0..4095
# ==== Servo & potentiometer ==== servo = Servo('P0') # servo on port P0 pot = ADC('A0') # potentiometer on A0 (0..4095)
Mapping Values
linear_map()converts the potentiometer reading into a servo angle in the range-90..90.def linear_map(x, in_min, in_max, out_min, out_max): """Map x from one range to another.""" return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
Drawing the UI
The
draw_bar()function:Clears the display
Draws the title
Shows numeric angle
Draws a horizontal bar and tick marks
Draws a pointer and filled segment indicating the angle direction
def draw_bar(angle_deg): """ Draw a centered horizontal bar with a moving pointer. -90° maps to the far left, +90° to the far right. 0° is at the bar center. """ # Clear screen draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0) # Title title = "Servo Angle" tw, th = text_size(font, title) draw.text(((WIDTH - tw) // 2, 4), title, font=font, fill=255) # Numeric angle txt = f"{angle_deg:>4} deg" nw, nh = text_size(font, txt) draw.text(((WIDTH - nw) // 2, 20), txt, font=font, fill=255) # Static bar background draw.rectangle( (BAR_MARGINX, BAR_TOP, BAR_MARGINX + BAR_WIDTH - 1, BAR_TOP + BAR_HEIGHT), outline=255, fill=0 ) # Ticks: left (-90), center (0), right (+90) for x in (BAR_MARGINX, BAR_CENTERX, BAR_MARGINX + BAR_WIDTH - 1): draw.line((x, BAR_TOP - 3, x, BAR_TOP + BAR_HEIGHT + 3), fill=255) # Map angle (-90..90) to bar position pos = int(linear_map(angle_deg, -90, 90, BAR_MARGINX, BAR_MARGINX + BAR_WIDTH - 1)) # Pointer: a solid vertical line draw.line((pos, BAR_TOP - 2, pos, BAR_TOP + BAR_HEIGHT + 2), fill=255) # Optional: filled segment from center to pointer (visualize direction) if pos >= BAR_CENTERX: draw.rectangle((BAR_CENTERX, BAR_TOP + 1, pos, BAR_TOP + BAR_HEIGHT - 1), outline=0, fill=255) else: draw.rectangle((pos, BAR_TOP + 1, BAR_CENTERX, BAR_TOP + BAR_HEIGHT - 1), outline=0, fill=255)
Hauptschleife
Das Skript führt wiederholt folgende Schritte aus:
Es liest den ADC-Wert.
Es berechnet den Servowinkel.
Es aktualisiert den Servo.
Es zeichnet die aktualisierte Benutzeroberfläche.
Es aktualisiert das OLED-Display.
while True: # Read potentiometer (0..4095) and map to angle (-90..90) raw = pot.read() angle = int(linear_map(raw, 0, 4095, -90, 90)) # Drive servo servo.angle(angle) # Draw UI and push to OLED draw_bar(angle) oled.image(image) oled.show() # Optional: print for debugging # print(f"pot={raw:4d} -> angle={angle:4d} deg") time.sleep(0.05) # ~20 FPS
Sauberes Beenden
Mit
Ctrl+C:kehrt der Servo auf 0° zurück
wird das OLED-Display gelöscht
Fehlerbehebung
OLED zeigt nichts an
Überprüfen Sie die I2C-Verdrahtung.
Stellen Sie sicher, dass die Geräteadresse
0x3Clautet.Vergewissern Sie sich, dass die erforderlichen Bibliotheken installiert sind.
Servo reagiert nicht
Überprüfen Sie die Stromversorgung des Servos.
Stellen Sie sicher, dass der Servo mit
P0verbunden ist.Prüfen Sie, ob das Signalkabel des Servos korrekt angeschlossen ist.
Bewegungsbereich ist falsch
Passen Sie Folgendes an:
angle = int(linear_map(raw, 0, 4095, -90, 90))
OLED flackert
Erhöhen Sie die Verzögerung:
time.sleep(0.1)
Probieren Sie es selbst aus
Servowinkelbegrenzung hinzufügen
Verhindern Sie eine mechanische Übersteuerung.
Kalibrierung hinzufügen
Ermitteln Sie die minimalen und maximalen Potentiometerwerte dynamisch.
Sanftere Bewegung
Wenden Sie Easing oder Tiefpassfilterung an.
Mehr Anzeigeinformationen
Zeigen Sie zusätzlich zum Winkel auch den rohen ADC-Wert an.
Warnhinweise
Lassen Sie den Zeiger in der Nähe der Grenzwerte (±75°) blinken.
Diese Erweiterungen machen den Servo-Winkelmesser zu einem leistungsfähigen Werkzeug zur Visualisierung von Eingaben.