Pathfinding алгоритм создания петель - PullRequest
3 голосов
/ 30 августа 2011

Я реализовал алгоритм D * -Lite (вот описание , это алгоритм для поиска пути, когда граничные затраты меняются со временем), но у меня возникают проблемы с обновлением граничных затрат , В основном это работает, но иногда он застревает в цикле, перемещаясь назад и вперед между двумя вершинами. Я пытаюсь создать тестовый пример, который демонстрирует это поведение, в настоящий момент это происходит в некоторых случаях при использовании в большом приложении, что затрудняет отладку.

Я получу тестовый пример, как только смогу, но, возможно, кто-то сразу заметит ошибку, которую я сделал, переходя от псевдо-к C ++. (Есть тест приведенный ниже случай) В статье представлена ​​оптимизированная версия Рисунок 4 , которую я реализовал. Псевдокод вставлен ниже.

Источник для моей реализации доступен здесь .

Если это поможет, я использую эти типы в своем коде:

struct VertexProperties { double x, y; };
typedef boost::adjacency_list<boost::vecS, 
                              boost::vecS, 
                              boost::undirectedS,
                              VertexProperties,
                              boost::property<boost::edge_weight_t, double> > Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
typedef DStarEuclidianHeuristic<Graph, Vertex> Heuristic; 
typedef DStarPathfinder<Graph, Heuristic> DStarPathfinder;

Если вам нужна дополнительная информация об использовании, просто спросите, слишком много, чтобы вставить.

Псевдокод для D * -Lite:

procedure CalculateKey(s)
{01”} return [min(g(s), rhs(s)) + h(s_start, s) + km;min(g(s), rhs(s))];

procedure Initialize()
{02”} U = ∅;
{03”} km = 0;
{04”} for all s ∈ S rhs(s) = g(s) = ∞;
{05”} rhs(s_goal) = 0;
{06”} U.Insert(s_goal, [h(s_start, s_goal); 0]);

procedure UpdateVertex(u)
{07”} if (g(u) != rhs(u) AND u ∈ U) U.Update(u,CalculateKey(u));
{08”} else if (g(u) != rhs(u) AND u /∈ U) U.Insert(u,CalculateKey(u));
{09”} else if (g(u) = rhs(u) AND u ∈ U) U.Remove(u);

