当前位置: 首页 > news >正文

zhm_real/MotionPlanning运动规划库中A*算法源码详细解读

   本文主要对zhm_real/MotionPlanning运动规划库中A*算法源码进行详细解读,即对astar.py文件中的内容进行详细的解读,另外本文是 Hybrid A * 算法源码解读的前置文章,为后续解读Hybrid A * 算法源码做铺垫。

在这里插入图片描述

   astar.py文件中的源码如下:

import heapq
import math
import numpy as np
import matplotlib.pyplot as pltclass Node:def __init__(self, x, y, cost, pind):self.x = x  # x position of nodeself.y = y  # y position of nodeself.cost = cost  # g cost of nodeself.pind = pind  # parent index of nodeclass Para:def __init__(self, minx, miny, maxx, maxy, xw, yw, reso, motion):self.minx = minxself.miny = minyself.maxx = maxxself.maxy = maxyself.xw = xwself.yw = ywself.reso = reso  # resolution of grid worldself.motion = motion  # motion setdef astar_planning(sx, sy, gx, gy, ox, oy, reso, rr):"""return path of A*.:param sx: starting node x [m]:param sy: starting node y [m]:param gx: goal node x [m]:param gy: goal node y [m]:param ox: obstacles x positions [m]:param oy: obstacles y positions [m]:param reso: xy grid resolution:param rr: robot radius:return: path"""n_start = Node(round(sx / reso), round(sy / reso), 0.0, -1)n_goal = Node(round(gx / reso), round(gy / reso), 0.0, -1)ox = [x / reso for x in ox]oy = [y / reso for y in oy]P, obsmap = calc_parameters(ox, oy, rr, reso)open_set, closed_set = dict(), dict()open_set[calc_index(n_start, P)] = n_startq_priority = []heapq.heappush(q_priority,(fvalue(n_start, n_goal), calc_index(n_start, P)))while True:if not open_set:break_, ind = heapq.heappop(q_priority)n_curr = open_set[ind]closed_set[ind] = n_curropen_set.pop(ind)for i in range(len(P.motion)):node = Node(n_curr.x + P.motion[i][0],n_curr.y + P.motion[i][1],n_curr.cost + u_cost(P.motion[i]), ind)if not check_node(node, P, obsmap):continuen_ind = calc_index(node, P)if n_ind not in closed_set:if n_ind in open_set:if open_set[n_ind].cost > node.cost:open_set[n_ind].cost = node.costopen_set[n_ind].pind = indelse:open_set[n_ind] = nodeheapq.heappush(q_priority,(fvalue(node, n_goal), calc_index(node, P)))pathx, pathy = extract_path(closed_set, n_start, n_goal, P)return pathx, pathydef calc_holonomic_heuristic_with_obstacle(node, ox, oy, reso, rr):n_goal = Node(round(node.x[-1] / reso), round(node.y[-1] / reso), 0.0, -1)ox = [x / reso for x in ox]oy = [y / reso for y in oy]P, obsmap = calc_parameters(ox, oy, reso, rr)open_set, closed_set = dict(), dict()open_set[calc_index(n_goal, P)] = n_goalq_priority = []heapq.heappush(q_priority, (n_goal.cost, calc_index(n_goal, P)))while True:if not open_set:break_, ind = heapq.heappop(q_priority)n_curr = open_set[ind]closed_set[ind] = n_curropen_set.pop(ind)for i in range(len(P.motion)):node = Node(n_curr.x + P.motion[i][0],n_curr.y + P.motion[i][1],n_curr.cost + u_cost(P.motion[i]), ind)if not check_node(node, P, obsmap):continuen_ind = calc_index(node, P)if n_ind not in closed_set:if n_ind in open_set:if open_set[n_ind].cost > node.cost:open_set[n_ind].cost = node.costopen_set[n_ind].pind = indelse:open_set[n_ind] = nodeheapq.heappush(q_priority, (node.cost, calc_index(node, P)))hmap = [[np.inf for _ in range(P.yw)] for _ in range(P.xw)]for n in closed_set.values():hmap[n.x - P.minx][n.y - P.miny] = n.costreturn hmapdef check_node(node, P, obsmap):if node.x <= P.minx or node.x >= P.maxx or \node.y <= P.miny or node.y >= P.maxy:return Falseif obsmap[node.x - P.minx][node.y - P.miny]:return Falsereturn Truedef u_cost(u):return math.hypot(u[0], u[1])def fvalue(node, n_goal):return node.cost + h(node, n_goal)def h(node, n_goal):return math.hypot(node.x - n_goal.x, node.y - n_goal.y)def calc_index(node, P):return (node.y - P.miny) * P.xw + (node.x - P.minx)def calc_parameters(ox, oy, rr, reso):minx, miny = round(min(ox)), round(min(oy))maxx, maxy = round(max(ox)), round(max(oy))xw, yw = maxx - minx, maxy - minymotion = get_motion()P = Para(minx, miny, maxx, maxy, xw, yw, reso, motion)obsmap = calc_obsmap(ox, oy, rr, P)return P, obsmapdef calc_obsmap(ox, oy, rr, P):obsmap = [[False for _ in range(P.yw)] for _ in range(P.xw)]for x in range(P.xw):xx = x + P.minxfor y in range(P.yw):yy = y + P.minyfor oxx, oyy in zip(ox, oy):if math.hypot(oxx - xx, oyy - yy) <= rr / P.reso:obsmap[x][y] = Truebreakreturn obsmapdef extract_path(closed_set, n_start, n_goal, P):pathx, pathy = [n_goal.x], [n_goal.y]n_ind = calc_index(n_goal, P)while True:node = closed_set[n_ind]pathx.append(node.x)pathy.append(node.y)n_ind = node.pindif node == n_start:breakpathx = [x * P.reso for x in reversed(pathx)]pathy = [y * P.reso for y in reversed(pathy)]return pathx, pathydef get_motion():motion = [[-1, 0], [-1, 1], [0, 1], [1, 1],[1, 0], [1, -1], [0, -1], [-1, -1]]return motiondef get_env():ox, oy = [], []for i in range(60):ox.append(i)oy.append(0.0)for i in range(60):ox.append(60.0)oy.append(i)for i in range(61):ox.append(i)oy.append(60.0)for i in range(61):ox.append(0.0)oy.append(i)for i in range(40):ox.append(20.0)oy.append(i)for i in range(40):ox.append(40.0)oy.append(60.0 - i)return ox, oydef main():sx = 10.0  # [m]sy = 10.0  # [m]gx = 50.0  # [m]gy = 50.0  # [m]robot_radius = 2.0grid_resolution = 1.0ox, oy = get_env()pathx, pathy = astar_planning(sx, sy, gx, gy, ox, oy, grid_resolution, robot_radius)plt.plot(ox, oy, 'sk')plt.plot(pathx, pathy, '-r')plt.plot(sx, sy, 'sg')plt.plot(gx, gy, 'sb')plt.axis("equal")plt.show()if __name__ == '__main__':main()

   接下来,我们对上面的源码展开介绍:

   (1)、导入必要的库

   这些是代码所需的库。其中,heapq用于实现优先队列,math提供数学计算功能,numpy用于数组操作,matplotlib.pyplot用于绘图。

