3

我正在开发一个机器人项目的前端(一种使用一些传感器和地图进行自我定位的“自动”汽车——从 SVG 文件生成)。

为了使机器人可控,我们必须在其当前位置和目标之间生成路径。我为此使用了最简单的算法:A*。

这样做我得到了一些奇怪的结果:汽车倾向于以 45° 的倍数行驶,还有一个特别烦人的问题:一些生成的路径非常嘈杂!

在这种情况下,请查看橙色矩形附近的嘈杂路径:

生成路径的可视化

有没有办法避免那些奇怪/嘈杂的结果?最终,我们希望建立一条航向角变化最少的路径。(汽车可以在不移动的情况下转动,因此我们不需要任何路径“平滑”)。

这是我的 A* 实现:

def search(self, begin, goal):
    if goal.x not in range(self.width) or goal.y not in range(self.height):
        print "Goal is out of bound"
        return []
    elif not self.grid[begin.y][begin.x].reachable:
        print "Beginning is unreachable"
        return []
    elif not self.grid[goal.y][goal.x].reachable:
        print "Goal is unreachable"
        return []
    else:

        self.cl = set()
        self.ol = set()

        curCell = begin
        self.ol.add(curCell)

        while len(self.ol) > 0:

            # We choose the cell in the open list having the minimum score as our current cell
            curCell = min(self.ol, key = lambda x : x.f)

            # We add the current cell to the closed list
            self.ol.remove(curCell)
            self.cl.add(curCell)

            # We check the cell's (reachable) neighbours :
            neighbours = self.neighbours(curCell)

            for cell in neighbours:
                # If the goal is a neighbour cell :
                if cell == goal:
                    cell.parent = curCell
                    self.path = cell.path()
                    self.display()
                    self.clear()
                    return self.path
                elif cell not in self.cl:
                    # We process the cells that are not in the closed list
                    # (processing <-> calculating the "F" score)
                    cell.process(curCell, goal)

                    self.ol.add(cell)

编辑1:根据大众需求,这是分数计算功能(过程):

def process(self, parent, goal):
    self.parent = parent
    self.g = parent.distance(self)
    self.h = self.manhattanDistance(goal)
    self.f = self.g + self.h

编辑这是邻居方法(根据user1884905的回答更新):

def neighbours(self, cell, radius = 1, unreachables = False, diagonal = True):
    neighbours = set()
    for i in xrange(-radius, radius + 1):
        for j in xrange(-radius, radius + 1):
            x = cell.x + j
            y = cell.y + i
            if 0 <= y < self.height and 0 <= x < self.width and ( self.grid[y][x].reachable or unreachables ) and (diagonal or (x == cell.x or y == cell.y)) :
                neighbours.add(self.grid[y][x])

    return neighbours

(这看起来很复杂,但它只给出了一个单元格的 8 个邻居——包括对角邻居;它也可以采用不同于 1 的半径,因为它用于其他特征)

和距离计算(取决于对角线邻居的使用与否:)

def manhattanDistance(self, cell):
    return abs(self.x - cell.x) + abs(self.y - cell.y)

def diagonalDistance(self, cell):

    xDist = abs(self.x - cell.x)
    yDist = abs(self.y - cell.y)

    if xDist > yDist:
        return 1.4 * yDist + (xDist - yDist)
    else:
        return 1.4 * xDist + (yDist - xDist)
4

2 回答 2

4

似乎实现不正确,因为它正在移动到尚未检查的单元格中,该单元格距离目标最近(如乌鸦飞过),而它应该尝试它并在找到障碍物时撤消路径以找到最优的。在 Wikipedia 上查看这个漂亮的动画来了解这个想法。

这里的问题与您如何计算有关cell.f,也许您在进行微积分时没有添加当前单元格的分数,一般而言,A* 应该采取此处标记为红色的步骤并生成类似的次优路径。

由于空间被划分为离散的单元格,当连续世界中的最佳路径(总是像乌鸦飞过一样)正好在两个离散移动之间时,它会尽可能地用那条奇怪的路径来近似它。

我在这里看到两种方法:

  1. 修复算法(这里是伪代码)为每个评估的单元格保持正确的距离值(在粘贴的单元格中没有关于如何cell.f计算的信息)。
  2. 使用Djikstra,只需对当前算法进行一些更改即可轻松实现。实际上,A* 只是它的优化版本。
于 2013-03-19T11:14:35.847 回答
3

在无法看到您如何实现您的neighbourdistance功能的情况下,我仍然可以很好地猜测出了什么问题:

如果允许对角线遍历,则不应使用曼哈顿距离。

目标函数中的曼哈顿距离应该是到目标的最短距离的度量。(事实并非如此,如果您可以沿对角线行驶穿过积木。)

解决此问题的最简单方法是将曼哈顿距离保持为目标函数,并将邻居的定义更改为仅包括四个左右上下相邻的单元格。

编辑

您的代码仍然存在问题。以下伪代码取自维基百科。我已经标记了您必须检查的重要行。您必须确保 i)如果您找到更好的解决方案,您正在更新开放集中的节点,并且 ii)您始终考虑先前行进的距离。

function A*(start,goal)
     closedset := the empty set    // The set of nodes already evaluated.
     openset := {start}    // The set of tentative nodes to be evaluated, initially containing the start node
     came_from := the empty map    // The map of navigated nodes.

     g_score[start] := 0    // Cost from start along best known path.
     // Estimated total cost from start to goal through y.
     f_score[start] := g_score[start] + heuristic_cost_estimate(start, goal)

     while openset is not empty
         current := the node in openset having the lowest f_score[] value
         if current = goal
             return reconstruct_path(came_from, goal)

         remove current from openset
         add current to closedset
         for each neighbor in neighbor_nodes(current)
             // -------------------------------------------------------------------
             // This is the way the tentative_g_score should be calculated.
             // Do you include the current g_score in your calculation parent.distance(self) ?
             tentative_g_score := g_score[current] + dist_between(current,neighbor)
             // ------------------------------------------------------------------- 
             if neighbor in closedset
                 if tentative_g_score >= g_score[neighbor]
                     continue

             // -------------------------------------------------------------------
             // You never make this comparrison
             if neighbor not in openset or tentative_g_score < g_score[neighbor]
             // -------------------------------------------------------------------
                 came_from[neighbor] := current
                 g_score[neighbor] := tentative_g_score
                 f_score[neighbor] := g_score[neighbor] + heuristic_cost_estimate(neighbor, goal)
                 if neighbor not in openset
                     add neighbor to openset

     return failure

 function reconstruct_path(came_from, current_node)
     if current_node in came_from
         p := reconstruct_path(came_from, came_from[current_node])
         return (p + current_node)
     else
         return current_node
于 2013-04-04T15:11:01.043 回答