Automatisierung der Obsternte und -sortierung: Lehrreicher Fall

Geschichte
Einführung
Als hochaktuelles Technologiefeld revolutioniert die Robotik Branchen und treibt Innovationen weltweit voran. Um dem wachsenden Bedarf an Fachkräften in diesem sich rasant entwickelnden Umfeld gerecht zu werden, hat Elephant Robotics eine bahnbrechende Robotik-Bildungslösung für Hochschulen und Universitäten entwickelt. Diese innovative Lösung kombiniert die Simulation eines automatisierten Obstpflückers mit der automatisierten Obstsortierung und -lieferung durch einen komplexen Roboter und bietet Studierenden so ein umfassendes Lernerlebnis in einem der gefragtesten und angesagtesten Technologiebereiche.
In diesem Artikel gehen wir detailliert auf Roboter zur Obsternte und -sortierung ein. Wir beginnen mit einer Einführung in das Kit und seine Einsatzmöglichkeiten und gehen anschließend auf die funktionale Umsetzung des Roboterarms ein.
Obstpflücker-Set

Das Bild zeigt das Obstpflücker-Set, das aus den folgenden Komponenten besteht.

Sie sind bestimmt neugierig, wie dieses Kit funktioniert. Lassen Sie mich die Funktionsweise erläutern. Wie Sie sehen, haben wir zwei Roboterarme, die unterschiedliche Funktionen erfüllen. Der Roboterarm, der dem Obstbaum am nächsten ist, ist der Obstpflückroboter, im Folgenden R1 genannt. Der Arm in der Mitte ist der Sortierroboter, abgekürzt R2. Ihre Bezeichnungen verdeutlichen ihre jeweiligen Aufgaben. R1 ist dafür zuständig, die Früchte vom Baum zu pflücken und auf das Förderband zu legen, während R2 minderwertige Früchte vom Förderband aussortiert.
Schritt:
Ernte: R1 identifiziert die Früchte am Baum mithilfe einer Tiefenkamera und lokalisiert sie anschließend. Die Koordinaten der Früchte werden zur Ernte an R1 übermittelt.
Transport: Durch die Kommissionierung von R1 gelangen die Früchte auf das Förderband. Das Förderband transportiert die Früchte in den von R2 erkennbaren Bereich zur Qualitätsbestimmung der Früchte.
Sortierung: Die Kamera über R2 erkennt die Früchte in ihrem Sichtbereich und ermittelt mithilfe eines Algorithmus deren Qualität. Wird die Frucht als gut eingestuft, wird sie über das Förderband zum Sammelbereich transportiert. Wird sie als schlechte Frucht eingestuft, werden die Koordinaten der schlechten Frucht an R2 übermittelt, der die Frucht greift und in einem bestimmten Bereich ablegt.
Der oben beschriebene Prozess wird kontinuierlich wiederholt:
Ernte->Transport->Sortierung->Ernte->Transport.
Produkt
Roboterarm - mechArm 270 M5Stack
Es handelt sich um einen kompakten sechsachsigen Roboterarm mit zentrumssymmetrischer Struktur (ähnlich einer industriellen Struktur), der im Kern vom M5Stack-Basic gesteuert und von ESP32 unterstützt wird. Der mechArm 270-M5 wiegt 1 kg und kann eine Last von 250 g tragen. Sein Arbeitsradius beträgt 270 mm. Er ist tragbar, klein, aber hochfunktional, einfach zu bedienen und kann sicher mit Menschen zusammenarbeiten.

Fließband
Ein Förderband ist ein mechanisches Gerät zum Transport von Gegenständen. Es besteht üblicherweise aus einem bandförmigen Objekt und einer oder mehreren Rollachsen. Förderbänder können verschiedene Gegenstände wie Pakete, Kisten, Lebensmittel, Mineralien, Baumaterialien und vieles mehr transportieren. Das Funktionsprinzip eines Förderbandes besteht darin, den Gegenstand auf das Band zu legen und ihn anschließend zum Zielort zu bewegen. Förderbänder bestehen typischerweise aus einem Motor, einem Getriebe, einem Band und einer Trägerstruktur. Der Motor liefert die Kraft, das Getriebe überträgt die Kraft auf das Band und versetzt es in Bewegung.

