代码地址如下:
http://www.demodashi.com/demo/13677.html

需求

之前做过一个无人车需要自主寻找最佳路径,所以研究了相关的寻路算法,最终选择A算法,因为其简单易懂,是入门级的寻路算法。

但是在验证的算法的时候,没有直观的感受,总是觉得会有什么问题,所以我就写了一个可视化的A
算法验证,界面基于Qt开发。

项目说明

本项目主要分为2个部分,Qt绘制网格和A算法实现。下面可以看到,界面的实现和A算法的实现基本上是分离的。也就是说可以单独使用,比如Qt网格绘制,可以用于扫雷游戏,A*算法的代码可以直接拷贝,稍作修改就可以直接用于游戏,或者无人车。

效果



操作说明

鼠标右键:设置障碍物;鼠标左键:设置起点和终点。

项目文件截图

项目实现

程序的实现流程:定义一个最小网格类->通过鼠标设置起点、终点和障碍物,并实时绘制->寻找路径->绘制路径。

定义最小网格类

首先,我们需要定义一个最小网格类,用于绘制网格地图(GridMap)。

网格地图是将二维场景中的地图划分为一个个小网格,这个小网格是最小的空间单位,我们可以设置该网格为障碍物、起点或终点。

下面是最小网格类。

  1. class Item
  2. {
  3. public:
  4. Item();
  5. Item(QPoint pos);
  6. QPoint m_pos; //position
  7. bool m_bIsObstacle; //whether is obstacle
  8. int m_nObjectType; //the object type , Nothing,Robot or goal
  9. };

可以看出最小网格对象很简单,只需要一个位置信息,障碍物标志位,机器人或目标点标志。以上就是最小网格的全部属性,当然如果想要实现更加复杂的功能,可以添加属性。

初始化地图

初始化地图是指将2维地图划分为网格的过程,其实就是new Item的过程。

  1. void MainWindow::InitItems()
  2. {
  3. for(int i=0; i<m_nColumes; i++)
  4. {
  5. QVector<Item*> rowItems;
  6. for(int j=0; j<m_nRows; j++)
  7. {
  8. QPoint pos = QPoint(i,j);
  9. Item* pItem = new Item(pos);
  10. rowItems.append(pItem);
  11. }
  12. m_items.append(rowItems);
  13. }
  14. }

其中m_nColumes和m_nRows是在初始化地图之前设置的长宽。

设置障碍物

在寻路之前,我们要先设置好障碍物、起点和终点。设置障碍物用鼠标右键,单击右键,设置小网格为障碍物,再次单击取消障碍物;设置起点和终点用鼠标左键,单击左键1次,设置小网格为起点,再次单击为终点,再次单击为空白,依次循环。

这里我们用鼠标事件实现,可以看到其实就是根据鼠标事件来设置相应位置Item对象的属性。代码很简单。在设置起点和终点是需要特别标记,因为如果地图中没有标定起点和终点,就无法寻路了。

  1. void MainWindow::mousePressEvent(QMouseEvent * e)
  2. {
  3. //得到鼠标处的格子坐标
  4. QPoint pt;
  5. pt.setX( (e->pos().x() - START_X ) / RECT_WIDTH);
  6. pt.setY( (e->pos().y() - START_X ) / RECT_HEIGHT);
  7. //wheather is the point in the Game area
  8. if (!PointInGameArea(pt))
  9. {
  10. return;
  11. }
  12. //获取所点击矩形元素
  13. Item* pItem = m_items[pt.x()][pt.y()];
  14. //leftbutton set object tpye,rightbutton set obstacle
  15. if(e->button()==Qt::LeftButton) //left button ,set Robot or goal
  16. {
  17. //
  18. pItem->m_nObjectType++;
  19. pItem->m_nObjectType%=3;
  20. pItem->m_bIsObstacle=false; //clear obstacle
  21. }
  22. else if(e->button()==Qt::RightButton) //set obstacle or not obstacle
  23. {
  24. pItem->m_nObjectType=0; //clear object type
  25. if (pItem->m_bIsObstacle)
  26. {
  27. pItem->m_bIsObstacle = false;
  28. }
  29. else
  30. {
  31. pItem->m_bIsObstacle = true;
  32. }
  33. }
  34. }

鼠标事件会自动触发paintEvent()函数,所以在点击鼠标后,地图会重绘。

新建地图

新建地图需要将上次的地图清除,也就是ReleaseItems(),同时清除路径ReleasePath(),然后再初始化地图。

  1. void MainWindow::NewGame()
  2. {
  3. resize(START_X*2 + m_nColumes*RECT_WIDTH ,START_Y*2 + m_nRows*RECT_HEIGHT);
  4. ReleaseItems();
  5. ReleasePath();
  6. InitItems();
  7. }

无论是初始化地图还是新建地图,都没有提到地图的绘制,那么地图什么时候绘制呢?在NewGame()中有一个Resize()函数,也就是改变窗口的大小,该函数会触发paintEvent()函数,所以当调用NewGame()函数后,就会绘制地图了。

