历史上的今天
今天是:2026年01月31日(星期六)
2023年01月31日 | 机器人路径规划之A*算法(附C++源码)
2023-01-31 来源:古月居
1. 基本原理
A*的本质是广度优先的图搜索。意在寻找一个从起点到目标节点的最短路径。
A*算法在Dijkstra的基础上加入了启发式变量,一般用启发式距离(两点的直线距离)表示。
启发式距离
2. 算法伪代码
本伪代码摘取自Principles of Robot Moon
其中O代表优先队列,C存放着已访问过的节点。
3. 关键代码剖析
先来看看A*算法运行的最终结果吧
首先先创建一个类代表节点(省略了构造函数等Method)。
class node {
private:
int x, y; // 坐标
double sumCost; // f(n)
double heurisTIc;// 启发值
bool obstacle; // 是否是障碍物
node* backpoint; // 前驱节点
bool isVisid; // 判断是否访问过
};
在main函数中定义起始节点和目标节点
node startNode(40, 10);// 起始点
node goalNode(10, 40); // 目标点
初始化地图,这里计算了每个节点的启发式距离
for (int i = 0; i 《 mapSize; ++i) {
vector《node*》 tmp;
for (int j = 0; j 《 mapSize; ++j) {
node* tmpNode = new node(i, j);
tmpNode-》setHeurisTIc(calHerisTIc(tmpNode, goalNode));
tmp.push_back(tmpNode);
}
romap.push_back(tmp);
}
添加障碍物
void defineObstacle(vector《vector《node*》》& roadmap) {
// 先框住四周
for (int i = 0; i 《 mapSize; ++i) {
roadmap[0][i]-》setObstacle();
roadmap[i][0]-》setObstacle();
roadmap[i][mapSize - 1]-》setObstacle();
roadmap[mapSize - 1][i]-》setObstacle();
}
// 再定义一个条形快
for (int i = 1; i 《 mapSize / 2; ++i) {
roadmap[mapSize * 2 / 3][i]-》setObstacle();
roadmap[mapSize * 2 / 3 - 1][i]-》setObstacle();
roadmap[mapSize * 1 / 3][mapSize - i]-》setObstacle();
roadmap[mapSize * 1 / 3 - 1][mapSize - i]-》setObstacle();
}
}
A*算法函数
void aStar(const node& startNode, const node& goalNode, vector《vector《node*》》& roadmap, Mat& background) {
// 使用Lambda表达式定义一个优先队列
auto cmp = [](node* left, node* right) { return left-》gN() 》 right-》gN(); };
priority_queue《node*, vector《node*》, decltype(cmp)》 O(cmp);
node* tmp = roadmap[startNode.coordinateX()][startNode.coordinateY()];
O.push(tmp);
// Algorithm 24 A* Algorithm
while (!O.empty()) {
// ck nbest from O such that f(nbest) 《= f(n)。
node* nBest = O.top();
// Remove nbest from O and add to C(isVisited)。
O.pop();
nBest-》setIsVisited();
// if nbest == qgoal, EXIT.
if (*nBest == goalNode)
break;
// 八个方向都可以走
std::vector《node》 motion = { node(1, 0, 1),
node(0, 1, 1),
node(-1, 0, 1),
node(0, -1, 1),
node(-1, -1, std::sqrt(2)),
node(-1, 1, std::sqrt(2)),
node(1, -1, std::sqrt(2)),
node(1, 1, std::sqrt(2)) };
for (int i = 0; i 《 8; i++) {
node tmpNode = motion[i];
tmpNode += *nBest;
node* tmpNodePointer = roadmap[tmpNode.coordinateX()][tmpNode.coordinateY()];
*tmpNodePointer = tmpNode;
if (verifyNode(*tmpNodePointer) && !tmpNodePointer-》returnIsVisited() && !tmpNodePointer-》isObstacle()) {
O.push(tmpNodePointer);
tmpNodePointer-》setIsVisited();
tmpNodePointer-》setBackpoint(nBest);
tmpNodePointer-》drawNode(background, imgGrize, Scalar(0, 255, 0), 0);
imshow(“aStar”, background);
wtKey(5);
}
}
}
// 画出最终的路径
tmp = roadmap[goalNode.coordinateX()][goalNode.coordinateY()];
while (!(*tmp == startNode)) {
tmp-》drawNode(background, imgGridSize, Scalar(255, 0, 0));
tmp = tmp-》returnBackpoint();
imshow(“aStar”, background);
waitKey(5);
}
}
4. 资源指路
A*算法其中大部分变量和算法过程我都尽量和Principles of Motion中的说明保持一致,源代码已上传github(非工程文件,需自行配置)
编辑:黄飞
史海拾趣
|
Gen2不是RFID技术的标志,它不属于任何一家公司企业、组织或技术供应商。简单来说,Gen2代表了开放供应链中无源RFID技术的最新的演进阶段。 近些天来,“Gen2”引起了供应链众多人士的很大反响。大多数人对其有一个总的概念理解,无非是RFID技术“ ...… 查看全部问答> |
|
AD8205是美国模拟器件公司推出的一种单电源高性能差分放大器,典型单电源供电电压为5V,其共模电压输入范围为-2~65V,可以耐受-5~+70V的输入共模电压,适用于高共模电压情况下检测小差分电压的工业设备中。它的增益固定为50V/V,工作温度范围为-40~ ...… 查看全部问答> |
|
在很多linux移植教程中都有在安装完kernel,给其打补丁的操作。我想知道以下问题: 1、这些补丁的作用是什么? 2、添加这些补丁的原因是什么,这些补丁是对其kernel的修正,还是因为移植平台(如ARM、MIPS)的CPU不同,还是因为移植平台开发板( ...… 查看全部问答> |
|
我在VC/EVC中定义了一个窗体dlg1,在窗体中已经封装好了一些事件以及组件,我现在想做另外一个窗体,但是该窗体的内容仅仅在dlg1的基础上增加一些东西,请问一下该如何继承dlg1?… 查看全部问答> |
|
evc4.0下用模拟器调试,打开文件数据,如何设置文件路径,可以顺利打开? evc4.0下用模拟器调试,打开文件数据,如何设置文件路径,可以顺利打开? ppc2003 emulator 打算文件与exe同级目录… 查看全部问答> |




