【算法】A* 寻路 可视化

如下图 【寻路图A】

使用A*算法,需要将地图抽象成一个个方块,蓝色代表不可以动【墙】,黄色为起始点,红色为目标点。其地图的二维坐标如图所示,每一个单位为1米

A*的基本公式为 F(n)=G(n)+H(n)

F:初始状态到目标状态n的代价估计【起始点到n的代价估计】

G:状态空间中初始状态到n的实际代价【n为中间某点,n到其parent的实际距离,n为parent的最小F所得】

H:从状态n到目标状态最佳路径的估计代价【n到B的,X之差的绝对值与Y之差的绝对值 之和】

算法的基本思路

首先我们有一个起始点,一个目标点,还有一些墙,

并声明一个开启【开启列表】一个【关闭列表】

【开启列表】:当前待判断点,

【关闭列表】:已经判断过的点,不再参与寻路搜索计算

1.开始时我们把起始点加入开启列表,然后获取起始点周围所有可达到的点,将它们的parent设置为起始点,然后分别计算它们的FGH值,并把这些点加入开启列表中,最后将起始点从开启列表中移除,再加入关闭列表。

2.接着遍历开始列表,寻找开始列表中F(到达目标点的预计代价)值最小的点,对此点进行如下操作

将此点加入关闭列表并从开始列表中移除

寻找此点周围所有【按你自己的规则】【可达到的点】,并移除已经加入关闭列表中的部分,然后计算这些新点的FGH值,这里需要重点注意的是更新parent的操作!

每个【可达到的点】都有自己的FGH,每次计算完一个【可达到的点】的G值后,重新计算下此点到该【可达到的点】的G值,比较这两个G值,若后者较小,这更新当前【可达到的点】的parent为此点

当此点所有【可达到的点】计算完成后,重复2,重新遍历【开启列表】寻找F值最小的点,并标记为此点,

 

终止的条件就是

1.开启列表为空:目标点无法到达

2.开启列表中包含目标点:已经到达

 

 

 

对于结果,我们每一个点都计算出了其最小代价的parent,我们根据目标点的parent依次向上寻找直到起始点,即为最佳路径

 

举一个代码示例如下:【by siki】

 

每个路点绑定如下point

 

public class Point {

 

public Point Parent { get; set; }

public float F { get; set; }

public float G { get; set; }

public float H { get; set; }

 

public int X { get; set; }

public int Y { get; set; }

 

public bool IsWall { get; set; }

 

public Point(int X,int Y,Point Parent=null) {

this.X = X;

this.Y = Y;

this.Parent = Parent;

IsWall = false;

}

 

public void UpdateParent(Point parent,float g) {

this.Parent = parent;

this.G = g;

this.F = G + H;

}

}

 

新建另一继承自Mono的脚本,挂到任意物体上

这里我们固定地图,固定障碍物,固定起始点,定义规则【正常可走斜角,若斜角周围有障碍物则不可通过】如上【寻路图A】

 

定义好地图的宽高等信息

public int mapWidth=8;

public int mapHeight = 6;

 

private Point[,] map = new Point[8, 6];

 

生成地图

private void InitMap() {

for (int x = 0; x < mapWidth; x++) {

for (int y = 0; y < mapHeight; y++) {

map[x, y] = new Point(x, y);

}

}

//设置障碍物

map[4, 2].IsWall = true;

map[4, 3].IsWall = true;

map[4, 4].IsWall = true;

map[4, 5].IsWall = true;

}

 

寻找路径

private void FindPath(Point start,Point end) {

List<Point> openList = new List<Point> { };

List<Point> closeList = new List<Point> { };

openList.Add(start);

while (openList.Count > 0) {

Point point = FindMinFOfPoint(openList);

 

openList.Remove(point);

closeList.Add(point);

List<Point> surroundPoints = GetSurroundPoints(point);

//过滤

PointsFilter(surroundPoints, closeList);

foreach (Point s in surroundPoints) {

if (openList.Contains(s)) {

 

float nowG = CalcG(s, point);

if (nowG < s.G) {

s.UpdateParent(point, nowG);

}

}

else {

s.Parent = point;

//Debug.Log(s.X + " " + s.Y + "--" + point.X + " " + point.Y);

CalcF(s, end);

openList.Add(s);

}

}

//判断end是否在开启列表里

if (openList.Contains(end)) {

break;

}

}

}

 

 

寻找周围点

//找到某point周围所有可达到的点

private List<Point> GetSurroundPoints(Point point) {

//上下左右的四个点

Point up = null, down = null, left = null, right = null;

if (point.Y < mapHeight - 1) {

up = map[point.X, point.Y + 1];

}

 

if (point.Y >0) {

down = map[point.X, point.Y - 1];

}

 

if (point.X < mapWidth - 1) {

right = map[point.X + 1, point.Y];

}

 

if (point.X > 0) {

left = map[point.X - 1, point.Y];

}

//四个角

Point upLeft = null, upRight = null, downLeft = null, downRight = null;

if (up != null && left != null) {

upLeft = map[point.X - 1, point.Y + 1];

}

if (up != null && right != null) {

upRight = map[point.X + 1, point.Y + 1];

}

if (down != null && left != null) {

downLeft = map[point.X - 1, point.Y - 1];

}

if (down != null && right != null) {

downRight = map[point.X + 1, point.Y - 1];

}

//构造集合

List<Point> list = new List<Point> { };

//不是墙,加入集合

if (down != null && down.IsWall == false) {

list.Add(down);

}

if (up != null && up.IsWall == false) {

list.Add(up);

}

if (left != null && left.IsWall == false) {

list.Add(left);

}

if (right != null && right.IsWall == false) {

list.Add(right);

}

//不是墙,且没有障碍物阻拦,加入集合

if (upLeft != null && upLeft.IsWall == false && left.IsWall == false && up.IsWall == false) {

list.Add(upLeft);

}

if (upRight != null && upRight.IsWall == false && up.IsWall == false && right.IsWall == false) {

list.Add(upRight);

}

if (downLeft != null && downLeft.IsWall == false && down.IsWall == false && left.IsWall == false) {

list.Add(downLeft);

}

if (downRight != null && downRight.IsWall == false && down.IsWall == false && right.IsWall == false) {

list.Add(downRight);

}

return list;

}

 