更新地图

不说了,看代码,很简单,后面详细介绍一下绘制地图和绘制路径。

  1. void MainWindow::paintEvent(QPaintEvent *e)
  2. {
  3. DrawChessboard(); //绘制地图背景
  4. DrawItems(); //绘制地图
  5. DrawPath(); //绘制路径
  6. update();
  7. }

绘制地图

绘制地图其实就是根据相应位置的Item对象的属性,来绘制该网格。网格属性在初始化和设置障碍物时,已经设置好了。代码很简单,就是判断网格属性是障碍物还是起点,然后直接绘制就好了。

  1. void MainWindow::DrawItem(QPainter& painter,Item* pItem)
  2. {
  3. if(pItem->m_bIsObstacle) //show obstacle
  4. {
  5. QRect rcSrc(0,0,m_ObstacleImage.width(),m_ObstacleImage.height());
  6. QRect rcTarget(START_X + pItem->m_pos.x()*RECT_WIDTH + 2,START_Y + pItem->m_pos.y()*RECT_HEIGHT + 2,RECT_WIDTH-4,RECT_HEIGHT-4);
  7. painter.drawPixmap(rcTarget,m_ObstacleImage,rcSrc);
  8. painter.setBrush(Qt::transparent);
  9. painter.drawRect( START_X + pItem->m_pos.x()*RECT_WIDTH,START_Y + pItem->m_pos.y()*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT);
  10. return;
  11. }
  12. else if (pItem->m_nObjectType!=0) //show Robot item or goal item
  13. {
  14. if(pItem->m_nObjectType == 1) //show Robot
  15. {
  16. QRect rcSrc(0,0,m_RobotImage.width(),m_RobotImage.height());
  17. QRect rcTarget(START_X + pItem->m_pos.x()*RECT_WIDTH + 2,START_Y + pItem->m_pos.y()*RECT_HEIGHT + 2,RECT_WIDTH-4,RECT_HEIGHT-4);
  18. painter.drawPixmap(rcTarget,m_RobotImage,rcSrc);
  19. painter.setBrush(Qt::transparent);
  20. painter.drawRect( START_X + pItem->m_pos.x()*RECT_WIDTH,START_Y + pItem->m_pos.y()*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT);
  21. return ;
  22. }
  23. else
  24. {
  25. QRect rcSrc(0,0,m_GoalImage.width(),m_GoalImage.height());
  26. QRect rcTarget(START_X + pItem->m_pos.x()*RECT_WIDTH + 2,START_Y + pItem->m_pos.y()*RECT_HEIGHT + 2,RECT_WIDTH-4,RECT_HEIGHT-4);
  27. painter.drawPixmap(rcTarget,m_GoalImage,rcSrc);
  28. painter.setBrush(Qt::transparent);
  29. painter.drawRect( START_X + pItem->m_pos.x()*RECT_WIDTH,START_Y + pItem->m_pos.y()*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT);
  30. return;
  31. }
  32. }
  33. else
  34. {
  35. painter.setBrush(Qt::green);
  36. }
  37. painter.drawRect( START_X + pItem->m_pos.x()*RECT_WIDTH,START_Y + pItem->m_pos.y()*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT);
  38. }

寻找路径

地图绘制好了以后,就可以寻路了。单击搜索路径后,就可以寻路了。寻路直线需要清除原来的路径,避免上次的路径影响。如果发现地图中没有起点和终点,就直接退出,避免造成寻路异常。

  1. void MainWindow::OnSearchPath()
  2. {
  3. ReleasePath();
  4. createMazeMap(); //create Maze map and start end point
  5. if(!m_bIsHaveRobot || !m_bIsHaveGoal)
  6. {
  7. std::cout<<"have not roobot or goal"<<std::endl;
  8. return;
  9. }
  10. astar.InitAstar(mazeMap);
  11. path=astar.GetPath(start, end, false);
  12. markPathInMazeMap();
  13. printMazeMap();
  14. printPath();
  15. if(!path.empty())
  16. path.pop_back();
  17. if(!path.empty())
  18. path.pop_front();
  19. update();
  20. }

代码很简单,关键就是path=astar.GetPath().下面重点分析一下寻路的过程。

寻路的代码不是本人写了,原作者已经不记得了,但是他的代码有点问题,我调试后做了修改。下面是我参考的博客:

https://www.cnblogs.com/wlzy/p/7096114.html

博客上对A*的算法描述的非常清晰,简单易懂,我就不重复了。但是我用他的代码时,总是会出现路径越界,调试之后发现是边界检测那块有一点问题,也就是isCanreach()函数,读者可以好好对比一下。同时我在源码的基础上添加了一些清除开路径和闭路径的操作,不知道为什么源码没有。如果没有的话,会有问题的。

绘制路径