Derzeit sind auf dem Markt verschiedene Arten von Förderbändern erhältlich, die an die Bedürfnisse des Benutzers angepasst werden können, beispielsweise hinsichtlich Länge, Breite und Höhe des Förderbands sowie des Schienenmaterials.
3D-Tiefenkamera
Aufgrund der Vielfalt der Einsatzszenarien erfüllen herkömmliche 2D-Kameras unsere Anforderungen nicht. In unserem Szenario verwenden wir eine Tiefenkamera. Eine Tiefenkamera ist ein Kameratyp, der Tiefeninformationen einer Szene erfassen kann. Sie erfasst nicht nur Farb- und Helligkeitsinformationen einer Szene, sondern erkennt auch Entfernung und Tiefe zwischen Objekten. Tiefenkameras nutzen üblicherweise Infrarot- oder andere Lichtquellen zur Messung, um Tiefeninformationen von Objekten und Szenen zu erfassen.

Die Tiefenkamera kann verschiedene Arten von Informationen erfassen, beispielsweise Tiefenbilder, Farbbilder, Infrarotbilder und Punktwolkenbilder.

Mithilfe der Tiefenkamera können wir die Position der Früchte am Baum sowie ihre Farbinformationen genau ermitteln.
Adaptiver Greifer - Roboterarm-Endeffektor
Der adaptive Greifer ist ein Endeffektor zum Greifen, Halten und Festhalten von Objekten. Er besteht aus zwei beweglichen Klauen, deren Öffnungs- und Schließwinkel sowie Geschwindigkeit über die Steuerung des Roboterarms angepasst werden können.
Projektfunktionen
Zunächst müssen wir die Kompilierumgebung vorbereiten. Dieses Szenario ist in Python programmiert. Daher ist es wichtig, die Umgebung für die Nutzung und das Lernen zu installieren.
numpy == 1 . 24 . 3
opencv-contrib-python== 4.6.0.66
openni== 2 . 3 . 0
pymycobot== 3 . 1 . 2
PyQt 5 == 5 . 15 . 9
PyQt 5 -Qt 5 == 5 . 15 . 2
PyQt 5 -sip== 12 . 12 . 1
pyserial== 3 . 5
Lassen Sie uns zunächst auf die Einführung des Bilderkennungsalgorithmus eingehen. Die Funktionalität des Projekts lässt sich im Wesentlichen in drei Teile unterteilen:
- Algorithmus zur maschinellen Bilderkennung, einschließlich Tiefenerkennungsalgorithmus
- Roboterarmsteuerung und Flugbahnplanung
- Kommunikation und logische Verarbeitung zwischen mehreren Maschinen
Algorithmus zur maschinellen Bilderkennung
Vor der Verwendung der Tiefenkamera ist eine Kamerakalibrierung erforderlich. Hier ist eine .
Kamerakalibrierung:
Unter Kamerakalibrierung versteht man die Bestimmung der internen und externen Parameter einer Kamera durch eine Reihe von Messungen und Berechnungen. Zu den internen Parametern der Kamera gehören Brennweite, Hauptpunktposition und Pixelabstand, während die externen Parameter ihre Position und Ausrichtung im Weltkoordinatensystem umfassen. Der Zweck der Kamerakalibrierung besteht darin, der Kamera die präzise Erfassung und Aufzeichnung von Informationen über Position, Größe und Form von Objekten im Weltkoordinatensystem zu ermöglichen.
Unsere Zielobjekte sind Früchte, die in verschiedenen Farben und Formen von Rot über Orange bis Gelb vorkommen. Um ein präzises und sicheres Greifen der Früchte zu gewährleisten, ist es notwendig, umfassende Informationen über die Früchte, einschließlich ihrer Breite, Dicke und anderer Eigenschaften, zu sammeln, um ein intelligentes Greifen zu ermöglichen.

