Legoteil Zählmaschine 2016

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
Legoteilzählmaschine im Überblick


Aufgabenstellung

Die vorhandene Legoteilzählmaschine (siehe Legoteilzählmaschine_2015 ) soll Soft- und Hardwaremäßig modifiziert werden, um weitere Anforderungen zu erfüllen.


Liste der offenen Punkten vom letzten Semester

  • Neues 24 V Netzteil, 24V/10A Netzteil gegen 24V/20A austauschen, um Überlast beim Lauf aller Motoren zu verhindern. Neues Netzteil ist vorhanden.
  • Software zur Bildverarbeitung (BV), Die Einbindung einer geeigneten Software zur Teileerkennung steht noch aus. Kommunikation mit Systems sowie Ansteuerung ist fertig. Bildverarbeitung mit 'String' als Output.
  • Bauteileliste in Excel, Die Excel Liste, welche alle Teile eines Baukasten erhält, muss mit dem Ergebnis der BV abgeglichen werden. Erkannte Teile in mindestens zwei möglichen Ausrichtungen müssen erkannt und in der Liste vermerkt werden.
  • Servo für Teilesortierung, Identisches Servo wie bisher gegenüberliegend montieren, um drei Sortierfächer (Erkannt/Nicht Erkannt/Fremdteil) zu sortieren, Konstruktion, Bestellung wurde an Fr. König geleitet.
    • Dieser Punkt wird in diesem Projekt nicht bearbeitet.
  • Zeit bis zur Servoöffnung, Das Delay zwsichen Befehl und öffnen der Servos muss ermittelt und je nach Geschwindigkeit angepasst werden. Es ist problemlos möglich, einen weiteren 'String' - Befehl als Buchstaben für die Verzögerungszeit einzubinden, String auslesen und verarbeiten muss in Arduino Software geschehen.
  • Kabelkanal montieren, Kabelkanal zur ordentlichen Verlegung der Leitungen an den Förderbändern montieren
  • Kamerahalterung, Konstruktion einer Kamerahalterung für die Logitech C920 WebCam Vorschlag: Brücke über Förderband.
  • Bildverarbeitungskasten, Für die Bildverarbeitung muss eine der gebauten Bildverarbeitungsbox-Prototypen ausgewählt und montiert werden. Hierfür wurden Lichttests durchgeführt. Ergebnisse sind protokolliert SVN Link zu den Lichttests, Prototypen der Lichtboxen vorhanden.
  • Teilezentrierung, Die provisorisch aus Holz gefertigte Teilezentrierung soll in fixierbarer Variante aus Aluminium gefertigt werden. Ergebnisse zu den Tests der Zentrierungsmethoden sind in diesem SVN Ordner protokolliert, Prototypen der gewählten Teilezentrierung ist vor der Bildverarbeitungsbox verbaut.
  • Idee Software: Flexible Bildverarbeitungssoftware, Durch definieren der Schnittstellen könnte in der GUI eine Einbindung frei wählbarer BV Software realisiert werden. Optimal geeignet um kleine Projekte dann an der Legoteilzaehlmaschine zu testen.
    • Wird in diesem Projekt nicht bearbeitet.

Aufgabenteilung inkl. weiteren Aufgaben aus dem Pflichtenheft

Es wurden für jede Aufgabe ein oder mehrere Verantwortliche(n) aus der Gruppe festgelegt. Jede Person ist dann dafür zuständig, dass diese Aufgaben erfüllt werden. Bei Bedarf steht dieser Person die ganze Gruppe zur Hilfe zur Verfügung.

  • Kevin Penner:
    • Separierung und Zentrierung der Lego-Teile
    • Pflichtenheft Dokumentation mit Software "DOORS"
    • Bildverarbeitung/Legoteil-Erkennung
  • Simon Hanemann:
    • GUI Programmierung
    • Dokumentation in HSHL-Wiki
    • Video-Anleitung
  • Adam Frankhauser:
    • Elektr. Sicherheit
    • Bildverarbeitungs-Box Fixierung/Stabilisierung
    • Video-Anleitung
  • Niklas Lingenauber:
    • Datenbankanbindung IN/OUT und Listen
    • Projektplanung
    • QM-Tests (nach Rücksprache mit Prof. Schneider auf das nächste Semester verlegt)
    • Bildverarbeitung/Legoteil-Erkennung
  • Sergej Krause:
    • Teach-IN Funktion
    • Datenbankanbindung IN/OUT und Listen
    • Separierung und Zentrierung der Lego-Teile
  • Christo Tsibadze:
    • Projektplanung
    • Bildverarbeitung/Legoteil-Erkennung
    • Visualisierung von Prozessen mit "Visio"

Projektplan

Abbildung 2: Aufgabenstellung und Projektplan

Meilensteine für das 6. Semester mit Deadlines:

  • 06.06.2016: Hardware und Software Inbetriebnahme
    • Inbetriebnahme "Erprobung von Sensoren und Aktoren"
    • Automatische Sortierung von mind. 30 Teilen
    • Sortierung: Erkannt und Nicht-Erkannt
    • Bildverarbeitung in "Echtzeit" am PC
    • Ausgabe der Inventurliste
  • 24.06.2016: Algorithmen:
    • Optimierung der Bildverarbeitung
    • Automatische vollständige Sortierung: NXT-Basis + Erweiterung
    • Planung und Beschaffung der Hardware-Änderung (Auswerfer) für die nächste Projektphase
  • 14.11.2016: wird noch festgelegt, entweder Auswerfer- oder Separierung vor der BV Optimierung
  • 16.01.2016: Algorithmen Optimierung, Teach-In XT/EV3 und Report












Realisierung

GUI

"UNTER BEARBEITUNG"

Bildverarbeitung/Legoteil-Erkennung

Bildverarbeitung_Main_Funktion

Abbildung 2: Bildverarbeitung_Mainfunktion