import heapq
import math
import numpy as np
import matplotlib.pyplot as plt

   (2)、定义Node类

   这个类用于表示图中的节点,包含了节点的位置、节点的累计代价值和父节点的索引值信息。

class Node:def __init__(self, x, y, cost, pind):self.x = x  # 节点的x坐标self.y = y  # 节点的y坐标self.cost = cost  # 节点的累计代价self.pind = pind  # 父节点的索引

   (3)、定义Para类

   这个类用于存储路径规划过程中所需的参数,如环境地图的范围、网格分辨率和运动集合。每个元素的具体含义如下:

   minx:环境中 x 坐标的最小值。

   miny:环境中 y 坐标的最小值。

   maxx:环境中 x 坐标的最大值。

   maxy:环境中 y 坐标的最大值。

   xw:环境的宽度

   yw:环境的高度。

   reso:网格的分辨率,即每个网格的大小。这会影响地图上障碍物的表示和搜索的精度。

   motion:运动集合,它是一个列表,包含了从当前节点移动到周围节点的不同运动方向。

class Para:def __init__(self, minx, miny, maxx, maxy, xw, yw, reso, motion):self.minx = minxself.miny = minyself.maxx = maxxself.maxy = maxyself.xw = xwself.yw = ywself.reso = reso  # 网格世界的分辨率self.motion = motion  # 运动集合

   通过这些参数,Para 类能够完整地描述问题的环境范围、网格分辨率、扩展方向等信息,从而在路径规划过程中进行合适的搜索。


   (4)、get_motion()函数

   get_motion()函数中编写了运动方向集合,本程序采用八邻域搜索方式,如下所示

def get_motion():motion = [[-1, 0], [-1, 1], [0, 1], [1, 1],[1, 0], [1, -1], [0, -1], [-1, -1]]return motion

   (5)、get_env()函数

   get_env()函数用于定义环境中的障碍物信息,本程序构建的是类似于下图所示的由线条构成的环境

在这里插入图片描述

   所以,下面程序中的一系列for循环依次描述了上图中的下边界、右边界、上边界、左边界、中间两条竖线。

def get_env():ox, oy = [], []for i in range(60):ox.append(i)oy.append(0.0)for i in range(60):ox.append(60.0)oy.append(i)for i in range(61):ox.append(i)oy.append(60.0)for i in range(61):ox.append(0.0)oy.append(i)for i in range(40):ox.append(20.0)oy.append(i)for i in range(40):ox.append(40.0)oy.append(60.0 - i)return ox, oy

   (6)、calc_obsmap()函数

