MATLAB implementation of three-dimensional obstacle avoidance of RRT algorithm

Posted by delboy1978uk on Sun, 26 Dec 2021 11:53:19 +0100

RRT algorithm, also known as fast random expansion number algorithm, is a universal path planning algorithm. Why it is a universal algorithm, because it is highly possible to find a path under any harsh conditions.

However, such algorithms often have disadvantages:

1. Every iteration is to find points randomly, which leads to the fact that the iteration time depends largely on luck

2. Due to the strong randomness of this algorithm, it is difficult to find a path quickly in a very simple obstacle avoidance environment

In this paper, the initial RRT algorithm (without any improvement of the algorithm) is simulated in code. It is intended to find a path under the condition of multiple obstacles. The algorithm itself does not involve optimization, so there is no optimization for the found path.

RRT algorithm model is explained in detail on the Internet and is very easy to understand. It is not repeated here, but only part of the code implementation is given. This is some of my own opinions on RRT algorithm, maybe some simple, just hope to give you more or less help, don't spray if you don't like it.

Code introduction:

In a space full of cuboid and cylinder obstacles, arbitrary points are given as target points for path planning

After the iteration, the path found is shown by the red line in the following part

Code part:

Main function, file named rrtmain m

%% Empty variable

clear
clc

%% Import data
[dat_chicun,dat_jiaodian,dat_xia] = xlsData();

%% Drawing
color_mat=rand(size(dat_jiaodian,1),3);   %Generate random matrix as color
hold on   %Draw on the same image
grid on   % Draw grid
for k1=1:size(dat_jiaodian,1)
    plotcube(dat_chicun(k1,[2,1,3]),dat_jiaodian(k1,1:3),0.6,color_mat(k1,:))%This is a function of drawing a cuboid obstacle
end

for k2=1:size(dat_xia,1)
    plot_cylinder(dat_xia(k2,1:3),dat_xia(k2,4),dat_xia(k2,5),1,rand(1,3));%This is a function of drawing a cylindrical obstacle
end
axis([0 2000 0 2000 -200 600 ])   %Set the visualization range of the image
axis equal     % The visual intervals of image axes are equal  
xlabel('x');   
ylabel('y')


%% Data reprocessing

num_cube = size(dat_jiaodian(2:end,:),1);                  %Number of cuboid obstacles 15
[dat_cube,dat_pingban] = cube_coor_deal(dat_jiaodian);     %The corner elements of the box are stored in the cell array for easy indexing
num_cylinder = size(dat_xia,1);                            %Number of cylindrical obstacles
dat_cylinder = cylinder_coor_deal(dat_xia);                %The cylinder data is stored in the cell array for easy access

% num_cube:     Number of cuboid obstacles
% dat_cube:     Cell array of box corners
% dat_pingban:  Plate corner cell array
% num_cylinder: Number of cylindrical obstacles
% dat_cylinder: Cylindrical obstacle data cell

%% rrt Algorithm part
GG = [704 700 -30;612 700 -30;537 680 -30;447 980 -30];%Target point coordinates. If there are multiple target points, put the coordinates of each target point here
PATH = [];
for k3 = 1:size(GG,1)-1
        start = GG(k3,:);
        goal = GG(k3+1,:);
        path_best = RRT(num_cube,dat_cube,num_cylinder,dat_cylinder,start,goal);%Every time the best path is found, it is stored in this variable
                                                                                %Note: this path point is processed through redundant points, so there are few points
%         line(path_best(:,1),path_best(:,2),path_best(:,3),'LineWidth',2,'Color','b');%Draw the set of points to find the path with a straight line
        PATH = [PATH;path_best];  
end
function dist = calculate_distance3(mat_start,mat_goal)
%% This function calculates the distance of 3D points
dist = sqrt((mat_start(1)-mat_goal(1))^2+(mat_start(2)-mat_goal(2))^2+(mat_start(3)-mat_goal(3))^2);
function flag = collision_checking_cube(num_cube,dat_cube,coor_new,coor_near,Delta,deta)
%Return 1 in case of collision
flag = 0;
% flag1 = 0;
% flag2 = 0;
% flag2_mid1 = 0;
% flag2_mid2 = 0;
% flag2_mid3 = 0;
for k1=1:num_cube
    x_min = min(dat_cube{k1}(:,1));
    x_max = max(dat_cube{k1}(:,1));
    y_min = min(dat_cube{k1}(:,2));
    y_max = max(dat_cube{k1}(:,2));
    z_min = min(dat_cube{k1}(:,3));
    z_max = max(dat_cube{k1}(:,3));
    
    for r=0:Delta/deta:Delta
        coor_mid = rrt_steer(coor_new,coor_near,r);
        if ((x_min<coor_mid(1))&&(coor_mid(1)<x_max))&&((y_min<coor_mid(2))&&(coor_mid(2)<y_max))&&((z_min<coor_mid(3))&&(coor_mid(3)<z_max))
             flag = 1;
             break;
        end
    end


