%% 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