Legoteilerkennung wird mit Hilfe von einer Main-Funktion (Bildverarbeitung) und weiteren kleineren Funktionen realisiert. In der Main-Funktion werden einmalig diverse Parameter und Variablen angelegt und initialisiert. Anschließend wird in der äußeren Schleife folgendes wiederholt durchgeführt bis die "Escape"-Taste getätigt wird:

  • Bild bzw. Frame wird von der Kamera eingelesen
  • Ränder werden weggeschnitten
  • Mit Gauß-Filter gefiltert
  • Bild wird in Rot-, Grün- und Blau-Anteilen separiert
  • Anschließend werden nur die Pixeln aus dem Bild in verschiedenen Binär-Bildern kopiert, die der Lego-Farben entsprechen --> Vorsegmentierung


Bei einigen vorsegmentierten Binär-Farbbildern z.B. bei einem Roten Legoteil mit Bohrungen, wird der Schatten in den Bohrungen als schwarze Pixeln bzw. "schwarzes Teil" erkannt. Dies bekommen wir mit folgender Algorithmus entfernt:

  • Mit einer If-Abfrage wird festgestellt ob nach der Vorsegmentierung ein Binär-Bild für schwarzes Teil und mind. eins für ein Teil in anderer Farbe erkannt wurde.
  • Falls Ja: werden die Binärbilder addiert, um wenige Pixel dilatiert (vergrößert), und anschließend geprüft ob das neue Bild nur einen zusammenhängenden Teil ergibt oder nicht:
    • Falls Ja: dann wird das schwarze Binär-Bild verworfen, da es sich dort nur um Schatten handelt.
    • Falls nicht: dann handelt es sich um zwei verschiedene Lego-Teile.
  • Um zu verhindern, dass die nicht optimal ausgeleuchtete Bereiche z.B. an einem grauen Legoteil als Dunkelgrau erkannt wird, wird folgendes durchgeführt:
    • If-Abfrage ob es z.B. ein graues und ein dunkelgraues Binärbild erkannt wurde:
      • Falls Ja: Summe der beiden bilden
      • Prüfen ob die Summe nur einen Zusammenhängenden Teil ergibt, falls Ja: dann handelt es sich nur um einen (helleren) Teil, falls nicht --> zwei verschiedene Teile.
    • Falls zwei verschiedene Teile erkannt wurden inkl. Schatten, dann wird durch Kombination von erst Summe und dann Differenz der Schatten herausgefiltert.


Segmentierung:

  • imclearborder: entfernt Rausch-Pixel an den Rändern
  • bwareaopen: entfernt Segmente deren Pixelanzahl unter einem vorgegebenen Wert sind. (Salt&Pepper, Rauschunterdrückung)
  • Bwlabel: die Summe aller Binärbilder werden Separiert und mit einem "Label" versehen.


Merkmalsextraktion:

  • Merkmalsberechnung_3:
    • Umfang, Fläche, Schwerpunkt, max. "Radius", min. "Radius"


Nachdem die Pixelfläche vom erkannten Teil berechnet wurde, wird solange iteriert, bis der Pixelflächenschwerpunkt in einem mittleren Kamerabild-Bereich befindet. Erst dann wird das Teil als Erkannt notiert. Da das mittlere Bereich im Kamerabild nicht nur aus einer Pixelspalte besteht, wurde noch ein weiteres Kriterium abgefragt, um das selbe Teil nicht doppelt zu zählen:

  • die Höhe (Y-Wert) von der Pixelflächenschwerpunkt der zuletzt erkannten Teil gespeichert und mit dem nächst erkannten Teil vergliechen, falls bestimmter Differenz überschritten wird --> dann handelt es sich um ein anderes Teil. Falls nicht --> das selbe Teil, wird nicht gezählt.

Der Kamerabildbereich wurde nicht als eine Pixelspalte mit der Breite von nur einem Pixel gewählt, da dadurch es schwieriger wurde, einen Pixelflächenschwerpunkt von einem erkannten Teil genau in dieser Pixelspalte zu erwischen.
Falls ein Teil erkannt (nicht doppelt):

  • dann wird die Binärbild-Matrix an die Funktion "FARBERKENNUNG_V2" übergegeben, wo es zur einer nächstegelegener Legofarbe zugeordnet wird.
  • Arduino wird signalisiert, dass in diesen Zeitpunkt ein Teil erkannt wurde, anhand der konstanten Bandgeschwindigkeit wird dann der Auswerfer angesteuert.
  • Funktion imfindcircles: ermittelt Kreise als Merkmal im Binärbild.


Anschließend werden die ermittelten Merkmale mit Datenbank abgeglichen. Bei Übereinstimmung mit vordefinierten Toleranzen wird ein eindeutiges ID in einer Liste gespeichert.
Falls die "Escape"-Taste betätigt wird:

  • dann werden wird die Schleife unterbrochen
  • Arduino wird signalisiert um Laufbänder zu stoppen
  • eine Liste der erkannten und fehlenden Teile wird als Excel automatisch geöffnet.


Quellcode Matlab:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                         %                                                                      
% Funktion        : Automatischer Zählprozess von Legoteilen mittels      %
%                   Bildverarbeitung                                      %
%                                                                         %
%                                                                         %
% Eingabe         : cam: konfiguriertes Kameraobjekt                      %
%                   conn_db: Verbidnung zur Datenbank                     %
%                   serial: Verbindung zur seriellen Schnittstelle        %
%                                                                         %
%                                                                         %
% Ausgabe         : Teileliste: Cell Array mit den gezählten ID's während %
%                   des automatischen Zählprozesses                       %
%                                                                         %
%                                                                         %
% Autor           : Kevin Penner & Christo Tsibadze                       %
%                                                                         %
% Implementation  : MATLAB R2015a                                         %
%                                                                         %
% Bemerkung       : Grober Ablauf:                                        %
%                   1) Binasisierung & Bereinigung                        %
%                   2) Merkmalsextraktion (Fläche, Farbe, Umfang etc.)    %
%                   3) Abgleich der Merkmale mit einer Datenbank          %
%                                                                         %
% Änderungsdatum  : 17.06.2016                                            %
%                                                                         %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [Teileliste] = AutomatischesZaehlen(cam, conn_db, serial)