def calc_obsmap(ox, oy, rr, P):obsmap = [[False for _ in range(P.yw)] for _ in range(P.xw)]for x in range(P.xw):xx = x + P.minxfor y in range(P.yw):yy = y + P.minyfor oxx, oyy in zip(ox, oy):if math.hypot(oxx - xx, oyy - yy) <= rr / P.reso:obsmap[x][y] = Truebreakreturn obsmap

   这个函数实现了障碍物地图的计算,用于在路径规划中表示障碍物的分布情况。逐段解释如下:

    obsmap = [[False for _ in range(P.yw)] for _ in range(P.xw)]

   首先,创建一个大小为 P.xw 行、P.yw 列的二维列表 obsmap,用于表示每个网格单元是否是障碍物。初始化所有单元为 False,即默认情况下没有障碍物。

    for x in range(P.xw):xx = x + P.minxfor y in range(P.yw):yy = y + P.minyfor oxx, oyy in zip(ox, oy):if math.hypot(oxx - xx, oyy - yy) <= rr / P.reso:obsmap[x][y] = Truebreak

   然后,通过两个嵌套的循环遍历每个网格单元,以及所有输入的障碍物坐标。在内部的循环中,使用 math.hypot() 函数计算当前网格单元的中心与每个障碍物之间的距离。如果距离小于等于机器人半径 rr,则说明该网格单元与障碍物相交,因此将该网格标记为 True,表示存在障碍物。随后,通过 break 退出内部循环,因为已经确认这个网格单元是一个障碍物。

   以上确定某个栅格处是否存在障碍物的方案,对于栅格地图中的每一个栅格都要计算与所有障碍物点的距离,假设地图栅格为100x100,障碍物点为300个,则最坏的情况下,需要计算100x100x300次,效率较低

    return obsmap

   最后,函数返回表示障碍物地图的二维布尔列表 obsmap,其中每个元素代表一个网格单元是否有障碍物。这个障碍物地图将在路径规划过程中用于检查每个节点的合法性,以确保规划的路径不会穿过障碍物。


   (7)、calc_parameters函数

   这个函数实现了路径规划所需的参数的计算,以及相关的数据初始化,并完成了障碍物环境地图的构建。

def calc_parameters(ox, oy, rr, reso):# 计算环境的边界minx, miny = round(min(ox)), round(min(oy))maxx, maxy = round(max(ox)), round(max(oy))# 计算环境的宽度和高度xw, yw = maxx - minx, maxy - miny# 获取运动方向集合motion = get_motion()# 创建 Para 对象,存储路径规划所需的参数P = Para(minx, miny, maxx, maxy, xw, yw, reso, motion)# 计算障碍物地图obsmap = calc_obsmap(ox, oy, rr, P)return P, obsmap

   逐段解释:

    minx, miny = round(min(ox)), round(min(oy))maxx, maxy = round(max(ox)), round(max(oy))

   这里使用 min()max() 函数来计算障碍物坐标列表 oxoy 中的最小和最大值,以便确定环境的边界框。

    xw, yw = maxx - minx, maxy - miny

   通过最小和最大坐标计算出环境的宽度 xw 和高度 yw。这将用于计算地图的网格数量。

    motion = get_motion()

   调用 get_motion() 函数,获取机器人允许的运动方向集合,通过对其具体程序的查看可知运动方向集合为八邻域搜索。

    P = Para(minx, miny, maxx, maxy, xw, yw, reso, motion)

   创建一个名为 PPara 对象,将计算得到的参数存储其中。这个对象将包含环境的边界、宽度、高度、分辨率以及允许的运动方向。

    obsmap = calc_obsmap(ox, oy, rr, P)

   调用 calc_obsmap() 函数,计算障碍物地图,以便在路径规划中使用。

    return P, obsmap

   最后,将 Para 对象 P 和障碍物地图 obsmap 作为元组返回。这些参数将在主要的 A* 路径规划过程中使用,以确保路径规划在正确的环境和条件下进行。


   (8)、astar_planning函数

   astar_planning函数,是A * 算法的主要实现函数,其输入参数包括起点和目标点的x轴、y轴坐标sx、sy、gx、gy,障碍物的x轴、y轴坐标ox、oy ,栅格地图的精度resolution、机器人的半径rr,函数的返回值是规划的路径path,首先来看astar_planning函数的第一部分:

