【问题标题】:A* search, grid, 8 directions, octile distance as heuristic, not finding the direct pathA* 搜索,网格,8 个方向,八分位距离作为启发式,没有找到直接路径
【发布时间】:2015-09-17 04:14:42
【问题描述】:

谁能帮我理解我的 A* 搜索实现有什么问题?

我基于这个非常有用的网站实现了一个基本的 A* 搜索:http://www.redblobgames.com/pathfinding/a-star/implementation.html#cplusplus (非常感谢作者 Amit!)。

我使用网格、八个方向和 Octile 距离作为启发式方法。 不幸的是,我从 start(0,h/2) 到 end(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 的建议,我现在得到以下路径:

FOLLOW-UP(高度相关,这就是我在这里添加它并且不打开新帖子的原因):

如果我只使用曼哈顿距离作为启发式的四个方向,并且所有四个步骤的移动成本为 1,我会得到一个抖动的对角线。当然,算法必须像这样走“楼梯”,但我仍然希望得到更直接的东西——我是否遗漏了一些明显的东西?

【问题讨论】:

    标签: c++ opencv path-finding


    【解决方案1】:

    对角线的移动成本与正交步骤相同。

    向东南、东南、东北、东北的路径与向东、向东、向东、向东的路径一样短。两者的成本都是 4。

    当有多个最短路径时,A* 会给出其中之一,但它不是您想要的。

    如果您将对角线设置为具有更高的移动成本(sqrt(2) 是您的启发式状态),那么 A* 会更喜欢东、东、东、东。改变

    int new_cost = cost_so_far[current] + 1;
    

    使用 1 或 sqrt(2) 取决于它是正交步还是对角步。您还需要将成本转换为浮点数/双精度数而不是整数,并使优先级队列也这样做。 (或者,如果你想继续使用整数,有些人会使用 14 和 10 作为成本,并将启发式扩展到 D2 和 D 使用 14 和 10。)

    【讨论】:

    • 是的,这很有意义(我刚刚意识到你的网站上也已经有了这个答案——真可惜)!我现在确实得到了完全笔直的水平和对角线:)。谢谢你的解释。作为健全性检查:我添加了一些带有您建议的更改的结果路径......它们符合预期,对吗?
    • 我还在我原来的帖子中添加了一个跟进。
    • 是的,我认为你遇到了“关系”——即使是对角线成本,你的 NE,NE,NE,E,E,E 与 NE,E,NE 的成本相同,E,NE,E(看起来更好)。我有一些易于实现的想法here(在“打破关系”下),但这些想法都没有那么好。要获得更好的解决方案,请考虑not using a grid:航路点或导航网格会为您提供更好看的路径,而 A* 也会运行得更快。
    • 另一种解决方案是使用“拉线”对输出路径进行后处理,使它们看起来更漂亮。使用画线算法来查看您是否可以从您所在的位置沿直线步行到更远的路径。如果你能沿着漂亮的直线走,你就沿着这条线走,而不是在中间走丑陋的 A* 产生的步骤。这远没有生成可见性图或导航网格那么快,但它相对容易实现。
    猜你喜欢
    • 2019-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 2014-12-20
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多