%% Parameter & Konstanten %%

%Bildgröße bestimmen
OrgImage = snapshot(cam);
[height,width,~] = size(OrgImage);

%Mindestgröße (in Pixeln) eines Legoteils
Ueberschneidung = 100;

%Bereinigungswert
Salt_Pepper = 300;

%Strukturelement für Bereinigung von Schatten
SE = strel('square',2);

%Sigma für Gaussfilter
sigma = 1;

%Bildminimierung durch Abschneiden von Bildrändern mit:
CropX = 1;
CropY = 30;

%Legoteil wird nur im folgenden Bildbereich gezählt (x-Achse):
BildmitteOffsetAnfang=20;
BildmitteOffsetEnde=150;
BildmitteAnfang = round(width/2)-BildmitteOffsetAnfang;
BildmitteEnde = round(width/2)+BildmitteOffsetEnde;

%Variable zur Speicherung des letzten (im letzten Frame) Schwerpunktes (nur
%y-Koordinate) und Toleranz für Schwerpunktsvergleich (damit ein Legoteil
%nur einmal gezählt wird)
%abs(Schwerpunkt_Y - SPY_MEM) >= 5 --> Dieses Legoteil wurde noch nicht gezählt
%abs(Schwerpunkt_Y - SPY_MEM) < 5 --> Dieses Legoteil wurde schon gezählt
SPY_MEM = 0;
SPY_DIFF = 5;

%Offset für die Helligkeit des Bildes (für Farberkennung)
FarbOffset = 1.2;

%Maximale und Minimale Größe eines gesuchten Kreises (Loch) festlegen.
%Und die Kreisförmigkeit
RadiusMin = 7;
RadiusMax = 15;
Circle_Sensitivity = 0.84;

%Anlegen eines Cell-Arrays zum Speichern der ID's
Teileliste = {0};
TeilelisteIndex = 1;

%Index für erkannte Teile
ErkannteTeileIndex = 0;

%Merkmalsspeicher für den Teach-In Prozess (Für das Füllen der Datenbank)
%Parameter = zeros(10,4);
%b = 1;

%% Algorirhmus %%

%Fenster für das Anzeigen von binasisierten Legoteilen
figure('name', 'Esc-Taste zum Beenden');

 while(true)
%% Bildvorverarbeitung %%
%Einlesen des Frames
OrgImage = snapshot(cam);

%Ränder abschneiden
Crop_Image = OrgImage(CropY:(height-CropY), CropX:(width-CropX),:); 

%Gauss filtern
Im_R = imgaussfilt(Crop_Image(:,:,1),sigma);
Im_G = imgaussfilt(Crop_Image(:,:,2),sigma);
Im_B = imgaussfilt(Crop_Image(:,:,3),sigma);

%% Binarisierung %%

%Binärbild für gelbe Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,1) = Im_R>255*0.9 & Im_G>255*0.68 & Im_B<255*0.05;
BW_FARBE(:,:,1) = bwareaopen(BW_FARBE(:,:,1), Salt_Pepper);

%Binärbild für weiße Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,2) = Im_R>255*0.9 & Im_G>255*0.9 & Im_B>255*0.9; 
BW_FARBE(:,:,2) = bwareaopen(BW_FARBE(:,:,2), Salt_Pepper);

%Binärbild für beige Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,3) = Im_R>255*0.65 & Im_G>255*0.62 & Im_G<255*0.95 & Im_B>255*0.21 & Im_B<255*0.65; 
BW_FARBE(:,:,3) = bwareaopen(BW_FARBE(:,:,3), Salt_Pepper);

%Binärbild für rote Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,4) = Im_R>255*0.7 & Im_R<255*0.96 & Im_G<255*0.1 & Im_B<255*0.1; 
BW_FARBE(:,:,4) = bwareaopen(BW_FARBE(:,:,4), Salt_Pepper);

%Binärbild für schwarze Legoteile (inklusive Bereinigung von Artefakten)
%Noch verbesserungswürdig (z.B. anderer Farbraum)
BW_FARBE(:,:,5) = Im_R<255*0.2 & Im_G<255*0.2 & Im_B<255*0.2 & Im_G>255*0.015; 
BW_FARBE(:,:,5) = bwareaopen(BW_FARBE(:,:,5), Salt_Pepper);

%Binärbild für blaue Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,6) = Im_R<255*0.05 & Im_G<255*0.52 & Im_B > 255*0.6 & Im_B < 255*0.9; 
BW_FARBE(:,:,6) = bwareaopen(BW_FARBE(:,:,6), Salt_Pepper);

%Binärbild für grüne Legoteile (inklusive Bereinigung von Artefakten)
%Noch verbesserungswürdig (z.B. anderer Farbraum)
BW_FARBE(:,:,7) = Im_R<255*0.2 & Im_B<255*0.33 & Im_G>255*0.3;
BW_FARBE(:,:,7) = bwareaopen(BW_FARBE(:,:,7), Salt_Pepper);

%Binärbild für hellgraue Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,8) = Im_R<255*0.77 & Im_G<255*0.85 & Im_B<255*0.79 & Im_R>255*0.5 & Im_G>255*0.6 & Im_B>255*0.54; %HELLGRAU
BW_FARBE(:,:,8) = bwareaopen(BW_FARBE(:,:,8), Salt_Pepper);

%Binärbild für dunkelgraue Legoteile (inklusive Bereinigung von Artefakten)
BW_FARBE(:,:,9) = Im_R<255*0.55 & Im_G<255*0.6 & Im_B<255*0.56 & Im_R>255*0.3 & Im_G>255*0.29 & Im_B>255*0.25; %DUNKELGRAU
BW_FARBE(:,:,9) = bwareaopen(BW_FARBE(:,:,9), Salt_Pepper);