去除已经处于关闭列表中的 周围可到达点

private void PointsFilter(List<Point> src,List<Point> closeList) {

foreach (Point p in closeList) {

if (src.Contains(p)) {

src.Remove(p);

}

}

}

 

寻找开启列表中 F值最小的点

private Point FindMinFOfPoint(List<Point> openList) {

float f = float.MaxValue;

Point temp = null;

foreach (Point p in openList) {

if (p.F < f) {

temp = p;

f = p.F;

}

}

return temp;

}

 

//计算F值

private void CalcF(Point now,Point end) {

//F=G+H

float h = Mathf.Abs(end.X - now.X) + Mathf.Abs(end.Y - now.Y);

float g = 0;

if (now.Parent == null) {

g = 0;

}

else {

g=Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.Parent.X, now.Parent.Y)) + now.Parent.G;

}

float f = g + h;

now.F = f;

now.G = g;

now.H = h;

}

 

//计算G值

private float CalcG(Point now,Point parent) {

return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y)) + parent.G;

}

 

打印结果

private void ShowPath(Point start,Point end) {

Point temp = end;

while (true) {

Debug.Log(temp.X + " " + temp.Y);

if (temp.Parent == null) {

break;

}

temp = temp.Parent;

}

}

 

最后开启算法

void Start() {

InitMap();

Point start = map[2, 3];

Point end = map[6, 3];

FindPath(start, end);

ShowPath(start, end);

}

 

执行后打印结果如下:6 3,6 2,5 1,4 1,3 1,3 2,2 3

笔者笔记原文链接 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: a*算法是一种常用的路径搜索算法,它可以通过广度优先搜索和启发式搜索来找到最短路径。在pyqt上实现a*算法,首先需要设计界面,并在界面上设置起点和终点,然后根据搜索结果,在界面上画出最短路径。 实现a*算法需要定义节点、相邻节点、起点和终点等。在pyqt上使用QWidget来实现界面,可以使用QPainter类来画线和节点。在实现a*算法时,需要用到优先队列来存储待扩展的节点,同时需要定义两个估价函数:启发式函数和代价函数。 在启发式函数中,可以根据节点和终点之间的距离来估计路径的长度。代价函数则用于计算从起点到当前节点的距离。 在算法实现时,可以用一个字典来存储节点和与之相邻的节点,每次扩展节点时,将其所有相邻节点加入优先队列中,按照估计路径长度从小到大排序,然后依次扩展,记录下最短路径。 最后,在界面上绘制最短路径,我们可以将其表示为一条线段,并在节点上标注代价函数的值。完成了路径的绘制,就可以使用pyqt的show()方法来显示整个界面,实现a*算法可视化效果。 总之,要在pyqt上实现a*算法,需要使用QWidget、QPainter和优先队列等类和数据结构,同时需要定义启发式函数和代价函数,最后,根据搜索结果在界面上绘制最短路径。 ### 回答2: PyQt是Python下使用的GUI工具库,提供了一个连接Qt和Python的桥梁。A*算法是一种广度优先搜索算法的变种,以最短路径为目标的搜索算法。 要实现连接线算法A*,我们需要使用PyQt中的QGraphicsScene和QGraphicsView,这些类提供了丰富的绘图功能和交互功能。我们还需要创建一个QGraphicsItem代表连接线,该item需要重写paint()方法绘制线条。然后按照A*算法的步骤实现路径搜索: 1. 创建节点列表并初始化起点。 2. 对节点列表进行排序,并将起点加入已搜索节点列表。 3. 将离目标节点最近的未搜索节点作为下一个节点。 4. 对该节点进行路径评估,如果路径短于已有路径,则更新该节点路径。 5. 如此反复执行直到找到目标节点或搜索完未搜索节点。 6. 从目标节点开始反向遍历路径,并标记所经过的连接线。 在实现过程中,我们还需要考虑如何将节点和连接线连接起来,如何存储地图信息以及如何进行路径评估等问题。 总体来说,使用PyQt实现连接线算法A*需要我们熟悉PyQt的绘图和交互功能,并具备A*算法和图形算法相关知识和技能。 ### 回答3: PyQt是一种基于Python语言的GUI框架,可以实现丰富的图形用户界面交互效果。而A*算法是一种广泛应用于图形搜索、路径规划等领域的算法,可以帮助我们找到两个点之间最短路径。 要在PyQt中实现连接线算法A*,需要以下步骤: 1. 定义节点类。节点对象需要保存坐标信息、与其他节点相邻的关系、以及A*算法中的相关信息,比如起点到该节点的距离和预估到目标节点的距离等。 2. 实现A*算法。根据节点之间的关系,利用A*算法算出最短路径。 3. 绘制连接线。在PyQt的绘图控件中,根据节点之间的关系,连接各节点,并高亮显示最短路径。 以上就是用PyQt实现连接线算法A*的简要步骤。当然,具体实现细节还需根据具体的场景和需求进行调整和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值