AStar算法通用实现+可视化(Matlab)

%% AStar算法通用实现+可视化

clc, clear, close all;

%% 定义结构体

% 全局变量 起点 终点 当前点
global start_point end_point current_point ValueSet

% 加载地图
load("mapData01.mat")
% 地图大小
[row, col] = size(map);

% 新节点
NewPoint = @(a, b) struct('x', a, 'y', b);

SetKeyStr = @(x, y) string(num2str([x, y], '%03d'));

InvalidKeyStr = "000000";
% 新待搜索的节点
NewSearchPoint = @(a, b) struct('key', SetKeyStr(a, b), 'x', a, 'y', b, 'G', 0, 'H', 0, 'F', 0, 'parent_key', InvalidKeyStr);
%启发式搜索代价函数类型
HType = 2; 
% 各类值设定(同样用于染色)
ValueSet = struct('passable', 255, 'impassable', 0, 'openlist', 180, 'closelist', 120, 'start', 60, 'target', 60, 'current', 30, 'path', 60);

% 定义起点 与 终点
[start_point, end_point] = deal(NewPoint(2, 2), NewPoint(20, 20)); %起始/结束点坐标
[map(start_point.x, start_point.y), map(end_point.x, end_point.y)] = deal(ValueSet.start, ValueSet.target);