%% Bereinigung des Binärbildes - schwarze Schatten löschen (z.B. Löcher) %%

BW_BLACK = 0;
%Alle Binärbilder (von allen Farben) in das schwarze Binärbild addieren
%--> Legoteile und deren Löcher bilden eine Fläche
for i=1:9
    if i~=5
        BW_BLACK = BW_BLACK+BW_FARBE(:,:,i); 
    end
end

%Löcher (schwarze Pixel auf weißem Hintergrund) füllen
BW_BLACK = imfill(BW_BLACK, 'holes');

%Unbereinigtes schwarzes Binärbild - erzeugtes Bild = Bereinigtes schwarzes
%Binärbild
BW_FARBE(:,:,5) = uint8(BW_FARBE(:,:,5) - BW_BLACK(:,:));

%% Labelbild erstellen %%

BW_IMAGE = 0;
%Alle Binärbilder aufaddieren und dannach Bereinigen bzw. Legoteil und
%deren Schattierungen "verschmelzen"
for i=1:9
    if((sum(sum(BW_FARBE(:,:,i))))<Ueberschneidung)
        BW_FARBE(:,:,i)=0;
    else
        BW_FARBE(:,:,i) = imdilate(BW_FARBE(:,:,i),SE);
        BW_IMAGE = BW_IMAGE + BW_FARBE(:,:,i);
    end
end
%Binärbild bereinigen (Artefakte und Bildrand)
BW_IMAGE = imclearborder(BW_IMAGE);
BW_IMAGE = bwareaopen(BW_IMAGE, Salt_Pepper);

%Binärbild labeln
[BW_IMAGE, numObjects] = bwlabel(BW_IMAGE);


%% Merkmalsextraktion der einzelnen Objekte im Labelbild und Vergleich mit Datenbank %%

for i=1:numObjects
    
    %Berechnen von Umfang, Fläche und Schwerpunkt in Pixeleinheiten
    %1.Möglichkeit: Löcher werden gefüllt
    %[Umfang, Flaeche, Schwerpunkt, EntfernungPunkteMaxMin] = Merkmalsberechnung_3(imfill(ismember(BW_IMAGE,i),'holes'));
    %2.Möglichkeit: Binärbild so belassen (Löcher werden nicht gefüllt)
    [Umfang, Flaeche, Schwerpunkt, EntfernungPunkteMaxMin] = Merkmalsberechnung_3(ismember(BW_IMAGE,i));
    
    %Überprüfung, ob Objekt miitig in der Kamerabox liegt und Überprüfung, ob Legoteil schon gezählt wurde
    if Schwerpunkt(1)>= BildmitteAnfang && Schwerpunkt(1)<= BildmitteEnde && abs(Schwerpunkt(2)-SPY_MEM)>SPY_DIFF 
        
        %Y-Koordinate des Schwerpunktes speichern (für spätere Überprüfung,
        %ob ein Teil schon gezählt wurde)
        SPY_MEM = Schwerpunkt(2);
        
        %Farberkennung des Legoteils
        [FARBE] = FARBERKENNUNG_V2( Crop_Image*FarbOffset, ismember(BW_IMAGE,i));
        
        %Kreise/Löcher im Legoteil erkennen und zählen (dunkle Kreise auf hellem
        %Hintergrund)
        [centers, radius] = imfindcircles(ismember(BW_IMAGE,i),[RadiusMin RadiusMax],'ObjectPolarity','dark','Sensitivity',Circle_Sensitivity);
        AnzahlKreise = size(centers);
        
%% Nur für Teach-In bzw. Datenbankfüllung %%
%         Parameter(b,1) = Umfang;
%         Parameter(b,2) = Flaeche;
%         Parameter(b,3) = EntfernungPunkteMaxMin(1);
%         Parameter(b,4) = EntfernungPunkteMaxMin(2);
%         
%         if b==11
%            b=1;
%            sprintf('Umfang = %d  Flaeche = %d  MaxSchwer = %d  MinSchwer = %d',mean(Parameter(:,1)),mean(Parameter(:,2)),mean(Parameter(:,3)),mean(Parameter(:,4)))
%            sprintf('Umfang = %d  Flaeche = %d  MaxSchwer = %d  MinSchwer = %d',median(Parameter(:,1)),median(Parameter(:,2)),median(Parameter(:,3)),median(Parameter(:,4)))
%         end
%         b = b+1;
%%
        %Merkmale mit Datenbank abgleichen
        ID = Datenbankabgleich(conn_db, Flaeche, Umfang, EntfernungPunkteMaxMin(1), EntfernungPunkteMaxMin(2), AnzahlKreise(1), FARBE);
        
        %Sollten mehrere ID's zutreffen --> Nicht erkannt (auf 0 setzten)
        if length(ID) > 1
           ID = {0}; 
        end
        
        %ID in Cell-Array speichern
        Teileliste(TeilelisteIndex) = ID;
        TeilelisteIndex = TeilelisteIndex + 1;
        
        %Sollte das Legoteil erkannt sein --> Sortierung mit Servo
        if cell2mat(ID) ~= 0
            serialstring = ['Z1', num2str(1)];
            fprintf(serial,'%s',serialstring); 
        end
        
        %% Ausgabe 
        
        imshow(ismember(BW_IMAGE,i));
        centersStrong = centers(1:AnzahlKreise(1),:);
        radiiStrong = radius(1:AnzahlKreise(1));
        viscircles(centersStrong, radiiStrong,'EdgeColor','r');
        
        if cell2mat(ID) ~= 0
            title(['Lego-ID: ', num2str(cell2mat(ID))]);
            ErkannteTeileIndex = ErkannteTeileIndex+1;
        else
            title(['Lego-ID: nicht erkannt']);
        end
        dim = [0 0 0.3 0.3];
        str = {['Teile erkannt: ', num2str(ErkannteTeileIndex)],['Teile nicht erkannt: ',num2str(TeilelisteIndex-1-ErkannteTeileIndex)]};
        annotation('textbox',dim,'String',str,'FitBoxToText','on','Color',[0 0 0],'BackgroundColor',[1 1 1]);
        
        drawnow;
        hold on;
        
        
    end