end
        
function flag = collision_checking_cylinder(num_cylinder,dat_cylinder,coor_new,coor_near,Delta,deta)
%Return 1 in case of collision
flag = 0;
 
for k1=1:num_cylinder
    x_coor = dat_cylinder{k1}(1);
    y_coor = dat_cylinder{k1}(2);
    z_coor = dat_cylinder{k1}(3);
    R = dat_cylinder{k1}(4)/2;
    height = dat_cylinder{k1}(5);
     
    for r=Delta/deta:Delta
        coor_mid = rrt_steer(coor_new,coor_near,r);
        if (((x_coor-coor_mid(1))^2+(y_coor-coor_mid(2))^2-R^2) < 0) && (z_coor<coor_mid(3)) && (coor_mid(3)<z_coor+height)
             flag = 1;
             break;
        end
    end
end
function [dat_cube,dat_pingban] = cube_coor_deal(dat_jiaodian)
%% Store 15 obstacle corners in 15 cell arrays
mid1 = dat_jiaodian(1:end,:);
lin = size(mid1,1);
col = size(mid1,2);
mid3 = zeros(col/3,3);
dat_cube_mid = cell(lin,1);

for k1=1:lin
    for k2=1:col/3
        mid2 = mid1(k1,3*(k2-1)+1:3*(k2-1)+3);
        mid3(k2,:) = mid2;
    end
    dat_cube_mid{k1}(:,:) = mid3;
end
dat_cube = dat_cube_mid;
dat_cube(1,:)=[];
dat_pingban{1} = dat_cube_mid{1};
function dat_cylinder = cylinder_coor_deal(dat_xia)
%% The diameter and height of 16 cylindrical obstacles are put in the cell array. As for why they exist in the cell array, there is no why, personal preference
lin = size(dat_xia,1);
dat_cylinder=cell(lin,1);
for k1=1:lin
    dat_cylinder{k1}=dat_xia(k1,:);
end
function path_best = delete_redundant_points(path,num_cylinder,dat_cylinder,num_cube,dat_cube)

num_points = size(path,1);
count = 1;
start_point = path(1,:);
index = zeros(1,num_points);
for k1 = 1:num_points-2
    count = count+1;
    final_point = path(count+1,:);
    if  (collision_checking_cylinder(num_cylinder,dat_cylinder,final_point,start_point,calculate_distance3(final_point,start_point),3)||...
           collision_checking_cube(num_cube,dat_cube,final_point,start_point,calculate_distance3(final_point,start_point),3))
       start_point = path(count,:);
    else
        index(count) = count;
    end
end
index(index==0) = [];
path([index(end:-1:1)],:) = [];
path_best = path;
    
function plot_cylinder(coor,diameter,height,facealpha,color)

%% plot_cylinder(dat_xia(k2,1:3),dat_xia(k2,4),dat_xia(k2,5),1,rand(1,3));
%  The first parameter is the coordinate value of the bottom center of the cylinder, the second parameter is the cylinder diameter, and the third parameter is the cylinder height
%  The fourth parameter is transparency and the fifth parameter is color matrix

%% Function explanation: treat this function as a black box. You only need to remember the input of the function to know what it is for and what is inside
%% The implementation is too complex to explain clearly

% coor:         Central coordinates
% diameter:     diameter
% height:       height
% facealpha:    transparency
% color:        colour

r = diameter/2;
theta = 0:0.3:pi*2;
hold on

for k1 = 1:length(theta)-1
    X=[coor(1)+r*cos(theta(k1)) coor(1)+r*cos(theta(k1+1)) coor(1)+r*cos(theta(k1+1)) coor(1)+r*cos(theta(k1))];
    Y=[coor(2)+r*sin(theta(k1)) coor(2)+r*sin(theta(k1+1)) coor(2)+r*sin(theta(k1+1)) coor(2)+r*sin(theta(k1))];
    Z=[coor(3),coor(3),coor(3)+height,coor(3)+height];
    h=fill3(X,Y,Z,color);
    set(h,'edgealpha',0,'facealpha',facealpha)  
end

