上一篇文章只发布了很粗糙的代码,属于能跑就行,确实难看懂。
这一篇继续说一下爬墙思维,,B*寻路算法就是贪婪思维 +攀爬思维,
但是比较难判定怎么算爬过的障碍,所以这里改为 贪婪思维 +穿透障碍,(空心障碍的话这不坑爹吗)
最开始研究B*寻路算法时,真是时 不知道怎么取解决爬墙,,,
从格子3 开始分路,开始考虑撞墙,换方向,又得判定是否已经翻过了障碍,起初我觉得可以用距离来判断翻过了障碍,因为对于B*算法来说,情况是这样的。
这里距离=该点到起点的距离+到终点的距离,这里有点像椭圆离的两个固定坐标,就是起点和终点,直接连线那么距离最短,遇到障碍后距离局部变大,爬过障碍后又变小。但是对于上图的包围型障碍来说,通过距离的起伏判定是失效的
也许有其他的解决方法,但是这里我转变的一下,就是如果我穿透障碍,从格子3 到格子4.。
那么就算爬过的障碍,这样就不用取判定怎么算爬过了障碍。只需要关心怎么沿着障碍,走到穿透后的点就行了
接下来就是怎么走了,怎么走好像也有点难。
但是这里我们发现
既然要沿着障碍边缘走,那么我们的就障碍边缘所有可以走的点找出来,那么这这些点连在一起,就是包围了障碍的圈。
判定方法,
1.从o(欧)点开始,遍历周围8个点,。。做完事后加到关闭列表
如果有一个点=0,那么就是该点O就是边缘点,把=0的点加入 障碍包围圈 path里,
如果=1,那么这个点是障碍点,是不是可以加到开启列表,
就看他的邻居有没有=0的点,如果有,说明该点是障碍边缘的点。加到开启列表,如果没有那么这个点是障碍内部的点,不需要探索(这里可以从前后左右四个方向开始,再从其他四个方向遍历)
2 从开启列表取一个点,继续 1 步骤
下面代码 0 代表可以走,1代表障碍
# 通过一个障碍内的点,得到障碍周围一圈的可探索点,,如果是环形空心障碍,这不设坑爹吗,穿透后的目标点都到不了
def obstacle_around_openSet(self, obstacle_point=(2,5)):
# 边缘可走点集合
path=[]
# 开启的表
obstacle_openSet = [obstacle_point, ]
# 关闭的表
obstacle_closeSet = []
while obstacle_openSet != []:
# 切换到关闭列表 .pop默认删除最后一个(这里后面会有点新发现的)
cur = obstacle_openSet.pop()
obstacle_closeSet.append(cur)
# 对当前格相邻的8格中的每一个
neighbors = self.get_neighbors(cur[0], cur[1])
for neighbor in neighbors:
# =0即边缘可以走的点 看看是否加过了 # 不能往回走 self.closeSet是全局关闭表(走过的表,用来回溯路劲,所以是带坐标的字典)
if map_be_search[neighbor[0]][neighbor[1]] == 0 and neighbor not in obstacle_openSet and neighbor not in [p["point"] for p in self.closeSet]:
path.append(neighbor)
# =1 这是相邻障碍点, 就是障碍内的点 且在边界内 =1的点可能是边界点,所以联合判定
if map_be_search[neighbor[0]][neighbor[1]] == 1 and border_x_min < neighbor[0] < border_x_max and border_y_min < neighbor[1] < border_y_max:
# 已经在 "关闭列表"(探索过) 中 不做处理
if neighbor in obstacle_closeSet:
continue
# 它不在 开启列表中,这个障碍点可能可以探索,但是如果是内部的点,而障碍不是边缘的点,就不能加到开启列表
if neighbor not in obstacle_openSet:
# 遍历 该邻居 相邻的8格中的每一个
neighbors_list = self.get_neighbors(neighbor[0], neighbor[1])
for neighbor_neighbor in neighbors_list:
# 如果该邻居周围的格子里有一个 0, 说明它在障碍边缘, 则加入 并且打断
if map_be_search[neighbor_neighbor[0]][neighbor_neighbor[1]] == 0 :
obstacle_openSet.append(neighbor)
break
得到障碍包围圈path 后,我们就沿着path 爬就行了。
1.我们新建一个openSe和 closeSet 开启的表和关闭的表,把格子3放进去,
2.从开放的表 取出格子3 作为当前点(也就是起点,每次从开启表中拿点都是重头再来),并放进关闭的表。
3.遍历一下格子3的8个邻居(这里的话还是优先遍历前后左右的四个点,可以减少遍历次数)
发现只有 *1和1* 两个点#(1个也行,都没有就是说三面都是障碍,要往回走了,那么在在上面计算path时,走过的点(只要是障碍边缘可走的点)也得加入path,不过这也有点坑爹的感觉)#
只有这两个点 在 障碍包围圈path里面,所以选择一个作为下一个点,更新下一个点的信息,另一个点加入开启的表。
4.把下一个点更新为当前点。并加到关闭的表。继续遍历邻居。
这里先向上爬,取出*1格子,把1*放进开启的表,
遍历周围八个点,只有*2(距离*1的距离是1)和*3 (距离*1的距离是1.4)。
但是这里我们需要确定下一个点,也就是*2,根据距离=1可以确定,把*3放进开启的表。所以更新该点为当前点,
继续遍历 *2,发现只有*3就是下一个点,记录一下点信息,把*3更新为当前点(*3马上就进入关闭的表。那么开启表的那个*3,取出来作为起点也没有用了)
继续遍历 *3,发现只有4*就是下一个点,记录一下点信息,把*34更新为当前点............继续就行了,
如果前面没有点可以走了,那么就是往上走是死路,下一个点的信息为空,判定一下,后从开启的表拿点出来作为起点重新走,也就是只有为唯一一个1* 可以作为新的起点。开启的表中其他的都已经走过了。
如果下一个点信息为穿透障碍后的点,说明该路路径可以走,但是我们还需要对另一条路走一遍,看看谁最短
也就是我先把格子3 关闭点,新建两个不同的开启表和关闭表,把*1和1* 分别放到两个不同的开启的表中,也就是只可以计算出两条路径。
这样我们就可以解决绕过障碍的难点了,,只要跑到对面那个点就可以算绕过障碍 了