end
    % Beendigung des Programms mit Ecp
    val = double(get(figure(1), 'CurrentCharacter'));
    if(val==27)
       break; 
    end

 end
 
%Fenster schließen
close(figure(1));
end


Farberkennung

Abbildung 3: Farberkennung


Die Funktion "FARBERKENNUNG_V2" bekommt als Übergabewerte zwei Matrizen:

  • Original-Frame (Farbbild) und Binärbild vom erkannten Teil
  • mit Hilfe der Übergabeparametern wird eine 3D-Farbmaske erstellt (3D --> RGB).
    • Farbmaske: Aus dem Originalbild werden nur die Pixeln in die Masken übernommen, die sich im Bereich vom Binärbild befinden
  • Anschließend werden Mittelwerte für Rot, Grün und Blau berechnet.


Zuordnung zum nächstgelegener Farbe:

  • Es werden die ermittelten RGB-Mittelwerte mit der Farbtabelle verglichen:
    • zu erst werden die Differenzen von jeder Farbanteil zur der Farbtabelle ermittelt, und davon die maximalen werte gespeichert. z.B.: Die aktuelle Farbe (vom Legoteil) hat die maximale Abweichung im Farbanteil Rot zur Farbe Weis aus der Farbtabelle, max. Abw. im G zur Farbe Schwarz aus der Farbtabelle usw.
    • Anschließend wird das minimalste Wert von den Maximalen Abweichungen ermittelt --> Es wird genau die Farbe ermittelt, welche die kleinste Abweichung zu der Legoteil-Farbe hat.


  • Zum Schluss wird die ermittelte Farbe als String zurückgegeben.





















Quelcode MATLAB:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Funktion        : Merkmalsextraktion: FARBERKENNUNG                     %
% Autor           : Christo Tsibadze                                      %
% Implementation  : MATLAB R2016a                                         %
%                                                                         %
% Funkion         : Die Funktion ermittelt die Farbe von dem Überlagerten      %
%                   Bereich aus Binär_Objekt_Bild und Originalbild und    %
%                   ermittelt zu welcher Farbe der Tabelle am besten      %
%                   ähnelt                                                %
% EINGABE         : RGB_BILD (:,:,3) in uint8 Format                      %
%                   BW_OBJECT(:,:) in Binärformat                         %
% AUSGABE         : FARBE LEGO-Farbe als string                           %
% Änderungsdatum  : 21.06.2016                                            %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [ FARBE ] = FARBERKENNUNG_V2( RGB_BILD, BW_OBJECT )
%% Farbwerte-Tabelle mit normierten Lego-Farben
% %           R(1) G(1) B(1)  Farbcode R(255) G(255) B(255)
Farbwerte=[   1.00 1.00 1.00;%     01      255  255    255; %'weiss'          1
              0.87 0.78 0.61;%     05      222	198	   156; %'beige'         2
              0.70 0.00 0.02;%     21      179    0	     6; %'rot'           3
              0.00 0.34 0.65;%     23        0	 87	   166; %'blau'          4
              0.97 0.82 0.09;%     24      247  209	    23; %'gelb'          5
              0.13 0.13 0.13;%     26       33	 33	    33; %'schwarz'       6
              0.06 0.80 0.19;%     37       16	203	    49; %'gruen'         7
              0.70 0.33 0.03;%     38      179	 84	     8; %'hellbraun'     8 % wird zurzeit nicht verwendet
%             0.69 0.71 0.78;%    194      175	181	   199; %'hell-grau'      9
              0.71 0.73 0.80;%    194      175	181	   199; %'hell-grau'      9 %angepasst
%             0.35 0.36 0.38;%    199       89	 93	    96; %'dunkel-grau'   10
              0.45 0.46 0.48;%    199       89	 93	    96; %'dunkel-grau'   10 %angepasst
              0.20 0.00 0.00;%    308       51	  0	     0; %'dunkelbraun'  11 %wird zurzeit nicht verwendet
              ];
%% Eine Maske für die Farbextrahierung aus Binärbild erzeugen          
[Hoehe, Breite]=size(BW_OBJECT);                                            %Hoehe und Breite des Bildes ermitteln
Farbmaske=zeros(Hoehe, Breite, 3);                                          %Null-Matrix für Farbmaske erstellen
RGB_BILD=im2double(RGB_BILD);                                               %in double umwandeln, besser zu rechnen
BW_BILD=im2double(BW_OBJECT);
%% nur die Farben aus übereinstimmenden Pixeln RGB zu BW in der Farbmaske übernehmen
Farbmaske(:,:,1) = RGB_BILD(:,:,1).*BW_BILD(:,:);                           %für Rot                     
Farbmaske(:,:,2) = RGB_BILD(:,:,2).*BW_BILD(:,:);                           %für Grün
Farbmaske(:,:,3) = RGB_BILD(:,:,3).*BW_BILD(:,:);                           %für Blau
%% Pixel-Fläche des Binärbildes berechnen für die Mittelwertbildung
Pixel_Flaeche = sum(sum(BW_OBJECT));                                        %Flächenberechnung, Pixelanzahl
%% Mittelwertwerte für R, G und Blau berechnen
FARBE(1,1) = (sum(sum(Farbmaske(:,:,1))))/Pixel_Flaeche;                    %für jede Farbe Summe im Bild bilden und durch die Anzahl der Pixel teilen
FARBE(1,2) = (sum(sum(Farbmaske(:,:,2))))/Pixel_Flaeche;
FARBE(1,3) = (sum(sum(Farbmaske(:,:,3))))/Pixel_Flaeche;
%% Die Farbe aus der Tabelle mit geringster Abweichung zu aktueller Farbe bestimmen
% 1) zu Jeder Tabellenfarbe wird der größstabweichender Farbanteil der
% aktuellen Farbe ermittelt und gespeichert
% 2) anschließend wird die Farbe mit geringsten Wert von den
% maximalabweichungen ermittelt
temp = max(abs(Farbwerte(1,:)-FARBE));                                      % Max von R,G,B Differenzen zu erster Farbe
MIN_Diff=1;
for i=2:11
    if(temp>(max(abs(Farbwerte(i,:)-FARBE))))                               % falls der gleiche Wert mit anderer Farbe kleiner ist, speichern
    MIN_Diff = i;                                                           % den Farbenindex mit kleinst. Abweichung speichern
    temp=max(abs(Farbwerte(i,:)-FARBE));
    end;
