前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >自动驾驶路径规划-Graph Based的BFS最短路径规划

自动驾驶路径规划-Graph Based的BFS最短路径规划

作者头像
YoungTimes
发布2022-04-28 15:59:10
1.3K0
发布2022-04-28 15:59:10
举报
文章被收录于专栏:半杯茶的小酒杯
自动驾驶运动规划(Motion Planning)

自动驾驶路径规划

1、Graph的基础定义及Python表达

在数学或者计算机数据结构的教材中,Graph由Node(或者vertices)组成,Node之间以Edge连接(如下图所示)。如果Node之间的连接是没有方向的,则称该Graph为无向图(Undirected Graph);反之,如果Node之间的连接是有方向的,则称为该Graph为有向图(Directed Graph);有向图(Directed Graph)的Edge被成为Arc。

上图的Graph在Python中可以借助Dictionary表达:

代码语言:javascript
复制
graph = { "a" : ["c"],
          "b" : ["c", "e"],
          "c" : ["a", "b", "d", "e"],
          "d" : ["c"],
          "e" : ["c", "b"],
          "f" : []
        }

Graph的Edge可以用tuple表达,比如连接a和b的Edge可以表示为(a, b)。我们可以实现一个从Graph生成Edge的函数。

代码语言:javascript
复制
def generate_edges(graph):
    edges = []
    for node in graph:
        for neighbour in graph[node]:
            edges.append((node, neighbour))

    return edges

print(generate_edges(graph))

generate_edges()函数生成Graph的所有Edge:

代码语言:javascript
复制
[('a', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'd'), ('c', 'e'), ('b', 'c'), ('b', 'e'), ('e', 'c'), ('e', 'b'), ('d', 'c')]

可以看到,由于Node f是孤立节点,所有所有Edge中都不包含Node f。

2、Python实现基础Graph类

在了解Graph的基础概念之后,用Python实现一个Graph类。

代码语言:javascript
复制
""" A Python Class
A simple Python graph class, demonstrating the essential 
facts and functionalities of graphs.
"""

class Graph(object):
    def __init__(self, graph_dict=None):
        """ initializes a graph object 
            If no dictionary or None is given, 
            an empty dictionary will be used
        """
        if graph_dict == None:
            graph_dict = {}
        self.__graph_dict = graph_dict

    def vertices(self):
        """ returns the vertices of a graph """
        return list(self.__graph_dict.keys())

    def edges(self):
        """ returns the edges of a graph """
        return self.__generate_edges()

    def add_vertex(self, vertex):
        """ If the vertex "vertex" is not in 
            self.__graph_dict, a key "vertex" with an empty
            list as a value is added to the dictionary. 
            Otherwise nothing has to be done. 
        """
        if vertex not in self.__graph_dict:
            self.__graph_dict[vertex] = []

    def add_edge(self, edge):
        """ assumes that edge is of type set, tuple or list; 
            between two vertices can be multiple edges! 
        """
        edge = set(edge)
        (vertex1, vertex2) = tuple(edge)
        if vertex1 in self.__graph_dict:
            self.__graph_dict[vertex1].append(vertex2)
        else:
            self.__graph_dict[vertex1] = [vertex2]

    def __generate_edges(self):
        """ A static method generating the edges of the 
            graph "graph". Edges are represented as sets 
            with one (a loop back to the vertex) or two 
            vertices 
        """
        edges = []
        for vertex in self.__graph_dict:
            for neighbour in self.__graph_dict[vertex]:
                if {neighbour, vertex} not in edges:
                    edges.append({vertex, neighbour})
        return edges

    def __str__(self):
        res = "vertices: "
        for k in self.__graph_dict:
            res += str(k) + " "
        res += "\nedges: "
        for edge in self.__generate_edges():
            res += str(edge) + " "
        return res

测试一下Graph类的实现。

