SigSys15 Spurerkennung

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen

Autor: Sergej Krause
Betreuer: Prof. Schneider


Motivation

Eine Aufgabe beim Carolo Cup ist der "Rundkurs mit und ohne Hindernissen". Hierbei ist das Fahrzeug autonom in der Spur zu halten.

Ziel

Die Fahrbahnbegrenzungen links, rechts und mitte soll robust erkannt und unterschieden werden. Aus den Begrenzungslinien ist eine Kurvenkrümmung oder der parabolische Kurvenverlauf in Weltkoordinaten zu ermitteln.

Aufgabe

  1. Der aufgezeichnete Rundkurs liegt als Video vor.
  2. Lesen Sie diesen als Endlosschleife in Matlab ein.
  3. Identifizieren Sie während der virtuellen Fahrt die Fahrbahnbegrenzungen mit Matlab.
  4. Vermeiden Sie Fehler ("false-positives").
  5. Filtern Sie die 3 Linien möglichst stabil zu einer Sollfahrspur.
  6. Optimieren Sie die Rechenzeit Ihres Algorithmus.



Lösungen

Erste Schritte

Bearbeitung des Videos

Um die Bearbeitung der einzelnen Frames in einer annehmbaren Zeit durchzuführen werden alle Frames des Videso erst als Imagedateien abgespeichert und wenn die Frames bearbeitet werden sollen können Sie wieder geladen werden. Mit "workingDir" kann entschieden werden in welches Verzeichnis die Bilder gespeichert werden.

readVideo = VideoReader('Rundkurs.mp4');
% choose working directory
workingDir = 'Img_Vid_temp';

% split video into Frames and create imagefolder
 VideoToImg(workingDir, readVideo, 'max');

% get all filenames from the directory 
imageNames = dir(fullfile(workingDir,'images','*.png'));
imageNames = {imageNames.name}';

% amount of images read from directory
maxImages = length(imageNames);

In der Funktion VideoToImg wird überprüft ob das angegebene Verzeichnis existitiert, wenn nicht wird es neu erstellt. Die Frames werden nummeriert und als *.png abgespeichert um einen einfachen Zugriff sicherzustellen.

%*******************************************%
%   Einlesen des Videos und in einzelne     %
%   Frames zerlegen, die dann in einem      %
%   Unterordner, der generiert wird, falls  % 
%   vorhanden erst gelöscht wird um mögliche%
%   Fehlerquellen zu umgehen,dann in        %
%   diesem gespeichert werden               %
%   Parameter:                              %
%       In:                                 %
%           -workingDir: arbeitsverzeichnis %
%               in dem Ordner erst gelöscht,%
%               falls vorhanden, dann neu   %
%               erzeugt werden              %
%           -readVideo: muss eine           %
%               eingelesene Videodatei      %
%               enthalten, die in Frames    %
%               gesplittet wird             %
%           -maxFrames: gibt die Anzahl der %
%               zu erzeugenden Frames an,   %
%               falls 'max' dann wird die   %
%               maximale Frameanzahl        %
%               ermittelt und benutzt       %
%       Out:                                %
%           -nix                            % 
%*******************************************%

function [] = VideoToImg (workingDir, readVideo, maxFrames)

    % Create Workspace and delete previous 
    % folder to save the images into it 
    if exist( workingDir, 'dir')
        rmdir(workingDir, 's');
    end

    mkdir(workingDir);
    mkdir(workingDir,'images');

    % Get the maximum number of frames in 
    % the video
    if strcmp(maxFrames,'max')
        maxFrames = get(readVideo, 'NumberOfFrames');
    else
        maxFrames = str2double(maxFrames);
    end
    
    % readVideo has to be recreated after 
    % get(readVideo, 'NumberOfFrames')
    readVideo = VideoReader('Rundkurs.mp4');

    % Settings for easier control of progress
    loopSize = maxFrames;  % to cover whole video
    reverseStr = '';        % necessary to show the progress

    % Breaking video into frames
    for index =  1:1:loopSize    
        video_Frames = readFrame(readVideo);
        filename = [sprintf('Frame_%04d',index),'.png'];
        fullname = fullfile(workingDir,'images',filename);
        imwrite(video_Frames,fullname);

        %showing progress
        percentDone = 100* index / loopSize;
        msg = sprintf('Image creation: %3.1f', percentDone);
        fprintf([reverseStr,msg]);
        reverseStr = repmat(sprintf('\b'),1,length(msg));

   end
end

Bearbeitung der einzelnen Frames