end
%% Farbenzuordnung FARBE als Rückgabe
% Anhand der gespeicherten Farbindex, wird eine Farbe zurückgegeben
% zurückgegeben
switch MIN_Diff
    case 1
        FARBE='weiss';
    case 2
        FARBE='beige';
    case 3
        FARBE='rot';
    case 4
        FARBE='blau';
    case 5
        FARBE='gelb';
    case 6
        FARBE='schwarz';
    case 7
        FARBE='gruen';
    case 8
        FARBE=38;
    case 9
        FARBE='hell-grau';
    case 10
        FARBE='dunkel-grau';
    case 11
        FARBE=308;
    otherwise
        %disp('Fehler')
        %hier ist noch ein Zusatzfall unterscheidbar, es ist gedacht einen
        %Maximalabweichung in der Berechnung einzubeziehen, falls die
        %kleinst ermittelte Abweichung größer ist als ein bestimmter
        %Grenzwert, dann soll hier Fehler ausgegeben werden.
end
end


Merkmale

UNTER BEARBEITUNG

Abbildung 4: Merkmale




















































Teach-IN

UNTER BEARBEITUNG

Datenbankanbindung, Listen IN/OUT

Datenbank

Erstellung einer Inventurliste

Eine Anforderung an die Legoteil Zählmaschine ist, dass dem Anwender nach Abschluss des Zählprozesses eine Liste in Excel ausgegeben wird, in der die gezählten Teile und dessen Anzahl aufgelistet sind. Dazu wurde zunächst eine Funktion InventurlistenTamplateMultiKaesten geschrieben, die aus der Datenbank die Bauteile ausliest, die in dem ausgewählten Baukästen enthalten sind und diese in eine Excel-Liste schreibt. Als Übergabeparameter benötigt die Funktion die entsprechenden BaukastenIDs, einen Dateinamen für die Inventurliste und die entsprechende Verbindung zur Datenbank. Als Rückgabewert gilt ein Cell-Array, welches den Inhalt der Inventurliste in Excel enthält.


Die Funktion InventurlistenTamplateMultiKaesten.m ist wie folgt implementiert:


function InventurListeContent=InventurlistenTamplateMultiKaesten( BaukastenName, InventurlisteName, conn)
%Die Funktion richtet ein Template für die Erstellung der Inventurliste ein
%   Sie liest die Inhalte des gewünschten Baukastens ein und trägt sie
%   mit Ihrer Anzahl in eine Excel-Liste ein. Es kann angepasst werden ob 
%   die Daten aus der SQL-Datenbank stammen oder aus einer Exceldatei mit
%   dem Namen des Baukastens im *.xls-Format.
%   Parameter:
%       BaukastenName: String mit dem Namen des zu zählenden Baukastens
%       InventurlisteName: String mit dem Namen der Exceldatei mit der
%                           Inventurliste
%       Conn: Adresse der SQL-DB, wenn == 0, dann wird eine Excelliste
%               eingelesen
%
%   Autor: Niklas Lingenauber
%   Letzte Änderung: 13.05.2016

%% Variablen definieren %%
    Header={'ID' 'Bezeichnung' 'Farbe' 'Soll-Anzahl'};          %Überschriften der Inventurliste definieren
    ID=1;               %an Baukasten-Datenbank anpassen
    Bezeichnung=2;      %an Baukasten-Datenbank anpassen
    Farbe=3;            %an Baukasten-Datenbank anpassen
    Anzahl=4;           %an Baukasten-Datenbank anpassen

%% Einlesen der benötigten Teile des Baukastens %%
if conn~=0  %aus einer SQL-Datenbank
    InventurListeContent=Header;        %Überschriften der Liste in erste Zeile
    KaestenN=size(BaukastenName,1);     %Anzahl der baukästen bestimmen
    for i=1:KaestenN
        
        %Auslesen der Baukästen aus der Datenbank
        sqlquery = strcat(['SELECT Legoteileliste.LegoteileID, Legoteileliste.Bezeichnung,'...
                           'Legoteileliste.Farbe, Kasten.Anzahl FROM Legoteileliste, Kasten '...
                           'WHERE Kasten.kastenID =',num2str(BaukastenName(i)),...
                           ' AND legoteileliste.LegoteileID = kasten.LegoteileID']);    %Erstellung des SQL-Befehls
        cursor = exec(conn, sqlquery);
        cursor = fetch(cursor);
        
        %Eintragen in die vorhandene Inventurliste
        InventurListeContent=vertcat(InventurListeContent,cursor.Data);     %Inhalte der Bauteil-IDs im Baukasten i unten anfügen
    end
else    %aus einer Excelliste
    %Definieren von Spalten in der Excel-Version
    BaukastenName=[BaukastenName '.xlsx'];      %Namensergänzung solange Datenbank eine Excel-Date
    
    % Auslesen aus Excel
    raw=xlsread(BaukastenName,3);       %vorrübergehend, später aus Datenbank
    ContentID=raw(2:175,ID);                    %Inhalte der Bauteil-IDs im Baukasten
    Elements=size(ContentID,1)+1;               %Anzahl der Teile im Kasten
    ContentBezeichnung=raw(2:Elements,Bezeichnung);  %Bezeichnungen der Bauteile
    ContentFarbe=raw(2:Elements,Farbe);              %Farben der Bauteile
    ContentAnzahl=raw(2:Elements,Anzahl);            %Anzahl der jeweiligen Bauteile IM Baukasten
    InventurListeContent=horzcat(ContentID,ContentBezeichnung, ContentFarbe, ContentAnzahl);  %Spalten zusammenfügen
