Python 实现 Dijkstar 路径规划算法

🎏Dijstar 最短路径算法(用于计算起始点到最终点的最短路径),一般采用的是贪心算法策略=.=🎃

原理可以参考

图解 Open list 和 close list

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述


环境

(Terminal) 需要预先安装两个库 matplotlib 和 math

pip3 install matplotlib
pip3 install math

可以运行 Python 的环境

  • ubuntu 18.04
  • python3

程序思路

代码片段

  • 开始点和终止点的初始化,设置 x y z
start_node = self.Node(self.calc_xy_index(sx, self.min_x),
                               self.calc_xy_index(sy, self.min_y), 0.0, -1)   # round((position - minp) / self.resolution)
goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
            self.calc_xy_index(gy, self.min_y), 0.0, -1)

open_set, closed_set = dict(), dict()     # 字典的形式,可以通过 key 得到值,可以
 open_set[self.calc_index(start_node)] = start_node
  • 获取最小点节点,并且更新和替换掉当前的节点位置
c_id = min(open_set, key=lambda o: open_set[o].cost)  # 取cost最小的节点
            current = open_set[c_id]

在这里插入图片描述

  • 机器人在栅格地图内的移动模型(3x3)
def get_motion_model():
    # dx, dy, cost 就是在 x 和 y 的方向和临近点的代价
    motion = [[1, 0, 1],
              [0, 1, 1],
              [-1, 0, 1],
              [0, -1, 1],
              [-1, -1, math.sqrt(2)],
              [-1, 1, math.sqrt(2)],
              [1, -1, math.sqrt(2)],
              [1, 1, math.sqrt(2)]]

     return motion

其中[第一位,第二位,第三位]表示在 x 方向和 y 方向移动的步数,第三位可以理解成为损失代价

  • 当前节点的 g(n) 运算,以及 cost
node = self.Node(current.x + move_x,
                 current.y + move_y,
                 current.cost + move_cost, c_id)

两个 List

设置的两个 list(Open list,Closed list)

  1. 将起点放到 Open list 当中
  2. 将循环的判断条件为 open list 是否为空
  3. 在循环体内不断的寻找 g(n) 的最小点,并且将其加入到 Closed list 当中
  4. 判断当前节点是否为终点,如果不是重点,继续遍历不在 Closed list 中的相邻的节点,与 Open list 中比较g(n),将值最小的存入到 Closed list 当中

总代码

"""
Dijkstra 栅格地图导航
author: Shen Mingsheng(@gcusms)
"""

import matplotlib.pyplot as plt
import math

show_animation = True