X=[coor(1)+r*cos(theta(end)) coor(1)+r*cos(theta(1)) coor(1)+r*cos(theta(1)) coor(1)+r*cos(theta(end))];
Y=[coor(2)+r*sin(theta(end)) coor(2)+r*sin(theta(1)) coor(2)+r*sin(theta(1)) coor(2)+r*sin(theta(end))];
Z=[coor(3),coor(3),coor(3)+height,coor(3)+height];
h=fill3(X,Y,Z,color);
set(h,'edgealpha',0,'facealpha',facealpha)


fill3(coor(1)+r*cos(theta),coor(2)+r*sin(theta),coor(3)*ones(1,size(theta,2)),color)
fill3(coor(1)+r*cos(theta),coor(2)+r*sin(theta),height+coor(3)*ones(1,size(theta,2)),color)
view(3)
function plotcube(varargin)
%% plotcube(dat_chicun(k1,[2,1,3]),dat_jiaodian(k1,1:3),0.6,color_mat(k1,:))
%% The first parameter is the length, width and height value of each box, the second parameter is the first coordinate value of the corner, and the third parameter is transparency with a range of 0-1. 
%% The fourth parameter is the color matrix[a,b,c]  abc Each value range is 0-1
%% 
inArgs = { ...
  [10 56 100] , ... % Default edge sizes (x,y and z)
  [10 10  10] , ... % Default coordinates of the origin point of the cube
  .7          , ... % Default alpha value for the cube's faces
  [1 0 0]       ... % Default Color for the cube
  };

inArgs(1:nargin) = varargin;

[edges,origin,alpha,clr] = deal(inArgs{:});

XYZ = { ...
  [0 0 0 0]  [0 0 1 1]  [0 1 1 0] ; ...
  [1 1 1 1]  [0 0 1 1]  [0 1 1 0] ; ...
  [0 1 1 0]  [0 0 0 0]  [0 0 1 1] ; ...
  [0 1 1 0]  [1 1 1 1]  [0 0 1 1] ; ...
  [0 1 1 0]  [0 0 1 1]  [0 0 0 0] ; ...
  [0 1 1 0]  [0 0 1 1]  [1 1 1 1]   ...
  };

XYZ = mat2cell(...
  cellfun( @(x,y,z) x*y+z , ...
    XYZ , ...
    repmat(mat2cell(edges,1,[1 1 1]),6,1) , ...
    repmat(mat2cell(origin,1,[1 1 1]),6,1) , ...
    'UniformOutput',false), ...
  6,[1 1 1]);


cellfun(@patch,XYZ{1},XYZ{2},XYZ{3},...
  repmat({clr},6,1),...
  repmat({'FaceAlpha'},6,1),...
  repmat({alpha},6,1)...
  );

view(3);
function path_best = RRT(num_cube,dat_cube,num_cylinder,dat_cylinder,start,goal)
%% Process initialization

Delta=2;                % Set the expansion step size and the maximum distance allowed by the expansion node. The larger the data, the faster the iteration, but the worse the effect than the optimal solution
max_iter = 10000;        % The maximum number of iterations. If the path is not found after this number, it is considered that the path cannot be found
Map = [goal(1)-start(1),goal(2)-start(2),goal(3)-start(3)];
count = 1;

%% Build initialization tree
T.x(1) = start(1);
T.y(1) = start(2);
T.z(1) = start(3);
T.xpar(1) = goal(1);
T.ypar(1) = goal(2);
T.zpar(1) = goal(3);
T.dist(1) = 0;
T.indpre(1) = 0;