end

%% Entfernen einer alten Inventurliste
xls_check_if_open(InventurlisteName,'close');
if exist(InventurlisteName, 'file') == 2    %Existiert eine alte Liste
    delete(InventurlisteName);              %Löschen
end

%% Erstellung der Excelliste %%
xlswrite(InventurlisteName,InventurListeContent);           %ExcelDatei erstellen
end


Nach der Erstellung des Templates werden die Gezählten IDs in die Liste eingetragen. Dazu wird der Inhalt des Templates nach den entsprechenden IDs durchsucht und die dazugehörige Anzahl notiert. Die Funktion InventurlisteFuellen benötigt ebenfalls die entsprechenden BaukastenIDs, einen Dateinamen für die Inventurliste und die entsprechende Verbindung zur Datenbank als Übergabeparameter.Außerdem werden die gezählten Legoteil-IDs und dessen Anzahl benötigt. Als Rückgabewert gilt erneut der Inhalt der Excel-Liste.


Die Funktion InventurlisteFuellen.m ist wie folgt implementiert:


function InventurlisteFuellen( InventurlisteName, InventurListeContent, GezaehlteIDs, conn)
%Die Funktion füllt eine Inventurliste aus
%   Die Funktion sucht in der Datei 'InventurlisteName' die Zeilen aus der 
%   Inventurliste  raus in der die gleiche ID der Elemente der Gezählten
%   IDs steht. Dann trägt es die Anzahl in die entsprechenden Spalten ein.
%   Parameter:
%       InventurlisteName: String mit dem Namen der Exceldatei mit der
%                           Inventurliste inkl. Format '*.xls'
%       InventurListeContent: Inhalt des unausgefüllten BaukastenTemplates
%       GezählteIDs: Matrix in der die IDs in der ersten Spalte und die
%                       Anzahl in der zweiten Spalte aufgeführt sind.
%       conn: Addresse der SQL-DB, wenn ==0, dann wird bei Bauteilen, die
%               nicht zum Kasten gehören ein Kommentar statt einer 
%               bezeichnung eingefügt
%
%   Autor: Niklas Lingenauber
%   Letzte Änderung: 13.05.2016

%% Variablen definieren %%
SpalteIstAnzahl='E';    %Spalte in der Excel, in der die Ist-Anzahl eingetragen wird
ID=1;
Bezeichnung=2;
Farbe=3;
SollAnzahl=4;
IstAnzahl=5;

Andere=0;               %Anzahl der Bauteile, die nicht zum Baukasten gehören
Vorkommen=zeros(length(InventurListeContent)-1,1);      %Füllt die Ist-Anzahl zunächst mit 0en auf, Zeigt die Anzahl der IDs an 
AnzahlIDs=GezaehlteIDs(2:length(GezaehlteIDs),2);       %Anzahl der jeweiligen IDs
GezaehlteIDs=GezaehlteIDs(2:length(GezaehlteIDs),1);    %entfernen der Überschriften und Konvertierung in Matrix
BaukastenIDs=InventurListeContent(2:length(InventurListeContent),1); %IDs die im Baukasten auftreten

%% Anzahlen der IDs ermitteln %%
for i=1:length(GezaehlteIDs)        %Alle gezählten IDs eintragen 
    SucheNach=GezaehlteIDs(i);    %gesuchte ID als Zahl
    Zeile=0;                        %Laufindex
    gefunden=false;                 %Variable zur Erkennung ob ID gefunden
    while Zeile<length(BaukastenIDs) && gefunden==false %solange nicht gefunden und nicht am Ende
        Zeile=Zeile+1;              %Laufindex erhöhen
        if cell2mat(BaukastenIDs(Zeile)) == SucheNach      %Wenn ID gefunden
          gefunden=true;            %Abbruchbedingung setzen
       end
    end
    if gefunden==true   % Wenn gefunden, dann in Cell-Array eintragen
        Vorkommen(Zeile)=Vorkommen(Zeile)+AnzahlIDs(i);   %Entsprechende Anzahl notieren
    else                %Wenn nicht ID und Anzahl merken
        %kein Bestandteil des Baukastens. IDs müssen angefügt werden
        Andere=Andere+1;
        if conn~=0
            sqlquery = strcat('SELECT Bezeichnung,Farbe FROM legoteileliste WHERE legoteileID = ' , num2str(GezaehlteIDs(i)));
            cursor = exec(conn, sqlquery);
            cursor = fetch(cursor);
            daten(1)=cursor.Data(1);    %Bezeichnung
            daten(2)=cursor.Data(2);    %Farbe
        else
            daten={'Zusätzlich','Farbe'};           %Bezeichnung und Farbe
        end
        
        ZusaetzlicheTeile(Andere,ID)=num2cell(GezaehlteIDs(i));     %ID eintragen
        ZusaetzlicheTeile(Andere,Bezeichnung)=daten(1);         %Bezeichnung eintragen
        ZusaetzlicheTeile(Andere,Farbe)=daten(2);               %Farbe eintragen
        ZusaetzlicheTeile(Andere,SollAnzahl)=num2cell(0);                 %Soll-Anzahl eintragen; 0, da nicht im Baukasten erwartet
        ZusaetzlicheTeile(Andere,IstAnzahl)=num2cell(AnzahlIDs(i));       %Ist-Anzahl eintragen
    end
end
%% Eintragen in die Liste %%
Vorkommen=vertcat('Ist-Anzahl',num2cell(Vorkommen)); %Überschrift einfügen
xlswrite(InventurlisteName,Vorkommen,'',[SpalteIstAnzahl '1']); %Bauteile des Baukastens
if exist ('ZusaetzlicheTeile')==1
    BeginnWeitererTeile=['A' num2str(size(BaukastenIDs,1)+2)];
    xlswrite(InventurlisteName,ZusaetzlicheTeile,'',BeginnWeitererTeile); %Anpassen