procedure ComputeShortestPath()
{10”} while (U.TopKey() < CalculateKey(s_start) OR rhs(s_start) > g(s_start))
{11”} u = U.Top();
{12”} k_old = U.TopKey();
{13”} k_new = CalculateKey(u));
{14”} if(k_old < k_new)
{15”}   U.Update(u, k_new);
{16”} else if (g(u) > rhs(u))
{17”}   g(u) = rhs(u);
{18”}   U.Remove(u);
{19”}   for all s ∈ Pred(u)
{20”}   if (s != s_goal) rhs(s) = min(rhs(s), c(s, u) + g(u));
{21”}   UpdateVertex(s);
{22”} else
{23”}   g_old = g(u);
{24”}   g(u) = ∞;
{25”}   for all s ∈ Pred(u) ∪ {u}
{26”}   if (rhs(s) = c(s, u) + g_old)
{27”}     if (s != s_goal) rhs(s) = min s'∈Succ(s)(c(s, s') + g(s'));
{28”}   UpdateVertex(s);

procedure Main()
{29”} s_last = s_start;
{30”} Initialize();
{31”} ComputeShortestPath();
{32”} while (s_start != s_goal)
{33”} /* if (g(s_start) = ∞) then there is no known path */
{34”}   s_start = argmin s'∈Succ(s_start)(c(s_start, s') + g(s'));
{35”}   Move to s_start;
{36”}   Scan graph for changed edge costs;
{37”}   if any edge costs changed
{38”}     km = km + h(s_last, s_start);
{39”}     s_last = s_start;
{40”}     for all directed edges (u, v) with changed edge costs
{41”}       c_old = c(u, v);
{42”}       Update the edge cost c(u, v);
{43”}       if (c_old > c(u, v))
{44”}         if (u != s_goal) rhs(u) = min(rhs(u), c(u, v) + g(v));
{45”}       else if (rhs(u) = c_old + g(v))
{46”}         if (u != s_goal) rhs(u) = min s'∈Succ(u)(c(u, s') + g(s'));
{47”}       UpdateVertex(u);
{48”}     ComputeShortestPath()

EDIT:

Мне удалось создать контрольный пример, который показывает ошибочное поведение. Запуская это вместе с кодом в pastebin, он будет зависать при последнем вызове get_path, переходя туда и обратно между узлами 1 и 2. Мне кажется, это потому, что к узлу 3 никогда не прикасались, и поэтому путь имеет бесконечную стоимость.

#include <cmath>
#include <boost/graph/adjacency_list.hpp>
#include "dstar_search.h"

template <typename Graph, typename Vertex>
struct DStarEuclidianHeuristic {
    DStarEuclidianHeuristic(const Graph& G_) : G(G_) {}

    double operator()(const Vertex& u, const Vertex& v) {
        double dx = G[u].x - G[v].x;
        double dy = G[u].y - G[v].y;
        double len = sqrt(dx*dx+dy*dy);
        return len;
    }

    const Graph& G;
};

struct VertexProp {
    double x, y;
};

int main() {
    typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
        VertexProp, boost::property<boost::edge_weight_t, double> > Graph;
    typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef boost::graph_traits<Graph>::edge_descriptor Edge;
    typedef DStarEuclidianHeuristic<Graph, Vertex> Heur;

    typedef boost::property_map<Graph, boost::edge_weight_t>::type WMap;

    Graph g(7);
    WMap weights = boost::get(boost::edge_weight, g);
    Edge e;
    // Create a graph
    e = boost::add_edge(0, 1, g).first;
    weights[e] = sqrt(2.);
    e = boost::add_edge(1, 2, g).first;
    weights[e] = 1;
    e = boost::add_edge(2, 3, g).first;
    weights[e] = 1;
    e = boost::add_edge(1, 4, g).first;
    weights[e] = 1;
    e = boost::add_edge(3, 4, g).first;
    weights[e] = 1;
    e = boost::add_edge(3, 5, g).first;
    weights[e] = sqrt(2.);
    e = boost::add_edge(2, 6, g).first;
    weights[e] = sqrt(2.);
    e = boost::add_edge(5, 6, g).first;
    weights[e] = 1;
    e = boost::add_edge(6, 7, g).first;
    weights[e] = 1;
    g[0].x = 1; g[0].y = 0;
    g[1].x = 0; g[1].y = 1;
    g[2].x = 0; g[2].y = 2;
    g[3].x = 1; g[3].y = 2;
    g[4].x = 1; g[4].y = 1;
    g[5].x = 2; g[5].y = 3;
    g[6].x = 1; g[6].y = 3;
    g[7].x = 1; g[7].y = 4;

    DStarPathfinder<Graph, Heur> dstar(g, Heur(g), 0, 7);
    std::list<std::pair<Edge, double>> changes;

    auto a =  dstar.get_path(); // Find the initial path, works well
    std::copy(a.begin(), a.end(), std::ostream_iterator<Vertex>(std::cout, ","));
    // Now change the cost of going from 2->6, and try to find a new path
    changes.push_back(std::make_pair(boost::edge(2, 6, g).first, 4.));
    dstar.update(changes);
    a = dstar.get_path(); // Stuck in loop
    std::copy(a.begin(), a.end(), std::ostream_iterator<Vertex>(std::cout, ","));

    return 0;
}

РЕДАКТИРОВАТЬ 2: Больше прогресса. Если я заменим условие разрыва в цикле while в ComputeShortestPath просто на U != Ø (U не пусто), путь найден! Это довольно медленно, так как он всегда проверяет каждый узел в графе, который не должен быть обязательным. Кроме того, поскольку я использую неориентированные графики, я добавил некоторый код в {40"} для обновления u и v.

Ответы [ 4 ]

6 голосов
/ 05 сентября 2011

Есть как минимум две проблемы с вашим кодом (не считая typename s, которые мне пришлось добавить к конструкциям типа std::vector<TemplateParameter>::iterator для его компиляции).

  1. Вы используете недопустимую эвристику, поскольку диагонали стоят 1, но имеют длину √2. Это не позволяет второму вызову ComputeShortestPath вообще что-либо делать.

  2. Метод обновления кучи, которую вы используете (которая по соглашению является частной для Boost и, таким образом, явно недокументированной), поддерживает только уменьшение ключа. D * Lite также нуждается в увеличении ключа.

2 голосов
/ 30 августа 2011

К сожалению, размещение псевдокода здесь не очень полезно, так как псевдокод может быть правильным, но фактическая реализация может быть ошибочной.

Как правило, в алгоритмах поиска пути, если вы выполняете циклическое переключение между узлами, есть очень хороший шанс, что алгоритм не удаляет посещенные узлы из набора потенциальных узлов маршрута. Обычно это делается путем установки флага на узле, когда вы проходите через него, и сбрасываете флаг, когда вы возвращаетесь на шаг вверх по дереву поиска.

0 голосов
/ 26 апреля 2012

У меня тоже твоя проблема. Я думаю, у меня есть причина, может быть, в этот раз вы найдете решение своей проблемы и сможете дать мне несколько советов.

Думаю, проблема в списке U.

Поскольку, вероятно, некоторые ключи каждой вершины имеют значение выше, чем ключ s_start. Таким образом, ComputeKey(s)<ComputeKeu(s_start) не выполняется (первое условие while в ComputePath), второе условие rhs(s_start)>g(s_start) не выполняется, поскольку при движении по пути вы проходите через ячейки, которые становятся согласованными.

Затем, когда эти два условия не удерживают остановку while, программа прекращает расширение новых ячеек.

Когда вы продолжаете вычислять путь, используя в качестве последовательного вдоль пути тот, который минимизирует g(s)+c(u,s), вы попадаете в ячейку, в которой все еще есть и бесконечная стоимость g (потому что она не была расширена во время цикла .

Это является причиной даже того факта, что если вы измените условие, используя алгоритм 101 * *, в то время как алгоритм работает, это вынудит программу развернуть все вершины в списке U. (Но вы определенно потеряли преимущества динамического алгоритма).

Теперь я надеюсь, что помог вам, если вам больше не нужна эта помощь, может быть, вы сможете мне помочь.

0 голосов
/ 10 сентября 2011

Проблема в функции UpdateVertex.

Псевдо-код был написан, предполагая, что сравнения даны по целым числам (которые они есть в статьях).В вашей реализации вы делаете сравнения по значениям с плавающей запятой.Вам нужно добавить допуск, если вы имеете дело с нецелыми затратами.

Вы можете проверить это на GCC, скомпилировав с -Wfloat-equal (или даже лучше -Werror = float-equal)

...