def astar_planning(sx, sy, gx, gy, ox, oy, reso, rr):# 初始化起点和目标节点n_start = Node(round(sx / reso), round(sy / reso), 0.0, -1)n_goal = Node(round(gx / reso), round(gy / reso), 0.0, -1)# 将障碍物坐标按照分辨率进行缩放ox = [x / reso for x in ox]oy = [y / reso for y in oy]# 计算路径规划所需的参数P, obsmap = calc_parameters(ox, oy, rr, reso)# 初始化开放和关闭节点集合,以及优先队列open_set, closed_set = dict(), dict()open_set[calc_index(n_start, P)] = n_startq_priority = []heapq.heappush(q_priority, (fvalue(n_start, n_goal), calc_index(n_start, P)))

   这部分代码执行了以下步骤:

   ①、创建并初始化起点节点 n_start 和目标节点 n_goal,将坐标函数的输入参数中的起点和终点坐标sx, sy, gx, gy 除以分辨率reso进行栅格化,并将起点和目标点的累计代价初始化为0、父节点初始化为-1。

   ②、将障碍物坐标也按照分辨率进行栅格化,以适应栅格地图。

   ③、调用 calc_parameters 函数,计算路径规划所需的参数 P 和障碍物地图 obsmap

   ④、初始化A*算法的开放集合 open_set 和闭合集合 closed_set,并将起点节点加入开放集合。同时,初始化一个优先队列 q_priority,其中包含了起点节点的 f 值和索引。


   接下来,来看astar_planning函数的第二部分:


    # A*算法主循环while True:if not open_set:break_, ind = heapq.heappop(q_priority)n_curr = open_set[ind]closed_set[ind] = n_curropen_set.pop(ind)for i in range(len(P.motion)):node = Node(n_curr.x + P.motion[i][0],n_curr.y + P.motion[i][1],n_curr.cost + u_cost(P.motion[i]), ind)if not check_node(node, P, obsmap):continuen_ind = calc_index(node, P)if n_ind not in closed_set:if n_ind in open_set:if open_set[n_ind].cost > node.cost:open_set[n_ind].cost = node.costopen_set[n_ind].pind = indelse:open_set[n_ind] = nodeheapq.heappush(q_priority, (fvalue(node, n_goal), calc_index(node, P)))

   这部分代码实现了 A* 算法的主要循环。在每次循环中,算法从开放集合中弹出一个具有最小 f 值的节点,将其加入闭合集合,并探索该节点周围的节点。对于每个可能的运动方向,都生成一个新的节点,并计算新节点的代价。然后检查节点的合法性,如果节点合法,就根据其在开放集合和闭合集合中的状态进行更新或添加。

    # 从关闭节点集合中提取路径pathx, pathy = extract_path(closed_set, n_start, n_goal, P)return pathx, pathy

   这部分代码从闭合集合中提取最终的路径。通过调用 extract_path 函数,从目标节点开始,回溯父节点的索引,得到从起点到目标的路径。最后,返回路径的 x 和 y 坐标。

   综合来看,astar_planning 函数实现了 A* 算法,从给定的起点和目标点出发,通过遍历地图网格,寻找到达目标的最优路径,并返回路径的坐标。在搜索过程中,使用开放集合、闭合集合和优先队列来维护已探索的节点和待探索的节点,并通过启发式函数来指导搜索方向。


   (9)、calc_holonomic_heuristic_with_obstacle函数

   calc_holonomic_heuristic_with_obstacle函数与上面介绍的astar_planning函数比较类似,所不同的是astar_planning函数是传统的A * 算法的核心部分,其输入参数中即包含起始点,也包含目标点,其作用是返回从起始点到目标点的可行路径,而calc_holonomic_heuristic_with_obstacle函数的输入参数中包含目标点,但不包含起始点,calc_holonomic_heuristic_with_obstacle函数是从目标点开始搜索的,最终的目的是创建一个启发式代价地图 hmap,而不是确定一条从起点到目标点的路径,启发式代价地图 hmap用于表示从栅格地图中的节点到目标节点的代价。作为Hybrid A * 算法中不考虑动力学约束,但考虑障碍物的启发式函数值。

def calc_holonomic_heuristic_with_obstacle(node, ox, oy, reso, rr):# 将目标节点的坐标按照分辨率进行缩放n_goal = Node(round(node.x[-1] / reso), round(node.y[-1] / reso), 0.0, -1)# 将障碍物坐标按照分辨率进行缩放ox = [x / reso for x in ox]oy = [y / reso for y in oy]# 计算路径规划所需的参数P, obsmap = calc_parameters(ox, oy, reso, rr)# 初始化开放和关闭节点集合,以及优先队列open_set, closed_set = dict(), dict()open_set[calc_index(n_goal, P)] = n_goalq_priority = []heapq.heappush(q_priority, (n_goal.cost, calc_index(n_goal, P)))# A*算法主循环while True:if not open_set:break_, ind = heapq.heappop(q_priority)n_curr = open_set[ind]closed_set[ind] = n_curropen_set.pop(ind)for i in range(len(P.motion)):node = Node(n_curr.x + P.motion[i][0],n_curr.y + P.motion[i][1],n_curr.cost + u_cost(P.motion[i]), ind)if not check_node(node, P, obsmap):continuen_ind = calc_index(node, P)if n_ind not in closed_set:if n_ind in open_set:if open_set[n_ind].cost > node.cost:open_set[n_ind].cost = node.costopen_set[n_ind].pind = indelse:open_set[n_ind] = nodeheapq.heappush(q_priority, (node.cost, calc_index(node, P)))# 创建启发式地图,并在关闭节点集合中填充代价信息hmap = [[np.inf for _ in range(P.yw)] for _ in range(P.xw)]for n in closed_set.values():hmap[n.x - P.minx][n.y - P.miny] = n.costreturn hmap

   综合来看,calc_holonomic_heuristic_with_obstacle 函数实现了从栅格地图中的节点到目标节点的启发式代价地图计算,并使用 A* 算法来搜索路径。在搜索过程中,通过考虑障碍物,帮助确定启发式代价,以便在规划路径时避免碰撞障碍物。


   (10)、check_node 函数

   这个函数主要用于检查给定节点的合法性,即判断节点是否在环境边界内并且不位于障碍物上。

