Full GraphSLAM Algorithmus mit festen Markern


Datei: myGraphSLAM.m

function [] = myGraphSLAM(N, numLandmarks, worldSize, measurementRange, motionNoise, measurementNoise, distance,
							plotWorld)
    if exist('plotWorld', 'var') == 0 || isempty(plotWorld)
        plotWorld = boolean(0); % false
    end

    %% Configuration
    %
    
    %numLandmarks          = 9;        % number of landmarks
    %N                  = 10;       % time steps
    %worldSize             = 100.0;    % size of world
    %measurementRange      = 50.0;     % range at which we can sense landmarks
    %motionNoise           = 2.0;      % noise in robot motion
    %measurementNoise      = 2.0;      % noise in the measurements
    %distance           = 20.0;     % distance by which robot (intends to) move each iteratation 



    if plotWorld
        figure();
    end
    data=makeData(N, numLandmarks, worldSize, measurementRange, motionNoise, measurementNoise, distance, plotWorld);
    [ result, Omega, Xi ]  = slam(data, N, numLandmarks, worldSize, motionNoise, measurementNoise);
    printResult(N, numLandmarks, result);
    if plotWorld
        for i = 0:N-1
            estimatedPose(i+1,:) = [result( (2*i)+1 ), result( (2*i+1)+1 )];
        end
        for i = 0:numLandmarks-1
            estimatedLandmark(i+1,:) = [result( (2*(N+i))+1 ), result( (2*(N+i)+1)+1 )];
        end
        
        hold on;
        plot(estimatedLandmark(:,1),estimatedLandmark(:,2), 'rh', 'MarkerSize', 7, 'MarkerFaceColor', 'r'
							, 'MarkerEdgeColor', 'r');
        %path = [worldSize / 2.0, worldSize / 2.0];
        
        plot(estimatedPose(:,1), estimatedPose(:,2), 'r-');
        axis([0 worldSize 0 worldSize]);
        hold off;
        legend('True Landmarks', 'True Path', 'Estimated Landmarks', 'Estimated Path');
    end
    

end







%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Robot Class Functions from CS373 - Course from UDACITY  %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% this is the robot class
%% 
%% our robot lives in x-y space, and its motion is
%% pointed in a random direction. It moves on a straight line
%% until is comes close to a wall at which point it turns
%% away from the wall and continues to move.
%%
%% For measurements, it simply senses the x- and y-distance
%% to landmarks. This is different from range and bearing as 
%% commonly studies in the literature, but this makes it much
%% easier to implement the essentials of SLAM without
%% cluttered math
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% init: 
%   creates robot and initializes location to 0, 0
%
function [ robot ] = createRobot(worldSize, measurementRange, motionNoise, measurementNoise)
    if isempty(worldSize)
        worldSize = 100.0;
    end
    if isempty(measurementRange)
        measurementRange = 30.0;
    end
    if isempty(motionNoise)
        motionNoise = 1.0;
    end
    if isempty(measurementNoise)
        measurementNoise = 1.0;
    end
    
    robot = [];
    robot.measurementNoise = 0.0;
    robot.worldSize = worldSize;
    robot.measurementRange = measurementRange;
    robot.x = worldSize / 2.0;
    robot.y = worldSize / 2.0;
    robot.motionNoise = motionNoise;
    robot.measurementNoise = measurementNoise;
    robot.landmarks = [];
    robot.numLandmarks = 0;
end


% make random landmarks located in the world
%
function [ robot ] = makeLandmarks(robot, numLandmarks)
    robot.landmarks = [];
    for i = 1:numLandmarks
        robot.landmarks(i,:) = [randi([1 robot.worldSize]), randi([1 robot.worldSize])];
    end
    robot.numLandmarks = numLandmarks;

end



% move: attempts to move robot by dx, dy. If outside world
%       boundary, then the move does nothing and instead returns failure
%
function [ robot, success ] = robotMove(robot, dx, dy)
    newX = robot.x + dx + (-1 + 2.0 * rand(1)) * robot.motionNoise;
    newY = robot.y + dy + (-1 + 2.0 * rand(1)) * robot.motionNoise;
    
    if newX <= 0 || newX > robot.worldSize || newY <= 0 || newY > robot.worldSize
        success = boolean(0);    % return false
    else
        robot.x = newX;
        robot.y = newY;
        success = boolean(1);    % return true
    end
end



% sense: returns x- and y- distances to landmarks within visibility range
%        because not all landmarks may be in this range, the list of measurements
%        is of variable length. Set measurement_range to -1 if you want all
%        landmarks to be visible at all times
%
function [ Z ] = robotSense(robot)
    Z = [];
    for i = 1:robot.numLandmarks
        dx = robot.landmarks(i,1) - robot.x + (-1 + 2.0 * rand(1)) * robot.measurementNoise;
        dy = robot.landmarks(i,2) - robot.y + (-1 + 2.0 * rand(1)) * robot.measurementNoise;
        if robot.measurementRange < 0 || abs(dx) + abs(dy) <= robot.measurementRange
            Z = [Z; i, dx, dy];
        end
    end
end



% print robot location
%
function [] = robotDisplay(robot)

    % display current position        
    disp(sprintf('Robot: [x=%.5f y=%.5f]', robot.x, robot.y));

end