Betrachten wir die Zielfrüchte. Der auffälligste Unterschied liegt derzeit in ihrer unterschiedlichen Farbe. Wir wählen die Ziele mit roten und orangen Farbtönen aus. Zur Lokalisierung verwenden wir den HSV-Farbraum. Der folgende Code dient zur Erkennung der Zielfrüchte.
Code:
class Detector:
class FetchType(Enum):
FETCH = False
FETCH_ALL = True
"" "
Detection and identification class
" ""
HSV_DIST = {
# "redA" : (np.array([ 0 , 120 , 50 ]), np.array([ 3 , 255 , 255 ])),
# "redB" : (np.array([ 176 , 120 , 50 ]), np.array([ 179 , 255 , 255 ])),
"redA" : (np.array([ 0 , 120 , 50 ]), np.array([ 3 , 255 , 255 ])),
"redB" : (np.array([ 118 , 120 , 50 ]), np.array([ 179 , 255 , 255 ])),
# "orange" : (np.array([ 10 , 120 , 120 ]), np.array([ 15 , 255 , 255 ])),
"orange" : (np.array([ 8 , 150 , 150 ]), np.array([ 20 , 255 , 255 ])),
"gelb" : (np.array([ 28 , 100 , 150 ]), np.array([ 35 , 255 , 255 ])), # alt
# "gelb" : (np.array([ 31 , 246 , 227 ]), np.array([ 35 , 255 , 255 ])), # neu
}
default_hough_params = {
"Methode" : cv 2 .HOUGH_GRADIENT_ALT,
"dp" : 1 , 5 ,
"minDist" : 20 ,
"param2" : 0 , 6 ,
"minRadius" : 15 ,
"maxRadius" : 40 ,
}
def __init__(selbst, Ziel):
self.bucket = ZielBucket()
self.detect_target = Ziel
def get_target(selbst):
return self.detect_target
def set_target(selbst, Ziel):
wenn self.detect_target == Ziel:
zurückkehren
self.detect_target = Ziel
wenn Ziel == "Apfel" :
self.bucket = Zielbucket(adj_tolerance= 25 , expire_time= 0 , 2 )
elif target == "orange" :
self.bucket = ZielBucket()
elif target == "Birne" :
self.bucket = Zielbucket(adj_tolerance= 35 )
def erkennen(selbst, rgb_daten):
wenn self.detect_target == "Apfel" :
self.__detect_apple(rgb_data)
elif self.detect_target == "orange" :
self.__detect_orange(rgb_data)
elif self.detect_target == "Birne" :
self.__detect_pear(rgb_data)
def __detect_apple(selbst, rgb_data):
maskA = Farberkennung(rgb_data, *self.HSV_DIST[ "redA" ])
maskB = Farberkennung(rgb_data, *self.HSV_DIST[ "redB" ])
Maske = MaskeA + MaskeB
kernelA = cv 2 .getStructuringElement(cv 2 .MORPH_RECT, ( 8 , 8 ))
kernelB = cv 2 .getStructuringElement(cv 2 .MORPH_RECT, ( 2 , 2 ))
mask = cv 2 .erode(mask, kernelA)
mask = cv 2 .dilate(mask, kernelA)
Ziele = Kreiserkennung(
Maske, { "minDist" : 15 , "param2" : 0 . 5 ,
"minRadius" : 10 , "maxRadius" : 50 }
)
self.bucket.add_all(Ziele)
self.bucket.update()
def __detect_orange(selbst, rgb_data):
Maske = Farberkennung(rgb_data, *self.HSV_DIST[ "orange" ])
Ziele = Kreiserkennung(
Maske, { "minDist" : 15 , "param2" : 0 . 1 ,
"minRadius" : 7 , "maxRadius" : 30 }
)
self.bucket.add_all(Ziele)
self.bucket.update()
def __detect_pear(selbst, rgb_data):
Maske = Farberkennung(rgb_data, *self.HSV_DIST[ "gelb" ])
kernelA = cv 2 .getStructuringElement(cv 2 .MORPH_RECT, ( 25 , 25 ))
kernelB = cv 2 .getStructuringElement(cv 2 .MORPH_RECT, ( 3 , 3 ))
mask = cv 2 .erode(mask, kernelA)
mask = cv 2 .dilate(mask, kernelA)
Maske = cv 2 .erode(Maske, KernelB)
Ziele = Kreiserkennung(
Maske, { "minDist" : 15 , "param2" : 0 . 1 ,
"minRadius" : 15 , "maxRadius" : 70 }
)
self.bucket.add_all(Ziele)
self.bucket.update()
def fetch(selbst):
returniere self.bucket.fetch()
def fetch_all(selbst):
return self.bucket.fetch_all()
def debug_view(selbst, bgr_data, view_all=True):
wenn alles anzeigen:
Ziele = self.bucket.fetch_all()
anders:
Ziele = self.bucket.fetch()
wenn Ziele nicht Keine sind:
Ziele = [Ziele]
wenn Ziele nicht Keine sind:
für Ziel in Zielen:
x, y, Radius = Ziel["x"], Ziel["y"], Ziel["Radius"]
# Umriss zeichnen
cv2.circle(bgr_data, (x, y), Radius, BGR_GREEN, 2)
# Kreismittelpunkt zeichnen
cv2.circle(bgr_data, (x, y), 1, BGR_RED, -1)