def check_node(node, P, obsmap):# 检查节点是否超出环境边界if node.x <= P.minx or node.x >= P.maxx or \node.y <= P.miny or node.y >= P.maxy:return False# 检查节点是否位于障碍物上if obsmap[node.x - P.minx][node.y - P.miny]:return False# 如果节点既没有超出边界也不位于障碍物上,返回 Truereturn True

   逐段解释如下

    if node.x <= P.minx or node.x >= P.maxx or \node.y <= P.miny or node.y >= P.maxy:return False

   首先,通过与环境的边界进行比较,检查节点是否超出了环境的有效范围。如果节点的 x 坐标小于最小 x 坐标 P.minx,或者大于最大 x 坐标 P.maxx,或者节点的 y 坐标小于最小 y 坐标 P.miny,或者大于最大 y 坐标 P.maxy,那么这个节点就不在合法的环境范围内,应返回 False

    if obsmap[node.x - P.minx][node.y - P.miny]:return False

   然后,通过检查障碍物地图 obsmap 中与节点位置对应的元素,判断节点是否位于障碍物上。如果障碍物地图中对应的位置为 True,表示这个节点处于障碍物上,因此应返回 False

    return True

   如果节点既没有超出边界,也不位于障碍物上,那么这个节点是合法的,函数将返回 True,表示节点可以被用于路径规划。

   综合来看,check_node 函数在路径规划过程中起到了很重要的作用,它帮助判断一个节点是否是一个合法的路径规划候选节点,以确保生成的路径不会超出环境边界或穿越障碍物。



   (11)、u_cost、fvalue、h、calc_index函数

   u_cost(u)函数用于计算从当前节点到新拓展出的邻居节点的距离,对于上下左右四个节点,其值为1,对于对角线上的四个节点,其值为sqrt(2),当前节点的g值加上该函数值,即为拓展出邻居节点的g值。

   fvalue(node, n_goal)函数计算 A* 算法中的 f 值,即从起始节点到目标节点 n_goal 经过节点 node 的预估总代价。

   h(node, n_goal)函数计算从节点 node 到目标节点 n_goal 的启发式代价。它使用 math.hypot() 函数来计算两个节点之间的欧几里德距离。作为从当前节点到目标节点的预估代价。在 A* 算法中,启发式函数(即 h 函数)帮助算法在搜索中更有可能朝着目标方向前进,以便更高效地找到最佳路径。

   calc_index 函数用于将节点的二维坐标映射到一维索引值,以便在搜索过程中对节点进行存储和检索。这种映射方式有助于提高算法的效率和性能。

def u_cost(u):return math.hypot(u[0], u[1])def fvalue(node, n_goal):return node.cost + h(node, n_goal)def h(node, n_goal):return math.hypot(node.x - n_goal.x, node.y - n_goal.y)def calc_index(node, P):return (node.y - P.miny) * P.xw + (node.x - P.minx)


   (12)、extract_path函数

   这个函数用于从已经完成路径搜索的关闭节点集合中提取生成的路径。函数返回一个包含路径的 x 和 y 坐标的列表,以及将这些坐标按照指定的分辨率进行栅格化。

   - closed_set: 一个字典,包含已经搜索过的节点。键是节点的索引,值是节点对象。

   - n_start: 起始节点。

   - n_goal: 目标节点。

   - P: Para 类的对象,包含了网格世界的参数。

def extract_path(closed_set, n_start, n_goal, P):pathx, pathy = [n_goal.x], [n_goal.y]n_ind = calc_index(n_goal, P)while True:node = closed_set[n_ind]pathx.append(node.x)pathy.append(node.y)n_ind = node.pindif node == n_start:breakpathx = [x * P.reso for x in reversed(pathx)]pathy = [y * P.reso for y in reversed(pathy)]return pathx, pathy

   分段介绍:

    pathx, pathy = [n_goal.x], [n_goal.y]n_ind = calc_index(n_goal, P)

   这部分代码初始化路径的 x 和 y 坐标列表,将目标节点的坐标添加为路径的起点,并获取目标节点的索引。

    while True:node = closed_set[n_ind]pathx.append(node.x)pathy.append(node.y)n_ind = node.pindif node == n_start:break

   这个循环根据节点的索引从关闭节点集合中提取路径。循环中的每一步,它将当前节点的坐标添加到路径的 x 和 y 列表中。然后,它将当前节点的父节点索引(node.pind)作为下一个要提取的节点索引。

   循环会一直进行,直到当前节点达到起始节点 n_start,此时路径提取完成。

    pathx = [x * P.reso for x in reversed(pathx)]pathy = [y * P.reso for y in reversed(pathy)]return pathx, pathy

   在循环结束后,路径的 x 和 y 坐标列表中包含的坐标是从目标节点反向提取的。这里,将坐标按照分辨率 P.reso 进行转换,以便将网格世界中的节点坐标转换为实际的环境坐标。最终,函数返回路径的 x 和 y 坐标列表。

   综合来看,extract_path 函数用于从已搜索节点中提取路径,并将其按照实际环境的分辨率进行缩放,以便在绘图等操作中使用。这个函数是路径规划算法中生成路径的重要步骤。


   (13)、主函数 main()

   主函数 main(),用于调用路径规划算法并绘制结果。以下是对这段代码的逐段详细解释:

