A* 搜索,网格,8 个方向,八分之一距离作为启发式,没有找到直接路径
A* search, grid, 8 directions, octile distance as heuristic, not finding the direct path
任何人都可以帮助我了解我的 A* 搜索实施有什么问题吗?
我基于这个非常有用的网站实现了基本的 A* 搜索:http://www.redblobgames.com/pathfinding/a-star/implementation.html#cplusplus
(在此非常感谢作者 Amit!)。
我正在使用网格、八个方向和 Octile 距离作为启发式。
不幸的是,我的路径,从开始(0,h/2)到结束(w-1,h/2),不是预期的直线,但看起来像这样:
我的代码(应该可以按原样编译但需要 OpenCv):
struct PriorityQueue
{
typedef pair<int, cv::Point> PQElement;
struct SortPairPoints
{
bool operator()(const PQElement & l, const PQElement & r)
{
return (l.first > r.first);
}
};
priority_queue<PQElement, vector<PQElement>, SortPairPoints> elements;
inline bool empty() { return elements.empty(); }
inline void put(int priority,cv::Point item)
{
elements.emplace(priority, item);
}
inline cv::Point get()
{
cv::Point best_item = elements.top().second;
elements.pop();
return best_item;
}
};
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
namespace std
{
template <>
struct hash<cv::Point>
{
size_t operator()(const cv::Point & p) const
{
size_t seed = 0;
hash_combine(seed,p.x);
hash_combine(seed,p.y);
return seed;
}
};
}
int heuristic(cv::Point next, cv::Point goal)
{
// int D = 1;
// int dx = abs(next.x - goal.x);
// int dy = abs(next.y - goal.y);
// return D * (dx + dy);
// return sqrt(dx * dx + dy * dy);
// int D = 1;
// int D2 = 1;
int D = 1;
int D2 = sqrt(2);
int dx = abs(next.x - goal.x);
int dy = abs(next.y - goal.y);
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy);
}
int w = 250;
int h = 250;
std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(0, 1),cv::Point(-1, 0), cv::Point(1, 1), cv::Point(-1, 1),cv::Point(-1, -1),cv::Point(1, -1)});
//std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(-1, 0),cv::Point(0, 1)});
cv::Rect scenebox(0,0,w,h);
void search(
cv::Mat map,
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from,
unordered_map<cv::Point, int>& cost_so_far
)
{
PriorityQueue frontier;
frontier.put(0,start);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty()) {
auto current = frontier.get();
if (current == goal) {
break;
}
for (auto dir : pathDirs)
{
cv::Point next(current.x + dir.x, current.y + dir.y);
if (scenebox.contains(next) && (map.at<uchar>(next) == 255))
{
int new_cost = cost_so_far[current] + 1;
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
int priority = new_cost + heuristic(next, goal);
frontier.put(priority,next);
came_from[next] = current;
}
}
}
}
}
vector<cv::Point> reconstruct_path(
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from
)
{
vector<cv::Point> path;
cv::Point current = goal;
path.push_back(current);
while (current != start) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
return path;
}
int main(int argc, const char * argv[])
{
cv::Mat tracemap = cv::Mat(w,h, CV_8UC1, cvScalar(255) );
cv::Point start(0,h/2);
cv::Point end(w-1,h/2);
// cv::Point start(0,0);
// cv::Point end(w-1,h-1);
// cv::line(tracemap,
// cv::Point (75,125),
// cv::Point (125,75),
// cvScalar(150),50);
unordered_map<cv::Point, cv::Point> came_from;
unordered_map<cv::Point, int> cost_so_far;
search(tracemap, start, end, came_from, cost_so_far);
vector<cv::Point> path = reconstruct_path(start, end, came_from);
for(int i = 0; i < path.size(); i++)
{
tracemap.at<uchar>(path[i]) = 0;
}
imshow("tracemap", tracemap);
cv::waitKey();
return 0;
}
非常感谢任何关于如何找到问题根源的见解或提示!
更新:根据 Amit 的建议,我现在得到了以下路径:
跟进(高度相关,这就是为什么我在这里添加它并且不打开新的 post):
如果我仅使用曼哈顿距离作为启发式的四个方向,并且所有四个步骤的移动成本均为 1,我会得到一条抖动的对角线。当然,算法必须像这样走“阶梯”,但我仍然期待更直接的东西 - 我是否遗漏了一些明显的东西?
对角线的移动成本与正交步长相同。
向东南、东南、东北、东北的路径与向东、向东、向东、向东的路径一样短。两者都花费了 4.
当有多条最短路径时,A* 给你其中一条,但它不是你想要的。
如果您将对角线设置为具有更高的移动成本(sqrt(2) 是您的启发式状态),那么 A* 会更喜欢东、东、东、东。变化
int new_cost = cost_so_far[current] + 1;
使用 1 或 sqrt(2),具体取决于它是正交步长还是对角线步长。您还需要将成本变成 floats/doubles 而不是整数,并使优先级队列做同样的事情。 (或者,如果你想继续使用整数,有些人会使用 14 和 10 作为成本,并将启发式扩大到对 D2 和 D 使用 14 和 10。)
任何人都可以帮助我了解我的 A* 搜索实施有什么问题吗?
我基于这个非常有用的网站实现了基本的 A* 搜索:http://www.redblobgames.com/pathfinding/a-star/implementation.html#cplusplus (在此非常感谢作者 Amit!)。
我正在使用网格、八个方向和 Octile 距离作为启发式。 不幸的是,我的路径,从开始(0,h/2)到结束(w-1,h/2),不是预期的直线,但看起来像这样:
我的代码(应该可以按原样编译但需要 OpenCv):
struct PriorityQueue
{
typedef pair<int, cv::Point> PQElement;
struct SortPairPoints
{
bool operator()(const PQElement & l, const PQElement & r)
{
return (l.first > r.first);
}
};
priority_queue<PQElement, vector<PQElement>, SortPairPoints> elements;
inline bool empty() { return elements.empty(); }
inline void put(int priority,cv::Point item)
{
elements.emplace(priority, item);
}
inline cv::Point get()
{
cv::Point best_item = elements.top().second;
elements.pop();
return best_item;
}
};
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
namespace std
{
template <>
struct hash<cv::Point>
{
size_t operator()(const cv::Point & p) const
{
size_t seed = 0;
hash_combine(seed,p.x);
hash_combine(seed,p.y);
return seed;
}
};
}
int heuristic(cv::Point next, cv::Point goal)
{
// int D = 1;
// int dx = abs(next.x - goal.x);
// int dy = abs(next.y - goal.y);
// return D * (dx + dy);
// return sqrt(dx * dx + dy * dy);
// int D = 1;
// int D2 = 1;
int D = 1;
int D2 = sqrt(2);
int dx = abs(next.x - goal.x);
int dy = abs(next.y - goal.y);
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy);
}
int w = 250;
int h = 250;
std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(0, 1),cv::Point(-1, 0), cv::Point(1, 1), cv::Point(-1, 1),cv::Point(-1, -1),cv::Point(1, -1)});
//std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(-1, 0),cv::Point(0, 1)});
cv::Rect scenebox(0,0,w,h);
void search(
cv::Mat map,
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from,
unordered_map<cv::Point, int>& cost_so_far
)
{
PriorityQueue frontier;
frontier.put(0,start);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty()) {
auto current = frontier.get();
if (current == goal) {
break;
}
for (auto dir : pathDirs)
{
cv::Point next(current.x + dir.x, current.y + dir.y);
if (scenebox.contains(next) && (map.at<uchar>(next) == 255))
{
int new_cost = cost_so_far[current] + 1;
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
int priority = new_cost + heuristic(next, goal);
frontier.put(priority,next);
came_from[next] = current;
}
}
}
}
}
vector<cv::Point> reconstruct_path(
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from
)
{
vector<cv::Point> path;
cv::Point current = goal;
path.push_back(current);
while (current != start) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
return path;
}
int main(int argc, const char * argv[])
{
cv::Mat tracemap = cv::Mat(w,h, CV_8UC1, cvScalar(255) );
cv::Point start(0,h/2);
cv::Point end(w-1,h/2);
// cv::Point start(0,0);
// cv::Point end(w-1,h-1);
// cv::line(tracemap,
// cv::Point (75,125),
// cv::Point (125,75),
// cvScalar(150),50);
unordered_map<cv::Point, cv::Point> came_from;
unordered_map<cv::Point, int> cost_so_far;
search(tracemap, start, end, came_from, cost_so_far);
vector<cv::Point> path = reconstruct_path(start, end, came_from);
for(int i = 0; i < path.size(); i++)
{
tracemap.at<uchar>(path[i]) = 0;
}
imshow("tracemap", tracemap);
cv::waitKey();
return 0;
}
非常感谢任何关于如何找到问题根源的见解或提示!
更新:根据 Amit 的建议,我现在得到了以下路径:
跟进(高度相关,这就是为什么我在这里添加它并且不打开新的 post):
如果我仅使用曼哈顿距离作为启发式的四个方向,并且所有四个步骤的移动成本均为 1,我会得到一条抖动的对角线。当然,算法必须像这样走“阶梯”,但我仍然期待更直接的东西 - 我是否遗漏了一些明显的东西?
对角线的移动成本与正交步长相同。
向东南、东南、东北、东北的路径与向东、向东、向东、向东的路径一样短。两者都花费了 4.
当有多条最短路径时,A* 给你其中一条,但它不是你想要的。
如果您将对角线设置为具有更高的移动成本(sqrt(2) 是您的启发式状态),那么 A* 会更喜欢东、东、东、东。变化
int new_cost = cost_so_far[current] + 1;
使用 1 或 sqrt(2),具体取决于它是正交步长还是对角线步长。您还需要将成本变成 floats/doubles 而不是整数,并使优先级队列做同样的事情。 (或者,如果你想继续使用整数,有些人会使用 14 和 10 作为成本,并将启发式扩大到对 D2 和 D 使用 14 和 10。)