Search code examples
matlabpathmotion-planning

How to remove lines that are on obstacles PRM example


I have the following code to generate a PRM map that will be used for A* application. There exist 2 problems with the code

  1. It keeps the blue lines representing the original PRM where lines can cross over obstacles. I don't want to keep the blue lines but I couldn't find the way to remove them.
  2. The green lines are going over obstacles even though they shouldn't

The code is as follows

clc;
clear all;
close all;


seed = 123512;
rng(seed);

xaxis = 100;
yaxis = 100;

obstacles = false(xaxis,yaxis);
[X,Y] = meshgrid(1:xaxis,1:yaxis);

obstacles(50:75,50:75) = true;
obstacles(25:35,30:40) = true;
obstacles(25:35,60:80) = true;

figure;
imshow(~obstacles,"InitialMagnification",1000);
axis([0 xaxis 0 yaxis]);
axis xy;
axis on;

%PRM PARAMETERS
max_nodes_connect = 4;
max_connect_len = 40;
segments = 1;
max_nodes_grid = 30;
skipped = 0;

%PRM ALGO
nodes = 0; %Counter
map = zeros(size(obstacles)); %generate map
map(obstacles) = 1; %put the obstacles
Graph_connections = Inf(max_nodes_grid,max_nodes_connect + 1); %rows = # nodes cols = ID and neighbors 

while (nodes < max_nodes_grid)
    node_x = randi(xaxis);
    node_y = randi(yaxis);

    if(map(node_y,node_x)==1 || map(node_y,node_x)==2)
        continue;
    end

    nodes = nodes + 1; %a valid node generated
    map(node_y,node_x) = 2; %2 means there exists a node at that location

    hold on 
    scatter(node_x,node_y,"red","filled")
    
    %NODES TO CONNECT
    nodes_to_connect = [];
    distances = [];

    for i= 1:numel(Graph_connections(:,1))
        if(Graph_connections(i,1)==Inf)
            break
        end
        [row,col] = ind2sub(size(map),Graph_connections(i,1));

        %Check if within range
        if(norm([node_y,node_x]-[row,col])>max_connect_len)
            continue;
        end

        line_on_obstacle = check_obstacle(map,node_x,node_y,row,col);
        %Check if obstacle thru line HAS TO BE WRITTEN
        if(line_on_obstacle)
            disp("Check Obstacle: " + line_on_obstacle);
            skipped = skipped + 1;
            continue;
        end

        nodes_to_connect = [nodes_to_connect, Graph_connections(i,1)];
        distances = [distances; [Graph_connections(i,1),norm([node_y,node_x]-[row,col])]];
    end
        Graph_connections(nodes,1) = sub2ind(size(map),node_y,node_x);
        if(size(distances)>0)
            sorted_distances = sortrows(distances,2);
            for i = 1:min(max_nodes_connect,size(sorted_distances,1))
                Graph_connections(nodes,i+1) = sorted_distances(i,1);
                [row,col] = ind2sub(size(map),sorted_distances(i,1));
                if(line_on_obstacle==false)
                    disp("Line is not on obstacle")
                    hold on
                    plot([node_x,col],[node_y,row],"green","LineWidth",1.5);
                    continue;                    
                else
                    disp("Line is on obstacle: " + [node_x,col] + " " + [node_y,row]);
                    break;
                end
            end
            disp("==========================")
        end
end

function on_line = check_obstacle(map,node_x,node_y,row,col)
    on_line = 0;
    my_line = line([node_x,col],[node_y,row]);
    line_spacing = max(abs(my_line.XData(1) - my_line.XData(2))+1,abs(my_line.XData(1) - my_line.XData(2))+1);
    x_coordinates_line = round(linspace(my_line.XData(1),my_line.XData(2),line_spacing));
    y_coordinates_line = round(linspace(my_line.YData(1),my_line.YData(2),line_spacing));
    for i = 1:line_spacing
        if(map(x_coordinates_line(i),y_coordinates_line(i))==1)
            disp("ON OBSTACLE: " + x_coordinates_line(i) + " " + y_coordinates_line(i));
            on_line = true;
            break;
        end
    end
end

The check_obstacle function is used to check if the points on the line are in the boundaries of obstacles. What am I missing here?