Der erste Schritt besteht darin, die Zielfrüchte präzise zu erfassen, um ihre Koordinaten, Tiefe und weitere relevante Informationen zu erhalten. Wir definieren die zu erfassenden Informationen und speichern sie für die spätere Verwendung und Abfrage.
Code:
class VideoCaptureThread(threading.Thread):
def __init__(self, detector, detect_type = Detector.FetchType.FETCH_ALL.value):
threading.Thread.__init__(self)
self.vp = VideoStreamPipe()
self.detector = detector
self.finished = True
selbst . Kamerakoordinatenliste = []
self.old_real_coord_list = []
self.real_coord_list = []
self.new_color_frame = Keine
selbst . Fruchttyp = Detektor.Ziel erkennen
self.detect_type = Erkennungstyp
self.rgb_show = Keine
selbst . depth_show = Keine
Unser Ziel ist es, die Weltkoordinaten der Früchte zu ermitteln und diese an den Roboterarm zur Ausführung der Greifaktion zu übertragen. Mit der Konvertierung der Tiefenkoordinaten in Weltkoordinaten haben wir bereits die Hälfte unseres Erfolgs erreicht. Abschließend müssen wir die Weltkoordinaten nur noch in das Koordinatensystem des Roboterarms transformieren, um die Greifkoordinaten der Zielfrüchte zu erhalten.
# get world coordinate
def convert_depth_to_world(self, x, y, z):
fx = 454.367
f y = 454.367
cx = 313.847
cy = 239.89
ratio = float (z / 1000 )
world_x = float ((x - cx) * ratio) / fx
Welt_x = Welt_x * 1000
Welt_y = Float ((y - cy) * Verhältnis) / fy
Welt_y = Welt_y * 1000
Welt_z = Float (z)
returniere Welt_x, Welt_y, Welt_z
Um fortzufahren, haben wir die greifbaren Koordinaten des Zielobjekts erfolgreich erfasst und erhalten, die nun an den Roboterarm übermittelt werden können. Unser nächster Schritt ist die Steuerung und Trajektorienplanung des Roboterarms.
Roboterarmsteuerung und Flugbahnplanung
Manchen fällt es schwer, den Roboterarm in die gewünschte Bewegungsbahn zu bringen. Doch keine Sorge. Unser MechArm270 ist mit Pymycobot ausgestattet, einer ausgereiften Roboterarm-Steuerungsbibliothek. Mit nur wenigen Codezeilen lässt sich der Roboterarm steuern.
(Hinweis: pymycobot ist eine Python-Bibliothek zur Steuerung der Bewegung von Roboterarmen. Wir verwenden die neueste Version, pymycobot==3.1.2.)
#Introduce two commonly used control methods
'''
Send the degrees of all joints to robot arm.
angle_list_degrees: a list of degree value(List [ float ] ), length 6
speed: ( int ) 0 ~ 100 ,robotic arm's speed
'''
send _angles([ angle_list_degrees ], speed )
send _angles([10,20,30,45,60,0],100)
'''
Senden Sie die Koordinaten an den Roboterarm
Koordinaten: eine Liste von Koordinaten Wert (Liste [ Float ] ), Länge 6
Geschwindigkeit: ( int ) 0 ~ 100 , Geschwindigkeit des Roboterarms
'''
sende _coords( Koordinaten , Geschwindigkeit )
sende _coords([125,45,78,-120,77,90],50)
Wir können die Flugbahn zur Ausführung entweder im Winkel- oder Koordinatenformat an den Roboterarm übertragen.
Trajektorienplanung für Ernteroboter
Nach der Implementierung der grundlegenden Steuerung entwerfen wir im nächsten Schritt die Trajektorienplanung für den Roboterarm zum Greifen der Früchte. Im Erkennungsalgorithmus haben wir die Weltkoordinaten der Früchte ermittelt. Wir verarbeiten diese Koordinaten und konvertieren sie in das Koordinatensystem des Roboterarms zum gezielten Greifen.
#deal with fruit world coordinates
def target_coords( self ):
coord = self .ma.get_coords()
while len (coord) == 0 :
coord = self .ma.get_coords()
target = self .model_track()
coord[: 3 ] = target.copy()
self .end_coords = coord[: 3 ]
wenn DEBUG == True:
drucken ( "Koordinate: " , Koordinate)
drucken ( "self.end_coords: " , self .end_coords)
self .end_coords = Koordinate
Rückgabekoordinaten
Mit den Zielkoordinaten können wir nun die Flugbahn des Roboterarms planen. Während der Bewegung des Roboterarms müssen wir sicherstellen, dass er nicht mit anderen Strukturen kollidiert oder Früchte umstößt.
Bei der Planung der Flugbahn können wir folgende Aspekte berücksichtigen:
● Ausgangshaltung
● Gewünschte Greifhaltung
● Hindernisvermeidungshaltung
Wir sollten je nach den spezifischen Anforderungen der Szene verschiedene Haltungen einnehmen.
Bahnplanung für Sortierroboter
Das vorherige Thema bezog sich auf den Ernteroboter. Nun befassen wir uns mit der Trajektorienplanung für Sortierroboter. Tatsächlich ist die Trajektorienplanung beider Roboter recht ähnlich. Das Sortierziel sind die auf dem Förderband vorhandenen Ziele. Diese werden mithilfe einer Tiefenkamera aussortiert, die die Koordinaten der verdorbenen Früchte auf dem Förderband erfasst.
Die Steuerung und Bahnplanung sowohl des Ernte- als auch des Sortierroboters wurden bereits besprochen. Nun ist es an der Zeit, einen entscheidenden Aspekt dieser Konstruktion zu beleuchten. Der Kommunikationskanal zwischen den beiden Roboterarmen ist von größter Bedeutung. Wie gewährleisten sie eine effektive Kommunikation, vermeiden Blockaden und arbeiten reibungslos mit dem Förderband zusammen? Darauf gehen wir als Nächstes ein.
Kommunikation und logische Verarbeitung zwischen mehreren Maschinen
Ohne eine umfassende und schlüssige Logik wären diese beiden Roboterarme inzwischen sicherlich aneinandergeraten. Sehen wir uns das Flussdiagramm des gesamten Programms an.

