2011-08-30 62 views
3

我已經實現了d * -Lite算法(這裏有一個description,它是做尋路時邊緣成本隨時間變化的算法),但我有跟做邊成本的更新問題。它主要工作,但有時它會卡在一個循環中,在兩個頂點之間來回切換。我試圖創建一個展現此行爲的測試用例,此時它在某些情況下發生在大型應用程序中,這使得它很難調試。尋路算法創建循環

我,只要我能得到一個測試用例,但也許有人能發現我所做的僞要去C++馬上錯誤。 (下面有包括一個測試用例)文章介紹的優化版本,圖4,這是一個我實現。僞代碼粘貼在下面。

來源爲我實現菱here

如果有幫助,我在我的代碼使用這些類型:

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() 

編輯:

我已經成功地創建了測試用例足見其erronous行爲。與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:更多進展。如果我用U != ØU不爲空)代替ComputeShortestPath中的while循環中的中斷條件,則會找到路徑!它的速度很慢,因爲它總是檢查圖中的每個節點,這不應該是必需的。此外,由於我使用無向圖,我添加了一些代碼到{40"}來更新uv

+0

使用下劃線:知道 - > k_new,gold-> g_old等。它使人們更容易解析代碼 - 我的大腦看到'黃金'並且想到金屬! – Skizz

+0

好點。修正了。 – carlpett

+0

在你對fred的迴應中,你寫道曼哈頓距離是abs(dx + dy)。你的意思是abs(dx)+ abs(dy),對嗎? – Clark

回答

6

你的代碼至少有兩個問題(不包括typename,爲了編譯它,我必須預先寫入像std::vector<TemplateParameter>::iterator這樣的結構)。

  1. 由於對角線的費用爲1但長度爲√2,因此您使用了不可接受的啓發式。這可以防止第二次致電ComputeShortestPath做任何事情。

  2. 你正在使用的堆的更新方法(按照慣例是私有的,因此顯然沒有記錄)僅支持鍵減少。 D * Lite也需要重點增加。

+0

感謝您的反饋!我已經改變了對角線遍歷的成本,這是精神崩潰的結果。我也在d堆堆上讀到,實際上它只允許減少。我改成'boost :: relaxed_heap',這個(我認爲它也沒有文檔......)允許增加和減少。但是,它仍然不起作用。如果我改用曼哈頓啓發式('dx + dy'),它適用於這種情況,但不是我真正的問題。爲什麼這應該有幫助?根據這篇論文,啓發式必須是非負的,低於成本,並且滿足三角函數。我想我的歐幾里德距離呢? – carlpett

+0

糾正,它不適用於正確實施的曼哈頓距離,即與'abs(dx + dy)'(它應該比歐幾里得距離更長,不知道我在想什麼) – carlpett

+0

事實證明一個問題我在「真實」問題中也有不可接受的啓發式方法(它使用動態權重,並且在某些情況下,乘法因子可能會降到1以下,從而導致不可接受性)。如果解決了這個問題,一個「非優化」版本的實現工作,但仍然不是我上面鏈接的那個。然而,如果沒有人知道優化版本有什麼問題,這個答案會得到賞金,因爲它確實指向了我的正確方向 – carlpett

2

不幸的是,發佈僞是不是真的有用,因爲這裏的僞代碼可能是正確的,但在實際執行可能發生故障。

一般來說,在尋路算法,如果你的節點之間的循環再有就是一個很好的機會,該算法沒有從該組潛在的路徑節點刪除訪問節點。這通常是通過在節點上遍歷節點時設置一個標誌來完成的,並且在您重新搜索樹時重置該標誌。

+0

標記方法對於MT的使用並不是真正可擴展的,在這種情況下,您更願意保留一組指向節點的指針,而不是「標記」節點。 –

+0

@Skizz:我很確定這是執行錯誤的';)'。我把它作爲一個pastebin鏈接到它,這裏包含了太長的時間。我包含了僞代碼,這樣人們就不必從文章中挖掘出來。 – carlpett

+0

關於循環,該算法計算到某些節點的距離,直到找到目標爲止(由啓發式指導)。當它試圖通過遵循計算的最短路徑來提取路徑時出現問題,因爲由於某種原因某個節點具有來自一個節點的最便宜的替代方案,這反過來也是來自該節點的最便宜的替代方案...(這個解釋不是那種'非常好,如果你有時間,文章會更好) – carlpett

0

問題出在UpdateVertex函數中。

僞代碼的編寫假定比較是整數(它們在論文中)。在你的實現中,你正在對浮點值進行比較。如果您處理非整數成本,則需要添加容差。

您可以通過使用-Wfloat平等編譯測試這個對GCC(甚至更好-Werror =浮動相等),我有你同樣的問題也

0

。我想我得到了原因,也許在這個時候你找到了解決問題的辦法,並且可以給我一些提示。

我覺得問題來自U列表。

由於可能每個頂點的某個關鍵字的值高於s_start的關鍵字。 因此ComputeKey(s)<ComputeKeu(s_start)不滿足(ComputePath中while的第一個條件),第二個條件rhs(s_start)>g(s_start)不滿足,因爲當您沿着路徑移動時,您將移動通過一致的單元格。

然後當這兩個條件不保持while stop時,程序停止擴展新的單元格。

當你去計算路徑,沿着路徑連續使用一個最小化g(s)+c(u,s)的單元,結果在一個單元上仍然具有無限的成本(因爲它在while循環中沒有被擴展)。

這就是爲什麼如果更改條件,使用U!=0算法工作,這會強制程序展開U列表中的所有頂點。 (但你絕對失去了動態算法的優點)。

現在,我希望我已經幫助過你,如果你不需要這個幫助,那麼也許你可以幫助我。