enter image description here


Solution

  • close all;clear all;clc
    format short
    
    nx=100;ny=100;     % grid size
    [X,Y]=meshgrid(1:nx,1:ny);
    
    Nobst=3      % amount obstacles
    Nnet=30      % amount net points
    Dmax=100     % net segment max length
    
    % OBSTACLES
    % define obstacles. 
    % Following are as defined in question
    P1=[50 50; 75 50; 75 75; 50 75; 50 50];
    P2=[25 30; 25 40; 35 40; 35 30; 25 30];
    P3=[25 60; 25 80; 35 80;35 60;25 60];
    
    % obstacle points all in one array
    Pobst=[P1(:);P2(:);P3(:)];
    Pobst=reshape(Pobst,[size(P1,1),size(P1,2),Nobst]);
    
    % plot obstacles
    hp=[]
    figure(1)
    ax=gca
    hp=patch(squeeze(Pobst(:,1,:)),squeeze(Pobst(:,2,:)),[.5 .5 .5])
    axis([1 nx 1 ny]);grid on
    hp.EdgeAlpha=0;
    ax.DataAspectRatio=[1 1 1]
    hold(ax,'on')
    
    % obstacle segments list : [x1 y1 x2 y2 d(X1,Y1)]
    
    Lobst1=[]
    for k=2:1:size(P1,1)
        Lobst1=[Lobst1; [P1(k-1,:) P1(k,:) ((P1(k-1,1)-P1(k,1))^2+(P1(k-1,2)-P1(k,2))^2)^.5]];
    end
    Lobst2=[]
    for k=2:1:size(P2,1)
        Lobst2=[Lobst2; [P2(k-1,:) P2(k,:) ((P2(k-1,1)-P2(k,1))^2+(P2(k-1,2)-P2(k,2))^2)^.5]];
    end
    Lobst3=[]
    for k=2:1:size(P3,1)
        Lobst3=[Lobst3; [P3(k-1,:) P3(k,:) ((P3(k-1,1)-P3(k,1))^2+(P3(k-1,2)-P3(k,2))^2)^.5]];
    end
    Lobst=[Lobst1;Lobst2;Lobst3]
    
    %% NETWORK
    % all grid points outside obstacles
    [in1,on1]=inpolygon(X(:),Y(:),P1(:,1),P1(:,2));
    [in2,on2]=inpolygon(X(:),Y(:),P2(:,1),P2(:,2));
    [in3,on3]=inpolygon(X(:),Y(:),P3(:,1),P3(:,2));
    
    Xout=X(~in1 & ~in2 & ~in3);Yout=Y(~in1 & ~in2 & ~in3);
    % plot(ax,Xout,Yout,'og','LineStyle','none')  % check
    
    % choose Nnet points outside obstacles
    nP2=randi([1 numel(Xout)],Nnet,1);
    Pnet=[Xout(nP2) Yout(nP2)];
    
    plot(ax,Pnet(:,1),Pnet(:,2),'or','LineStyle','none')
    

    enter image description here

    % net segments list [x1 y1 x2 y2 d(X2,Y2) 0/1] 6th column [0 1] 1: draw 0: do not draw
    nLnet=nchoosek([1:size(Pnet,1)],2);
    Lnet=[Pnet(nLnet(:,1),1) Pnet(nLnet(:,1),2) ...   % segment 1st point
             Pnet(nLnet(:,2),1) Pnet(nLnet(:,2),2) ...    % segment 2nd point
             ((Pnet(nLnet(:,1),1)-Pnet(nLnet(:,1),2)).^2+(Pnet(nLnet(:,2),1)+Pnet(nLnet(:,2),2)).^2).^.5 ... % segment length
             ones(size(nLnet,1),1)];   
    
    % check all net links are plotted
    for k=1:1:size(Lnet,1)
        plot(ax,[Lnet(k,1) Lnet(k,3)],[Lnet(k,2) Lnet(k,4)],'b');
        hold(ax,'on')
    end
    

    enter image description here

    % remove segments longer than Dmax
    Lnet=sortrows(Lnet,5,'descend');
    [~,n1]=max(Lnet(:,5)<=Dmax);
    Lnet(1:n1-1,:)=[];
    for k=1:1:size(Lnet,1)
        plot(ax,[Lnet(k,1) Lnet(k,3)],[Lnet(k,2) Lnet(k,4)],'r');
        hold(ax,'on')
    end
    

    enter image description here

    %%
    

    Redrawing and NOT removing net segments longer than Dmax

    close all
    hp=[]
    figure(1)
    ax=gca
    hp=patch(squeeze(Pobst(:,1,:)),squeeze(Pobst(:,2,:)),[.5 .5 .5])
    axis([1 nx 1 ny]);grid on
    hp.EdgeAlpha=0;
    ax.DataAspectRatio=[1 1 1]
    hold(ax,'on')
    plot(ax,Pnet(:,1),Pnet(:,2),'or','LineStyle','none')
    Lnet=[Pnet(nLnet(:,1),1) Pnet(nLnet(:,1),2) ...   % segment 1st point
             Pnet(nLnet(:,2),1) Pnet(nLnet(:,2),2) ...    % segment 2nd point
             ((Pnet(nLnet(:,1),1)-Pnet(nLnet(:,1),2)).^2+(Pnet(nLnet(:,2),1)+Pnet(nLnet(:,2),2)).^2).^.5 ... % segment length
             ones(size(nLnet,1),1)];
    
    % check what pair segments intersect
    for k2=1:1:size(Lnet,1)
        allclear=ones(1,size(Lobst,1));
    %     allclear=zeros(1,size(Lobst,1));
        for k1=1:1:size(Lobst,1)
            
            % segments are contained in lines : check lines intersect
            x1o=Lobst(k1,1);y1o=Lobst(k1,2);x2o=Lobst(k1,3);y2o=Lobst(k1,4);
            x1n=Lnet(k2,1);y1n=Lnet(k2,2);x2n=Lnet(k2,3);y2n=Lnet(k2,4);
            
            x1=x1o;x2=x2o;y1=y1o;y2=y2o;
            x3=x1n;x4=x2n;y3=y1n;y4=y2n;
            
            % t1 : x parameter
            t1=((x1-x3)*(y3-y4)-(y1-y3)*(x3-x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));  
            xp=x1+t1*(x2-x1);  % xp : crossing x coordinage
            
            % u1 : y parameter
            u1=-((x1-x2)*(y1-y3)-(y1-y2)*(x1-x3))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
            yp=y1+t1*(y2-y1);  % yp : crossing y coordinate
    
            % checks
            plot(ax,x1o,y1o,'c*');plot(ax,x2o,y2o,'c*');plot(ax,[x1o x2o],[y1o y2o],'c')
            plot(ax,x1n,y1n,'m*');plot(ax,x2n,y2n,'m*'); % plot(ax2,[x1n x2n],[y1n y2n],'m')
             
            
            m1o=(y2o-y1o)/(x2o-x1o);       % slopes
            m2n=(y2n-y1n)/(x2n-x1n) ; 
      
            if (t1>=0 && t1<=1 && u1>=0 && u1<=1) && ...
               (xp>=0 || xp<=nx || yp>=0 || yp<=ny)
                    allclear(k1)=0;  % then do not plot this segment
            end
    
        end
        
        if sum(allclear)==size(Lobst,1)   %k2-th net segment hits no obstacles
            plot(ax,x1n,y1n,'m*');plot(ax,x2n,y2n,'m*');plot(ax,[x1n x2n],[y1n y2n],'m')
            elseif sum(allclear)~=size(Lobst,1)
                Lnet(k2,end)=0;
        end
         
    end
    

    enter image description here

    enter image description here

    enter image description here

    Comments

    1.- Note I have added format short because with format long there is decimal accretion right at the bottom of a few intersection points, that believe it or not, cause some false results.

    2.- I like Torsen's explanation to find line segment intersections available here.

    3.- There are faster ways to implement the main loop, but I make it go through all net-segment vs obstacle-element in case you may need it this way, to for instance count hits.

    4.- There's also room for improvement in the way Lobst is generated.

    5.- These lines

    x1=x1o;x2=x2o;y1=y1o;y2=y2o;
    x3=x1n;x4=x2n;y3=y1n;y4=y2n;
    

    it's just an easy way to plug in formulation to already written lines, reducing amount variables is also left for next version.