Wenn R2 eine verdorbene Frucht erkennt, wird R1s Roboterarm vorübergehend angehalten. Nachdem R2 seine Sortieraufgabe abgeschlossen hat, sendet er ein Signal an R1, sodass dieser die Erntearbeiten fortsetzen kann.
Buchse
Da Kommunikation unerlässlich ist, ist die Socket-Bibliothek unverzichtbar. Socket ist eine Standardbibliothek, die häufig in der Netzwerkprogrammierung eingesetzt wird. Sie bietet eine Reihe von APIs für die Netzwerkkommunikation und ermöglicht so einen einfachen Datentransfer zwischen verschiedenen Computern. Um das Kommunikationsproblem zwischen R1 und R2 zu lösen, haben wir eine Lösung entwickelt, bei der ein Server und ein Client eingerichtet werden.

Nachfolgend finden Sie den relevanten Code und die Initialisierungsinformationen zur Servereinrichtung.
Code:
class TcpServer(threading. Thread ):
def __init__( selbst , Serveradresse) -> Keine :
Threading. Thread .__init__( selbst )
selbst .s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
selbst .s.bind(Serveradresse)
print( "Serverbindung erfolgreich!" )
selbst .s.listen( 1 )
selbst .connected_obj = Keine
self.good_fruit_str = "Apfel"
selbst .bad_fruit_str = "orange"
selbst .invalid_fruit_str = "keine"
selbst .Ziel = selbst .ungültige_Frucht_Str
self.target_copy = selbst .target
self.start_move = Falsch
Hier ist der Kunde.
Code:
class TcpClient(threading.Thread):
def __init__( self , host, port , max_fruit = 8 , recv_interval = 0.1 , recv_timeout = 30 ):
threading.Thread.__init__( self )
self .good_fruit_str = "apple"
self .bad_fruit_str = "orange"
self .invalid_fruit_str = "none"
selbst .host = Host
selbst. Port = Port
# Initialisieren des TCP-Socket-Objekts
# Angabe der Verwendung der IPv4-Adressfamilie
# bezeichnet die Verwendung des TCP-Stream-Übertragungsprotokolls
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.connect((selbst.host, selbst.port))
selbst.aktuell_extrahiert = 0
self.max_fruit = max_fruit
self.recv_interval = Empfangsintervall
self.recv_timeout = Empfangs-Timeout
self.response_copy = self.invalid_fruit_str
self.action_ready = Wahr
Nachdem Server und Client eingerichtet wurden, können wir R1 und R2 die Kommunikation ermöglichen, genau wie wir es über Textnachrichten tun.
R1: „Ich bin gerade dabei, Früchte zu ernten.“
R2: "Nachricht empfangen, Ende."\
Technische Punkte
Tatsächlich ist in diesem Projekt die effektive Kommunikation und logische Verarbeitung zwischen mehreren Roboterarmen von größter Bedeutung. Es gibt verschiedene Möglichkeiten, die Kommunikation zwischen zwei Roboterarmen herzustellen, z. B. physische Kommunikation über serielle Schnittstellen , Ethernet-Kommunikation über LAN , Bluetooth-Kommunikation usw.
In unserem Projekt nutzen wir die Ethernet-Kommunikation über das bestehende TCP/IP -Protokoll und implementieren diese über die Socket-Bibliothek in Python . Wie wir alle wissen, ist ein solides Fundament beim Hausbau unerlässlich; ebenso ist die Einrichtung eines Rahmens zu Beginn eines Projekts entscheidend. Darüber hinaus ist es unerlässlich, die Prinzipien der Roboterarmsteuerung zu verstehen. Es ist notwendig zu lernen, wie man das Zielobjekt in Weltkoordinaten umwandelt und es anschließend in die Zielkoordinaten im Referenzrahmen des Roboterarms transformiert.
Zusammenfassung
Der Einsatz dieser Obsternte- und Sortierroboter hilft Schülern nicht nur, die Prinzipien der Mechanik und elektronischen Steuerungstechnik besser zu verstehen, sondern fördert auch ihr Interesse und ihre Leidenschaft für wissenschaftliche Technologie. Er bietet die Möglichkeit zur praktischen Ausbildung und zum wettbewerbsorientierten Denken.
Das Erlernen von Roboterarmen erfordert die praktische Anwendung. Diese Anwendung bietet Schülern die Möglichkeit, ihr Verständnis und Wissen über Roboterarme durch die tatsächliche Anwendung zu vertiefen. Darüber hinaus unterstützt diese Anwendung Schüler beim Erlernen und Beherrschen von Technologien wie Bewegungssteuerung, visueller Erkennung und Objektgreifen und trägt so zu einem besseren Verständnis der relevanten Kenntnisse und Fähigkeiten von Roboterarmen bei.
Darüber hinaus kann diese Anwendung Schülern helfen, ihre Teamfähigkeit, ihr innovatives Denken und ihr Wettbewerbsdenken zu entwickeln und so eine solide Grundlage für ihre zukünftige berufliche Entwicklung zu legen. Durch die Teilnahme an Roboterwettbewerben, Technologieausstellungen und anderen Aktivitäten können Schüler ihr Wettbewerbsniveau und ihre Innovationsfähigkeit kontinuierlich verbessern und sind so besser für zukünftige gesellschaftliche Entwicklungen und technologische Veränderungen gerüstet.