def main():sx = 10.0  # 起始点的 x 坐标 [m]sy = 10.0  # 起始点的 y 坐标 [m]gx = 50.0  # 目标点的 x 坐标 [m]gy = 50.0  # 目标点的 y 坐标 [m]robot_radius = 2.0  # 机器人的半径 [m]grid_resolution = 1.0  # 网格的分辨率 [m]ox, oy = get_env()  # 获取环境中的障碍物坐标# 使用A*算法进行路径规划pathx, pathy = astar_planning(sx, sy, gx, gy, ox, oy, grid_resolution, robot_radius)# 绘制环境、路径、起始点和目标点plt.plot(ox, oy, 'sk')  # 绘制障碍物plt.plot(pathx, pathy, '-r')  # 绘制路径plt.plot(sx, sy, 'sg')  # 绘制起始点plt.plot(gx, gy, 'sb')  # 绘制目标点plt.axis("equal")  # 设置坐标轴比例相等plt.show()  # 显示绘图结果if __name__ == '__main__':main()

   这个函数是程序的主要入口点。它首先定义了起始点 (sx, sy) 和目标点 (gx, gy) 的坐标,以及机器人的半径 robot_radius 和网格的分辨率 grid_resolution

   然后,通过调用 get_env() 函数,获取环境中的障碍物坐标 oxoy

   接着,调用 astar_planning() 函数,使用 A* 路径规划算法计算从起始点到目标点的路径。返回的路径坐标存储在 pathxpathy 列表中。

   最后,通过使用 matplotlib 库来绘制环境、路径、起始点和目标点的图像。plt.plot() 函数用于在图上绘制点和线段。plt.axis("equal") 用于设置坐标轴比例相等,以确保图像不会因为比例问题而变形。最后,plt.show() 用于显示绘制的图像。

   总的来说,main() 函数是程序的执行入口,负责调用路径规划算法并可视化结果。这使得您可以直观地看到起始点、目标点、障碍物和路径的相对位置。

在这里插入图片描述


   参考资料:

   zhm_real/MotionPlanning运动规划库


相关文章:

zhm_real/MotionPlanning运动规划库中A*算法源码详细解读

本文主要对zhm_real/MotionPlanning运动规划库中A*算法源码进行详细解读&#xff0c;即对astar.py文件中的内容进行详细的解读&#xff0c;另外本文是 Hybrid A * 算法源码解读的前置文章&#xff0c;为后续解读Hybrid A * 算法源码做铺垫。 astar.py文件中的源码如下&#xff…...

SpringMVC中Controller层获取前端请求参数的几种方式

SpringMVC中Controller层获取前端请求参数的几种方式 1、SpringMVC自动绑定2、使用RequestParam 注解进行接收3、RequestBody注解&#xff08;1&#xff09; 使用实体来接收JSON&#xff08;2&#xff09;使用 Map 集合接收JSON&#xff08;3&#xff09; 使用 List集合接收JSO…...

记Flask-Migrate迁移数据库失败的两个Bug——详解循环导入问题

文章目录 Flask-Migrate迁移数据库失败的两个Bug1、找不到数据库&#xff1a;Unknown database ***2、迁移后没有效果&#xff1a;No changes in schema detected. Flask-Migrate迁移数据库失败的两个Bug 1、找不到数据库&#xff1a;Unknown database ‘***’ 若还没有创建数…...

在线求助。。npm i 报错,连公司内部网,无法连外网

各位前端朋友 &#xff0c;有没有遇到我这种npm i 报错的问题。 公司内网&#xff0c;无法连外网&#xff0c;使用公司内部的Nexus镜像源 我在公司内网执行npm i 报错&#xff0c;报network连接失败。 我都已经在npm设置了内部镜像源&#xff0c;它为啥还要去外网下载呢。而…...

TCP/UDP/IP协议简介

IP协议简介 特指为实现一个相互连接的网络系统上从源地址到目的地址传输数据包(互联网数据包) 所提供必要功能的协议 特点&#xff1a; 不可靠&#xff1a;不能保证IP数据包能够成功的到达它的目的地只能提供尽力而为的传输服务。 无连接&#xff1a;IP并不维护任何关于后续数…...

写点感想3:关于本人近期的说明与一点感受

按照我今年以来7月之前的更新频率&#xff0c;我已经好久没有更新博文了(或者说静下来写点东西)。 我其实有规划蛮多的有意思的且想要去研究下的topic&#xff0c;最近好久没能更新主要的原因包括&#xff1a; 开启了我职业生涯的第二份工作&#xff1a;在某研究院工作2年零3…...

opencv-全景图像拼接

运行环境 python3.6 opencv 3.4.1.15 stitcher.py import numpy as np import cv2class Stitcher:#拼接函数def stitch(self, images, ratio0.75, reprojThresh4.0,showMatchesFalse):#获取输入图片(imageB, imageA) images#检测A、B图片的SIFT关键特征点&#xff0c;并计算…...

如何将下载的安装包导入PyCharm