end

%% Fehlteilliste erstellen %%
SpalteDifferenz=6;                  %Anpassen, SpaltenNummer mit der zu bestllenden Differenz
FehlteillisteName='Fehlteile.xls';  %Anpassen, Name der Excel-Datei

InventurListeContent= horzcat(InventurListeContent,Vorkommen);      %Anzahlen der IDs zur Inventurliste hinzufügen
FehlendeID=0;                                                       %Anzahl der nicht gezählten BauteilIDs, zunächst 0 annehmen
for i=1:length(InventurListeContent)                                %Liste durchsuchen
    Differenz=InventurListeContent(i,4)-InventurListeContent(i,5);  %Differenz bestimmen
   if Differenz>0                                                   %Wenn Teile fehlen
      FehlendeID=FehlendeID+1;                                      %Anzahl nicht gezählter IDs erhöhen
      Fehlteile(FehlendeID)= InventurListeContent(i);               %Inhalte merken
      Fehlteile(FehlendeID,6)=Differenz;                            %Differenz eintragen
   end
end

Fehlteile=vertcat({'ID','Fehlteile'},num2cell(Fehlteile)); %Überschrift einfügen
%% Entfernen einer alten Fehlteilliste
xls_check_if_open(FehlteillisteName,'close');
if exist(FehlteillisteName, 'file') == 2    %Existiert eine alte Liste
    delete(FehlteillisteName);              %Löschen
end
xlswrite(FehlteillisteName,Fehlteile,'');     %Liste Speichern
winopen(FehlteillisteName);
end

end

Separierung und Zentrierung der Legoteile

Teilezentrierung

Abbildung X: Zentrierung

Die provisorisch aus Holz gefertigte Teilezentrierung wurde mit fixierbarer Variante aus Aluminium ersetzt. An den Zentrierungskomponenten wurden elastische Kunststoffstreifen geklebt und anschließend an den Alu-Profilen vor der Bildverarbeitungsbox festgeschraubt.





Separierung

Die Separierung wurde mit Hilfe von Geschwindigkeitsdifferenzen an den Laufbandübergängen realisiert. Je größer die Geschwindigkeitsdifferenzen, desto bessere Separierung ist möglich. Aber die Bilderkennung fungiert in diesem Punkt als "Flaschenhals", wenn das letzte Laufband zu schnell dreht, dann werden weniger Teile erkannt. --> Somit wurden experimentell die optimalen Geschwindigkeiten für jedes Laufband separiert für die Optimale Separierung und Bilderkennung.
















Elektronik und Sicherheit

Neues 24V Netzteil

24V/10A Netzteil gegen 24V/20A austauschen, um Überlast beim Lauf aller Motoren zu verhindern.

Abbildung 5: 24VDC Installation




















































Kabelkanal

Kabelkanal zur ordentlichen Verlegung der Leitungen an den Förderbändern montieren

Abbildung X: Kabelkanal



































Kamera- und Bildbox-Halterung

Abbildung X: Kamera_BildBox_Fixierung

Konstruktion einer Kamerahalterung für die Logitech C920 WebCam. Vorschlag: Brücke über Förderband Für die Bildverarbeitung muss eine der gebauten Bildverarbeitungsbox-Prototypen ausgewählt und montiert werden. Hierfür wurden Lichttests durchgeführt. Ergebnisse sind protokolliert SVN Link zu den Lichttests Prototypen der Lichtboxen vorhanden






























Höhenanpassung der Laufbänder

und Legoförderer (Bunker)

HSHL-Wiki-Dokumentation

gemeinsame Bearbeitung, Jede Aufgabe wird vom zuständigen Person dokumentiert...

Video-Anleitung

Projektplanung

hier können wir Unterschiede nennen: was wir anfangs geplant hatten und was wir wegen Machbarkeit mit Genehmigung von Prof. Schneider abgeändert haben...

Pflichtenheft Dokumentation in DOORS

Prozessvisualisierung mit Visio

Abbildung X: MAIN inkl. GUI und Datenbank




















































QM-Tests

Wurde mit Genehmigung von Prof. Schneider auf das nächst Semester verlegt.

Auswerfer

z.B. Inbetriebnahme des vorhandenen Auswerfers

Planung der Aufgaben für das nächste Semester

Konzept für Sortieranlage

Abbildung X: Konzept_Sortierung





















Zum Öffnen des 3D-Modells vom Konzept, laden Sie bitte folgende frei erhältliche Software 3DXML-Player herunter und öffnen Sie die im SVN Sortieranlage.3dxml gespeicherte 3dxml-Datei.


Abbildung X: Konzept CAD





















Vereinzelung von Legoteilen (vor Bildverarbeitung)

Ergebnisse

Anleitung Programmstart

  • Vorbereitung
    • LED-Stecker anschließen
    • Hauptstecker anschließen
    • Hauptschalter betätigen
  • Datenbank starten
    • XAMPP control panel starten
    • Apache Modul starten
    • MySQL Modul starten
  • Matlab Programm öffnen
    • unter SVN\MTR_SDE_Praktikum_2015\SRC die main-datei öffnen
    • Programm starten
  • Baukasten in der GUI auswählen
  • handgezählte Teile eintragen
  • automatischer Zählprozess
    • Motoren werden automatisch gestartet
    • Kästen hinter das Förderband stellen
    • Legoteile in den Bunker füllen
    • Esc-Taste betätigen zur Beendigung des automatischen Zählprozess
  • Motoren werden automatisch ausgeschaltet
  • Inventurliste wird ausgegeben


Video

Video oder kein Video?

Fazit

Abgeschlossene Punkte

Offene Punkte

Dokumentation

SVN

Link zur Bildverarbeitungs-Software --> Algorithmus und Module

Gefährdungsbeurteilung AKTUALLISIEREN

SVN Link zur Gefährdungsbeurteilung