Um eine vernünftige Erkennung der Spur zu gewährleisten wird das Bild entzerrt. Dies geschieht mit den Kameraparametern VRM_Calib. Damit wird die Verzerrung der horizontalen Linien des Bildes durch die Kamera aufgehoben. Desweiteren muss das Farbbild in ein Schwarz-Weiß-Bild konvertiert werden um einen Sobel-filter zur Kanten detektion anwenden zu können. Da das Fahrzeug im Bild ist und hin und wieder die Spurlinie kreuzt wird das Fahrzeug mit einer schwarzen Maske überdeckt.

orgFrame = imread(fullfile(workingDir,'images',imageNames{index}));
    % make image blacke and white, put a black mask of the car to
    % improve performance and show only the edges, sobel for fastes
    % performance
    bwFrame = undistortImage(orgFrame,cameraParams_15_05_14);
    bwFrame = im2bw(bwFrame,0.6);
    bwFrame = bwFrame .*maske_auto;
    bwFrame = edge(bwFrame,'sobel');

Endlosschleife

Die Endlosschleife wird erzeugt in dem eine while 1 Schleife um die eigentliche Bildbearbeitungsschleife gelegt wird. Zu beachten ist das in der Bildbearbeitungsschleife ein Video erzeugt wird und dieses in einer Endlosschleife sehr groß werden kann. Um dies zu verhindern kann ein IF abfrage gemacht werden um das Video nur beim ersten Durchlauf zu erzeugen.

firstRun = 0;
while 1
 for index = 1:1:maxImages
    
    orgFrame = imread(fullfile(workingDir,'images',imageNames{index}));
    % make image blacke and white, put a black mask of the car to
    % improve performance and show only the edges, sobel for fastes
    % performance
    bwFrame = undistortImage(orgFrame,cameraParams_15_05_14);
    bwFrame = im2bw(bwFrame,0.6);
    bwFrame = bwFrame .*maske_auto;
    bwFrame = edge(bwFrame,'sobel');

    .
    .
    .
    if firstRun == 0
      writeVideo(outputVideo,orgFrame);
      firstRun = 1;
    end

 end
end

Die Spurerkennung

Die Spurerkennung erfolgt nach einem simplen Muster:

  1. Suche ab der Mitte des Bildes nach rechts ein weißes Pixel
  2. wenn ein weißes Pixel gefunden wurde suche nach einem weiteren weißen Pixel auf der X-Achse -> 4
  3. wenn kein weißes Pixel gefunden wurde inkrementiere den Y-Wert um 1 -> 1
  4. wenn das zweite weiße Pixel gefunden wurde, überprüfe ob der Abstand kleiner als 20 ist -> 6
  5. wenn kein zweites weißes Pixel gefunden wurde inkrementiere den Y-Wert um 1 -> 1
  6. wenn der Abstand kleiner als 20 ist, markiere den Bereich zwischen den Pixeln, inkrementiere den Y-Wert um 1 -> 1
  7. wenn der Abstand gößer ist als 20 inkrementiere den Y-Wert um 1 -> 1
while  endSearch == 0 && emptyCounter < 20
        foundPoI_start = findWhiteinLine(bwFrame, PoI,'right');
        if abs(historyPoI(2)-foundPoI_start(2)) > 5
            emptyCounter = emptyCounter + 1;
        else if foundPoI_start == 0
            emptyCounter = emptyCounter + 1;     
        else    
            %+5 because next pixel can also be white and to have a bit tolerance 
            foundPoI_end = findWhiteinLine(bwFrame, [foundPoI_start(1) (foundPoI_start(2)+5)],'right');

            lineWidth = foundPoI_end(2) - foundPoI_start(2);
            if lineWidth >= 20
                emptyCounter = emptyCounter + 1;

            else if (foundPoI_end == 0) 
                emptyCounter = emptyCounter + 1; 

            else
                emptyCounter = 0;
                historyPoI = foundPoI_start;


    %           paint orignial frame from first found whitepixel 
    %           to the second found whitepixel
                markingLength = markingLength - 1;
                orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),1) = 255;
                orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),2) = 0;
                orgFrame(foundPoI_start(1),foundPoI_start(2):foundPoI_end(2),3) = 0;
            end
            end
        end
        
        end
        PoI(1) = PoI(1) - 1;

        if markingLength == 0
            endSearch = 1;
        end
    end

Fazit und Ausblick

Weblinks


→ zurück zum Hauptartikel: Signalverarbeitende Systeme SoSe2015