1. 下载安装包 这里以pyke为例。下载好之后解压缩&#xff0c;然后放入/Lib/site-packages/pyke-1.1.1 2. 打开PyCharm的终端进行安装 python setup.py install 3. 安装好之后导入即可使用 import pyke...

【redis问题】Caused by: io.netty.channel

遇到的问题&#xff1a; 在使用 RedisTemplate 连接 Redis 进行操作的时候&#xff0c;发生了如下报错&#xff1a; 测试代码为&#xff1a; 配置文件&#xff1a; 问题根源&#xff1a; redis没有添加端口映射解决方案&#xff1a; 删除原来的redis容器&#xff0c;添加新…...

Elasticsearch 处理地理信息

1、GeoHash ​ GeoHash是一种地理坐标编码系统&#xff0c;可以将地理位置按照一定的规则转换为字符串&#xff0c;以方便对地理位置信息建立空间索引。首先要明确的是&#xff0c;GeoHash代表的不是一个点而是一个区域。GeoHash具有两个显著的特点&#xff1a;一是通过改变 G…...

ARM开发,stm32mp157a-A7核IIC实验(采集温湿度传感器值)

1.实验目标&#xff1a;采集温湿度传感器值&#xff1b; 2.分析框图&#xff08;模拟IIC控制器&#xff09;&#xff1b; 3.代码&#xff1b; ---iic.h封装时序协议头文件--- #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "st…...

021-从零搭建微服务-短信服务(一)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;https://gitee.com/csps/mingyue 源码地址&#xff08;前端&#xff09;&#xff1a;https://gitee.com/csps…...

基于jenkins自动化部署PHP环境

实验环境 操作系统 IP地址 主机名 角色 CentOS7.5 192.168.147.141 git git服务器 CentOS7.5 192.168.147.142 Jenkins git客户端 jenkins服务器 CentOS7.5 192.168.147.143 web web服务器 具体环境配置见上一篇&#xff01; 准备git仓库 [rootgit ~]# su -…...

数据库表结构导出为word、html、markdown【转载,已解决,已验证,开源】

注&#xff1a;本文为gitcode代码验证&#xff0c;转载gitcode gitcode&#xff1a;https://gitcode.net/mirrors/pingfangushi/screw?utm_sourcecsdn_github_accelerator 整理数据库文档&#xff1a;https://mp.weixin.qq.com/s/Bo_U5_cl82hfQ6GmRs2vtA <!--数据库文档核…...

【计算机视觉|生成对抗】用于高保真自然图像合成的大规模GAN训练用于高保真自然图像合成的大规模GAN训练(BigGAN)

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Large Scale GAN Training for High Fidelity Natural Image Synthesis 链接&#xff1a;[1809.11096] Large Scale GAN Training for High Fidelity Natural Image Synthesis (arxiv.org…...

三维重建_体素重建_空间雕刻法/体素着色法

目录 1. 三角化和体素重建的区别 2. 空间雕刻法 空间雕刻法的一致性定义 空间雕刻法具体实现 基于八叉树的空间雕刻法具体实现​编辑 空间雕刻法效果展示 3. 体素着色法 体素着色法的缺点&#xff1a;不唯一性​编辑 体素着色法不唯一性解决措施​编辑 体素着色发实验环境与…...

4-redis哨兵搭建安装

1.先决条件 1.1.OS基础配置 CentOS为了能够正常安装redis,需要对CentOS进行常规的一些基础配置,主要有:关闭防火墙与selinux,设置主机名,配置虚拟机IP地址使其能够与外网ping通,配置IP地址与主机名映射,配置yum源。具体配置参见: Linux常规基础配置_小黑要上天的博客…...

架构评估-架构师之路(十二)

软件系统质量属性 软件系统质量熟悉分为 开发期质量属性 和 运行期质量属性。 质量属性 性能&#xff1a;指 系统的响应能力&#xff0c;如 响应时间&#xff0c;吞吐率。 设计策略&#xff1a;优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度。 可靠…...

手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换

实现Tomcat和Jetty的切换 前言 上一篇文章我们聊到&#xff0c;SpringBoot中内置了web服务器&#xff0c;包括Tomcat、Jetty&#xff0c;并且实现了SpringBoot启动Tomcat的流程。 那么SpringBoot怎样自动切换成Jetty服务器呢&#xff1f; 接下来我们继续学习如何实现Tomcat…...

Python土力学与基础工程计算.PDF-土的三项组成

5.3 Python求解 Python 求解代码如下&#xff1a; 1. # 定义已知参数 2. G_s 2.7 # 比重 3. w 0.2 # 含水量 4. e 0.6 # 孔隙比 5. gamma_w 9.81 # 水的重度 6. 7. # 根据公式计算饱和度 8. S_r G_s * w / e 9. print("饱和度为", S_r) 10. 11.…...

危化安全生产信息化平台在煤化领域的应用

一、背景介绍 煤化工行业是一个集煤炭、石油、化工等多种产业于一体的综合性行业&#xff0c;其特点是工艺流程复杂、设备繁多、安全隐患大。近年来&#xff0c;随着煤化工行业的快速发展&#xff0c;安全生产问题日益凸显。为了有效提高危化安全生产水平&#xff0c;某煤化工…...