寻路完成以后,就可以直接绘制路径了。

  1. void MainWindow::DrawPath()
  2. {
  3. QPainter painter(this);
  4. painter.setBrush(Qt::yellow);
  5. if(!path.empty())
  6. for(auto &p:path)
  7. {
  8. painter.drawRect( START_X + p->x*RECT_WIDTH,START_Y + p->y*RECT_HEIGHT,RECT_WIDTH,RECT_HEIGHT);
  9. }
  10. }

总结

完成了基于Qt的A算法可视化分析后,我发现以后很多算法都可以可视化分析了,非常直观,有助于理解算法。

基于Qt的A
算法可视化分析

代码地址如下:
http://www.demodashi.com/demo/13677.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

基于Qt的A*算法可视化分析的更多相关文章

  1. PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品,产品设计严格遵循国际数据挖掘标准CRISP-DM(跨行业数据挖掘过程标准),具备完备的数据准备、模型构建、模型评估、模型管理、海量数据处理和高纬数据可视化分析能力。

    http://www.meritdata.com.cn/article/90 PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品, ...

  2. 基于Qt有限状态机的一种实现方式和完善的人工智能方法

    基于Qt有限状态机的一种实现方式和完善的人工智能方法 人工智能在今年是一个非常火的方向,当然了.不不过今年,它一直火了非常多年,有关人工智能的一些算法层出不穷.人工智能在非常多领域都有应用,就拿我熟悉 ...

  3. 【机器学习】DBSCAN Algorithms基于密度的聚类算法

    一.算法思想: DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法.与划分和层 ...

  4. 使用 QuickBI 搭建酷炫可视化分析

    随着各行各业大数据的渗透,BI 类数据分析需求与日俱增,如何让可视化更好的展现数据的价值,是 BI 类产品一直努力的方向.对此国内外的BI产品都有自己的方法,如国外大牌的 PowerBI.Tablea ...

  5. 基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#)

    基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#) 为了完美解析GLSL源码,获取其中的信息(都有哪些in/out/uniform等),我决定做个GLSL编译器的前端(以后简称编译器或 ...

  6. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  7. mahout入门指南之基于mahout的itembased算法

    基于mahout的itembased算法 事实上mahout分布式上仅仅是实现了部分算法.比方推荐算法中Item-based和slopone都有hadoop实现和单机版实现,User-based没有分 ...

  8. 基于ReliefF和K-means算法的医学应用实例

    基于ReliefF和K-means算法的医学应用实例 数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据 ...

  9. 大数据算法->推荐系统常用算法之基于内容的推荐系统算法

    港真,自己一直非常希望做算法工程师,所以自己现在开始对现在常用的大数据算法进行不断地学习,今天了解到的算法,就是我们生活中无处不在的推荐系统算法. 其实,向别人推荐商品是一个很常见的现象,比如我用了一 ...

随机推荐

  1. JZYZOJ1544 [haoi2016T2]放棋子 错排公式 组合数学 高精度

    http://172.20.6.3/Problem_Show.asp?ID=1544&a=ProbNF 看了题解才意识到原题有错排的性质(开始根本不知道错排是什么). 十本不同的书放在书架上. ...

  2. [Codeforces-div.1 167B] Wizards and Huge Prize

    [Codeforces-div.1 167B] Wizards and Huge Prize 试题分析 注意到每个物品互相独立,互不干扰之后就非常好做了. 算出一个物品最后的价值期望,然后乘以K即可. ...

  3. 【并查集】星球大战starwar

    BZOJ1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 6407  Solved: 2973[Su ...

  4. BZOJ 3505 [Cqoi2014]数三角形(组合数学)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3505 [题目大意] 给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个. 注 ...

  5. ObjC的initialize和init

    Objective-C很有趣的一个地方是,它非常非常像C.实际上,它就是C语言加上一些其他扩展和一个运行时间(runtime). 有了这个在每个Objective-C程序中都会起作用的附加运行时间,给 ...

  6. HDU 4630 No Pain No Game(2013多校3 1010题 离线处理+树状数组求最值)

    No Pain No Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  7. Windows删除文件时出现,“正在准备 再循环”

    初步分析这个问题是由于文件写入时,由于意外事情的发生(如,文件太大,正在写入时你取消了,而系统又没有来得及删除没有写完的数据等原因)没有写完. 错误的类型如下 出现此问题.当你删除不成时.返回系统根目 ...

  8. 【java】随机生成6位的数字 /生成例如010 045这样的三位数

    int radomInt = new Random().nextInt(999999) @org.junit.Test public void testName() throws Exception ...

  9. Helm安装和项目使用

    整体架构 1.为什么要用? 首先在原来项目中都是基于yaml文件来进行部署发布的,而目前项目大部分微服务化或者模块化,会分成很多个组件来部署,每个组件可能对应一个deployment.yaml,一个s ...

  10. unitest跳过测试用例

    http://www.51testing.com/html/35/n-3718035.html @unittest.skip(u"强制跳过示例") @unittest.skip(u ...