我已经实现了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()
修改
我成功地创建了一个显示错误行为的测试用例。将其与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
。
答案 0 :(得分:6)
您的代码至少存在两个问题(不包括typename
我必须在std::vector<TemplateParameter>::iterator
之类的构造中添加它才能编译它。
您使用的是不允许的启发式算法,因为对角线的成本为1,但长度为√2。这可以防止第二次调用ComputeShortestPath
做任何事情。
您正在使用的堆的更新方法(按照惯例对Boost进行私有,因此显然未记录)仅支持密钥减少。 D * Lite也需要增加密钥。
答案 1 :(得分:2)
不幸的是,发布伪代码在这里并没有用,因为伪代码可能是正确的,但实际的实现可能有问题。
通常,在路径查找算法中,如果您在节点之间循环,那么该算法很可能不会从潜在路由节点集中删除受访节点。这通常是通过在遍历它时在节点上设置一个标志并在你向后升级搜索树时重置标志来完成的。
答案 2 :(得分:0)
问题出在UpdateVertex函数中。
伪造的代码是在假设比较是整数的情况下编写的(它们在论文中)。在您的实现中,您正在对浮点值进行比较。如果处理非整数成本,则需要添加容差。
您可以通过使用-Wfloat-equal(甚至更好的-Werror = float-equal)进行编译来在GCC上进行测试
答案 3 :(得分:0)
我也有同样的问题。我想我得到了原因,也许在这个时候你找到问题的解决方案并且可以给我一些提示。
我认为问题来自U
列表。
因为每个顶点的某些键可能具有高于s_start
的键的值。
因此ComputeKey(s)<ComputeKeu(s_start)
不满意(ComputePath中while的第一个条件),第二个条件rhs(s_start)>g(s_start)
不满足,因为当您沿着路径移动时,您将移动通过正在变为一致的单元格。
然后当这两个条件不能保持while停止时,程序停止扩展新单元格。
当你计算路径时,沿着路径连续使用最小化g(s)+c(u,s)
最终在一个单元格上的那个仍然具有无限g
成本(因为它没有被扩展) while while循环)。
这也是因为如果你改变条件,使用while U!=0
算法可以工作,这会强制程序扩展U
列表中的所有顶点。 (但你肯定失去了动态算法的优点)。
现在我希望我帮助了你,如果你不需要这个帮助,也许你可以帮助我。
答案 4 :(得分:0)
在实施 D * Lite(常规版)和优化版时,我自己遇到了这个问题。我不太确定为什么会首先发生这种情况,但似乎是因为某种障碍物的排列(十字形或高大的垂直障碍物)而被触发,在这种情况下,算法突然无法探索更多的选择,并最终在两个障碍物之间来回跳跃。无限循环中的两个选项。我已经在较早的here和我如何绕过无限循环问题上创建了一篇文章,但是以算法为代价可能会变慢一些。