% this routine makes the robot data
%
function [ data ] = makeData(N, numLandmarks, worldSize, measurementRange, motionNoise, measurementNoise, distance,
	plotWorld)
    complete = boolean(0);    % false
    
    while complete == 0
        data = [];
        
        % make robot and landmarks
        r = createRobot(worldSize, measurementRange, motionNoise, measurementNoise);
        r = makeLandmarks(r, numLandmarks);
        seen = boolean( zeros( numLandmarks, 1 ) );
        
        % guess an initial motion
        orientation = rand(1) * 2.0 * pi;
        dx = cos(orientation) * distance;
        dy = sin(orientation) * distance;
        
        for k = 1:(N-1)
    
            % sense
            Z = robotSense(r);

            % check off all landmarks that were observed 
            for i = 1:size(Z,1)
                seen(Z(i,1)) = boolean(1); % true
            end
    
            % move
            [ r, success ] = robotMove(r, dx, dy);
            while success == 0
                % if we'd be leaving the robot world, pick instead a new direction
                orientation = rand(1) * 2.0 * pi;
                dx = cos(orientation) * distance;
                dy = sin(orientation) * distance;
                [ r, success ] = robotMove(r, dx, dy);
            end

            % memorize data
            append(1,1).Z       = Z;
            append(1,1).motion  = [dx,dy];
            data = [data; append];
        end

        % we are done when all landmarks were observed; otherwise re-run
        if sum(seen) == numLandmarks
            complete = boolean(1);
            if plotWorld
                xlabel('X');
                ylabel('Y');
                plot(r.landmarks(:,1), r.landmarks(:,2), 'bh', 'MarkerSize', 10, 'MarkerFaceColor', 'b',
							'MarkerEdgeColor', 'b');
                hold on;
                path = [worldSize / 2.0, worldSize / 2.0];
                for i = 1:size(data,1)
                    path(i+1,:) = [path(i,:) + data(i).motion];
                end
                plot(path(:,1), path(:,2), 'g-');
                axis([0 worldSize 0 worldSize]);
                hold off;
            end
        else
            complete = boolean(0);
        end        
    end
    
    disp(' ');
    for i = 1:size(r.landmarks,1)
        disp(sprintf('Landmarks: [x=%.5f y=%.5f]', r.landmarks(i,1), r.landmarks(i,2)));
    end
    %print ' '
    %print 'Landmarks: ', r.landmarks
    %print r
end



% full_slam - retains entire path and all landmarks
%             Feel free to use this for comparison.
%

function [ mu, Omega, Xi ] = slam(data, N, numLandmarks, worldSize, motionNoise, measurementNoise)

    % Set the dimension of the filter
    dim = 2 * (N + numLandmarks); 

    % make the constraint information matrix and vector
    Omega = zeros(dim, dim);
    Omega(1,1) = 1.0;
    Omega(2,2) = 1.0;

    Xi = zeros(dim, 1);
    Xi(1,1) = worldSize / 2.0;
    Xi(2,1) = worldSize / 2.0;
    
    % process the data

    for k = 0:size(data,1)-1

        % n is the index of the robot pose in the matrix/vector
        n = k * 2;
    
        %
        measurement = [];
        motion      = [];
        measurement = data(k+1).Z;
        motion      = data(k+1).motion;
    
        % integrate the measurements
        for i = 0:size(measurement,1)-1
    
            % m is the index of the landmark coordinate in the matrix/vector
            m = 2 * (N + measurement(i+1,1)-1);
    
            % update the information maxtrix/vector based on the measurement
            for b = 0:1
                Omega(n+b+1,n+b+1)    =  Omega(n+b+1,n+b+1) + 1.0 / measurementNoise;
                Omega(m+b+1,m+b+1)    =  Omega(m+b+1,m+b+1) + 1.0 / measurementNoise;
                Omega(n+b+1,m+b+1)    =  Omega(n+b+1,m+b+1) - 1.0 / measurementNoise;
                Omega(m+b+1,n+b+1)  =  Omega(m+b+1,n+b+1) - 1.0 / measurementNoise;
                Xi(n+b+1,1)         =  Xi(n+b+1,1) - measurement(i+1,2+b) / measurementNoise;
                Xi(m+b+1,1)         =  Xi(m+b+1,1) + measurement(i+1,2+b) / measurementNoise;
            end
        end


        % update the information maxtrix/vector based on the robot motion
        for b = 0:3
            Omega(n+b+1,n+b+1)      = Omega(n+b+1,n+b+1) + 1.0 / motionNoise;
        end
        for b = 0:1
            Omega(n+b+1  ,n+b+2+1)  = Omega(n+b+1  ,n+b+2+1) - 1.0 / motionNoise;
            Omega(n+b+2+1  ,n+b+1)  = Omega(n+b+2+1  ,n+b+1) - 1.0 / motionNoise;
            Xi(n+b+1,1)             = Xi(n+b+1,1)   - motion(b+1) / motionNoise;
            Xi(n+b+2+1,1)           = Xi(n+b+2+1,1) + motion(b+1) / motionNoise;
        end

    end
    
    % compute best estimate
    mu = inv( Omega ) * Xi;
end

% print the result of SLAM, the robot pose(s) and the landmarks
%

function [ ] = printResult(N, numLandmarks, result)
    disp(' ');
    disp('Estimated Pose(s):');
    for i = 0:N-1
        disp( sprintf('    [%.3f, %.3f]', result( (2*i)+1 ), result( (2*i+1)+1 ) ) );
    end
    disp(' '); 
    disp('Estimated Landmarks:');
    for i = 0:numLandmarks-1
        disp( sprintf('    [%.3f, %.3f]', result( (2*(N+i))+1 ), result( (2*(N+i)+1)+1 ) ) );
    end
end

Full GraphSLAM-Algorithmus mit festen Markern von UDACITY-Kurs CS373

Übersetzung von python nach MatLab, Lösung des SLAM-Problems mit GraphSLAM-Algorithmus


  • Full GraphSLAM
  • Verwendung von festen Orientierungspunkten (Landmarks)
  • Übersetzung von python nach MatLab
  • Ergänzung um Visualisierung
  • Aufruf z.B.: myGraphSLAM(20, 6, 100, 50, 2.0, 2.0, 5, 1)