Linux(CentOS)运维脚本工具集合

使用说明 备份指定目录 # 备份指定目录文件到指定目录,备份文件名称为&#xff1a;备份目录最后一层目录"_"日期.tar.gz # 第一个参数&#xff1a;backdir 第二参数&#xff1a;备份文件保存目录 第三个参数&#xff1a;备份目录/文件 sh script.sh backdir /root/…...

【Java alibabahutool】JSON、Map、实体对象间的相互转换

首先要知道三者的互转关系&#xff0c;可以先将JSON理解成是String类型。这篇博文主要是记录阿里巴巴的JSONObject的两个方法。toJSONString()以及parseObject()方法。顺便巩固Map与实体对象的转换技巧。 引入依赖 <!-- 阿里巴巴 JSON转换 以下二选一即可 没有去细研究两者…...

按软件开发阶段的角度划分:单元测试、集成测试、系统测试、验收测试

1.单元测试&#xff08;Unit Testing&#xff09; 单元测试&#xff0c;又称模块测试。对软件的组成单位进行测试&#xff0c;其目的是检验软件基本组成单位的正确性。测试的对象是软件里测试的最小单位&#xff1a;模块。 测试阶段&#xff1a;编码后或者编码前&#xff08;…...

【python】Leetcode(primer-dict-list)

文章目录 260. 只出现一次的数字 III&#xff08;字典 / 位运算&#xff09;136. 只出现一次的数字&#xff08;字典&#xff09;137. 只出现一次的数字 II&#xff08;字典&#xff09;169. 求众数&#xff08;字典&#xff09;229. 求众数 II&#xff08;字典&#xff09;200…...

网络安全(黑客)入门

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全…...

在CSS中,盒模型中的padding、border、margin是什么意思?

在CSS中&#xff0c;盒模型&#xff08;Box Model&#xff09;是用来描述和布局HTML元素的基本概念。它将每个HTML元素看作是一个矩形的盒子&#xff0c;这个盒子包括了内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&a…...

有线耳机插入电脑没声音

有线耳机插入电脑没声音 首先确保耳机和电脑都没问题&#xff0c;那就有可能是声音输出设备设置错误 右击任务栏的声音图标-打开声音设置-选择输出设备。...

【面试 反思】Retrofit源码与设计 7 连问

前言 在实际项目中往往是使用Retrofit来做网络请求工作。Retrofit采用RESTful风格&#xff0c;本质上只是对OkHttp进行封装&#xff0c;今天我们根据几个问题来进一步学习一下Retrofit的源码与设计思想。 1. 使用方法 直接看一下官方介绍的使用方法。 public final class S…...

flutter 雷达图

通过CustomPainter自定义雷达图 效果如下 主要代码 import package:flutter/material.dart; import dart:math; import dash_painter.dart; import model/charts_model.dart;class RadarChart extends StatelessWidget {final List<ChartModel> list;final double maxV…...

下载好模板该怎么做网站/百度首页百度

为什么80%的码农都做不了架构师&#xff1f;>>> /** nodejs模拟表单提交 */ var http require(http); var querystring require(querystring); var contents querystring.stringify({ p:2, na: 看阿奎, ad: 成都市锦江区天府广场人民中路5段67耗 }); //console.…...

聊城公司做网站/站长工具关键词挖掘

一般Java培训要多久高中生学习Java需要一个系统的过程&#xff0c;不同的学习方向也需要不同的学习时间。目前Java广泛用于Web开发、大数据开发、Android开发以及各种后端服务开发领域&#xff0c;通常情况下&#xff0c;学习Java都从Web开发开始学起。 Java Web开发需要学习三…...

wordpress文章归档插件/小红书怎么推广

MATLAB 数据及其运算MATLAB数值数据整数浮点数复数数据的输出格式变量及其操作变量与赋值语句预定义变量变量的管理MATLAB矩阵的表示矩阵的建立冒号表达式矩阵的引用MATLAB数值数据 整数 带符号8位整数数据的最大值时127&#xff0c;int8函数转换时只输出最大值。 浮点数 单…...

没有数据库的网站/网址大全下载到桌面

1.关于sudo不需要输密码&#xff0c;低权限执行高权限&#xff0c;在root下的命令visudo放开%wheel ALL&#xff1b;保存退出&#xff0c; 执行gpasswd -a yourusername wheel 2.脚本命令下的&#xff0c;权限变更执行跨用户脚本&#xff0c;有两种方式&#xff1a; 《1》字符串…...

网站建设的公司如何选/小红书网络营销策划方案

HDU 1251 http://acm.hdu.edu.cn/showproblem.php?pid1251 Problem DescriptionIgnatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).Input输入数据的第一部分是一…...

无忧网站建设公司/建立网站流程

from:http://blog.csdn.net/wdaming1986/article/details/8267156 先画一个Launche启动的流程图&#xff0c;虽然不是特别规范&#xff0c;但是勉强能看看&#xff0c;我也整理下Launcher的一系列的流程图&#xff0c;最近修改Launcher&#xff0c;又对Launcher加深了一些了…...