代码语言:javascript
复制
if __name__ == "__main__":
    g = { "a" : ["d"],
          "b" : ["c"],
          "c" : ["b", "c", "d", "e"],
          "d" : ["a", "c"],
          "e" : ["c"],
          "f" : []
        }


    graph = Graph(g)

    print("Vertices of graph:")
    print(graph.vertices())

    print("Edges of graph:")
    print(graph.edges())

    print("Add vertex:")
    graph.add_vertex("z")

    print("Vertices of graph:")
    print(graph.vertices())
 
    print("Add an edge:")
    graph.add_edge({"a","z"})
    
    print("Vertices of graph:")
    print(graph.vertices())

    print("Edges of graph:")
    print(graph.edges())

    print('Adding an edge {"x","y"} with new vertices:')
    graph.add_edge({"x","y"})
    print("Vertices of graph:")
    print(graph.vertices())
    print("Edges of graph:")
    print(graph.edges())

程序的输出如下:

代码语言:javascript
复制
Vertices of graph:
['a', 'c', 'b', 'e', 'd', 'f']
Edges of graph:
[{'a', 'd'}, {'c', 'b'}, {'c'}, {'c', 'd'}, {'c', 'e'}]
Add vertex:
Vertices of graph:
['a', 'c', 'b', 'e', 'd', 'f', 'z']
Add an edge:
Vertices of graph:
['a', 'c', 'b', 'e', 'd', 'f', 'z']
Edges of graph:
[{'a', 'd'}, {'c', 'b'}, {'c'}, {'c', 'd'}, {'c', 'e'}, {'a', 'z'}]
Adding an edge {"x","y"} with new vertices:
Vertices of graph:
['a', 'c', 'b', 'e', 'd', 'f', 'y', 'z']
Edges of graph:
[{'a', 'd'}, {'c', 'b'}, {'c'}, {'c', 'd'}, {'c', 'e'}, {'a', 'z'}, {'y', 'x'}]

3、Graph中的路径查找(Path Finding)

有了Graph结构之后,我们看看如何实现查找从一个Node到另一个Node的路径的问题。在实现Python代码之前,我们再复习一些概念:

邻接节点(Adjacent Vertices):如果两个Vertices存在一条连接Edge,则称它们是相邻接的。

无向图中的Path: 无向图中的Path是一个点序列

P = (v_1, v_2, ..., v_n)

,序列中相邻的节点都是相邻接的。

简单路径(Simple Path):没有重复节点的Path称为Simple Path。

3.1 Graph中路径查找的递归实现

实现查找一条从开始顶点(Start Vertex)到结束顶点(End Vertex)的简单路径(Simple Path) 的算法。

代码语言:javascript
复制
def find_path(self, start_vertex, end_vertex, path=None):
    """ find a path from start_vertex to end_vertex 
        in graph """
    if path == None:
        path = []
    graph = self.__graph_dict
    path = path + [start_vertex]
    if start_vertex == end_vertex:
        return path
    if start_vertex not in graph:
         return None
    for vertex in graph[start_vertex]:
        if vertex not in path:
            extended_path = self.find_path(vertex, 
                                           end_vertex, 
                                           path)
            if extended_path: 
                return extended_path
    return None

查找从开始顶点(Start Vertex)到结束顶点(End Vertex)的所有简单路径(Simple Path)的算法。

代码语言:javascript
复制
def find_all_paths(self, start_vertex, end_vertex, path=[]):
    """ find all paths from start_vertex to 
        end_vertex in graph """
    graph = self.__graph_dict 
    path = path + [start_vertex]
    if start_vertex == end_vertex:
        return [path]
    if start_vertex not in graph:
        return []
    paths = []
    for vertex in graph[start_vertex]:
        if vertex not in path:
            extended_paths = self.find_all_paths(vertex, 
                                                 end_vertex, 
                                                 path)
            for p in extended_paths: 
                paths.append(p)
    return paths

查找从开始顶点(Start Vertex)到结束顶点(End Vertex)的最短路径(Simple Path)的算法。