tic
for iter = 1:max_iter

    % step1: Random sampling on map
    coor_rand = rrt_sample(Map,goal,start);     %Random sampling in space, coor_rand It's a 1×3  Array of
    % plot3(coor_rand(1),coor_rand(2),coor_rand(3),'r*')
    
    % step2 : Traverse the tree to find the nearest parent node
    [coor_near,coor_index] = rrt_near(coor_rand,T);
    
    % step3: Expand to get new nodes
    coor_new = rrt_steer(coor_rand,coor_near,Delta);
    
    % step4: collision detection ,In case of collision, 1 will be returned
    flag1 = collision_checking_cube(num_cube,dat_cube,coor_new,coor_near,Delta,3);%This part is a function to detect whether it collides with a box obstacle
    flag2 = collision_checking_cylinder(num_cylinder,dat_cylinder,coor_new,coor_near,Delta,3);%This part is the parameter to detect whether it collides with the cylinder obstacle

    if flag1 || flag2
        continue;
    end

    count = count+1;
    % step5:Insert new point
    T.x(count) = coor_new(1);
    T.y(count) = coor_new(2);
    T.z(count) = coor_new(3);
    T.xpar(count) = coor_near(1);
    T.ypar(count) = coor_near(2);
    T.zpar(count) = coor_near(3);
    T.dist(count) = calculate_distance3(coor_new,coor_near);
    T.indpre(count) = coor_index;
    line([coor_near(1),coor_new(1)],[coor_near(2),coor_new(2)],[coor_near(3),coor_new(3)],'LineWidth',1);
    pause(0.1); %Pause 0.1s,bring RRT The expansion process is easy to observe;
    % be careful:  pause Pause the function when the function is used. This part is very important if it is used to display animation. However, if it is not added, the animation will be still and the moving picture cannot be displayed

    % step6:After each iteration, check whether it can be directly connected to the end point
    if    ~(collision_checking_cylinder(num_cylinder,dat_cylinder,goal,coor_new,calculate_distance3(goal,coor_new),20)||...
           collision_checking_cube(num_cube,dat_cube,goal,coor_new,calculate_distance3(goal,coor_new),20))
        count = count+1;
        T.x(count) = goal(1);
        T.y(count) = goal(2);
        T.z(count) = goal(3);
        T.xpar(count) = coor_new(1);
        T.ypar(count) = coor_new(2);
        T.zpar(count) = coor_new(3);
        T.dist(count) = calculate_distance3(coor_new,goal);
        T.indpre(count) = 0;
        line([goal(1),coor_new(1)],[goal(2),coor_new(2)],[goal(3),coor_new(3)],'LineWidth',3,'MarkerSize',2);
        pause(0.1); %Pause 0.1s,bring RRT The expansion process is easy to observe
        break;
    end

    
end

toc

% After finding the path point, the algorithm finds the parent point set that reaches the destination and stores it in the path Variable
if iter>max_iter
    error('Path planning fails when the maximum number of iterations is exceeded');
end
path(1,1) = T.x(end);path(1,2) = T.y(end);path(1,3) = T.z(end);
path(2,1) = T.x(end-1);path(2,2) = T.y(end-1);path(2,3) = T.z(end-1);
count2 = 2;
ind_pre = T.indpre(end-1);
if iter<=max_iter
    while ~(ind_pre==0)
        count2 = count2+1;
        path(count2,1) = T.x(ind_pre);
        path(count2,2) = T.y(ind_pre);
        path(count2,3) = T.z(ind_pre);
        ind_pre = T.indpre(ind_pre);
    end

end
% line(path(:,1),path(:,2),path(:,3),'LineWidth',1,'Color','r');

        
%% RRT The algorithm finds all sets of new points, and then removes redundant points
path_best = delete_redundant_points(path,num_cylinder,dat_cylinder,num_cube,dat_cube);
line(path_best(:,1),path_best(:,2),path_best(:,3),'LineWidth',3,'Color','r');

function [coor_near,coor_index] = rrt_near(coor_rand,T)

min_distance = calculate_distance3(coor_rand,[T.x(1),T.y(1),T.z(1)]);

for T_iter=1:size(T.x,2)
    temp_distance=calculate_distance3(coor_rand,[T.x(T_iter),T.y(T_iter),T.z(T_iter)]);
    
    if temp_distance<=min_distance
        min_distance=temp_distance;
        coor_near(1)=T.x(T_iter);
        coor_near(2)=T.y(T_iter);
        coor_near(3)=T.z(T_iter);
        coor_index=T_iter;
    end
end
function coor_rand = rrt_sample(Map,goal,start)

rat = 1.5;
if unifrnd(0,1)<0.5
   coor_rand(1)= unifrnd(-0.2,rat)* Map(1);   
   coor_rand(2)= unifrnd(-0.2,rat)* Map(2);   
   coor_rand(3)= unifrnd(-0.2,rat)* Map(3);   
   coor_rand = coor_rand+start;
else
   coor_rand=goal;
end
function coor_new = rrt_steer(coor_rand,coor_near,Delta)

 deltaX = coor_rand(1)-coor_near(1);
 deltaY = coor_rand(2)-coor_near(2);
 deltaZ = coor_rand(3)-coor_near(3);
 r = sqrt(deltaX^2+deltaY^2+deltaZ^2);
 fai = atan2(deltaY,deltaX);  
 theta = acos(deltaZ/r);
 
 
 R = Delta;
 x1 = R*sin(theta)*cos(fai);
 x2 = R*sin(theta)*sin(fai);
 x3 = R*cos(theta);
 
 coor_new(1) = coor_near(1)+x1;
 coor_new(2) = coor_near(2)+x2;
 coor_new(3) = coor_near(3)+x3;
 
end
 
function PATH = smooth_deal(PATH)