% 搜索点(下左右4点 分别为dx dy 以及移动代价(实际为sqrt(dx*dx+dy*dy))
SearchDxy = [0, -1, 1; -1, 0, 1; 1, 0, 1; 0, 1, 1];

%% 初始化 1.起始点加入open_list

% 初始状态设定
open_list(1) = NewSearchPoint(start_point.x, start_point.y);
% 计算代价 根据函数: F = G + H
open_list(1).H = CalcH(start_point.x, start_point.y, end_point.x, end_point.y, HType);
open_list(1).G = 0;
open_list(1).F = open_list.H;
%待确定代价的点
open_list = struct2table(open_list);
%已确定代价的点
close_list = [];
%当前点(设定为初始点)
current_point = open_list; 
figure
% 是否发现路径
b_find_path = 0; 


%% 2.遍历open_list,找到最小合代价F点
while ~isempty(open_list)
    
    index_min_open_list_F = SearchOptimalPoint(open_list, close_list, current_point, end_point);
    % 最小代价F的点选中为当前点,进行后续open_list选取
    current_point = open_list(index_min_open_list_F, :); 
    % 在open_list中将其删除
    open_list(index_min_open_list_F, :) = [];
    % 将其加入close_list
    close_list = [close_list; current_point]; 
    % 将新加入的close_list点标记(染色)
    map(current_point.x, current_point.y) = ValueSet.closelist; 
    DrawMap(map); %绘图

    % 3.检查是否符合退出条件
    if current_point.x == end_point.x && current_point.y == end_point.y
        b_find_path = true;
        break;
    end

    % 4. 检查当前点周围可移动点,并加入open_list中
    for search_dxy = SearchDxy'
        search_point = NewSearchPoint(current_point.x + search_dxy(1), current_point.y + search_dxy(2));
        key = SetKeyStr(search_point.x, search_point.y);
        %  4.1如果它是不可抵达的或者它在 close list 中,忽略它
        if search_point.x <= 0 || search_point.y <= 0 || map(search_point.x, search_point.y) == ValueSet.impassable || map(search_point.x, search_point.y) == ValueSet.closelist
            continue;
        end
        search_point = struct2table(search_point);
        % 移动代价
        search_point.G = current_point.G + search_dxy(3); 
        % 估算成本
        search_point.H = CalcH(search_point.x, search_point.y, end_point.x, end_point.y, HType); 
        search_point.F = search_point.G + search_point.H;
        search_point.parent_key = current_point.key;
        % 判定当前open_list中是否存在该搜索点
        index_existed_in_openlist = find(open_list.key == key, 1); 

        % 4.2如果它不在 open list 中 把它加入 open list
        % 并且把当前方格设置为它的父亲 记录该方格的 F  G 和 H 值
        if map(search_point.x, search_point.y) ~= ValueSet.openlist
            open_list = [open_list; search_point];
            % 将新加入的open_list点标记(染色)
            map(search_point.x, search_point.y) = ValueSet.openlist; 

        % 4.3如果它已经在 open list 中 检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考
        % 更小的 G 值表示这是更好的路径。如果是这样 把它的父亲设置为当前方格 并重新计算它的 G 和 F 值
        else 
            if search_point.G < open_list.G(index_existed_in_openlist)%若open_list中存在值的G值更大,表示由当前点到达该值更优,将原本储存点的信息替换为当前搜索点信息
                open_list(index_existed_in_openlist, :) = search_point; % 进行替换
            end
        end
    end
end


%% 从候选点中选取与目标方向最接近的点
function index_min_dyaw = FindMinDyaw(base_point_start, base_point_end, candidate_point_start, candidate_point_end, b_output_single)
    if nargin < 5%是否只输出唯一值
        b_output_single = false;
    end
    end_yaw = atan2(base_point_end.y - base_point_start.y, base_point_end.x - base_point_start.x);
    open_list_yaw = atan2(candidate_point_end.y - candidate_point_start.y, candidate_point_end.x - candidate_point_start.x);
    dyaw = abs(LimitInPi(open_list_yaw - end_yaw));
    if ~b_output_single
        index_min_dyaw = find(dyaw == min(dyaw));
    else
        index_min_dyaw = find(dyaw == min(dyaw), 1, 'last');
    end
end


%% 估算代价计算
% 参考文章《A*算法中启发函数H的选取》
% h_diagonal = 沿着对角线移动的步数, h_straight = 曼哈顿距离,
% 通过考虑所有的对角线移动的步数(每步耗散D2)以及剩下的直线移动的步数(每步耗散D)将这两者结合在一起.
function H = CalcH(x1, y1, x2, y2, type)
    if nargin < 5
        type = 3;
    end
    dx = x2 - x1;
    dy = y2 - y1;
    switch type
        case 1
            % 欧式距离 乘以系数可以加快搜索距离,但可能造成无解情况
            H = sqrt(dx * dx + dy * dy); 
        case 2
            % 曼哈顿距离
            H = abs(dx) + abs(dy); 
        case 3
            h_diagonal = min(abs(dx), abs(dy));
            h_straight = abs(dx) + abs(dy);
            H = sqrt(2) * h_diagonal + (h_straight - 2 * h_diagonal); % 可沿对角移动时的代价函数
        case 4
            % Dijkstra算法
            H = 0; 
    end
end


%% 绘制map图
function DrawMap(map)
    global start_point end_point current_point ValueSet
    % 注意这里对map的操作只是为了显示效果,不会影响到主函数内的map,
    [map(start_point.x, start_point.y), map(end_point.x, end_point.y), map(current_point.x, current_point.y)] = deal(ValueSet.start, ValueSet.target, ValueSet.current);
    imagesc(map')% 注意用plot和imagesc的区别,这里加了一个转置
    set(gca, 'XDir', 'normal', 'YDir', 'normal'); %如果不加normal是直接显示A的结构
    pause(0.0001);
end


%% 搜索最小代价点
function index_min_open_list_F = SearchOptimalPoint(open_list, close_list, current_point, end_point)
    %找到最小代价index
    index_min_open_list_F = find(open_list.F == min(open_list.F)); 
    %如果有多个最小代价值,按一定规则优先选取最优解
    if length(index_min_open_list_F) > 1
        % 起点时出现,则优先选取同起始/结束连线夹角最接近者
        if height(close_list) == 1
            index_min_dyaw_end = FindMinDyaw(current_point, end_point, current_point, open_list(index_min_open_list_F, :), 1);
            index_min_open_list_F = index_min_open_list_F(index_min_dyaw_end);
        % 否则找到与上一刻方向最接近的点
        else 
            index_last = find(close_list.key == current_point.parent_key, 1);
            last_point = close_list(index_last, :);
            index_min_dyaw_last = FindMinDyaw(last_point, current_point, current_point, open_list(index_min_open_list_F, :));
            index_min_open_list_F = index_min_open_list_F(index_min_dyaw_last);
            % 如果还有多个结果,优先选取同当前/结束连线夹角最接近者
            if length(index_min_open_list_F) > 1
                index_min_dyaw_end = FindMinDyaw(current_point, end_point, current_point, open_list(index_min_open_list_F, :), 1);
                index_min_open_list_F = index_min_open_list_F(index_min_dyaw_end);
            end
        end
    end
end



%%  功能:将输入角度范围限制在+-pi以内
function angle = LimitInPi(angle)
    % 输入:弧度
    % 输出:限制在+-pi以内的弧度
    angle = mod(angle, 2 * pi); % 对2pi取余
    kk = find(abs(angle) > pi);
    if ~isempty(kk)
        angle(kk) = angle(kk) - sign(angle(kk)) * 2 * pi;
    end
end

说明

  • matlab实现并不实用,仅作演示,会考虑用c#的实现
  • load("mapData01.mat")这里,地图数据如下表在这里插入图片描述
  • csv数据格式,直接改扩展名为.xlsx即可
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0
0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  • 参考&感谢

    https://www.cnblogs.com/technology/archive/2011/05/26/2058842.html

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值