代码语言:javascript
复制
def findShortestPath(graph,start,end,path=[]):
    path = path +[start]
    if start == end:
        return path
    
    shortestPath = []
    for node in graph[start]:
        if node not in path:
            newpath = findShortestPath(graph,node,end,path)
            if newpath:
                if not shortestPath or len(newpath)<len(shortestPath):
                    shortestPath = newpath

测试代码如下:

代码语言:javascript
复制
g = { "a" : ["d"],
      "b" : ["c"],
      "c" : ["b", "c", "d", "e"],
      "d" : ["a", "c"],
      "e" : ["c"],
      "f" : []
    }

graph = Graph(g)

print('The path from vertex "a" to vertex "b":')
path = graph.find_path("a", "b")
print(path)

print('All paths from vertex "a" to vertex "b":')
path = graph.find_all_paths("a", "b")
print(path)

路径查找的结果如下:

代码语言:javascript
复制
The path from vertex "a" to vertex "b": ['a', 'd', 'c', 'b']

All paths from vertex "a" to vertex "b":
[['a', 'd', 'c', 'b'], ['a', 'f', 'd', 'c', 'b']]

3.2 Graph路径查找的非递归实现

Graph中查询最短路径的非递归遍历算法利用Queue的先进先出的特性,以起点Node为中心,波浪式的向外查找,直至找到目标Node。这种波浪式的查找方法,保证了找到的一定是起点Node到终点Node的最短路径。在查找过程中,记录了查询路径上所有Node的前驱节点,从而保证了在查到目标节点之后能够追溯到完整的路径。

Python的实现代码如下:

代码语言:javascript
复制
def extractPath(self, u, pred):
    path = []
    k = u
    path.append(k)
    while k in pred:
        path.append(pred[k])
        k = pred[k]
        
    path.reverse()
   
    return path
    
def findShortestPath(self, start, end, path=[]): 
    # Mark all the vertices as not visited 
    closed = set()
     
    # Create a queue for BFS 
    opened = []
    pred = {} 
  
    # Mark the source node as visited and enqueue it 
    opened.append(start) 
    closed.add(start)
  
    while opened: 
        u = opened.pop(0) 
        if u == end:
            path = self.extractPath(u, pred)
            return path
  
        for i in self.__graph_dict[u]: 
            if i not in closed: 
                opened.append(i) 
                pred[i] = u
                closed.add(i)

测试代码如下:

代码语言:javascript
复制
g = { "a" : ["d"],
      "b" : ["c"],
      "c" : ["b", "c", "d", "e"],
      "d" : ["a", "c"],
      "e" : ["c"],
      "f" : []
    }

graph = Graph(g)

print('The path from vertex "a" to vertex "b":')
path = graph.findShortestPath("a", "b")
print(path)

输出最短路径:

代码语言:javascript
复制
The path from vertex "a" to vertex "b":
['a', 'd', 'c', 'b']

4、Mission Planner的路径规划

目前为止,我们已经知道,在路径规划技术中,首先将地图构建为Node-Edge的Graph结构,然后基于Graph和BFS算法实现从起始Node和目的地Node的路径查找。但是,我们必须知道到,本文介绍的路径规划是Graph的所有Edge权重是完全相等,这是不符合实际情况的,实际的工程应用的路径规划要更为复杂,要考虑到道路交通状况、路径长度、到达时间、乘客上下车位置等等,每个Edge都会赋予不同的权重,不同的权重表达了该Edge被选中的可能性。后面我们将继续学习在有权重的Graph中如何实现路径查找。

参考链接

1、Graphs in Python(https://www.python-course.eu/graphs_python.php) 2、Coursera多伦多大学自动驾驶课程-Motion Planning for Self-Driving Cars

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半杯茶的小酒杯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、Graph的基础定义及Python表达
  • 2、Python实现基础Graph类
  • 3、Graph中的路径查找(Path Finding)
    • 3.1 Graph中路径查找的递归实现
      • 3.2 Graph路径查找的非递归实现
        • 4、Mission Planner的路径规划
          • 参考链接
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档