Version und Dokumentstand
Version 1.2.0,
die ausführbare Konstante lautet jedoch noch
VERSION = "1.1.0".
Dadurch gibt dobot.version() momentan Version 1.1.0 zurück.
Für einen vollständig konsistenten Stand muss die Konstante im Modul auf
VERSION = "1.2.0" geändert werden.
| Komponente | Version / Stand |
|---|---|
| Dokumentation | 1.2.0 |
| Dateikopf des Moduls | 1.2.0 · 15.07.2026 - 23:40 |
| Interne Konstante im Upload | VERSION = "1.1.0" |
| Versionsdatum | 15.07.2026 |
| Öffentliche Funktionen | 27 |
| Interne Funktionen | 6 |
Neuerungen in Version 1.2.0
lochposition_lesen(api)trennt die reine Wertrückgabe von der Bildschirmausgabe.lochposition_anzeigen(api)zeigt Spalte, Zeile, Höhe und das nächstgelegene Loch.fahre_sofort_zu(api, x, y, z)sendet einen PTP-Befehl ohne Queue.sauger_status(api),sauger_aktivieren(api)undsauger_deaktivieren(api)steuern den Sauger.alarme_lesen(api)liefert aktive Alarmnummern.alarme_anzeigen(api)zeigt Dezimalnummer, Hex-Code und deutsche Fehlermeldung.alarme_loeschen(api)löscht gespeicherte Alarmzustände.
Grundlage und Ordnerstruktur
dobot.py bildet eine eigene, gut lesbare Programmierschicht über DobotDllType.py und DobotDll.dll.
Dobot_Python\
├── dobot.py
├── sdk64\
│ ├── __init__.py
│ ├── DobotDllType.py
│ └── DobotDll.dll
└── projekt01\
└── start.py
Empfohlener Programmstart
Die Reihenfolge berücksichtigt Alarmprüfung, Queue-Start, HOME-Fahrt und ein sicheres Beenden.
from pathlib import Path
import sys
HAUPTORDNER = Path(__file__).resolve().parent.parent
if str(HAUPTORDNER) not in sys.path:
sys.path.insert(0, str(HAUPTORDNER))
import dobot
print(dobot.version())
api = dobot.init("COM10")
# Alarmzustand prüfen, bevor eine Bewegung gestartet wird.
dobot.alarme_anzeigen(api)
# Queue für HOME-, Fahr- und eingereihte Saugerbefehle starten.
dobot.queue_starten(api)
try:
dobot.home(api)
dobot.position_anzeigen(api)
dobot.lochposition_anzeigen(api)
dobot.fahre_zu_loch(
api,
spalte=20,
zeile=20,
hoehe=30.0,
)
dobot.sauger_aktivieren(api)
finally:
dobot.queue_stoppen(api)
Konstanten
VERSION = "1.2.0"
VERSIONSDATUM = "15.07.2026"
PLATTE_SPALTEN = 40
PLATTE_ZEILEN = 27
PLATTE_RASTER_MM = 16.0
Die Lochrasterplatte besitzt 40 Spalten, 27 Zeilen und einen Rasterabstand von 16 mm.
Verbindung und Grundfunktionen
version()Gibt die Versionsinformation dieser Hilfsbibliothek zurück.com_ports_ermitteln()Gibt die vom Betriebssystem erkannten seriellen Schnittstellen zurück.comport_pruefen()Prüft, ob der gewünschte COM-Port vom Betriebssystem erkannt wird.init()Lädt die 64-Bit-DLL, verbindet den Dobot und gibt ``api`` zurück.warten_bis_fertig()Wartet, bis ein Queue-Befehl vollständig ausgeführt wurde.queue_starten()Stoppt eine laufende Queue, löscht sie und startet sie neu.queue_stoppen()Stoppt die Ausführung der Queue.version()
Gibt die Versionsinformation dieser Hilfsbibliothek zurück.
Aufrufsignatur
def version():
Beispiel
import dobot
print(dobot.version())
Vollständige Definition aus dobot.py
def version():
"""Gibt die Versionsinformation dieser Hilfsbibliothek zurück."""
return f"dobot.py Version {VERSION} - Stand {VERSIONSDATUM}"
com_ports_ermitteln()
Gibt die vom Betriebssystem erkannten seriellen Schnittstellen zurück.
Das Ergebnis ist eine Liste aus Paaren:
``[(Portname, Beschreibung), ...]``.
Wenn PySerial installiert ist, werden dessen ausführliche Angaben
verwendet. Unter Windows dient andernfalls die Registrierungsdatenbank
als Fallback.
Aufrufsignatur
def com_ports_ermitteln():
Beispiel
for port, beschreibung in dobot.com_ports_ermitteln():
print(port, beschreibung)
Vollständige Definition aus dobot.py
def com_ports_ermitteln():
"""Gibt die vom Betriebssystem erkannten seriellen Schnittstellen zurück.
Das Ergebnis ist eine Liste aus Paaren:
``[(Portname, Beschreibung), ...]``.
Wenn PySerial installiert ist, werden dessen ausführliche Angaben
verwendet. Unter Windows dient andernfalls die Registrierungsdatenbank
als Fallback.
"""
try:
from serial.tools import list_ports
except ImportError:
list_ports = None
if list_ports is not None:
ports = [
(port.device, port.description or "Keine Beschreibung")
for port in list_ports.comports()
]
return sorted(ports, key=lambda eintrag: eintrag[0].upper())
# Fallback ohne PySerial für Windows.
if os.name == "nt":
try:
import winreg
schluessel = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"HARDWARE\DEVICEMAP\SERIALCOMM",
)
ports = []
index = 0
while True:
try:
_, portname, _ = winreg.EnumValue(schluessel, index)
ports.append((portname, "Serielle Schnittstelle"))
index += 1
except OSError:
break
winreg.CloseKey(schluessel)
return sorted(
set(ports),
key=lambda eintrag: eintrag[0].upper(),
)
except OSError:
return []
return []
comport_pruefen()
Prüft, ob der gewünschte COM-Port vom Betriebssystem erkannt wird.
Ist der Port nicht vorhanden, werden alle erkannten seriellen
Schnittstellen ausgegeben und das Programm mit Fehlercode 1 beendet.
Aufrufsignatur
def comport_pruefen(comport):
Beispiel
dobot.comport_pruefen("COM10")
Vollständige Definition aus dobot.py
def comport_pruefen(comport):
"""Prüft, ob der gewünschte COM-Port vom Betriebssystem erkannt wird.
Ist der Port nicht vorhanden, werden alle erkannten seriellen
Schnittstellen ausgegeben und das Programm mit Fehlercode 1 beendet.
"""
ports = com_ports_ermitteln()
vorhandene_portnamen = {
portname.upper()
for portname, _ in ports
}
if comport.upper() in vorhandene_portnamen:
print(f"Serielle Schnittstelle: {comport} ist vorhanden.")
return
print()
print(f"FEHLER: Die serielle Schnittstelle {comport} ist nicht verfügbar.")
print()
if ports:
print("Vom Betriebssystem erkannte COM-Ports:")
for portname, beschreibung in ports:
print(f" {portname:<8} {beschreibung}")
else:
print("Es wurden keine seriellen Schnittstellen gefunden.")
print()
print("Das Programm wird beendet.")
sys.exit(1)
init()
Lädt die 64-Bit-DLL, verbindet den Dobot und gibt ``api`` zurück.
Aufrufsignatur
def init(
comport="COM10",
device_name=(
"Dobot Magician - AG Young Engineers - "
"Martin-Rinckart-Gymnasium"
),
):
Beispiel
api = dobot.init("COM10")
Vollständige Definition aus dobot.py
def init(
comport="COM10",
device_name=(
"Dobot Magician - AG Young Engineers - "
"Martin-Rinckart-Gymnasium"
),
):
"""Lädt die 64-Bit-DLL, verbindet den Dobot und gibt ``api`` zurück."""
global _dll_suchpfad
hauptverzeichnis = Path(__file__).resolve().parent
sdk_verzeichnis = hauptverzeichnis / "sdk64"
dll_datei = sdk_verzeichnis / "DobotDll.dll"
print("Python-Version: ", platform.python_version())
print("Python-Architektur:", platform.architecture()[0])
print("SDK-Verzeichnis: ", sdk_verzeichnis)
print("DLL-Datei: ", dll_datei)
print("DLL vorhanden: ", dll_datei.exists())
# Vor dem Laden der DLL prüfen, ob der gewünschte COM-Port existiert.
comport_pruefen(comport)
if not dll_datei.exists():
raise FileNotFoundError(
"Die Dobot-DLL wurde nicht gefunden:\n"
f"{dll_datei}\n\n"
"Erwartete Ordnerstruktur:\n"
"Dobot_Python\\\n"
"├── dobot.py\n"
"├── sdk64\\\n"
"│ ├── DobotDll.dll\n"
"│ └── DobotDllType.py\n"
"└── projekt01\\\n"
" └── start.py"
)
# Unter Windows können sich in sdk64 weitere benötigte DLLs befinden.
if hasattr(os, "add_dll_directory"):
_dll_suchpfad = os.add_dll_directory(str(sdk_verzeichnis))
# DobotDll.dll über ihren vollständigen Pfad laden.
api = ctypes.CDLL(str(dll_datei))
result = dType.ConnectDobot(api, comport, 115200)
print("ConnectDobot-Ergebnis:", result)
# 0 bedeutet bei der Dobot-API: Verbindung erfolgreich.
if result[0] != 0:
meldungen = {
1: "Dobot wurde nicht gefunden.",
2: "Der COM-Port ist bereits belegt.",
}
meldung = meldungen.get(result[0], "Unbekannter Verbindungsfehler.")
raise ConnectionError(
f"Verbindung über {comport} fehlgeschlagen: {meldung} "
f"(Fehlercode {result[0]})"
)
if device_name:
dType.SetDeviceName(api, device_name)
name = dType.GetDeviceName(api)
seriennummer = dType.GetDeviceSN(api)
print("Gerätename: ", name)
print("Seriennummer: ", seriennummer)
return api
warten_bis_fertig()
Wartet, bis ein Queue-Befehl vollständig ausgeführt wurde.
Aufrufsignatur
def warten_bis_fertig(api, ziel_index):
Beispiel
ziel_index = dType.SetPTPCmd(
api, dType.PTPMode.PTPMOVJXYZMode,
200.0, 100.0, 50.0, 0.0,
isQueued=1,
)[0]
dobot.warten_bis_fertig(api, ziel_index)
Vollständige Definition aus dobot.py
def warten_bis_fertig(api, ziel_index):
"""Wartet, bis ein Queue-Befehl vollständig ausgeführt wurde."""
while dType.GetQueuedCmdCurrentIndex(api)[0] < ziel_index:
dType.dSleep(100)
queue_starten()
Stoppt eine laufende Queue, löscht sie und startet sie neu.
Aufrufsignatur
def queue_starten(api):
Beispiel
dobot.queue_starten(api)
Vollständige Definition aus dobot.py
def queue_starten(api):
"""Stoppt eine laufende Queue, löscht sie und startet sie neu."""
dType.SetQueuedCmdStopExec(api)
dType.SetQueuedCmdClear(api)
dType.SetQueuedCmdStartExec(api)
queue_stoppen()
Stoppt die Ausführung der Queue.
Aufrufsignatur
def queue_stoppen(api):
Beispiel
dobot.queue_stoppen(api)
Vollständige Definition aus dobot.py
def queue_stoppen(api):
"""Stoppt die Ausführung der Queue."""
dType.SetQueuedCmdStopExec(api)
Position und Bewegung
position_lesen()Liest die kartesische Position X, Y, Z und R.position_anzeigen()Liest und zeigt die aktuelle kartesische Position an.fahre_zu()Fährt zu einer Zielposition und wartet auf das Bewegungsende.fahre_sofort_zu()Sendet eine Bewegung sofort, ohne sie in die Queue einzureihen.home()Führt eine HOME-Fahrt aus und wartet auf deren Abschluss.test_z()Erlaubt das interaktive Verändern der aktuellen Z-Koordinate.ausfuehren()Startet den auf der Webseite beschriebenen Beispielablauf.position_lesen()
Liest die kartesische Position X, Y, Z und R.
Aufrufsignatur
def position_lesen(api):
Beispiel
x, y, z, r = dobot.position_lesen(api)
print(x, y, z, r)
Vollständige Definition aus dobot.py
def position_lesen(api):
"""Liest die kartesische Position X, Y, Z und R."""
return dType.GetPose(api)[:4]
position_anzeigen()
Liest und zeigt die aktuelle kartesische Position an.
Aufrufsignatur
def position_anzeigen(api):
Beispiel
dobot.position_anzeigen(api)
Vollständige Definition aus dobot.py
def position_anzeigen(api):
"""Liest und zeigt die aktuelle kartesische Position an."""
x, y, z, r = position_lesen(api)
print(
f"X={x:.1f} mm, "
f"Y={y:.1f} mm, "
f"Z={z:.1f} mm, "
f"R={r:.1f}°"
)
fahre_zu()
Fährt zu einer Zielposition und wartet auf das Bewegungsende.
Aufrufsignatur
def fahre_zu(api, x, y, z, r, modus=None):
Beispiel
dobot.queue_starten(api)
dobot.fahre_zu(
api,
x=200.0,
y=100.0,
z=50.0,
r=0.0,
)
Vollständige Definition aus dobot.py
def fahre_zu(api, x, y, z, r, modus=None):
"""Fährt zu einer Zielposition und wartet auf das Bewegungsende."""
if modus is None:
modus = dType.PTPMode.PTPMOVJXYZMode
ziel_index = dType.SetPTPCmd(
api,
modus,
x,
y,
z,
r,
isQueued=1,
)[0]
warten_bis_fertig(api, ziel_index)
fahre_sofort_zu()
Sendet eine Bewegung sofort, ohne sie in die Queue einzureihen.
Die aktuelle Werkzeugrotation R wird beibehalten.
Als Bewegungsart wird PTPMOVJXYZMode verwendet.
Aufrufsignatur
def fahre_sofort_zu(api, x, y, z):
Beispiel
# Ohne Queue und ohne Warten auf das Bewegungsende:
dobot.fahre_sofort_zu(api, 200.0, 100.0, 50.0)
# Aktive Alarme verhindern normalerweise trotzdem die Bewegung.
dobot.alarme_anzeigen(api)
Vollständige Definition aus dobot.py
def fahre_sofort_zu(api, x, y, z):
"""Sendet eine Bewegung sofort, ohne sie in die Queue einzureihen.
Die aktuelle Werkzeugrotation R wird beibehalten.
Als Bewegungsart wird PTPMOVJXYZMode verwendet.
"""
if api is None:
raise RuntimeError("Der Dobot ist nicht verbunden.")
# Aktuelle Werkzeugrotation beibehalten.
_, _, _, r = position_lesen(api)
return dType.SetPTPCmd(
api,
dType.PTPMode.PTPMOVJXYZMode,
x,
y,
z,
r,
isQueued=0,
)
home()
Führt eine HOME-Fahrt aus und wartet auf deren Abschluss.
Aufrufsignatur
def home(api):
Beispiel
dobot.queue_starten(api)
dobot.home(api)
Vollständige Definition aus dobot.py
def home(api):
"""Führt eine HOME-Fahrt aus und wartet auf deren Abschluss."""
print("HOME-Fahrt wird gestartet.")
ziel_index = dType.SetHOMECmd(
api,
0,
isQueued=1,
)[0]
warten_bis_fertig(api, ziel_index)
print("HOME-Fahrt abgeschlossen.")
test_z()
Erlaubt das interaktive Verändern der aktuellen Z-Koordinate.
Aufrufsignatur
def test_z(api):
Beispiel
dobot.queue_starten(api)
dobot.test_z(api)
Vollständige Definition aus dobot.py
def test_z(api):
"""Erlaubt das interaktive Verändern der aktuellen Z-Koordinate."""
print()
print("Interaktiver Test der Z-Koordinate")
print("----------------------------------")
while True:
x, y, z, r = position_lesen(api)
print(
f"Position: X={x:.1f}, Y={y:.1f}, "
f"Z={z:.1f}, R={r:.1f}"
)
eingabe = input(
"Neue Z-Koordinate oder 'a' zum Abbrechen: "
).strip()
if eingabe.lower() == "a":
break
# Auch eine Eingabe mit deutschem Dezimalkomma wird akzeptiert.
try:
neue_z = float(eingabe.replace(",", "."))
except ValueError:
print("Ungültige Eingabe. Bitte eine Zahl oder 'a' eingeben.")
continue
fahre_zu(
api,
x,
y,
neue_z,
r,
dType.PTPMode.PTPMOVLXYZMode,
)
print("Erreichte Position:")
position_anzeigen(api)
print("Z-Test beendet.")
ausfuehren()
Startet den auf der Webseite beschriebenen Beispielablauf.
Aufrufsignatur
def ausfuehren(api):
Beispiel
dobot.ausfuehren(api)
Vollständige Definition aus dobot.py
def ausfuehren(api):
"""Startet den auf der Webseite beschriebenen Beispielablauf."""
print()
print("Roboterprogramm wird gestartet.")
print("--------------------------------")
queue_starten(api)
try:
print("Aktuelle Position:")
position_anzeigen(api)
test_z(api)
finally:
queue_stoppen(api)
print()
print("Roboterprogramm beendet.")
Lochrasterplatte
plattenkalibrierung_setzen()Kalibriert die Platte mit drei beliebigen erreichbaren Löchern.plattenkalibrierung_anzeigen()Zeigt Referenzlöcher, Ursprung und Rastervektoren an.fahre_zu_loch()Fährt gelenkoptimiert zu einer Position im Lochraster.fahre_zu_loch_linear()Fährt auf einer geraden Bahn zu einer Position im Lochraster.lochposition_lesen()Liest die aktuelle Position im Koordinatensystem der Lochrasterplatte.lochposition_anzeigen()Liest und zeigt die aktuelle Position auf der Lochrasterplatte an.aktuelle_plattenposition()Kompatibler Alias für die Lochpositionsfunktionen.plattenkalibrierung_setzen()
Kalibriert die Platte mit drei beliebigen erreichbaren Löchern.
Neue Schreibweise der Referenzpunkte:
``(spalte, zeile, x, y)``
Beispiel:
``referenzloch_1=(2, 1, -73.8, -311.1)``
Die drei Rasterpositionen dürfen nicht auf einer gemeinsamen Geraden
liegen. Günstig sind zwei weit auseinanderliegende Löcher einer Zeile
und ein drittes Loch in einer möglichst weit entfernten Zeile.
Aus Gründen der Abwärtskompatibilität wird auch die bisherige
Eckpunkt-Schreibweise mit drei XY-Tupeln unterstützt:
``loch_1_1=(x, y)``
``loch_40_1=(x, y)``
``loch_1_27=(x, y)``
Aufrufsignatur
def plattenkalibrierung_setzen(
referenzloch_1,
referenzloch_2,
referenzloch_3,
platten_z,
standard_r=0.0,
):
Beispiel
dobot.plattenkalibrierung_setzen(
referenzloch_1=(2, 1, -73.8, -311.1),
referenzloch_2=(39, 1, -56.1, 312.2),
referenzloch_3=(22, 26, 310.5, -18.5),
platten_z=-67.2,
standard_r=0.0,
)
Vollständige Definition aus dobot.py
def plattenkalibrierung_setzen(
referenzloch_1,
referenzloch_2,
referenzloch_3,
platten_z,
standard_r=0.0,
):
"""Kalibriert die Platte mit drei beliebigen erreichbaren Löchern.
Neue Schreibweise der Referenzpunkte:
``(spalte, zeile, x, y)``
Beispiel:
``referenzloch_1=(2, 1, -73.8, -311.1)``
Die drei Rasterpositionen dürfen nicht auf einer gemeinsamen Geraden
liegen. Günstig sind zwei weit auseinanderliegende Löcher einer Zeile
und ein drittes Loch in einer möglichst weit entfernten Zeile.
Aus Gründen der Abwärtskompatibilität wird auch die bisherige
Eckpunkt-Schreibweise mit drei XY-Tupeln unterstützt:
``loch_1_1=(x, y)``
``loch_40_1=(x, y)``
``loch_1_27=(x, y)``
"""
global _plattenkalibrierung
# Alte Schreibweise erkennen:
# (x, y), (x, y), (x, y)
alte_schreibweise = all(
hasattr(ref, "__len__") and len(ref) == 2
for ref in (
referenzloch_1,
referenzloch_2,
referenzloch_3,
)
)
if alte_schreibweise:
referenzloch_1 = (
1,
1,
referenzloch_1[0],
referenzloch_1[1],
)
referenzloch_2 = (
PLATTE_SPALTEN,
1,
referenzloch_2[0],
referenzloch_2[1],
)
referenzloch_3 = (
1,
PLATTE_ZEILEN,
referenzloch_3[0],
referenzloch_3[1],
)
a = _referenzloch_lesen(referenzloch_1, "referenzloch_1")
b = _referenzloch_lesen(referenzloch_2, "referenzloch_2")
c = _referenzloch_lesen(referenzloch_3, "referenzloch_3")
spalte_a, zeile_a, x_a, y_a = a
spalte_b, zeile_b, x_b, y_b = b
spalte_c, zeile_c, x_c, y_c = c
ds_b = spalte_b - spalte_a
dz_b = zeile_b - zeile_a
ds_c = spalte_c - spalte_a
dz_c = zeile_c - zeile_a
determinante = ds_b * dz_c - ds_c * dz_b
if abs(determinante) < 1e-9:
raise ValueError(
"Die drei Referenzlöcher sind für eine Kalibrierung "
"ungeeignet: Ihre Rasterpositionen liegen auf einer Geraden."
)
dx_b = x_b - x_a
dx_c = x_c - x_a
dy_b = y_b - y_a
dy_c = y_c - y_a
# Dobot-Vektor für einen Schritt in Spaltenrichtung.
sx = (dx_b * dz_c - dx_c * dz_b) / determinante
sy = (dy_b * dz_c - dy_c * dz_b) / determinante
# Dobot-Vektor für einen Schritt in Zeilenrichtung.
zx = (ds_b * dx_c - ds_c * dx_b) / determinante
zy = (ds_b * dy_c - ds_c * dy_b) / determinante
# Aus dem erreichbaren Referenzloch A wird rechnerisch die Position
# des möglicherweise nicht erreichbaren Lochs (1, 1) bestimmt.
x_1_1 = (
x_a
- (spalte_a - 1.0) * sx
- (zeile_a - 1.0) * zx
)
y_1_1 = (
y_a
- (spalte_a - 1.0) * sy
- (zeile_a - 1.0) * zy
)
_plattenkalibrierung = {
"loch_1_1": (x_1_1, y_1_1),
"spaltenvektor": (sx, sy),
"zeilenvektor": (zx, zy),
"referenzloecher": (a, b, c),
"platten_z": float(platten_z),
"standard_r": float(standard_r),
}
plattenkalibrierung_anzeigen()
Zeigt Referenzlöcher, Ursprung und Rastervektoren an.
Aufrufsignatur
def plattenkalibrierung_anzeigen():
Beispiel
dobot.plattenkalibrierung_anzeigen()
Vollständige Definition aus dobot.py
def plattenkalibrierung_anzeigen():
"""Zeigt Referenzlöcher, Ursprung und Rastervektoren an."""
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
print("Kalibrierung der Lochrasterplatte")
print("---------------------------------")
for nummer, referenz in enumerate(k["referenzloecher"], start=1):
spalte, zeile, x, y = referenz
print(
f"Referenz {nummer}: "
f"Loch ({spalte:g}, {zeile:g}) -> "
f"X={x:.2f}, Y={y:.2f}"
)
x11, y11 = k["loch_1_1"]
print()
print(
"Rechnerisches Loch (1, 1): "
f"X={x11:.2f}, Y={y11:.2f}"
)
print(f"Platten-Z: {k['platten_z']:.2f} mm")
print(f"Standard-R: {k['standard_r']:.2f}°")
print()
print(
"Rastervektor Spalte: "
f"dx={spaltenvektor[0]:.4f}, "
f"dy={spaltenvektor[1]:.4f}"
)
print(
"Rastervektor Zeile: "
f"dx={zeilenvektor[0]:.4f}, "
f"dy={zeilenvektor[1]:.4f}"
)
fahre_zu_loch()
Fährt gelenkoptimiert zu einer Position im Lochraster.
``spalte`` und ``zeile`` beginnen bei 1.
``hoehe`` ist die Höhe in Millimetern über der Plattenoberfläche.
Der sichere Standardwert beträgt 30 mm.
Beispiel:
``fahre_zu_loch(api, 10, 8, hoehe=30)``
Aufrufsignatur
def fahre_zu_loch(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
Beispiel
dobot.queue_starten(api)
dobot.fahre_zu_loch(api, spalte=20, zeile=20, hoehe=30.0)
Vollständige Definition aus dobot.py
def fahre_zu_loch(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
"""Fährt gelenkoptimiert zu einer Position im Lochraster.
``spalte`` und ``zeile`` beginnen bei 1.
``hoehe`` ist die Höhe in Millimetern über der Plattenoberfläche.
Der sichere Standardwert beträgt 30 mm.
Beispiel:
``fahre_zu_loch(api, 10, 8, hoehe=30)``
"""
x, y, z, rotation = _loch_zu_dobot(
spalte=spalte,
zeile=zeile,
hoehe=hoehe,
r=r,
)
fahre_zu(
api,
x,
y,
z,
rotation,
dType.PTPMode.PTPMOVJXYZMode,
)
return x, y, z, rotation
fahre_zu_loch_linear()
Fährt auf einer geraden Bahn zu einer Position im Lochraster.
Der gesamte Weg wird linear ausgeführt. Vor allem bei niedriger Höhe
muss deshalb geprüft werden, ob der Fahrweg frei ist.
Beispiel:
``fahre_zu_loch_linear(api, 10, 8, hoehe=5)``
Aufrufsignatur
def fahre_zu_loch_linear(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
Beispiel
dobot.queue_starten(api)
dobot.fahre_zu_loch_linear(api, spalte=20, zeile=20, hoehe=5.0)
Vollständige Definition aus dobot.py
def fahre_zu_loch_linear(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
"""Fährt auf einer geraden Bahn zu einer Position im Lochraster.
Der gesamte Weg wird linear ausgeführt. Vor allem bei niedriger Höhe
muss deshalb geprüft werden, ob der Fahrweg frei ist.
Beispiel:
``fahre_zu_loch_linear(api, 10, 8, hoehe=5)``
"""
x, y, z, rotation = _loch_zu_dobot(
spalte=spalte,
zeile=zeile,
hoehe=hoehe,
r=r,
)
fahre_zu(
api,
x,
y,
z,
rotation,
dType.PTPMode.PTPMOVLXYZMode,
)
return x, y, z, rotation
lochposition_lesen()
Liest die aktuelle Position im Koordinatensystem der Lochrasterplatte.
Rückgabe:
``(spalte, zeile, hoehe)``
``spalte`` und ``zeile`` können Dezimalstellen enthalten. Ganzzahlige
Werte entsprechen den Mittelpunkten der Rasterlöcher. ``hoehe`` ist die
Höhe in Millimetern über der kalibrierten Plattenoberfläche.
Aufrufsignatur
def lochposition_lesen(api):
Beispiel
spalte, zeile, hoehe = dobot.lochposition_lesen(api)
print(spalte, zeile, hoehe)
Vollständige Definition aus dobot.py
def lochposition_lesen(api):
"""Liest die aktuelle Position im Koordinatensystem der Lochrasterplatte.
Rückgabe:
``(spalte, zeile, hoehe)``
``spalte`` und ``zeile`` können Dezimalstellen enthalten. Ganzzahlige
Werte entsprechen den Mittelpunkten der Rasterlöcher. ``hoehe`` ist die
Höhe in Millimetern über der kalibrierten Plattenoberfläche.
"""
x, y, z, _r = position_lesen(api)
return _dobot_zu_platte(x, y, z)
lochposition_anzeigen()
Liest und zeigt die aktuelle Position auf der Lochrasterplatte an.
Zusätzlich zur genauen Rasterposition wird das nächstgelegene Loch
ausgegeben. Die gelesenen Werte werden auch als Tupel zurückgegeben.
Aufrufsignatur
def lochposition_anzeigen(api):
Beispiel
dobot.lochposition_anzeigen(api)
Vollständige Definition aus dobot.py
def lochposition_anzeigen(api):
"""Liest und zeigt die aktuelle Position auf der Lochrasterplatte an.
Zusätzlich zur genauen Rasterposition wird das nächstgelegene Loch
ausgegeben. Die gelesenen Werte werden auch als Tupel zurückgegeben.
"""
spalte, zeile, hoehe = lochposition_lesen(api)
naechste_spalte = round(spalte)
naechste_zeile = round(zeile)
print("Aktuelle Lochposition:")
print(f" Spalte: {spalte:.3f}")
print(f" Zeile: {zeile:.3f}")
print(f" Höhe: {hoehe:.2f} mm")
print(
" Nächstes Loch: "
f"({naechste_spalte}, {naechste_zeile})"
)
return spalte, zeile, hoehe
aktuelle_plattenposition()
Kompatibler Alias für die Lochpositionsfunktionen.
Mit ``anzeigen=True`` wird ``lochposition_anzeigen(api)`` verwendet.
Mit ``anzeigen=False`` wird ``lochposition_lesen(api)`` verwendet.
Rückgabe:
``(spalte, zeile, hoehe)``
Aufrufsignatur
def aktuelle_plattenposition(api, anzeigen=True):
Beispiel
# Kompatibler Alias:
dobot.aktuelle_plattenposition(api)
# Ohne Bildschirmausgabe:
spalte, zeile, hoehe = dobot.aktuelle_plattenposition(
api,
anzeigen=False,
)
Vollständige Definition aus dobot.py
def aktuelle_plattenposition(api, anzeigen=True):
"""Kompatibler Alias für die Lochpositionsfunktionen.
Mit ``anzeigen=True`` wird ``lochposition_anzeigen(api)`` verwendet.
Mit ``anzeigen=False`` wird ``lochposition_lesen(api)`` verwendet.
Rückgabe:
``(spalte, zeile, hoehe)``
"""
if anzeigen:
return lochposition_anzeigen(api)
return lochposition_lesen(api)
Sauger
sauger_status()
Gibt True zurück, wenn der Sauger eingeschaltet ist.
Aufrufsignatur
def sauger_status(api):
Beispiel
if dobot.sauger_status(api):
print("Sauger ist eingeschaltet.")
else:
print("Sauger ist ausgeschaltet.")
Vollständige Definition aus dobot.py
def sauger_status(api):
"""Gibt True zurück, wenn der Sauger eingeschaltet ist."""
return bool(dType.GetEndEffectorSuctionCup(api)[0])
sauger_aktivieren()
Aktiviert den Sauger.
Aufrufsignatur
def sauger_aktivieren(api, isQueued=1):
Beispiel
# In die Queue einreihen:
dobot.sauger_aktivieren(api)
# Sofort ausführen:
dobot.sauger_aktivieren(api, isQueued=0)
Vollständige Definition aus dobot.py
def sauger_aktivieren(api, isQueued=1):
"""Aktiviert den Sauger."""
return dType.SetEndEffectorSuctionCup(
api,
True,
True,
isQueued=isQueued,
)
sauger_deaktivieren()
Deaktiviert den Sauger.
Aufrufsignatur
def sauger_deaktivieren(api, isQueued=1):
Beispiel
# In die Queue einreihen:
dobot.sauger_deaktivieren(api)
# Sofort ausführen:
dobot.sauger_deaktivieren(api, isQueued=0)
Vollständige Definition aus dobot.py
def sauger_deaktivieren(api, isQueued=1):
"""Deaktiviert den Sauger."""
return dType.SetEndEffectorSuctionCup(
api,
True,
False,
isQueued=isQueued,
)
Alarme
alarme_lesen()
Liest die aktuell gesetzten Alarmnummern des Dobot.
Aufrufsignatur
def alarme_lesen(api):
Beispiel
aktive_alarme = dobot.alarme_lesen(api)
print(aktive_alarme)
Vollständige Definition aus dobot.py
def alarme_lesen(api):
"""Liest die aktuell gesetzten Alarmnummern des Dobot."""
alarmdaten, laenge = dType.GetAlarmsState(api)
aktive_alarme = []
for byte_index, wert in enumerate(alarmdaten[:laenge]):
for bit_index in range(8):
if wert & (1 << bit_index):
alarmnummer = byte_index * 8 + bit_index
aktive_alarme.append(alarmnummer)
return aktive_alarme
alarme_anzeigen()
Liest die aktiven Alarme und zeigt Nummer und Fehlermeldung an.
Aufrufsignatur
def alarme_anzeigen(api):
Beispiel
dobot.alarme_anzeigen(api)
Vollständige Definition aus dobot.py
def alarme_anzeigen(api):
"""Liest die aktiven Alarme und zeigt Nummer und Fehlermeldung an."""
aktive_alarme = alarme_lesen(api)
if not aktive_alarme:
print("Keine Alarme aktiv.")
return
print(f"{len(aktive_alarme)} Alarm(e) aktiv:")
print()
for alarmnummer in aktive_alarme:
meldung = ALARM_MELDUNGEN.get(
alarmnummer,
"Unbekannter Alarm"
)
print(
f" Alarm {alarmnummer:3d} "
f"(0x{alarmnummer:02X}): "
f"{meldung}"
)
alarme_loeschen()
Löscht alle gespeicherten Alarmzustände des Dobot.
Achtung:
Besteht die Ursache weiterhin, wird der Alarm erneut ausgelöst.
Aufrufsignatur
def alarme_loeschen(api):
Beispiel
dobot.alarme_loeschen(api)
dobot.alarme_anzeigen(api)
Vollständige Definition aus dobot.py
def alarme_loeschen(api):
"""Löscht alle gespeicherten Alarmzustände des Dobot.
Achtung:
Besteht die Ursache weiterhin, wird der Alarm erneut ausgelöst.
"""
if api is None:
raise RuntimeError("Der Dobot ist nicht verbunden.")
dType.ClearAllAlarmsState(api)
Alarmnummern und Fehlermeldungen
Die Tabelle entspricht dem Wörterbuch ALARM_MELDUNGEN im hochgeladenen Modul.
| Dezimal | Hex-Code | Fehlermeldung |
|---|---|---|
| 0 | 0x00 | Reset-Alarm |
| 1 | 0x01 | Unbekannter oder ungültiger Befehl |
| 2 | 0x02 | Dateisystemfehler |
| 3 | 0x03 | Kommunikationsfehler zwischen MCU und FPGA |
| 4 | 0x04 | Fehler des Winkelsensors |
| 16 | 0x10 | Zielposition liegt in einer Singularität |
| 17 | 0x11 | Zielposition liegt außerhalb des Arbeitsbereichs |
| 18 | 0x12 | Zielposition überschreitet einen Gelenkgrenzwert |
| 19 | 0x13 | Doppelte oder ungeeignete Punkte bei ARC/JUMP |
| 20 | 0x14 | Ungültige Eingabeparameter für ARC |
| 21 | 0x15 | Ungültige JUMP-Parameter |
| 32 | 0x20 | Bewegungsbahn führt durch eine Singularität |
| 33 | 0x21 | Bewegungsbahn liegt außerhalb des Arbeitsbereichs |
| 34 | 0x22 | Bewegung überschreitet einen Gelenkgrenzwert |
| 48 | 0x30 | Gelenk 1: Geschwindigkeit zu hoch |
| 49 | 0x31 | Gelenk 2: Geschwindigkeit zu hoch |
| 50 | 0x32 | Gelenk 3: Geschwindigkeit zu hoch |
| 51 | 0x33 | Gelenk 4: Geschwindigkeit zu hoch |
| 64 | 0x40 | Gelenk 1: positiver Grenzwert erreicht |
| 65 | 0x41 | Gelenk 1: negativer Grenzwert erreicht |
| 66 | 0x42 | Gelenk 2: positiver Grenzwert erreicht |
| 67 | 0x43 | Gelenk 2: negativer Grenzwert erreicht |
| 68 | 0x44 | Gelenk 3: positiver Grenzwert erreicht |
| 69 | 0x45 | Gelenk 3: negativer Grenzwert erreicht |
| 70 | 0x46 | Gelenk 4: positiver Grenzwert erreicht |
| 71 | 0x47 | Gelenk 4: negativer Grenzwert erreicht |
| 72 | 0x48 | Parallelogramm: positiver Grenzwert erreicht |
| 73 | 0x49 | Parallelogramm: negativer Grenzwert erreicht |
| 80 | 0x50 | Gelenk 1: Schrittverlust erkannt |
| 81 | 0x51 | Gelenk 2: Schrittverlust erkannt |
| 82 | 0x52 | Gelenk 3: Schrittverlust erkannt |
| 83 | 0x53 | Gelenk 4: Schrittverlust erkannt |
Interne Funktionen
_loch_zu_dobot(spalte, zeile, ...) erhält kein api-Objekt.
# Richtig:
x, y, z, r = dobot._loch_zu_dobot(20, 20)
# Falsch:
# dobot._loch_zu_dobot(api, 20, 20)
_referenzloch_lesen() – Prüft und zerlegt ein Referenzloch.
def _referenzloch_lesen(referenz, bezeichnung):
"""Prüft und zerlegt ein Referenzloch.
Neue Schreibweise:
``(spalte, zeile, x, y)``
Für die bisherige Eckpunktkalibrierung wird zusätzlich die alte
Schreibweise ``(x, y)`` unterstützt.
"""
try:
werte = tuple(referenz)
except TypeError as exc:
raise TypeError(
f"{bezeichnung} muss ein Tupel oder eine Liste sein."
) from exc
if len(werte) != 4:
raise ValueError(
f"{bezeichnung} muss als "
"(spalte, zeile, x, y) angegeben werden."
)
spalte, zeile, x, y = map(float, werte)
_plattenkoordinate_pruefen(spalte, zeile)
return spalte, zeile, x, y
_plattenkalibrierung_holen() – Liefert die Kalibrierung oder erzeugt eine verständliche Meldung.
def _plattenkalibrierung_holen():
"""Liefert die Kalibrierung oder erzeugt eine verständliche Meldung."""
if _plattenkalibrierung is None:
raise RuntimeError(
"Die Lochrasterplatte ist noch nicht kalibriert.\n"
"Bitte zuerst plattenkalibrierung_setzen(...) aufrufen."
)
return _plattenkalibrierung
_plattenkoordinate_pruefen() – Prüft, ob die angegebene Position innerhalb des Lochfeldes liegt.
def _plattenkoordinate_pruefen(spalte, zeile):
"""Prüft, ob die angegebene Position innerhalb des Lochfeldes liegt."""
if not 1 <= spalte <= PLATTE_SPALTEN:
raise ValueError(
f"Spalte {spalte} liegt außerhalb der Platte. "
f"Erlaubt sind Werte von 1 bis {PLATTE_SPALTEN}."
)
if not 1 <= zeile <= PLATTE_ZEILEN:
raise ValueError(
f"Zeile {zeile} liegt außerhalb der Platte. "
f"Erlaubt sind Werte von 1 bis {PLATTE_ZEILEN}."
)
_platten_rastervektoren() – Liefert die kalibrierten Dobot-XY-Vektoren eines Rasterabstands.
def _platten_rastervektoren():
"""Liefert die kalibrierten Dobot-XY-Vektoren eines Rasterabstands."""
k = _plattenkalibrierung_holen()
return k["spaltenvektor"], k["zeilenvektor"]
_loch_zu_dobot() – Rechnet Lochrasterkoordinaten in Dobot-Koordinaten um.
def _loch_zu_dobot(spalte, zeile, hoehe=30.0, r=None):
"""Rechnet Lochrasterkoordinaten in Dobot-Koordinaten um."""
spalte = float(spalte)
zeile = float(zeile)
hoehe = float(hoehe)
_plattenkoordinate_pruefen(spalte, zeile)
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
x11, y11 = k["loch_1_1"]
spaltenschritte = spalte - 1.0
zeilenschritte = zeile - 1.0
x = (
x11
+ spaltenschritte * spaltenvektor[0]
+ zeilenschritte * zeilenvektor[0]
)
y = (
y11
+ spaltenschritte * spaltenvektor[1]
+ zeilenschritte * zeilenvektor[1]
)
z = k["platten_z"] + hoehe
rotation = k["standard_r"] if r is None else float(r)
return x, y, z, rotation
_dobot_zu_platte() – Rechnet Dobot-Koordinaten in Lochrasterkoordinaten zurück.
def _dobot_zu_platte(x, y, z):
"""Rechnet Dobot-Koordinaten in Lochrasterkoordinaten zurück."""
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
x11, y11 = k["loch_1_1"]
px = float(x) - x11
py = float(y) - y11
sx, sy = spaltenvektor
zx, zy = zeilenvektor
determinante = sx * zy - sy * zx
if abs(determinante) < 1e-9:
raise RuntimeError(
"Die Plattenkalibrierung ist ungültig. "
"Die beiden Rasterrichtungen sind nicht unabhängig."
)
# Lösung des linearen Gleichungssystems:
#
# [sx zx] [a] = [px]
# [sy zy] [b] [py]
#
# a und b sind die Rasterabstände vom Loch (1, 1).
a = (px * zy - py * zx) / determinante
b = (sx * py - sy * px) / determinante
spalte = a + 1.0
zeile = b + 1.0
hoehe = float(z) - k["platten_z"]
return spalte, zeile, hoehe
Vollständiger Quelltext des hochgeladenen Moduls
Der folgende Quelltext wurde unverändert aus der hochgeladenen Datei dobot.py übernommen.
"""
Hilfsfunktionen zur Steuerung eines Dobot Magician auf Basis von DobotDllType.py.
Version: 1.2.0
Stand: 15.07.2026 - 23:40
Die Datei liegt im Hauptordner ``Dobot_Python``. Die 64-Bit-Version des
Dobot-SDK wird aus dem Unterordner ``sdk64`` geladen.
"""
from pathlib import Path
import ctypes
import os
import platform
import sys
from sdk64 import DobotDllType as dType
VERSION = "1.1.0"
VERSIONSDATUM = "15.07.2026"
def version():
"""Gibt die Versionsinformation dieser Hilfsbibliothek zurück."""
return f"dobot.py Version {VERSION} - Stand {VERSIONSDATUM}"
def com_ports_ermitteln():
"""Gibt die vom Betriebssystem erkannten seriellen Schnittstellen zurück.
Das Ergebnis ist eine Liste aus Paaren:
``[(Portname, Beschreibung), ...]``.
Wenn PySerial installiert ist, werden dessen ausführliche Angaben
verwendet. Unter Windows dient andernfalls die Registrierungsdatenbank
als Fallback.
"""
try:
from serial.tools import list_ports
except ImportError:
list_ports = None
if list_ports is not None:
ports = [
(port.device, port.description or "Keine Beschreibung")
for port in list_ports.comports()
]
return sorted(ports, key=lambda eintrag: eintrag[0].upper())
# Fallback ohne PySerial für Windows.
if os.name == "nt":
try:
import winreg
schluessel = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"HARDWARE\DEVICEMAP\SERIALCOMM",
)
ports = []
index = 0
while True:
try:
_, portname, _ = winreg.EnumValue(schluessel, index)
ports.append((portname, "Serielle Schnittstelle"))
index += 1
except OSError:
break
winreg.CloseKey(schluessel)
return sorted(
set(ports),
key=lambda eintrag: eintrag[0].upper(),
)
except OSError:
return []
return []
def comport_pruefen(comport):
"""Prüft, ob der gewünschte COM-Port vom Betriebssystem erkannt wird.
Ist der Port nicht vorhanden, werden alle erkannten seriellen
Schnittstellen ausgegeben und das Programm mit Fehlercode 1 beendet.
"""
ports = com_ports_ermitteln()
vorhandene_portnamen = {
portname.upper()
for portname, _ in ports
}
if comport.upper() in vorhandene_portnamen:
print(f"Serielle Schnittstelle: {comport} ist vorhanden.")
return
print()
print(f"FEHLER: Die serielle Schnittstelle {comport} ist nicht verfügbar.")
print()
if ports:
print("Vom Betriebssystem erkannte COM-Ports:")
for portname, beschreibung in ports:
print(f" {portname:<8} {beschreibung}")
else:
print("Es wurden keine seriellen Schnittstellen gefunden.")
print()
print("Das Programm wird beendet.")
sys.exit(1)
# ---------------------------------------------------------------------------
# Lochrasterplatte
# ---------------------------------------------------------------------------
PLATTE_SPALTEN = 40
PLATTE_ZEILEN = 27
PLATTE_RASTER_MM = 16.0
# Die Kalibrierung wird mit plattenkalibrierung_setzen(...) eingetragen.
# Verwendet werden drei beliebige erreichbare Referenzlöcher, deren
# Rasterpositionen nicht auf einer gemeinsamen Geraden liegen dürfen.
_plattenkalibrierung = None
def _referenzloch_lesen(referenz, bezeichnung):
"""Prüft und zerlegt ein Referenzloch.
Neue Schreibweise:
``(spalte, zeile, x, y)``
Für die bisherige Eckpunktkalibrierung wird zusätzlich die alte
Schreibweise ``(x, y)`` unterstützt.
"""
try:
werte = tuple(referenz)
except TypeError as exc:
raise TypeError(
f"{bezeichnung} muss ein Tupel oder eine Liste sein."
) from exc
if len(werte) != 4:
raise ValueError(
f"{bezeichnung} muss als "
"(spalte, zeile, x, y) angegeben werden."
)
spalte, zeile, x, y = map(float, werte)
_plattenkoordinate_pruefen(spalte, zeile)
return spalte, zeile, x, y
def plattenkalibrierung_setzen(
referenzloch_1,
referenzloch_2,
referenzloch_3,
platten_z,
standard_r=0.0,
):
"""Kalibriert die Platte mit drei beliebigen erreichbaren Löchern.
Neue Schreibweise der Referenzpunkte:
``(spalte, zeile, x, y)``
Beispiel:
``referenzloch_1=(2, 1, -73.8, -311.1)``
Die drei Rasterpositionen dürfen nicht auf einer gemeinsamen Geraden
liegen. Günstig sind zwei weit auseinanderliegende Löcher einer Zeile
und ein drittes Loch in einer möglichst weit entfernten Zeile.
Aus Gründen der Abwärtskompatibilität wird auch die bisherige
Eckpunkt-Schreibweise mit drei XY-Tupeln unterstützt:
``loch_1_1=(x, y)``
``loch_40_1=(x, y)``
``loch_1_27=(x, y)``
"""
global _plattenkalibrierung
# Alte Schreibweise erkennen:
# (x, y), (x, y), (x, y)
alte_schreibweise = all(
hasattr(ref, "__len__") and len(ref) == 2
for ref in (
referenzloch_1,
referenzloch_2,
referenzloch_3,
)
)
if alte_schreibweise:
referenzloch_1 = (
1,
1,
referenzloch_1[0],
referenzloch_1[1],
)
referenzloch_2 = (
PLATTE_SPALTEN,
1,
referenzloch_2[0],
referenzloch_2[1],
)
referenzloch_3 = (
1,
PLATTE_ZEILEN,
referenzloch_3[0],
referenzloch_3[1],
)
a = _referenzloch_lesen(referenzloch_1, "referenzloch_1")
b = _referenzloch_lesen(referenzloch_2, "referenzloch_2")
c = _referenzloch_lesen(referenzloch_3, "referenzloch_3")
spalte_a, zeile_a, x_a, y_a = a
spalte_b, zeile_b, x_b, y_b = b
spalte_c, zeile_c, x_c, y_c = c
ds_b = spalte_b - spalte_a
dz_b = zeile_b - zeile_a
ds_c = spalte_c - spalte_a
dz_c = zeile_c - zeile_a
determinante = ds_b * dz_c - ds_c * dz_b
if abs(determinante) < 1e-9:
raise ValueError(
"Die drei Referenzlöcher sind für eine Kalibrierung "
"ungeeignet: Ihre Rasterpositionen liegen auf einer Geraden."
)
dx_b = x_b - x_a
dx_c = x_c - x_a
dy_b = y_b - y_a
dy_c = y_c - y_a
# Dobot-Vektor für einen Schritt in Spaltenrichtung.
sx = (dx_b * dz_c - dx_c * dz_b) / determinante
sy = (dy_b * dz_c - dy_c * dz_b) / determinante
# Dobot-Vektor für einen Schritt in Zeilenrichtung.
zx = (ds_b * dx_c - ds_c * dx_b) / determinante
zy = (ds_b * dy_c - ds_c * dy_b) / determinante
# Aus dem erreichbaren Referenzloch A wird rechnerisch die Position
# des möglicherweise nicht erreichbaren Lochs (1, 1) bestimmt.
x_1_1 = (
x_a
- (spalte_a - 1.0) * sx
- (zeile_a - 1.0) * zx
)
y_1_1 = (
y_a
- (spalte_a - 1.0) * sy
- (zeile_a - 1.0) * zy
)
_plattenkalibrierung = {
"loch_1_1": (x_1_1, y_1_1),
"spaltenvektor": (sx, sy),
"zeilenvektor": (zx, zy),
"referenzloecher": (a, b, c),
"platten_z": float(platten_z),
"standard_r": float(standard_r),
}
def _plattenkalibrierung_holen():
"""Liefert die Kalibrierung oder erzeugt eine verständliche Meldung."""
if _plattenkalibrierung is None:
raise RuntimeError(
"Die Lochrasterplatte ist noch nicht kalibriert.\n"
"Bitte zuerst plattenkalibrierung_setzen(...) aufrufen."
)
return _plattenkalibrierung
def _plattenkoordinate_pruefen(spalte, zeile):
"""Prüft, ob die angegebene Position innerhalb des Lochfeldes liegt."""
if not 1 <= spalte <= PLATTE_SPALTEN:
raise ValueError(
f"Spalte {spalte} liegt außerhalb der Platte. "
f"Erlaubt sind Werte von 1 bis {PLATTE_SPALTEN}."
)
if not 1 <= zeile <= PLATTE_ZEILEN:
raise ValueError(
f"Zeile {zeile} liegt außerhalb der Platte. "
f"Erlaubt sind Werte von 1 bis {PLATTE_ZEILEN}."
)
def _platten_rastervektoren():
"""Liefert die kalibrierten Dobot-XY-Vektoren eines Rasterabstands."""
k = _plattenkalibrierung_holen()
return k["spaltenvektor"], k["zeilenvektor"]
def _loch_zu_dobot(spalte, zeile, hoehe=30.0, r=None):
"""Rechnet Lochrasterkoordinaten in Dobot-Koordinaten um."""
spalte = float(spalte)
zeile = float(zeile)
hoehe = float(hoehe)
_plattenkoordinate_pruefen(spalte, zeile)
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
x11, y11 = k["loch_1_1"]
spaltenschritte = spalte - 1.0
zeilenschritte = zeile - 1.0
x = (
x11
+ spaltenschritte * spaltenvektor[0]
+ zeilenschritte * zeilenvektor[0]
)
y = (
y11
+ spaltenschritte * spaltenvektor[1]
+ zeilenschritte * zeilenvektor[1]
)
z = k["platten_z"] + hoehe
rotation = k["standard_r"] if r is None else float(r)
return x, y, z, rotation
def _dobot_zu_platte(x, y, z):
"""Rechnet Dobot-Koordinaten in Lochrasterkoordinaten zurück."""
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
x11, y11 = k["loch_1_1"]
px = float(x) - x11
py = float(y) - y11
sx, sy = spaltenvektor
zx, zy = zeilenvektor
determinante = sx * zy - sy * zx
if abs(determinante) < 1e-9:
raise RuntimeError(
"Die Plattenkalibrierung ist ungültig. "
"Die beiden Rasterrichtungen sind nicht unabhängig."
)
# Lösung des linearen Gleichungssystems:
#
# [sx zx] [a] = [px]
# [sy zy] [b] [py]
#
# a und b sind die Rasterabstände vom Loch (1, 1).
a = (px * zy - py * zx) / determinante
b = (sx * py - sy * px) / determinante
spalte = a + 1.0
zeile = b + 1.0
hoehe = float(z) - k["platten_z"]
return spalte, zeile, hoehe
def plattenkalibrierung_anzeigen():
"""Zeigt Referenzlöcher, Ursprung und Rastervektoren an."""
k = _plattenkalibrierung_holen()
spaltenvektor, zeilenvektor = _platten_rastervektoren()
print("Kalibrierung der Lochrasterplatte")
print("---------------------------------")
for nummer, referenz in enumerate(k["referenzloecher"], start=1):
spalte, zeile, x, y = referenz
print(
f"Referenz {nummer}: "
f"Loch ({spalte:g}, {zeile:g}) -> "
f"X={x:.2f}, Y={y:.2f}"
)
x11, y11 = k["loch_1_1"]
print()
print(
"Rechnerisches Loch (1, 1): "
f"X={x11:.2f}, Y={y11:.2f}"
)
print(f"Platten-Z: {k['platten_z']:.2f} mm")
print(f"Standard-R: {k['standard_r']:.2f}°")
print()
print(
"Rastervektor Spalte: "
f"dx={spaltenvektor[0]:.4f}, "
f"dy={spaltenvektor[1]:.4f}"
)
print(
"Rastervektor Zeile: "
f"dx={zeilenvektor[0]:.4f}, "
f"dy={zeilenvektor[1]:.4f}"
)
# Das von os.add_dll_directory() gelieferte Objekt muss erhalten bleiben,
# solange die DLL verwendet wird.
_dll_suchpfad = None
def init(
comport="COM10",
device_name=(
"Dobot Magician - AG Young Engineers - "
"Martin-Rinckart-Gymnasium"
),
):
"""Lädt die 64-Bit-DLL, verbindet den Dobot und gibt ``api`` zurück."""
global _dll_suchpfad
hauptverzeichnis = Path(__file__).resolve().parent
sdk_verzeichnis = hauptverzeichnis / "sdk64"
dll_datei = sdk_verzeichnis / "DobotDll.dll"
print("Python-Version: ", platform.python_version())
print("Python-Architektur:", platform.architecture()[0])
print("SDK-Verzeichnis: ", sdk_verzeichnis)
print("DLL-Datei: ", dll_datei)
print("DLL vorhanden: ", dll_datei.exists())
# Vor dem Laden der DLL prüfen, ob der gewünschte COM-Port existiert.
comport_pruefen(comport)
if not dll_datei.exists():
raise FileNotFoundError(
"Die Dobot-DLL wurde nicht gefunden:\n"
f"{dll_datei}\n\n"
"Erwartete Ordnerstruktur:\n"
"Dobot_Python\\\n"
"├── dobot.py\n"
"├── sdk64\\\n"
"│ ├── DobotDll.dll\n"
"│ └── DobotDllType.py\n"
"└── projekt01\\\n"
" └── start.py"
)
# Unter Windows können sich in sdk64 weitere benötigte DLLs befinden.
if hasattr(os, "add_dll_directory"):
_dll_suchpfad = os.add_dll_directory(str(sdk_verzeichnis))
# DobotDll.dll über ihren vollständigen Pfad laden.
api = ctypes.CDLL(str(dll_datei))
result = dType.ConnectDobot(api, comport, 115200)
print("ConnectDobot-Ergebnis:", result)
# 0 bedeutet bei der Dobot-API: Verbindung erfolgreich.
if result[0] != 0:
meldungen = {
1: "Dobot wurde nicht gefunden.",
2: "Der COM-Port ist bereits belegt.",
}
meldung = meldungen.get(result[0], "Unbekannter Verbindungsfehler.")
raise ConnectionError(
f"Verbindung über {comport} fehlgeschlagen: {meldung} "
f"(Fehlercode {result[0]})"
)
if device_name:
dType.SetDeviceName(api, device_name)
name = dType.GetDeviceName(api)
seriennummer = dType.GetDeviceSN(api)
print("Gerätename: ", name)
print("Seriennummer: ", seriennummer)
return api
def warten_bis_fertig(api, ziel_index):
"""Wartet, bis ein Queue-Befehl vollständig ausgeführt wurde."""
while dType.GetQueuedCmdCurrentIndex(api)[0] < ziel_index:
dType.dSleep(100)
def queue_starten(api):
"""Stoppt eine laufende Queue, löscht sie und startet sie neu."""
dType.SetQueuedCmdStopExec(api)
dType.SetQueuedCmdClear(api)
dType.SetQueuedCmdStartExec(api)
def queue_stoppen(api):
"""Stoppt die Ausführung der Queue."""
dType.SetQueuedCmdStopExec(api)
def position_lesen(api):
"""Liest die kartesische Position X, Y, Z und R."""
return dType.GetPose(api)[:4]
def position_anzeigen(api):
"""Liest und zeigt die aktuelle kartesische Position an."""
x, y, z, r = position_lesen(api)
print(
f"X={x:.1f} mm, "
f"Y={y:.1f} mm, "
f"Z={z:.1f} mm, "
f"R={r:.1f}°"
)
def fahre_zu(api, x, y, z, r, modus=None):
"""Fährt zu einer Zielposition und wartet auf das Bewegungsende."""
if modus is None:
modus = dType.PTPMode.PTPMOVJXYZMode
ziel_index = dType.SetPTPCmd(
api,
modus,
x,
y,
z,
r,
isQueued=1,
)[0]
warten_bis_fertig(api, ziel_index)
def fahre_zu_loch(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
"""Fährt gelenkoptimiert zu einer Position im Lochraster.
``spalte`` und ``zeile`` beginnen bei 1.
``hoehe`` ist die Höhe in Millimetern über der Plattenoberfläche.
Der sichere Standardwert beträgt 30 mm.
Beispiel:
``fahre_zu_loch(api, 10, 8, hoehe=30)``
"""
x, y, z, rotation = _loch_zu_dobot(
spalte=spalte,
zeile=zeile,
hoehe=hoehe,
r=r,
)
fahre_zu(
api,
x,
y,
z,
rotation,
dType.PTPMode.PTPMOVJXYZMode,
)
return x, y, z, rotation
def fahre_zu_loch_linear(
api,
spalte,
zeile,
hoehe=30.0,
r=None,
):
"""Fährt auf einer geraden Bahn zu einer Position im Lochraster.
Der gesamte Weg wird linear ausgeführt. Vor allem bei niedriger Höhe
muss deshalb geprüft werden, ob der Fahrweg frei ist.
Beispiel:
``fahre_zu_loch_linear(api, 10, 8, hoehe=5)``
"""
x, y, z, rotation = _loch_zu_dobot(
spalte=spalte,
zeile=zeile,
hoehe=hoehe,
r=r,
)
fahre_zu(
api,
x,
y,
z,
rotation,
dType.PTPMode.PTPMOVLXYZMode,
)
return x, y, z, rotation
def lochposition_lesen(api):
"""Liest die aktuelle Position im Koordinatensystem der Lochrasterplatte.
Rückgabe:
``(spalte, zeile, hoehe)``
``spalte`` und ``zeile`` können Dezimalstellen enthalten. Ganzzahlige
Werte entsprechen den Mittelpunkten der Rasterlöcher. ``hoehe`` ist die
Höhe in Millimetern über der kalibrierten Plattenoberfläche.
"""
x, y, z, _r = position_lesen(api)
return _dobot_zu_platte(x, y, z)
def lochposition_anzeigen(api):
"""Liest und zeigt die aktuelle Position auf der Lochrasterplatte an.
Zusätzlich zur genauen Rasterposition wird das nächstgelegene Loch
ausgegeben. Die gelesenen Werte werden auch als Tupel zurückgegeben.
"""
spalte, zeile, hoehe = lochposition_lesen(api)
naechste_spalte = round(spalte)
naechste_zeile = round(zeile)
print("Aktuelle Lochposition:")
print(f" Spalte: {spalte:.3f}")
print(f" Zeile: {zeile:.3f}")
print(f" Höhe: {hoehe:.2f} mm")
print(
" Nächstes Loch: "
f"({naechste_spalte}, {naechste_zeile})"
)
return spalte, zeile, hoehe
def aktuelle_plattenposition(api, anzeigen=True):
"""Kompatibler Alias für die Lochpositionsfunktionen.
Mit ``anzeigen=True`` wird ``lochposition_anzeigen(api)`` verwendet.
Mit ``anzeigen=False`` wird ``lochposition_lesen(api)`` verwendet.
Rückgabe:
``(spalte, zeile, hoehe)``
"""
if anzeigen:
return lochposition_anzeigen(api)
return lochposition_lesen(api)
def home(api):
"""Führt eine HOME-Fahrt aus und wartet auf deren Abschluss."""
print("HOME-Fahrt wird gestartet.")
ziel_index = dType.SetHOMECmd(
api,
0,
isQueued=1,
)[0]
warten_bis_fertig(api, ziel_index)
print("HOME-Fahrt abgeschlossen.")
def test_z(api):
"""Erlaubt das interaktive Verändern der aktuellen Z-Koordinate."""
print()
print("Interaktiver Test der Z-Koordinate")
print("----------------------------------")
while True:
x, y, z, r = position_lesen(api)
print(
f"Position: X={x:.1f}, Y={y:.1f}, "
f"Z={z:.1f}, R={r:.1f}"
)
eingabe = input(
"Neue Z-Koordinate oder 'a' zum Abbrechen: "
).strip()
if eingabe.lower() == "a":
break
# Auch eine Eingabe mit deutschem Dezimalkomma wird akzeptiert.
try:
neue_z = float(eingabe.replace(",", "."))
except ValueError:
print("Ungültige Eingabe. Bitte eine Zahl oder 'a' eingeben.")
continue
fahre_zu(
api,
x,
y,
neue_z,
r,
dType.PTPMode.PTPMOVLXYZMode,
)
print("Erreichte Position:")
position_anzeigen(api)
print("Z-Test beendet.")
def fahre_sofort_zu(api, x, y, z):
"""Sendet eine Bewegung sofort, ohne sie in die Queue einzureihen.
Die aktuelle Werkzeugrotation R wird beibehalten.
Als Bewegungsart wird PTPMOVJXYZMode verwendet.
"""
if api is None:
raise RuntimeError("Der Dobot ist nicht verbunden.")
# Aktuelle Werkzeugrotation beibehalten.
_, _, _, r = position_lesen(api)
return dType.SetPTPCmd(
api,
dType.PTPMode.PTPMOVJXYZMode,
x,
y,
z,
r,
isQueued=0,
)
def sauger_status(api):
"""Gibt True zurück, wenn der Sauger eingeschaltet ist."""
return bool(dType.GetEndEffectorSuctionCup(api)[0])
def sauger_aktivieren(api, isQueued=1):
"""Aktiviert den Sauger."""
return dType.SetEndEffectorSuctionCup(
api,
True,
True,
isQueued=isQueued,
)
def sauger_deaktivieren(api, isQueued=1):
"""Deaktiviert den Sauger."""
return dType.SetEndEffectorSuctionCup(
api,
True,
False,
isQueued=isQueued,
)
def ausfuehren(api):
"""Startet den auf der Webseite beschriebenen Beispielablauf."""
print()
print("Roboterprogramm wird gestartet.")
print("--------------------------------")
queue_starten(api)
try:
print("Aktuelle Position:")
position_anzeigen(api)
test_z(api)
finally:
queue_stoppen(api)
print()
print("Roboterprogramm beendet.")
# ---------------------------------------------------------------------------
# Alarmfunktionen
# ---------------------------------------------------------------------------
ALARM_MELDUNGEN = {
# Allgemeine Fehler
0x00: "Reset-Alarm",
0x01: "Unbekannter oder ungültiger Befehl",
0x02: "Dateisystemfehler",
0x03: "Kommunikationsfehler zwischen MCU und FPGA",
0x04: "Fehler des Winkelsensors",
# Planungsfehler
0x10: "Zielposition liegt in einer Singularität",
0x11: "Zielposition liegt außerhalb des Arbeitsbereichs",
0x12: "Zielposition überschreitet einen Gelenkgrenzwert",
0x13: "Doppelte oder ungeeignete Punkte bei ARC/JUMP",
0x14: "Ungültige Eingabeparameter für ARC",
0x15: "Ungültige JUMP-Parameter",
# Bewegungsfehler
0x20: "Bewegungsbahn führt durch eine Singularität",
0x21: "Bewegungsbahn liegt außerhalb des Arbeitsbereichs",
0x22: "Bewegung überschreitet einen Gelenkgrenzwert",
# Geschwindigkeitsfehler
0x30: "Gelenk 1: Geschwindigkeit zu hoch",
0x31: "Gelenk 2: Geschwindigkeit zu hoch",
0x32: "Gelenk 3: Geschwindigkeit zu hoch",
0x33: "Gelenk 4: Geschwindigkeit zu hoch",
# Grenzwertfehler
0x40: "Gelenk 1: positiver Grenzwert erreicht",
0x41: "Gelenk 1: negativer Grenzwert erreicht",
0x42: "Gelenk 2: positiver Grenzwert erreicht",
0x43: "Gelenk 2: negativer Grenzwert erreicht",
0x44: "Gelenk 3: positiver Grenzwert erreicht",
0x45: "Gelenk 3: negativer Grenzwert erreicht",
0x46: "Gelenk 4: positiver Grenzwert erreicht",
0x47: "Gelenk 4: negativer Grenzwert erreicht",
0x48: "Parallelogramm: positiver Grenzwert erreicht",
0x49: "Parallelogramm: negativer Grenzwert erreicht",
# Schrittverluste
0x50: "Gelenk 1: Schrittverlust erkannt",
0x51: "Gelenk 2: Schrittverlust erkannt",
0x52: "Gelenk 3: Schrittverlust erkannt",
0x53: "Gelenk 4: Schrittverlust erkannt",
}
def alarme_loeschen(api):
"""Löscht alle gespeicherten Alarmzustände des Dobot.
Achtung:
Besteht die Ursache weiterhin, wird der Alarm erneut ausgelöst.
"""
if api is None:
raise RuntimeError("Der Dobot ist nicht verbunden.")
dType.ClearAllAlarmsState(api)
def alarme_lesen(api):
"""Liest die aktuell gesetzten Alarmnummern des Dobot."""
alarmdaten, laenge = dType.GetAlarmsState(api)
aktive_alarme = []
for byte_index, wert in enumerate(alarmdaten[:laenge]):
for bit_index in range(8):
if wert & (1 << bit_index):
alarmnummer = byte_index * 8 + bit_index
aktive_alarme.append(alarmnummer)
return aktive_alarme
def alarme_anzeigen(api):
"""Liest die aktiven Alarme und zeigt Nummer und Fehlermeldung an."""
aktive_alarme = alarme_lesen(api)
if not aktive_alarme:
print("Keine Alarme aktiv.")
return
print(f"{len(aktive_alarme)} Alarm(e) aktiv:")
print()
for alarmnummer in aktive_alarme:
meldung = ALARM_MELDUNGEN.get(
alarmnummer,
"Unbekannter Alarm"
)
print(
f" Alarm {alarmnummer:3d} "
f"(0x{alarmnummer:02X}): "
f"{meldung}"
)
def main():
"""Hinweis beim direkten Start dieser Bibliotheksdatei."""
print(
"Diese Datei wird normalerweise nicht direkt gestartet.\n"
"Bitte 'start.py' ausführen."
)
if __name__ == "__main__":
main()