A星算法函数

%% A星算法函数
% 输入参数:start起始位置 goal目标位置 mapSize地图尺寸 map地图二维数组 show_flag: 显示过程标志位,0:直接显示  1:显示搜索过程
%          take是否取货物  save是否存货物 0代表不是 1代表是
% 输出参数:road最短路径列表  turnTime转弯次数 travelTime行驶时间
function [road, turnTimes, travelTime] = Astar(AGV, goal, mapSize, map, show_flag, take, save, travelTime)
    global UnitTime TurningTime
    global PickUpTime
    global DrapTime
    UnitTime = 1;  TurningTime = 1;
    PickUpTime = 1;  DrapTime = 1;
    % 取出坐标值
    startx = AGV(1);
    starty = AGV(2);
    endx = goal(1);
    endy = goal(2);

    %-----已探索边界列表-----%
    f = 0 + abs(startx-endx)+abs(starty-endy);  %开始的当前代价为0
    already_frontier = {f,startx,starty,0,[startx,starty]};

    %-----待探索边界列表-----%
    % (第一行是总代价,第二行是横坐标,第三行是纵坐标,第四行是当前代价,第五行是路径)
    frontier = cell(0,5);
    % 寻找邻居点
    % N的第一列和第二列是坐标 第三列是对应的转弯代价
    N = find_frontier(startx, starty, mapSize.up, mapSize.up+mapSize.left, mapSize.down, mapSize.down+mapSize.right, map, already_frontier);
    % 循环当前边界邻居
    for i = 1:size(N,1)
        % 1.传统代价函数 欧式距离 不会出现局部绕行,但可能会出现转弯较多的情况
        % g = sqrt((N(1, i) - startx)^2 + (N(2, i) - starty)^2); 
        % h = sqrt((N(1, i) - endx)^2 + (N(2, i) - endy)^2); 
        % f = g + h ; 

        % 2.曼哈顿距离 转弯较少,但可能会出现局部绕行
        g = norm([startx,starty]-[N(i,1),N(i,2)]);   % 计算当前代价
        h = abs(N(i,1)-endx)+abs(N(i,2)-endy);
        f = g + h ; 
        frontier{i,1} = f;
        frontier{i,2} = N(i,1);
        frontier{i,3} = N(i,2);
        frontier{i,4} = g;
        frontier{i,5} = [[startx,starty];[N(i,1),N(i,2)]];     % 将当前节点设置为父亲
    end
    frontier = sortrows(frontier,1);

    %-----待插入待边界列表的列表-----%
    temp_frontier = cell(0,5);
    
    % 更新待探索列表
    while isempty(frontier) == 0
        % 1.将当前节点移入already_frontier
        current = frontier(1,:);
        already_frontier(end+1,:) = frontier(1,:);    %当前节点加入already_frontier
        frontier(1,:) = [];     %删除当前节点(frontier的第一行)

        % 当前节点坐标
        mx = current{1,2};
        my = current{1,3};

        if show_flag == 2
            plot(current{1,2},current{1,3},'g*');   %当前节点标绿色*号
        end

        % 2.查找当前节点边界(忽略already_frontier中的)
        N = find_frontier(mx,my,mapSize.up,mapSize.up+mapSize.left,mapSize.down,mapSize.down+mapSize.right,map,already_frontier);
        % 循环当前节点边界
        for i = 1:size(N,1)
            % 3.判断边界节点是否在frontier中
            b = find([frontier{:,2}]==N(i,1));
            a = b([frontier{b,3}]==N(i,2));

            % 曼哈顿距离 转弯较少,但可能会出现局部绕行
            g = current{1,4} + norm([mx, my]-[N(i,1),N(i,2)]) + N(i, 3);   % 计算当前代价+转弯代价
            h = abs(N(i,1)-endx)+abs(N(i,2)-endy);
            f = g + h; % 加上转弯代价

            % 3.1 如果边界节点已经在frontier中,检查其当前代价是否更小
            if a > 0
                if frontier{a,4} > g   %如果经过边界的当前代价更小,则重新计算g、f,并更换路径
                    frontier{a,5} = [current{1,5};[N(i,1),N(i,2)]];
                    frontier{a,4} = g;  %重新计算当前代价
                    frontier{a,1} = f;  %重新计算总代价
                end
            % 3.2 如果边界节点没有加入frontiner则加入,成为待探测节点
            else
                temp{1,1} = f;
                temp{1,2} = N(i,1);
                temp{1,3} = N(i,2);
                temp{1,4} = g;
                temp{1,5} = [current{1,5};[N(i,1),N(i,2)]];     %将当前节点设置为父亲
                %frontier = [temp;frontier];
                temp_frontier = [temp;temp_frontier];   %合法边界插入到frontier的第二列(因为第一列之后要移动到already_frontier)
            end
        end

        % 4.如果新加入already_frontier的节点是终点,则跳出循环
        if already_frontier{end,2}==endx && already_frontier{end,3}==endy
            % plot(already_frontier{end,5}(:,1),already_frontier{end,5}(:,2),'r-','LineWidth',2);   %画出最终路径
            % drawnow;
            break; 
        end

        % 5.插入排序
        frontier = [frontier; temp_frontier];
        frontier = sortrows(frontier,1);
        temp_frontier = cell(0,5);    %清空临时列表
        

        if show_flag == 1
            plot(already_frontier{end,5}(:,1),already_frontier{end,5}(:,2),'r-','LineWidth',2);
            drawnow; % 更新图窗并处理回调
            pause(0.05)
        end
    end

    road = already_frontier{end,5};    %最短路径列表

    % 为了计算后续的时间窗算法,需要得出最短路径的实际转弯次数
    turnTimes = 0;
    road(:,3) = zeros(size(road,1) ,1);
    for i = 2:size(road,1)-1
        if isTurn(road(i-1,:), road(i,:), road(i+1,:))
            turnTimes = turnTimes + 1;
            road(i,3) = 1; % 转弯标记
        end
    end

    % 求出路径对应的时间窗信息
    % 定义:小车移动一个格子的时间为1s,转向花费的时间为1s
    %       小车到达取货点时取货时间1s 卸货时间1s
    road(:,4) = zeros(size(road,1) ,1); % 总时间
    road(:,5) = zeros(size(road,1) ,1); % 是否负载

    % 将刚卸货的点的负载优先级设置为3 表示正在卸货 优先级最高
    if (road(1,1) == 4 && road(1,2) == 16) || (road(1,1) == 27 && road(1,2) == 16)...
            || (road(1,1) == 16 && road(1,2) == 4) || (road(1,1) == 16 && road(1,2) == 27)
        road(1,5) = 3;
    end

    tmp = road(:,3); % 是否发生了转向
    % 1.如果是去取货物 即路程是空载运行
    if take == 1
        for i = 1:size(road, 1)
            road(i, 4) = travelTime;
            if i == size(road, 1)
                break
            end
            if tmp(i) == 0 % 如果没有转向 则表明正常行驶
                travelTime = travelTime + UnitTime; 
            else           % 如果有转向 则正常行驶时间+转弯时间
                travelTime = travelTime + TurningTime;
            end
        end
    end

    % 1.如果是去卸货物 即路程是带载运行
    if save == 1
        for i = 1:size(road, 1)
            road(i, 4) = travelTime;
            % 四个入库点    入库A [4,16] [27,16] 入库B [16,4] [16,27] 
            if isequal(goal, [4,16]) || isequal(goal, [27,16]) % 代表是去优先级高的卸货点
                road(i, 5) = 2;
            else
                road(i, 5) = 1; % 代表是去优先级低的卸货点
            end
            if i == size(road, 1)
                break
            end
            if tmp(i) == 0
                travelTime = travelTime + UnitTime;
            else
                travelTime = travelTime + TurningTime;
            end
        end
    end

end

你可能感兴趣的:(算法)