# 定义一个个 Dijkstra 的类
class Dijkstra:

    def __init__(self, ox, oy, resolution, robot_radius):
        """
        初始化地图

        ox: x 方向上的障碍物
        oy: y 方向上的障碍物
        resolution: 可以理解为栅格之间的步长(分辨率)
        rr: 机器人的半径
        """

        self.min_x = None
        self.min_y = None
        self.max_x = None
        self.max_y = None
        self.x_width = None
        self.y_width = None
        self.obstacle_map = None

        self.resolution = resolution
        self.robot_radius = robot_radius
        self.calc_obstacle_map(ox, oy)
        self.motion = self.get_motion_model()

    class Node:
        def __init__(self, x, y, cost, parent_index):
            self.x = x  # index of grid
            self.y = y  # index of grid
            self.cost = cost  # g(n)
            self.parent_index = parent_index  # index of previous Node

        def __str__(self):
            return str(self.x) + "," + str(self.y) + "," + str(
                self.cost) + "," + str(self.parent_index)

    def planning(self, sx, sy, gx, gy):

        
        start_node = self.Node(self.calc_xy_index(sx, self.min_x),
                               self.calc_xy_index(sy, self.min_y), 0.0, -1)   # round((position - minp) / self.resolution)
        goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
                              self.calc_xy_index(gy, self.min_y), 0.0, -1)

        open_set, closed_set = dict(), dict()     # 字典的形式,可以通过 key 得到值,可以
        open_set[self.calc_index(start_node)] = start_node

        while 1:
            c_id = min(open_set, key=lambda o: open_set[o].cost)  # 取cost最小的节点
            current = open_set[c_id]

            # 显示图像
            if show_animation:  # pragma: 不覆盖参数
                plt.plot(self.calc_position(current.x, self.min_x),
                         self.calc_position(current.y, self.min_y), "xc")
                # ESC 键退出
                plt.gcf().canvas.mpl_connect(
                    'key_release_event',
                    lambda event: [exit(0) if event.key == 'escape' else None])
                if len(closed_set.keys()) % 10 == 0:
                    plt.pause(0.001)

            # 判断是否是终点
            if current.x == goal_node.x and current.y == goal_node.y:
                print("Find goal")
                goal_node.parent_index = current.parent_index
                goal_node.cost = current.cost
                break

            # 如果不是终点的话就将 Open list 当中的最小 g(n) key 值删除
            del open_set[c_id]

            # 将当前的节点放入到 Close list 节点当中
            closed_set[c_id] = current

            # 收录和判断当前节点附近的点
            for move_x, move_y, move_cost in self.motion:
                node = self.Node(current.x + move_x,
                                 current.y + move_y,
                                 current.cost + move_cost, c_id)
                n_id = self.calc_index(node)  # 计算当前节点的 key

                if n_id in closed_set:  # 判断 key 是否收录到 open list 里面
                    continue

                if not self.verify_node(node):
                    continue

                if n_id not in open_set:
                    open_set[n_id] = node  # 发现新的点
                else:
                    if open_set[n_id].cost >= node.cost:
                        # 当前的节点花费已经足够小,已经不需要接着往下检索
                        open_set[n_id] = node

        rx, ry = self.calc_final_path(goal_node, closed_set)

        return rx, ry
    # 找到最终的路径
    def calc_final_path(self, goal_node, closed_set):
        # 生成最终的路径
        rx, ry = [self.calc_position(goal_node.x, self.min_x)], [
            self.calc_position(goal_node.y, self.min_y)]
        parent_index = goal_node.parent_index
        while parent_index != -1:
            n = closed_set[parent_index]
            rx.append(self.calc_position(n.x, self.min_x))
            ry.append(self.calc_position(n.y, self.min_y))
            parent_index = n.parent_index

        return rx, ry

    def calc_position(self, index, minp):
        pos = index * self.resolution + minp
        return pos

    # 将(当前的位置 - 最小的一个栅格值) / 栅格的大小 = 索引值
    def calc_xy_index(self, position, minp):
        return round((position - minp) / self.resolution)

    def calc_index(self, node):
        return node.y * self.x_width + node.x

    def verify_node(self, node):
        px = self.calc_position(node.x, self.min_x)
        py = self.calc_position(node.y, self.min_y)

        if px < self.min_x:
            return False
        if py < self.min_y:
            return False
        if px >= self.max_x:
            return False
        if py >= self.max_y:
            return False

        if self.obstacle_map[node.x][node.y]:
            return False

        return True

    def calc_obstacle_map(self, ox, oy):
        ''' 第1步: 构建栅格地图 '''
        self.min_x = round(min(ox))
        self.min_y = round(min(oy))
        self.max_x = round(max(ox))
        self.max_y = round(max(oy))
        print("min_x:", self.min_x)
        print("min_y:", self.min_y)
        print("max_x:", self.max_x)
        print("max_y:", self.max_y)

        self.x_width = round((self.max_x - self.min_x) / self.resolution)  # x 方向上的栅格数量
        self.y_width = round((self.max_y - self.min_y) / self.resolution)  # y 方向上的栅格数量
        print("x_width:", self.x_width)
        print("y_width:", self.y_width)

        # 初始化地图
        self.obstacle_map = [[False for _ in range(self.y_width)]
                             for _ in range(self.x_width)]
        # 设置障碍物
        for ix in range(self.x_width):
            x = self.calc_position(ix, self.min_x)  # 栅格加上偏移值
            for iy in range(self.y_width):
                y = self.calc_position(iy, self.min_y)
                for iox, ioy in zip(ox, oy):
                    d = math.hypot(iox - x, ioy - y)   # 计算障碍物到栅格的距离
                    ''' 如果机器人的的宽度大于栅格的大小,就将栅格地图膨胀'''
                    if d <= self.robot_radius:
                        self.obstacle_map[ix][iy] = True
                        break

    @staticmethod
    def get_motion_model():
        # dx, dy, cost 就是在 x 和 y 的方向和临近点的代价
        motion = [[1, 0, 1],
                  [0, 1, 1],
                  [-1, 0, 1],
                  [0, -1, 1],
                  [-1, -1, math.sqrt(2)],
                  [-1, 1, math.sqrt(2)],
                  [1, -1, math.sqrt(2)],
                  [1, 1, math.sqrt(2)]]

        return motion

def main():
    # 设置起始点 sx sy,和终止点 gx gy,机器人的半径
    sx = -5.0  # [m]
    sy = -5.0  # [m]
    gx = 50.0  # [m]
    gy = 50.0  # [m]
    grid_size = 2.0  # [m]
    robot_radius = 1.0  # [m]

    # 设置障碍物的位置
    ox, oy = [], []
    for i in range(-10, 60):
        ox.append(i)
        oy.append(-10.0)
    for i in range(-10, 60):
        ox.append(60.0)
        oy.append(i)
    for i in range(-10, 61):
        ox.append(i)
        oy.append(60.0)
    for i in range(-10, 61):
        ox.append(-10.0)
        oy.append(i)
    for i in range(-10, 40):
        ox.append(20.0)
        oy.append(i)
    for i in range(0, 40):
        ox.append(40.0)
        oy.append(60.0 - i)

    if show_animation:  
        plt.plot(ox, oy, ".k")
        plt.plot(sx, sy, "og")
        plt.plot(gx, gy, "xb")
        plt.grid(True)
        plt.axis("equal")

    dijkstra = Dijkstra(ox, oy, grid_size, robot_radius)
    rx, ry = dijkstra.planning(sx, sy, gx, gy)

    if show_animation:  
        plt.plot(rx, ry, "-r")
        plt.pause(0.01)
        plt.show()

if __name__ == '__main__':
    main()

路径规划算法

在这里插入图片描述


🌸🌸🌸🌸🌸完结撒花🌸🌸🌸🌸🌸🌸🌸🌸


🌈🌈Redamancy🌈🌈


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值