8.2 Incremental A*算法
Incremental A*算法是A算法的变体,允许在搜索过程中动态添加或删除障碍物。它能够实时更新路径而不需要重新搜索整个地图,使其适用于环境动态变化的情况下进行路径规划。
8.2.1 Incremental A*算法介绍
Incremental A* 算法是基于 A* 算法的一种变体,该算法的背景与发展历程可以概括如下。
- A*算法:A*算法是一种基于启发式搜索的路径规划算法,由 Peter Hart、Nils Nilsson 和 Bertram Raphael 在 1968 年提出。A*算法在搜索过程中通过启发式估计函数(heuristic function)来评估每个节点的潜在成本,并选择最有希望的节点进行探索,以最大程度地减少搜索空间。
- 动态环境需求:随着技术的发展,越来越多的应用场景需要在动态环境中进行路径规划,例如自动驾驶车辆、移动机器人等。在这些场景下,地图上的障碍物可能会随着时间变化而动态增加或删除,因此传统的路径规划算法无法直接应对这种变化。
- Incremental A*算法的提出:为了解决动态环境下的路径规划问题,研究者开始探索对现有的路径规划算法进行改进,使其能够实时更新路径。Incremental A* 算法就是在这种背景下提出的。它利用 A* 算法的基本思想,通过动态地更新地图信息,实现路径的实时更新。
- 发展历程:Incremental A* 算法是在对 A* 算法及其相关变体进行研究的基础上发展而来的。研究者们提出了许多不同的动态路径规划方法,其中 Incremental A* 算法是其中之一,它在 A* 算法的基础上引入了实时更新的能力。随着对动态路径规划算法的研究不断深入,Incremental A* 算法也在不断地被改进和优化,以适应不同应用场景的需求。
总的来说,Incremental A* 算法的发展是对路径规划算法在动态环境中的需求的回应,它代表了路径规划算法在应对现实世界复杂情况下的持续发展与进步。
8.2.2 Incremental A*算法的实现步骤
Incremental A*算法旨在实现对于在地图上动态增加或删除障碍物的情况下,实时更新路径规划的能力。Incremental A*是基于A*算法的启发式搜索方法,其核心实现步骤如下所示。
(1)初始化:算法从起始节点开始,初始化起始节点的代价为0,并将其添加到待探索节点列表中。
(2)循环搜索:重复以下步骤直到找到目标节点或者确定无解为止:
- 从待探索节点列表中选择具有最小总代价(代价函数f值)的节点进行探索。
- 对于选择的节点,计算其相邻节点的代价并更新它们的代价值和父节点。
- 将更新后的节点添加到待探索节点列表中。
(3)实时更新:在实时环境中,如果有新的障碍物出现或者旧的障碍物被移除,Incremental A*算法会根据变化情况重新计算路径。当发生障碍物变化时,算法可以根据改变的地图信息,更新已知路径,而无需完全重新搜索整个地图。
(4)路径回溯:一旦找到目标节点,算法通过追溯每个节点的父节点,构建最终路径。
Incremental A*算法通过动态地更新地图信息来适应环境的变化,从而实现实时的路径规划。这使得它特别适用于需要在动态环境中进行路径规划的场景,如移动机器人、自动驾驶等领域。
例如在下面的实例中实现了一个简单的 Incremental A* 算法,用于在一个10x10的网格地图上寻找从起点到目标点的最短路径。本实例的主要功能包括:
- 初始化地图和设置起始点、目标点。
- 执行 Incremental A* 算法,搜索路径并输出结果。
- 模拟动态障碍物的变化:在地图上添加或移除障碍物,并重新执行算法来更新路径。
- 添加了等待时间,使得每次修改障碍物后的路径搜索结果能够在控制台中逐步显示。
总之,本实例展示了 Incremental A* 算法在动态环境中寻找最短路径的基本原理。
实例8-1:使用Incremental A* 算法在动态环境中寻找最短路径(codes/8/Incremental .cpp)
实例文件Incremental .cpp的具体实现代码如下所示。
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <cmath>
#include <set>
#include <unistd.h> // 包含 sleep 函数
using namespace std;
// 定义节点结构体
struct Node {
int x, y; // 节点的坐标
int g; // 从起始节点到当前节点的实际代价
int h; // 从当前节点到目标节点的估计代价
Node* parent; // 父节点指针
Node(int x, int y, int g, int h, Node* parent)
: x(x), y(y), g(g), h(h), parent(parent) {}
// 计算节点的总代价
int f() const {
return g + h;
}
// 定义比较函数,用于std::set中的节点排序
bool operator<(const Node& other) const {
return f() < other.f();
}
};
// 定义地图类
class Map {
private:
vector<vector<int>> grid; // 地图网格
int rows, cols; // 行数和列数
pair<int, int> start, goal; // 起始点和目标点坐标
public:
Map(int rows, int cols) : rows(rows), cols(cols) {
grid.resize(rows, vector<int>(cols, 0)); // 初始化地图网格
}
// 设置起始点和目标点
void setStartAndGoal(int startX, int startY, int goalX, int goalY) {
start = make_pair(startX, startY);
goal = make_pair(goalX, goalY);
}
// 设置障碍物
void setObstacle(int x, int y) {
grid[x][y] = 1;
}
// 检查节点是否在地图范围内且可通行
bool isValid(int x, int y) const {
return x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] == 0;
}
// 计算启发式函数(曼哈顿距离)
int heuristic(int x, int y) const {
return abs(x - goal.first) + abs(y - goal.second);
}
// 执行Incremental A*算法
void incrementalAStar() {
set<Node> openList;
vector<vector<bool>> closedList(rows, vector<bool>(cols, false));
Node* startNode = new Node(start.first, start.second, 0, heuristic(start.first, start.second), nullptr);
openList.insert(*startNode);
while (!openList.empty()) {
Node current = *openList.begin();
openList.erase(openList.begin());
if (current.x == goal.first && current.y == goal.second) {
// 找到目标节点,回溯路径并输出
vector<pair<int, int>> path;
while (current.parent != nullptr) {
path.push_back(make_pair(current.x, current.y));
current = *current.parent;
}
path.push_back(make_pair(start.first, start.second));
cout << "Found path:";
for (int i = path.size() - 1; i >= 0; --i) {
cout << " (" << path[i].first << "," << path[i].second << ")";
}
cout << endl;
return;
}
closedList[current.x][current.y] = true;
// 遍历当前节点的相邻节点
for (int dx = -1; dx <= 1; ++dx) {
for (int dy = -1; dy <= 1; ++dy) {
if (dx == 0 && dy == 0) continue; // 忽略当前节点
int newX = current.x + dx;
int newY = current.y + dy;
if (isValid(newX, newY) && !closedList[newX][newY]) {
int newG = current.g + 1;
Node* newNode = new Node(newX, newY, newG, heuristic(newX, newY), new Node(current.x, current.y, 0, 0, nullptr));
openList.insert(*newNode);
}
}
}
}
cout << "No path found!" << endl;
}
};
int main() {
// 创建一个10x10的地图
Map map(10, 10);
// 设置起始点和目标点
map.setStartAndGoal(0, 0, 9, 9);
// 执行Incremental A*算法
map.incrementalAStar();
// 模拟障碍物的动态变化
map.setObstacle(9, 7);
cout << "Add obstacle at (9,7)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(5, 0);
cout << "Remove obstacle at (5,0)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(5, 6);
cout << "Add obstacle at (5,6)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(1, 8);
cout << "Remove obstacle at (1,8)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(7, 2);
cout << "Add obstacle at (7,2)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(5, 4);
cout << "Remove obstacle at (5,4)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(6, 8);
cout << "Add obstacle at (6,8)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(3, 3);
cout << "Remove obstacle at (3,3)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
map.setObstacle(2, 1);
cout << "Add obstacle at (2,1)" << endl;
map.incrementalAStar();
sleep(1); // 添加等待时间
return 0;
}
上述代码的实现流程如下所示。
- 首先,定义了一个 Node 结构体,表示地图中的节点,包含节点的坐标、实际代价 g、估计代价 h 和指向父节点的指针。
- 然后,通过类Map实现了一个地图,包括设置地图的起始点和目标点,以及设置障碍物的功能。该类还提供了一个启发式函数来估计节点到目标点的代价。
- 接着,在类Map中实现了 Incremental A* 算法。该算法使用优先队列来管理开放列表,并利用 set 来实现节点的排序。在算法中,从起始节点开始,每次选择开放列表中代价最小的节点进行扩展,直到找到目标节点或者开放列表为空。
- 最后,在主函数main中创建了一个10x10的地图,并设置了起始点和目标点。然后,通过调用 incrementalAStar 函数执行了 Incremental A* 算法。随后,模拟了障碍物的动态变化,每次添加或移除障碍物后重新执行算法,并在控制台输出路径结果。执行后会输出:
Found path: (0,0) (9,9)
Add obstacle at (9,7)
Found path: (0,0) (9,9)
Remove obstacle at (5,0)
Found path: (0,0) (9,9)
Add obstacle at (5,6)
Found path: (0,0) (9,9)
Remove obstacle at (1,8)
Found path: (0,0) (9,9)
Add obstacle at (7,2)
Found path: (0,0) (9,9)
Remove obstacle at (5,4)
Found path: (0,0) (9,9)
Add obstacle at (6,8)
Found path: (0,0) (9,9)
Remove obstacle at (3,3)
Found path: (0,0) (9,9)
Add obstacle at (2,1)
Found path: (0,0) (9,9)