hold on;
x = PATH(:,1)';
y = PATH(:,2)';
z = PATH(:,3)';
%Cubic spline interpolation
t1=1:1:size(PATH,1);
t=1:0.5:size(PATH,1);
XX=spline(t1,x,t);
YY=spline(t1,y,t);
ZZ=spline(t1,z,t);
plot3(XX,YY,ZZ,'r-')
 
view(3)
function [dat_chicun,dat_jiaodian,dat_xia] = xlsData()
%% Fifteen cuboid obstacles are wide, long and high. The first row is not, and the first row is the floor. They are not used as obstacles

dat_chicun =  [0.1,  0.1,    0.1;
                624,   358,  700;
                140,   173,  267;
                121,   210,  105;
                150,    90,  130;
                150,    90,  130;
                115,    88,  122;
                140,   103,  142;
                140,   103,  142;
                135,    75,   91;
                75,    160,  216;
                75,    160,  216;
                111,    60,   98;
                118,    44,   31;
                75,    160,  216;
                75,     88,  125];    
 %% Corner coordinates of fifteen cuboid obstacles, the first row is not
            
  dat_jiaodian = [0,0,0,0,0,1,1767,0,1,1767,0,0,1767,1679,0,1767,1679,1,0,1679,0,0,1679,1;
                  704,573,-80,704,573,620,1062,573,620,1062,573,-80,1062,1197,-80,1062,1197,620,704,1197,-80,704,1197,620;
                  1550,1539,1,1550,1539,268,1723,1539,268,1723,1539,1,1723,1679,1,1723,1679,268,1550,1679,1,1550,1679,268;
                  150,1280,-74,150,1280,31,360,1280,31,360,1280,-74,360,1401,-74,360,1401,31,150,1401,-74,150,1401,31;
                  264,1080,1,264,1080,131,354,1080,131,354,1080,1,354,1230,1,354,1230,131,264,1230,1,264,1230,131;
                  264,910,1,264,910,131,354,910,131,354,910,1,354,1060,1,354,1060,131,264,1060,1,264,1060,131;
                  398,1020,1,398,1020,123,486,1020,123,486,1020,1,486,1135,1,486,1135,123,398,1135,1,398,1135,123;
                  447,858,-40,447,858,102,550,858,102,550,858,-40,550,998,-40,550,998,102,447,998,-40,447,998,102;
                  447,710,-40,447,710,102,550,710,102,550,710,-40,550,850,-40,550,850,102,447,850,-40,447,850,102;
                  537,565,-41,537,565,50,612,565,50,612,565,-41,612,700,-41,612,700,50,537,700,-41,537,700,50;
                  1450,1320,1,1450,1320,217,1610,1320,217,1610,1320,1,1610,1395,1,1610,1395,217,1450,1395,1,1450,1395,217;
                  1450,1201,1,1450,1201,217,1610,1201,217,1610,1201,1,1610,1276,1,1610,1276,217,1450,1276,1,1450,1276,217;
                  1170,579,-48,1170,579,50,1230,579,50,1230,579,-48,1230,690,-48,1230,690,50,1170,690,-48,1170,690,50;
                  451,1160,1,451,1160,32,495,1160,32,495,1160,1,495,1278,1,495,1278,32,451,1278,1,451,1278,32;
                  1390,640,1,1390,640,217,1550,640,217,1550,640,1,1550,715,1,1550,715,217,1390,715,1,1390,715,217;
                  1262,525,1,1262,525,126,1350,525,126,1350,525,1,1350,600,1,1350,600,126,1262,600,1,1262,600,126];
              
              
 %% The first three columns are the coordinates of the center of the circle at the bottom of sixteen cylindrical obstacles, the fourth column is the diameter, and the fifth column is the height
              
 dat_xia = [  952,1330,-51,50,181;
             1032,1330,-51,50,181;
             1112,1330,-51,50,181;
             1430,1079,-51,40,145;
             1420,1032,-51,40,145;
             1410,985,-51,40,145;
             882,1330,-45,30,123;
             707,1330,-51,50,181;
             607,1330,-45,30,123;
             1450,1600,-43,30,123;
             1310,500,-53,50,167;
             1310,430,-53,50,167;
             1112,1570,1,130,310;
             960,1570,1,60,215;
             360,1570,1,180,370;
             657,1570,1,180,370];

Summary:

The code may be a little long, but it's much cleaner to divide it into multiple sub functions. Specific operation steps: copy each code into a new script file. Except that the main function is named arbitrarily, other script files are named function names. The specific form is as follows.

Each sub function has comments, and corresponding comments are added to the main function. I hope it can be helpful to you. Thank you for your support~

Topics: MATLAB