跑通子任务树生成
This commit is contained in:
parent
0c0355af46
commit
5c893d72e1
|
@ -0,0 +1,4 @@
|
||||||
|
selector{
|
||||||
|
cond At(Robot, Table)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import numpy as np
|
||||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||||
|
|
||||||
|
|
||||||
from dstar_lite import DStarLite
|
from robowaiter.algos.navigate.DstarLite.dstar_lite import DStarLite
|
||||||
|
|
||||||
class Navigator:
|
class Navigator:
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -19,13 +19,13 @@ if __name__ == '__main__':
|
||||||
with open(file_name, 'rb') as file:
|
with open(file_name, 'rb') as file:
|
||||||
map = pickle.load(file)
|
map = pickle.load(file)
|
||||||
|
|
||||||
init_world(1, 3)
|
init_world(1, 11)
|
||||||
scene = Scene(sceneID=0)
|
scene = Scene(sceneID=0)
|
||||||
|
|
||||||
navigator = Navigator(scene=scene, area_range=[-350, 600, -400, 1450], map=map)
|
navigator = Navigator(scene=scene, area_range=[-350, 600, -400, 1450], map=map)
|
||||||
|
|
||||||
'''场景1: 无行人环境 robot到达指定目标'''
|
'''场景1: 无行人环境 robot到达指定目标'''
|
||||||
# goal = np.array((-100, 700))
|
goal = np.array((-100, 700))
|
||||||
|
|
||||||
|
|
||||||
'''场景2: 静止行人环境 robot到达指定目标'''
|
'''场景2: 静止行人环境 robot到达指定目标'''
|
||||||
|
@ -35,12 +35,12 @@ if __name__ == '__main__':
|
||||||
# goal = np.array((-100, 700))
|
# goal = np.array((-100, 700))
|
||||||
|
|
||||||
'''场景3: 移动行人环境 robot到达指定目标'''
|
'''场景3: 移动行人环境 robot到达指定目标'''
|
||||||
scene.clean_walker()
|
# scene.clean_walker()
|
||||||
scene.add_walker(50, 300, 0)
|
# scene.add_walker(50, 300, 0)
|
||||||
scene.add_walker(-50, 500, 0)
|
# scene.add_walker(-50, 500, 0)
|
||||||
scene.control_walker([scene.walker_control_generator(walkerID=0, autowalk=False, speed=20, X=-50, Y=600, Yaw=0)])
|
# scene.control_walker([scene.walker_control_generator(walkerID=0, autowalk=False, speed=20, X=-50, Y=600, Yaw=0)])
|
||||||
scene.control_walker([scene.walker_control_generator(walkerID=1, autowalk=False, speed=20, X=100, Y=150, Yaw=0)])
|
# scene.control_walker([scene.walker_control_generator(walkerID=1, autowalk=False, speed=20, X=100, Y=150, Yaw=0)])
|
||||||
goal = np.array((-100, 700))
|
# goal = np.array((-100, 700))
|
||||||
|
|
||||||
'''场景4: 行人自由移动 robot到达指定目标'''
|
'''场景4: 行人自由移动 robot到达指定目标'''
|
||||||
# # TODO: autowalk=True仿真器会闪退 ???
|
# # TODO: autowalk=True仿真器会闪退 ???
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# enconding = utf8
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import grpc
|
||||||
|
|
||||||
|
from explore import Explore
|
||||||
|
|
||||||
|
sys.path.append('./')
|
||||||
|
sys.path.append('../')
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||||
|
|
||||||
|
import GrabSim_pb2_grpc
|
||||||
|
import GrabSim_pb2
|
||||||
|
|
||||||
|
channel = grpc.insecure_channel('localhost:30001', options=[
|
||||||
|
('grpc.max_send_message_length', 1024 * 1024 * 1024),
|
||||||
|
('grpc.max_receive_message_length', 1024 * 1024 * 1024)
|
||||||
|
])
|
||||||
|
|
||||||
|
sim_client = GrabSim_pb2_grpc.GrabSimStub(channel)
|
||||||
|
|
||||||
|
'''
|
||||||
|
初始化,卸载已经加载的关卡,清除所有机器人
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def Init():
|
||||||
|
sim_client.Init(GrabSim_pb2.NUL())
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
获取当前可加载的地图信息(地图名字、地图尺寸)
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def AcquireAvailableMaps():
|
||||||
|
AvailableMaps = sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL())
|
||||||
|
print(AvailableMaps)
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
1、根据mapID加载指定地图
|
||||||
|
2、如果scene_num>1,则根据地图尺寸偏移后加载多个相同地图
|
||||||
|
3、这样就可以在一个关卡中训练多个地图
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def SetWorld(map_id=0, scene_num=1):
|
||||||
|
print('------------------SetWorld----------------------')
|
||||||
|
world = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
返回场景的状态信息
|
||||||
|
1、返回机器人的位置和旋转
|
||||||
|
2、返回各个关节的名字和旋转
|
||||||
|
3、返回场景中标记的物品信息(名字、类型、位置、旋转)
|
||||||
|
4、返回场景中行人的信息(名字、位置、旋转、速度)
|
||||||
|
5、返回机器人手指和双臂的碰撞信息
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def Observe(scene_id=0):
|
||||||
|
print('------------------show_env_info----------------------')
|
||||||
|
scene = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
|
||||||
|
print(
|
||||||
|
f"location:{[scene.location]}, rotation:{scene.rotation}\n",
|
||||||
|
f"joints number:{len(scene.joints)}, fingers number:{len(scene.fingers)}\n",
|
||||||
|
f"objects number: {len(scene.objects)}, walkers number: {len(scene.walkers)}\n"
|
||||||
|
f"timestep:{scene.timestep}, timestamp:{scene.timestamp}\n"
|
||||||
|
f"collision:{scene.collision}, info:{scene.info}")
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
重置场景
|
||||||
|
1、重置桌子的宽度和高度
|
||||||
|
2、清除生成的行人和物品
|
||||||
|
3、重置关节角度、位置旋转
|
||||||
|
4、清除碰撞信息
|
||||||
|
5、重置场景中标记的物品
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def Reset(scene_id=0):
|
||||||
|
print('------------------Reset----------------------')
|
||||||
|
scene = sim_client.Reset(GrabSim_pb2.ResetParams(scene=scene_id))
|
||||||
|
print(scene)
|
||||||
|
|
||||||
|
# 如果场景支持调整桌子
|
||||||
|
# sim_client.Reset(GrabSim_pb2.ResetParams(scene = scene_id, adjust = True, height = 100.0, width = 100.0))"
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
导航移动
|
||||||
|
yaw:机器人朝向;
|
||||||
|
velocity:速度,>0代表移动,<0代表瞬移,=0代表只查询;
|
||||||
|
dis:最终达到的位置距离目标点最远距离,如果超过此距离则目标位置不可达
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def navigation_move(v_list, scene_id=0, map_id=11):
|
||||||
|
print('------------------navigation_move----------------------')
|
||||||
|
scene = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
|
||||||
|
walk_value = [scene.location.X, scene.location.Y, scene.rotation.Yaw]
|
||||||
|
print("position:", walk_value)
|
||||||
|
|
||||||
|
# if map_id == 11: # coffee
|
||||||
|
# v_list = [[0, 880], [250, 1200], [-55, 750], [70, -200]]
|
||||||
|
# else:
|
||||||
|
# v_list = [[0.0, 0.0]]
|
||||||
|
|
||||||
|
for walk_v in v_list:
|
||||||
|
walk_v = walk_v + [scene.rotation.Yaw - 90, 60, 0]
|
||||||
|
print("walk_v", walk_v)
|
||||||
|
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.WalkTo, values=walk_v)
|
||||||
|
scene = sim_client.Do(action)
|
||||||
|
print(scene.info)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
map_id = 11 # 地图编号
|
||||||
|
scene_num = 1 # 场景数量
|
||||||
|
node_list = [] # 探索点
|
||||||
|
explorer = Explore(_explore_range=100)
|
||||||
|
|
||||||
|
print('------------ 初始化加载场景 ------------')
|
||||||
|
Init()
|
||||||
|
AcquireAvailableMaps()
|
||||||
|
SetWorld(map_id, scene_num)
|
||||||
|
time.sleep(5.0)
|
||||||
|
|
||||||
|
for i in range(scene_num):
|
||||||
|
print('------------ 场景操作 ------------')
|
||||||
|
Observe(i)
|
||||||
|
Reset(i)
|
||||||
|
|
||||||
|
print('------------ 自主探索 ------------')
|
||||||
|
while True:
|
||||||
|
goal = explorer.explore(cur_pos) # cur_pos 指的是当前机器人的位置,场景中应该也有接口可以获取
|
||||||
|
if goal is None:
|
||||||
|
break
|
||||||
|
node_list.append(goal)
|
||||||
|
cur_pos = goal
|
||||||
|
navigation_move(i, map_id, node_list)
|
||||||
|
|
||||||
|
# TODO:node_list就是机器人拍照的点,目前没有设置拍照角度,需要机器人到达一个拍照点后,前后左右各拍一张照片然后获取语义信息
|
|
@ -0,0 +1,5 @@
|
||||||
|
from . import apf
|
||||||
|
from . import rrt
|
||||||
|
from . import rrt_star
|
||||||
|
from . import pathsmoothing
|
||||||
|
from . import navigate
|
|
@ -0,0 +1,91 @@
|
||||||
|
import numpy as np
|
||||||
|
import copy
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('TkAgg') # 防止画图卡死
|
||||||
|
|
||||||
|
## 初始化车的参数
|
||||||
|
|
||||||
|
|
||||||
|
# P0 = np.array([0, - d / 2, 1, 1]) #车辆起点位置,分别代表x,y,vx,vy
|
||||||
|
#
|
||||||
|
# Pg = np.array([99, d / 2, 0, 0]) # 目标位置
|
||||||
|
#
|
||||||
|
# # 障碍物位置
|
||||||
|
# Pobs = np.array([
|
||||||
|
# [15, 7 / 4, 0, 0],
|
||||||
|
# [30, - 3 / 2, 0, 0],
|
||||||
|
# [45, 3 / 2, 0, 0],
|
||||||
|
# [60, - 3 / 4, 0, 0],
|
||||||
|
# [80, 3/2, 0, 0]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def APF(Pi, Pg, Pobs=None, step_length=100):
|
||||||
|
'''
|
||||||
|
Args:
|
||||||
|
Pi: robot位置 (x,y)
|
||||||
|
Pg: 目标位置 (x,y)
|
||||||
|
Pobs: 障碍物位置 [(x,y), ...]
|
||||||
|
step_length: 单次移动步长 int
|
||||||
|
returns:
|
||||||
|
next_step: robot下一步位置
|
||||||
|
UnitVec_Fsum: 合力方向向量
|
||||||
|
'''
|
||||||
|
|
||||||
|
# 目标检测
|
||||||
|
if np.linalg.norm(Pi-Pg) < step_length: # 目标检测
|
||||||
|
return Pg, (Pg-Pi) / np.linalg.norm(Pg-Pi)
|
||||||
|
|
||||||
|
Eta_att = 5 # 引力的增益系数
|
||||||
|
Eta_rep_ob = 15 # 斥力的增益系数
|
||||||
|
d0 = 200 # 障碍影响的最大距离
|
||||||
|
n = 1 # {P_g}^n 为到目标点的距离,n为可选的正常数 (解决目标不可达问题)
|
||||||
|
|
||||||
|
## 计算引力
|
||||||
|
delta_g = Pg - Pi # 目标方向向量(指向目标)
|
||||||
|
dist_g = np.linalg.norm(delta_g) # 目标距离
|
||||||
|
UniteVec_g = delta_g / dist_g # 目标单位向量
|
||||||
|
|
||||||
|
F_att = Eta_att * dist_g * UniteVec_g # F_att = Eta_att * dist(pi,pg) * unite_vec(pi,pg)
|
||||||
|
F_sum = F_att
|
||||||
|
|
||||||
|
## 计算斥力
|
||||||
|
if Pobs:
|
||||||
|
delta = np.zeros((len(Pobs), 2))
|
||||||
|
unite_vec = np.zeros((len(Pobs), 2))
|
||||||
|
dists = []
|
||||||
|
# 计算车辆当前位置与所有障碍物的单位方向向量
|
||||||
|
for j in range(len(Pobs)):
|
||||||
|
delta[j] = Pi - Pobs[j] # 斥力向量
|
||||||
|
dists.append(np.linalg.norm(delta[j])) # 障碍物距离
|
||||||
|
unite_vec[j] = delta[j] / dists[j] # 斥力单位向量
|
||||||
|
# 每一个障碍物对车辆的斥力,带方向
|
||||||
|
F_rep_ob = np.zeros((len(Pobs), 2))
|
||||||
|
# 在原斥力势场函数增加目标调节因子(即车辆至目标距离),以使车辆到达目标点后斥力也为0
|
||||||
|
for j in range(len(Pobs)):
|
||||||
|
if dists[j] >= d0:
|
||||||
|
F_rep_ob[j] = np.array([0, 0]) # 距离超过阈值则斥力为0
|
||||||
|
else:
|
||||||
|
# 障碍物的斥力1,方向由障碍物指向车辆
|
||||||
|
F_rep_ob1_abs = Eta_rep_ob * (1 / dists[j] - 1 / d0) * dist_g ** n / dists[j] ** 2 # 斥力大小
|
||||||
|
F_rep_ob1 = F_rep_ob1_abs * unite_vec[j] # 斥力向量
|
||||||
|
# 障碍物的斥力2,方向由车辆指向目标点
|
||||||
|
F_rep_ob2_abs = n / 2 * Eta_rep_ob * (1 / dists[j] - 1 / d0) ** 2 * dist_g ** (n - 1) # 斥力大小
|
||||||
|
F_rep_ob2 = F_rep_ob2_abs * UniteVec_g # 斥力向量
|
||||||
|
# 改进后的障碍物合斥力计算
|
||||||
|
F_rep_ob[j] = F_rep_ob1 + F_rep_ob2
|
||||||
|
|
||||||
|
F_rep = np.sum(F_rep_ob, axis=0) # 斥力合力
|
||||||
|
F_sum += F_rep # 总合力
|
||||||
|
|
||||||
|
## 合力方向
|
||||||
|
UnitVec_Fsum = F_sum / np.linalg.norm(F_sum) # 合力方向向量: F / |F|
|
||||||
|
|
||||||
|
# 计算车的下一步位置
|
||||||
|
next_step = copy.deepcopy(Pi + step_length * UnitVec_Fsum) # 沿合力方向前进单位距离
|
||||||
|
# print(next_step)
|
||||||
|
|
||||||
|
return next_step, UnitVec_Fsum
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# !/usr/bin/env python3
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import pickle
|
||||||
|
import os
|
||||||
|
|
||||||
|
from scene_utils import control
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_grid_map(grid_map):
|
||||||
|
# 生成新的地图图像
|
||||||
|
plt.imshow(grid_map, cmap='binary', alpha=0.5, origin='lower') # 黑白网格
|
||||||
|
|
||||||
|
# 绘制坐标轴
|
||||||
|
plt.xlabel('y', loc='right')
|
||||||
|
plt.ylabel('x', loc='top')
|
||||||
|
|
||||||
|
# 显示网格线
|
||||||
|
plt.grid(color='black', linestyle='-', linewidth=0.5)
|
||||||
|
|
||||||
|
# 显示图像
|
||||||
|
plt.show()
|
||||||
|
#plt.pause(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# control.init_world(scene_num=1, mapID=3)
|
||||||
|
# scene = control.Scene(sceneID=0)
|
||||||
|
#
|
||||||
|
# X = int(950/5) # 采点数量
|
||||||
|
# Y = int(1850/5)
|
||||||
|
# map = np.zeros((X, Y))
|
||||||
|
#
|
||||||
|
# for x in range(X):
|
||||||
|
# for y in range(Y):
|
||||||
|
# if not scene.reachable_check(x*5-350, y*5-400, Yaw=0):
|
||||||
|
# map[x, y] = 1
|
||||||
|
# print(x, y)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# file_name = 'map_5.pkl'
|
||||||
|
# if not os.path.exists(file_name):
|
||||||
|
# open(file_name, 'w').close()
|
||||||
|
# with open(file_name, 'wb') as file:
|
||||||
|
# pickle.dump(map, file)
|
||||||
|
# print('保存成功')
|
||||||
|
|
||||||
|
|
||||||
|
file_name = 'map_5.pkl'
|
||||||
|
if os.path.exists(file_name):
|
||||||
|
with open(file_name, 'rb') as file:
|
||||||
|
map = pickle.load(file)
|
||||||
|
draw_grid_map(map)
|
|
@ -0,0 +1,91 @@
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
# from rrt import RRT
|
||||||
|
# from rrt_star import RRTStar
|
||||||
|
|
||||||
|
|
||||||
|
# def real2map(min_x, min_y, scale_ratio, x, y):
|
||||||
|
# '''
|
||||||
|
# 实际坐标->地图坐标 (向下取整)
|
||||||
|
# '''
|
||||||
|
# # x = round((x - self.min_x) / self.scale_ratio)
|
||||||
|
# # y = round((y - self.min_y) / self.scale_ratio)
|
||||||
|
# x = math.floor((x - min_x) / scale_ratio)
|
||||||
|
# y = math.floor((y - min_y) / scale_ratio)
|
||||||
|
# return x, y
|
||||||
|
|
||||||
|
|
||||||
|
def getDistance(pos1, pos2):
|
||||||
|
return math.sqrt((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2)
|
||||||
|
|
||||||
|
|
||||||
|
def getNearestFrontier(cur_pos, frontiers):
|
||||||
|
dis_min = sys.maxsize
|
||||||
|
frontier_best = None
|
||||||
|
for frontier in frontiers:
|
||||||
|
dis = getDistance(frontier, cur_pos)
|
||||||
|
if dis <= dis_min:
|
||||||
|
dis_min = dis
|
||||||
|
frontier_best = frontier
|
||||||
|
return frontier_best
|
||||||
|
|
||||||
|
|
||||||
|
class Explore:
|
||||||
|
def __init__(self, _explore_range):
|
||||||
|
self.explore_range = _explore_range # 机器人感知范围(以机器人为中心,边长为2 * _explore_range的正方形)
|
||||||
|
self.visited = set()
|
||||||
|
self.all_frontier_list = set()
|
||||||
|
|
||||||
|
def explore(self, cur_pos):
|
||||||
|
for i in range(cur_pos[0] - self.explore_range, cur_pos[0] + self.explore_range + 1):
|
||||||
|
for j in range(cur_pos[1] - self.explore_range, cur_pos[1] + self.explore_range + 1):
|
||||||
|
# if self.isOutMap((i, j)):
|
||||||
|
# continue
|
||||||
|
# x, y = real2map(self.area_range[0], self.area_range[2], self.scale_ratio, i, j)
|
||||||
|
# if self.map[x, y] == 0:
|
||||||
|
if self.reachable((i, j)):
|
||||||
|
self.visited.add((i, j))
|
||||||
|
for i in range(cur_pos[0] - self.explore_range, cur_pos[0] + self.explore_range + 1):
|
||||||
|
for j in range(cur_pos[1] - self.explore_range, cur_pos[1] + self.explore_range + 1):
|
||||||
|
# if self.isOutMap((i, j)):
|
||||||
|
# continue
|
||||||
|
# x, y = real2map(self.area_range[0], self.area_range[2], self.scale_ratio, i, j)
|
||||||
|
if self.reachable((i, j)):
|
||||||
|
if self.isNewFrontier((i, j)):
|
||||||
|
self.all_frontier_list.add((i, j))
|
||||||
|
if len(self.all_frontier_list) == 0:
|
||||||
|
free_list = list(self.visited)
|
||||||
|
free_array = np.array(free_list)
|
||||||
|
print(f"探索完成!以下是场景中可以到达的点:{free_array};其余点均是障碍物不可达")
|
||||||
|
return None
|
||||||
|
return getNearestFrontier(cur_pos, self.all_frontier_list)
|
||||||
|
|
||||||
|
def isNewFrontier(self, pos):
|
||||||
|
around_nodes = [(pos[0], pos[1] + 1), (pos[0], pos[1] - 1), (pos[0] - 1, pos[1]), (pos[0] + 1, pos[1])]
|
||||||
|
|
||||||
|
for node in around_nodes:
|
||||||
|
if node not in self.visited and self.reachable(node):
|
||||||
|
return True
|
||||||
|
if (pos[0], pos[1]) in self.all_frontier_list:
|
||||||
|
self.all_frontier_list.remove((pos[0], pos[1]))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reachable(self, pos):
|
||||||
|
# x, y = real2map(self.area_range[0], self.area_range[2], self.scale_ratio, pos[0], pos[1])
|
||||||
|
# if self.map[x, y] == 1:
|
||||||
|
# return True
|
||||||
|
# else:
|
||||||
|
# return False
|
||||||
|
|
||||||
|
# TODO: 调用reachable_check函数
|
||||||
|
pass
|
||||||
|
|
||||||
|
# def isOutMap(self, pos):
|
||||||
|
# if pos[0] <= self.area_range[0] or pos[0] >= self.area_range[1] or pos[1] <= self.area_range[2] or pos[1] >= \
|
||||||
|
# self.area_range[3]:
|
||||||
|
# return True
|
||||||
|
# return False
|
Binary file not shown.
|
@ -0,0 +1,164 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||||
|
from scene_utils import control
|
||||||
|
|
||||||
|
from rrt import RRT
|
||||||
|
from rrt_star import RRTStar
|
||||||
|
from apf import APF
|
||||||
|
|
||||||
|
|
||||||
|
class Navigator:
|
||||||
|
'''
|
||||||
|
导航类
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, scene, area_range, map, scale_ratio=5):
|
||||||
|
self.scene = scene
|
||||||
|
self.area_range = area_range # 地图实际坐标范围 xmin, xmax, ymin, ymax
|
||||||
|
self.map = map # 缩放并离散化的地图 array(X,Y)
|
||||||
|
self.scale_ratio = scale_ratio # 地图缩放率
|
||||||
|
self.step_length = 50 # 步长(单次移动)
|
||||||
|
self.v = 100 # 速度
|
||||||
|
self.step_time = self.step_length/self.v # 单步移动时长
|
||||||
|
|
||||||
|
self.planner = RRTStar(rand_area=area_range, map=map, scale_ratio=scale_ratio, max_iter=400, search_until_max_iter=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_reached(pos: np.array((float, float)), goal: np.array((float, float)), dis_limit=25):
|
||||||
|
'''
|
||||||
|
判断是否到达目标
|
||||||
|
'''
|
||||||
|
dis = np.linalg.norm(pos - goal)
|
||||||
|
return dis < dis_limit
|
||||||
|
|
||||||
|
def reset_goal(self, goal:(float, float)):
|
||||||
|
# TODO: 使目标可达
|
||||||
|
# 目标在障碍物上:从目标开始方形向外扩展,直到找到可行点
|
||||||
|
# 目标在地图外面:起点和目标连线最靠近目标的可行点
|
||||||
|
pass
|
||||||
|
|
||||||
|
def navigate(self, goal: (float, float), path_smoothing=True, animation=True):
|
||||||
|
pos = np.array((self.scene.status.location.X, self.scene.status.location.Y)) # 机器人当前: 位置 和 朝向
|
||||||
|
yaw = self.scene.status.rotation.Yaw
|
||||||
|
print('------------------navigation_start----------------------')
|
||||||
|
|
||||||
|
path = self.planner.planning(pos, goal, path_smoothing, animation)
|
||||||
|
if path:
|
||||||
|
self.planner.draw_graph(final_path=path) # 画出探索过程
|
||||||
|
for (x, y) in path:
|
||||||
|
self.scene.walk_to(x, y, yaw, velocity=self.v)
|
||||||
|
time.sleep(self.step_time)
|
||||||
|
pos = np.array((self.scene.status.location.X, self.scene.status.location.Y))
|
||||||
|
|
||||||
|
self.planner.reset()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''APF势场法暂不可用'''
|
||||||
|
# while not self.is_reached(pos, goal):
|
||||||
|
# # 1. 路径规划
|
||||||
|
# path = self.planner.planning(pos, goal, path_smoothing, animation)
|
||||||
|
# self.planner.draw_graph(final_path=path) # 画出探索过程
|
||||||
|
#
|
||||||
|
# # 2. 使用APF导航到路径中的每个waypoint
|
||||||
|
# traj = [(pos[0], pos[1])]
|
||||||
|
# #self.planner.draw_graph(final_path=traj) # 画出探索过程
|
||||||
|
# for i, waypoint in enumerate(path[1:]):
|
||||||
|
# print('waypoint [', i, ']:', waypoint)
|
||||||
|
# # if (not self.scene.reachable_check(waypoint[0], waypoint[1], yaw)) and self.map[self.planner.real2map(waypoint[0], waypoint[1])] == 0:
|
||||||
|
# # print('error')
|
||||||
|
# while not self.is_reached(pos, waypoint):
|
||||||
|
# # 2.1 计算next_step
|
||||||
|
# pos = np.array((self.scene.status.location.X, self.scene.status.location.Y))
|
||||||
|
# Pobs = [] # 障碍物(顾客)位置数组
|
||||||
|
# for walker in self.scene.status.walkers:
|
||||||
|
# Pobs.append((walker.pose.X, walker.pose.Y))
|
||||||
|
# next_step, _ = APF(Pi=pos, Pg=waypoint, Pobs=Pobs, step_length=self.step_length)
|
||||||
|
# traj.append((next_step[0], next_step[1]))
|
||||||
|
# #self.planner.draw_graph(final_path=traj) # 画出探索过程
|
||||||
|
# while not self.scene.reachable_check(next_step[0], next_step[1], yaw): # 取中点直到next_step可达
|
||||||
|
# traj.pop()
|
||||||
|
# next_step = (pos + next_step) / 2
|
||||||
|
# traj.append((next_step[0], next_step[1]))
|
||||||
|
# #self.planner.draw_graph(final_path=traj) # 画出探索过程
|
||||||
|
# # 2.2 移动robot
|
||||||
|
# self.scene.walk_to(next_step[0], next_step[1], yaw, velocity=self.v)
|
||||||
|
# # print(self.scene.status.info) # print navigation info
|
||||||
|
# # print(self.scene.status.collision)
|
||||||
|
# time.sleep(self.step_time)
|
||||||
|
# # print(self.scene.status.info) # print navigation info
|
||||||
|
# # print(self.scene.status.collision)
|
||||||
|
# self.planner.reset()
|
||||||
|
if self.is_reached(pos, goal):
|
||||||
|
print('The robot has achieved goal !!')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# class Walker:
|
||||||
|
# def __int__(self, scene):
|
||||||
|
# self.scene = scene
|
||||||
|
#
|
||||||
|
# def add_walkers(self, walker_loc, scene_id=0):
|
||||||
|
# """
|
||||||
|
# 批量添加行人
|
||||||
|
# Args:
|
||||||
|
# walker_loc: 行人的初始位置列表( 列表元素数量对应行人数量 )
|
||||||
|
# """
|
||||||
|
# print('------------------add walker----------------------')
|
||||||
|
# walker_list = []
|
||||||
|
# for i in range(len(walker_loc)):
|
||||||
|
# # 只有可达的位置才能成功初始化行人,显示unreachable的地方不能初始化行人
|
||||||
|
# walker_list.append(
|
||||||
|
# GrabSim_pb2.WalkerList.Walker(id=i, pose=GrabSim_pb2.Pose(X=walker_loc[0], Y=walker_loc[1], Yaw=90)))
|
||||||
|
# scene = self.client.AddWalker(GrabSim_pb2.WalkerList(walkers=walker_list, scene=scene_id)) # 生成环境中行人
|
||||||
|
# # print(self.client.Observe(GrabSim_pb2.SceneID(value=scene_id)).walkers) # 打印行人信息
|
||||||
|
# return scene
|
||||||
|
#
|
||||||
|
# def control_walkers(self, walker_loc, scene_id=0):
|
||||||
|
# """
|
||||||
|
# 批量移动行人
|
||||||
|
# Args:
|
||||||
|
# walker_loc: 行人的终止位置列表
|
||||||
|
# """
|
||||||
|
# scene = self.client.Observe(GrabSim_pb2.SceneID(value=scene_id))
|
||||||
|
# controls = []
|
||||||
|
# for i in range(len(scene.walkers)):
|
||||||
|
# loc = walker_loc[i]
|
||||||
|
# is_autowalk = False # True: 随机移动; False: 移动到目标点
|
||||||
|
# pose = GrabSim_pb2.Pose(X=loc[0], Y=loc[1], Yaw=180)
|
||||||
|
# controls.append(GrabSim_pb2.WalkerControls.WControl(id=i, autowalk=is_autowalk, speed=200, pose=pose))
|
||||||
|
# scene = self.client.ControlWalkers(
|
||||||
|
# GrabSim_pb2.WalkerControls(controls=controls, scene=scene_id)) # 设置行人移动速度和目标点
|
||||||
|
# # print(self.client.Observe(GrabSim_pb2.SceneID(value=scene_id)).walkers) # 打印行人信息
|
||||||
|
# time.sleep(10)
|
||||||
|
# return scene
|
||||||
|
#
|
||||||
|
# def remove_walkers(self, ids, scene_id=0):
|
||||||
|
# '''
|
||||||
|
# 按编号移除行人
|
||||||
|
# Args:
|
||||||
|
# ids: 待移除的行人编号列表
|
||||||
|
# '''
|
||||||
|
# scene = self.client.RemoveWalkers(GrabSim_pb2.RemoveList(IDs=ids, scene=scene_id)) # 按编号移除行人
|
||||||
|
# # print(self.client.Observe(GrabSim_pb2.SceneID(value=scene_id)).walkers) # 打印行人信息
|
||||||
|
# time.sleep(2)
|
||||||
|
# return scene
|
||||||
|
#
|
||||||
|
# def clean_walkers(self, scene_id=0):
|
||||||
|
# '''
|
||||||
|
# 删除环境中所有行人
|
||||||
|
# '''
|
||||||
|
# scene = self.client.CleanWalkers(GrabSim_pb2.SceneID(value=scene_id))
|
||||||
|
# return scene
|
|
@ -0,0 +1,132 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
Path planning Sample Code with RRT with path smoothing
|
||||||
|
|
||||||
|
@author: AtsushiSakai(@Atsushi_twi)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import sys
|
||||||
|
import pathlib
|
||||||
|
sys.path.append(str(pathlib.Path(__file__).parent))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_path_length(path):
|
||||||
|
'''
|
||||||
|
计算路径长度
|
||||||
|
'''
|
||||||
|
le = 0
|
||||||
|
for i in range(len(path) - 1):
|
||||||
|
dx = path[i + 1][0] - path[i][0]
|
||||||
|
dy = path[i + 1][1] - path[i][1]
|
||||||
|
d = math.hypot(dx, dy)
|
||||||
|
le += d
|
||||||
|
|
||||||
|
return le
|
||||||
|
|
||||||
|
|
||||||
|
def get_target_point(path, targetL):
|
||||||
|
le = 0
|
||||||
|
ti = 0
|
||||||
|
lastPairLen = 0
|
||||||
|
for i in range(len(path) - 1):
|
||||||
|
dx = path[i + 1][0] - path[i][0]
|
||||||
|
dy = path[i + 1][1] - path[i][1]
|
||||||
|
d = math.hypot(dx, dy)
|
||||||
|
le += d
|
||||||
|
if le >= targetL:
|
||||||
|
ti = i - 1
|
||||||
|
lastPairLen = d
|
||||||
|
break
|
||||||
|
|
||||||
|
partRatio = (le - targetL) / lastPairLen
|
||||||
|
|
||||||
|
x = path[ti][0] + (path[ti + 1][0] - path[ti][0]) * partRatio
|
||||||
|
y = path[ti][1] + (path[ti + 1][1] - path[ti][1]) * partRatio
|
||||||
|
|
||||||
|
return [x, y, ti]
|
||||||
|
|
||||||
|
|
||||||
|
# def line_collision_check(first, second, map, path_resolution=25.0, robot_radius=5, scale_ratio=5):
|
||||||
|
# '''
|
||||||
|
# 检查first-second的直线是否穿过障碍物
|
||||||
|
# Args:
|
||||||
|
# path_resolution: 采样点分辨率
|
||||||
|
# '''
|
||||||
|
# x1 = first[0]
|
||||||
|
# y1 = first[1]
|
||||||
|
# x2 = second[0]
|
||||||
|
# y2 = second[1]
|
||||||
|
# path_x = [x1]
|
||||||
|
# path_y = [y1]
|
||||||
|
# # 计算距离和夹角
|
||||||
|
# dx = x2 - x1
|
||||||
|
# dy = y2 - y1
|
||||||
|
# d = math.hypot(dx, dy) # 欧式距离
|
||||||
|
# theta = math.atan2(dy, dx) # 夹角
|
||||||
|
#
|
||||||
|
# n_expand = math.floor(d / path_resolution) # first 和 second 之间的采样点数
|
||||||
|
# for _ in range(n_expand):
|
||||||
|
# x1 += path_resolution * math.cos(theta)
|
||||||
|
# y1 += path_resolution * math.sin(theta)
|
||||||
|
# path_x.append(x1)
|
||||||
|
# path_y.append(y1)
|
||||||
|
# if d <= path_resolution:
|
||||||
|
# path_x.append(x2)
|
||||||
|
# path_y.append(y2)
|
||||||
|
#
|
||||||
|
# for (x, y) in zip(path_x, path_y):
|
||||||
|
# (scale_x, scale_y) = (math.floor(x / scale_ratio), math.floor(y / scale_ratio)) # 向下取整
|
||||||
|
# if robot_radius > scale_ratio: # TODO: 根据robot_radius和scale_ratio选择不同的判断方式
|
||||||
|
# if map[scale_x, scale_y] or map[scale_x + 1, scale_y] or \
|
||||||
|
# map[scale_x, scale_y + 1] or map[scale_x + 1, scale_y + 1]: # 如果node周围的四个缩放坐标有障碍物
|
||||||
|
# return False
|
||||||
|
# return True
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def path_smoothing(path, map, max_iter=1000):
|
||||||
|
# '''
|
||||||
|
# 输入原路径,输出平滑后的路径
|
||||||
|
# Args:
|
||||||
|
# path: [(x,y),...]
|
||||||
|
# '''
|
||||||
|
# le = get_path_length(path)
|
||||||
|
#
|
||||||
|
# for i in range(max_iter):
|
||||||
|
# # Sample two points
|
||||||
|
# pickPoints = [random.uniform(0, le), random.uniform(0, le)] # 总路径长度中采样2个点
|
||||||
|
# pickPoints.sort()
|
||||||
|
# first = get_target_point(path, pickPoints[0])
|
||||||
|
# second = get_target_point(path, pickPoints[1])
|
||||||
|
#
|
||||||
|
# if first[2] <= 0 or second[2] <= 0:
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# if (second[2] + 1) > len(path):
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# if second[2] == first[2]:
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# # collision check
|
||||||
|
# if not line_collision_check(first, second, map):
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# # Create New path
|
||||||
|
# newPath = []
|
||||||
|
# newPath.extend(path[:first[2] + 1])
|
||||||
|
# newPath.append([first[0], first[1]])
|
||||||
|
# newPath.append([second[0], second[1]])
|
||||||
|
# newPath.extend(path[second[2] + 1:])
|
||||||
|
# path = newPath
|
||||||
|
# le = get_path_length(path)
|
||||||
|
#
|
||||||
|
# return path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
apf.py: 势场法实现
|
||||||
|
|
||||||
|
discretize_map.py: 地图离散化并压缩
|
||||||
|
|
||||||
|
map_5.pkl: 地图文件(5倍压缩)
|
||||||
|
|
||||||
|
*navigate.py: 导航类*
|
||||||
|
|
||||||
|
pathsmoothing.py: 路径平滑
|
||||||
|
|
||||||
|
rrt.py: RRT实现
|
||||||
|
|
||||||
|
rrt_star.py: RRTStar 实现
|
||||||
|
|
||||||
|
test.py: 测试文件
|
||||||
|
|
||||||
|
默认使用RRTStar+路径平滑
|
|
@ -0,0 +1,440 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
Path planning Sample Code with Randomized Rapidly-Exploring Random Trees (RRT)
|
||||||
|
|
||||||
|
author: AtsushiSakai(@Atsushi_twi)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from pathsmoothing import get_path_length, get_target_point
|
||||||
|
|
||||||
|
show_animation = True
|
||||||
|
|
||||||
|
|
||||||
|
class RRT:
|
||||||
|
"""
|
||||||
|
Class for RRT planning
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
"""
|
||||||
|
RRT Node
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, x=0.0, y=0.0):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.path_x = [] # Node 到 parent 间的路径链
|
||||||
|
self.path_y = []
|
||||||
|
self.parent = None
|
||||||
|
|
||||||
|
class AreaBounds:
|
||||||
|
|
||||||
|
def __init__(self, area):
|
||||||
|
self.xmin = float(area[0])
|
||||||
|
self.xmax = float(area[1])
|
||||||
|
self.ymin = float(area[2])
|
||||||
|
self.ymax = float(area[3])
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
rand_area, # 横纵坐标的采样范围 [minx,maxx,miny,maxy]
|
||||||
|
map, # 离散化地图
|
||||||
|
scale_ratio=5, # 地图缩放率
|
||||||
|
expand_dis=200, # 最大扩展距离
|
||||||
|
path_resolution=10.0, # 路径链分辨率
|
||||||
|
goal_sample_rate=5, # 5%的几率采样到目标点
|
||||||
|
max_iter=500,
|
||||||
|
robot_radius=12.0, # robot身位半径
|
||||||
|
):
|
||||||
|
|
||||||
|
self.map = map
|
||||||
|
self.scale_ratio = scale_ratio
|
||||||
|
self.start = self.Node(0, 0)
|
||||||
|
self.goal = self.Node(0, 0)
|
||||||
|
(self.min_x, self.max_x, self.min_y, self.max_y) = rand_area
|
||||||
|
# self.play_area = self.AreaBounds(rand_area) # 采样区域(边界框启发式)
|
||||||
|
self.play_area = None
|
||||||
|
self.expand_dis = expand_dis
|
||||||
|
self.path_resolution = path_resolution
|
||||||
|
self.goal_sample_rate = goal_sample_rate
|
||||||
|
self.max_iter = max_iter
|
||||||
|
self.node_list = []
|
||||||
|
self.robot_radius = robot_radius
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
'''
|
||||||
|
重置rrt的变量和数据结构
|
||||||
|
'''
|
||||||
|
self.node_list.clear()
|
||||||
|
self.start = self.Node(0, 0)
|
||||||
|
self.goal = self.Node(0, 0)
|
||||||
|
|
||||||
|
def update_play_area(self):
|
||||||
|
'''
|
||||||
|
更新采样区域
|
||||||
|
'''
|
||||||
|
if self.goal.x > self.start.x:
|
||||||
|
self.play_area.xmin = self.start.x
|
||||||
|
self.play_area.xmax = self.max_x
|
||||||
|
else:
|
||||||
|
self.play_area.xmin = self.min_x
|
||||||
|
self.play_area.xmax = self.start.x
|
||||||
|
if self.goal.y > self.start.y:
|
||||||
|
self.play_area.ymin = self.start.y
|
||||||
|
self.play_area.ymax = self.max_y
|
||||||
|
else:
|
||||||
|
self.play_area.ymin = self.min_y
|
||||||
|
self.play_area.ymax = self.start.y
|
||||||
|
|
||||||
|
def planning(self, start, goal, path_smoothing=True, animation=False):
|
||||||
|
"""
|
||||||
|
rrt path planning, return a path list
|
||||||
|
|
||||||
|
animation: flag for animation on or off
|
||||||
|
"""
|
||||||
|
(self.start.x, self.start.y) = start
|
||||||
|
(self.goal.x, self.goal.y) = goal
|
||||||
|
# self.update_play_area()
|
||||||
|
self.node_list = [self.start] # 已经采样的节点列表
|
||||||
|
for i in range(self.max_iter):
|
||||||
|
rnd_node = self.get_random_node() # 随机采样节点
|
||||||
|
nearest_ind = self.get_nearest_node_index(self.node_list, rnd_node)
|
||||||
|
nearest_node = self.node_list[nearest_ind] # node_list中距离新采样点最近的节点
|
||||||
|
|
||||||
|
new_node = self.steer(nearest_node, rnd_node, self.expand_dis)
|
||||||
|
|
||||||
|
# 确保新采样节点在范围内且不是障碍物
|
||||||
|
if self.check_if_outside_play_area(new_node, self.play_area) and \
|
||||||
|
self.check_collision(new_node):
|
||||||
|
self.node_list.append(new_node)
|
||||||
|
|
||||||
|
if animation and i % 5 == 0:
|
||||||
|
self.draw_graph(rnd_node)
|
||||||
|
|
||||||
|
if self.calc_dist_to_goal(self.node_list[-1].x,
|
||||||
|
self.node_list[-1].y) <= self.expand_dis:
|
||||||
|
final_node = self.steer(self.node_list[-1], self.goal,
|
||||||
|
self.expand_dis)
|
||||||
|
if self.check_collision(final_node):
|
||||||
|
path = self.generate_final_course(len(self.node_list) - 1)
|
||||||
|
if path_smoothing:
|
||||||
|
return self.path_smoothing(path)
|
||||||
|
else:
|
||||||
|
return path
|
||||||
|
|
||||||
|
if animation and i % 5:
|
||||||
|
self.draw_graph(rnd_node)
|
||||||
|
|
||||||
|
return None # cannot find path
|
||||||
|
|
||||||
|
def steer(self, from_node, to_node, extend_length=float("inf")):
|
||||||
|
'''
|
||||||
|
返回from_node 到 to_node 距离限制内最远的节点,并设置其父节点为from_node
|
||||||
|
并计算路径链
|
||||||
|
Args:
|
||||||
|
entend_length: 距离限制
|
||||||
|
return:
|
||||||
|
new_node: from_node 到 to_node 距离限制内最远的节点
|
||||||
|
( 如果 to_node 在距离限制内则返回 to_node )
|
||||||
|
'''
|
||||||
|
new_node = self.Node(from_node.x, from_node.y)
|
||||||
|
d, theta = self.calc_distance_and_angle(new_node, to_node) # from 和 to 之间的距离和方向角
|
||||||
|
|
||||||
|
new_node.path_x = [new_node.x]
|
||||||
|
new_node.path_y = [new_node.y]
|
||||||
|
|
||||||
|
if extend_length > d:
|
||||||
|
extend_length = d
|
||||||
|
|
||||||
|
n_expand = math.floor(extend_length / self.path_resolution) # from 和 to 之间的采样点数
|
||||||
|
|
||||||
|
for _ in range(n_expand):
|
||||||
|
new_node.x += self.path_resolution * math.cos(theta)
|
||||||
|
new_node.y += self.path_resolution * math.sin(theta)
|
||||||
|
new_node.path_x.append(new_node.x)
|
||||||
|
new_node.path_y.append(new_node.y)
|
||||||
|
|
||||||
|
d, _ = self.calc_distance_and_angle(new_node, to_node)
|
||||||
|
if d <= self.path_resolution:
|
||||||
|
new_node.path_x.append(to_node.x)
|
||||||
|
new_node.path_y.append(to_node.y)
|
||||||
|
new_node.x = to_node.x
|
||||||
|
new_node.y = to_node.y
|
||||||
|
|
||||||
|
new_node.parent = from_node
|
||||||
|
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
def generate_final_course(self, goal_ind) -> [(float, float), ]:
|
||||||
|
'''
|
||||||
|
得到路径(起点->终点)
|
||||||
|
'''
|
||||||
|
path = [(self.goal.x, self.goal.y)]
|
||||||
|
node = self.node_list[goal_ind]
|
||||||
|
while node.parent is not None:
|
||||||
|
path.append((node.x, node.y))
|
||||||
|
node = node.parent
|
||||||
|
path.append((node.x, node.y))
|
||||||
|
path.reverse()
|
||||||
|
return path
|
||||||
|
|
||||||
|
def calc_dist_to_goal(self, x, y):
|
||||||
|
'''
|
||||||
|
计算节点到终点距离
|
||||||
|
'''
|
||||||
|
dx = x - self.goal.x
|
||||||
|
dy = y - self.goal.y
|
||||||
|
return math.hypot(dx, dy)
|
||||||
|
|
||||||
|
def get_random_node(self):
|
||||||
|
'''
|
||||||
|
在规定范围内随机采样节点(一定概率采样到目标节点)
|
||||||
|
'''
|
||||||
|
if random.randint(0, 100) > self.goal_sample_rate:
|
||||||
|
rnd = self.Node(
|
||||||
|
random.uniform(self.min_x, self.max_x),
|
||||||
|
random.uniform(self.min_y, self.max_y))
|
||||||
|
else: # goal point sampling
|
||||||
|
rnd = self.Node(self.goal.x, self.goal.y) # 返回goal
|
||||||
|
return rnd
|
||||||
|
|
||||||
|
def map2real(self, x, y):
|
||||||
|
'''
|
||||||
|
地图坐标->实际坐标
|
||||||
|
'''
|
||||||
|
x = x * self.scale_ratio + self.min_x
|
||||||
|
y = y * self.scale_ratio + self.min_y
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def real2map(self, x, y):
|
||||||
|
'''
|
||||||
|
实际坐标->地图坐标 (向下取整)
|
||||||
|
'''
|
||||||
|
# x = round((x - self.min_x) / self.scale_ratio)
|
||||||
|
# y = round((y - self.min_y) / self.scale_ratio)
|
||||||
|
x = math.floor((x - self.min_x) / self.scale_ratio)
|
||||||
|
y = math.floor((y - self.min_y) / self.scale_ratio)
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def check_collision(self, node):
|
||||||
|
'''
|
||||||
|
判断 node 到 父节点之间 是否有障碍物
|
||||||
|
'''
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
return False
|
||||||
|
for (x, y) in zip(node.path_x, node.path_y):
|
||||||
|
map_x, map_y = self.real2map(x, y)
|
||||||
|
# TODO: 碰撞检测考虑robot_radius和scale_ratio
|
||||||
|
if self.map[map_x, map_y] or self.map[map_x + 1, map_y] or \
|
||||||
|
self.map[map_x, map_y + 1] or self.map[map_x + 1, map_y + 1]:
|
||||||
|
return False
|
||||||
|
return True # safe
|
||||||
|
|
||||||
|
def draw_graph(self, new=None, final_path=None):
|
||||||
|
# 清空当前figure内容,保留figure对象
|
||||||
|
plt.clf()
|
||||||
|
# for stopping simulation with the esc key. 按esc结束
|
||||||
|
plt.gcf().canvas.mpl_connect(
|
||||||
|
'key_release_event',
|
||||||
|
lambda event: [exit(0) if event.key == 'escape' else None])
|
||||||
|
|
||||||
|
# 画地图: X行Y列,第一行在下面
|
||||||
|
# 范围: 横向Y[-70,120] 纵向X[-80,290]
|
||||||
|
plt.imshow(self.map, cmap='binary', alpha=0.5, origin='lower',
|
||||||
|
extent=(self.min_y / self.scale_ratio, self.max_y / self.scale_ratio,
|
||||||
|
self.min_x / self.scale_ratio, self.max_x / self.scale_ratio))
|
||||||
|
# 画新采样点
|
||||||
|
if new is not None:
|
||||||
|
(scale_x, scale_y) = (new.x / self.scale_ratio, new.y / self.scale_ratio)
|
||||||
|
plt.plot(scale_y, scale_x, "^k")
|
||||||
|
if self.robot_radius > 0.0:
|
||||||
|
self.plot_circle(scale_y, scale_x, self.robot_radius / self.scale_ratio, '-r')
|
||||||
|
# 画新边
|
||||||
|
if new.parent:
|
||||||
|
plt.plot([y / self.scale_ratio for y in new.path_y],
|
||||||
|
[x / self.scale_ratio for x in new.path_x], "-g")
|
||||||
|
|
||||||
|
# 画整个搜索树
|
||||||
|
for node in self.node_list:
|
||||||
|
if node.parent:
|
||||||
|
plt.plot([y / self.scale_ratio for y in node.path_y],
|
||||||
|
[x / self.scale_ratio for x in node.path_x], "-g")
|
||||||
|
# 画起点和目标
|
||||||
|
plt.plot(self.start.y / self.scale_ratio, self.start.x / self.scale_ratio, "xr")
|
||||||
|
plt.plot(self.goal.y / self.scale_ratio, self.goal.x / self.scale_ratio, "xr")
|
||||||
|
|
||||||
|
plt.xlabel('y', loc='right')
|
||||||
|
plt.ylabel('x', loc='top')
|
||||||
|
plt.grid(color='black', linestyle='-', linewidth=0.5)
|
||||||
|
plt.pause(0.2)
|
||||||
|
|
||||||
|
# 画最终路径
|
||||||
|
if final_path:
|
||||||
|
plt.plot([y / self.scale_ratio for (x, y) in final_path],
|
||||||
|
[x / self.scale_ratio for (x, y) in final_path], '-r') # 画出最终路径
|
||||||
|
plt.pause(2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def plot_circle(x, y, size, color="-b"): # pragma: no cover
|
||||||
|
'''
|
||||||
|
以(x,y)为圆心,size为半径 画圆
|
||||||
|
'''
|
||||||
|
deg = list(range(0, 360, 5))
|
||||||
|
deg.append(0)
|
||||||
|
xl = [x + size * math.cos(np.deg2rad(d)) for d in deg]
|
||||||
|
yl = [y + size * math.sin(np.deg2rad(d)) for d in deg]
|
||||||
|
plt.plot(xl, yl, color)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_nearest_node_index(node_list, rnd_node):
|
||||||
|
'''
|
||||||
|
得到距离rnd_node最近的节点编号
|
||||||
|
'''
|
||||||
|
dlist = [(node.x - rnd_node.x) ** 2 + (node.y - rnd_node.y) ** 2
|
||||||
|
for node in node_list]
|
||||||
|
minind = dlist.index(min(dlist))
|
||||||
|
|
||||||
|
return minind
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_if_outside_play_area(node, play_area):
|
||||||
|
|
||||||
|
if play_area is None:
|
||||||
|
return True # no play_area was defined, every pos should be ok
|
||||||
|
|
||||||
|
if node.x < play_area.xmin or node.x > play_area.xmax or \
|
||||||
|
node.y < play_area.ymin or node.y > play_area.ymax:
|
||||||
|
return False # outside - bad
|
||||||
|
else:
|
||||||
|
return True # inside - ok
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def calc_distance_and_angle(from_node, to_node):
|
||||||
|
'''
|
||||||
|
计算2点间的距离和角度( 以from_node为原点 )
|
||||||
|
'''
|
||||||
|
dx = to_node.x - from_node.x
|
||||||
|
dy = to_node.y - from_node.y
|
||||||
|
d = math.hypot(dx, dy) # 欧式距离
|
||||||
|
theta = math.atan2(dy, dx)
|
||||||
|
return d, theta
|
||||||
|
|
||||||
|
# **************************Path Smoothing**************************************************** #
|
||||||
|
# **************************Path Smoothing**************************************************** #
|
||||||
|
# **************************Path Smoothing**************************************************** #
|
||||||
|
|
||||||
|
def line_collision_check(self, first, second):
|
||||||
|
'''
|
||||||
|
检查first-second的直线是否穿过障碍物
|
||||||
|
Args:
|
||||||
|
path_resolution: 采样点分辨率
|
||||||
|
'''
|
||||||
|
x1 = first[0]
|
||||||
|
y1 = first[1]
|
||||||
|
x2 = second[0]
|
||||||
|
y2 = second[1]
|
||||||
|
path_x = [x1]
|
||||||
|
path_y = [y1]
|
||||||
|
# 计算距离和夹角
|
||||||
|
dx = x2 - x1
|
||||||
|
dy = y2 - y1
|
||||||
|
d = math.hypot(dx, dy) # 欧式距离
|
||||||
|
theta = math.atan2(dy, dx) # 夹角
|
||||||
|
|
||||||
|
n_expand = math.floor(d / self.path_resolution) # first 和 second 之间的采样点数
|
||||||
|
for _ in range(n_expand):
|
||||||
|
x1 += self.path_resolution * math.cos(theta)
|
||||||
|
y1 += self.path_resolution * math.sin(theta)
|
||||||
|
path_x.append(x1)
|
||||||
|
path_y.append(y1)
|
||||||
|
if d <= self.path_resolution:
|
||||||
|
path_x.append(x2)
|
||||||
|
path_y.append(y2)
|
||||||
|
|
||||||
|
for (x, y) in zip(path_x, path_y):
|
||||||
|
map_x, map_y = self.real2map(x, y) # 向下取整
|
||||||
|
# TODO: 碰撞检测考虑robot_radius和scale_ratio
|
||||||
|
if self.map[map_x, map_y] or self.map[map_x + 1, map_y] or \
|
||||||
|
self.map[map_x, map_y + 1] or self.map[map_x + 1, map_y + 1]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def path_smoothing(self, path, max_iter=1000):
|
||||||
|
'''
|
||||||
|
输入原路径,输出平滑后的路径
|
||||||
|
Args:
|
||||||
|
path: [(x,y),...]
|
||||||
|
'''
|
||||||
|
le = get_path_length(path)
|
||||||
|
|
||||||
|
for i in range(max_iter):
|
||||||
|
# Sample two points
|
||||||
|
pickPoints = [random.uniform(0, le), random.uniform(0, le)] # 总路径长度中采样2个点
|
||||||
|
pickPoints.sort()
|
||||||
|
first = get_target_point(path, pickPoints[0])
|
||||||
|
second = get_target_point(path, pickPoints[1])
|
||||||
|
|
||||||
|
if first[2] <= 0 or second[2] <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (second[2] + 1) > len(path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if second[2] == first[2]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# collision check
|
||||||
|
if not self.line_collision_check(first, second):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create New path
|
||||||
|
newPath = []
|
||||||
|
newPath.extend(path[:first[2] + 1])
|
||||||
|
newPath.append([first[0], first[1]])
|
||||||
|
newPath.append([second[0], second[1]])
|
||||||
|
newPath.extend(path[second[2] + 1:])
|
||||||
|
path = newPath
|
||||||
|
le = get_path_length(path)
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def main(gx=6.0, gy=10.0):
|
||||||
|
print("start " + __file__)
|
||||||
|
|
||||||
|
# ====Search Path with RRT====
|
||||||
|
obstacleList = [(5, 5, 1), (3, 6, 2), (3, 8, 2), (3, 10, 2), (7, 5, 2),
|
||||||
|
(9, 5, 2), (8, 10, 1)] # [x, y, radius]
|
||||||
|
# Set Initial parameters
|
||||||
|
rrt = RRT(
|
||||||
|
start=[0, 0],
|
||||||
|
goal=[gx, gy],
|
||||||
|
rand_area=[-2, 15],
|
||||||
|
obstacle_list=obstacleList,
|
||||||
|
# play_area=[0, 10, 0, 14]
|
||||||
|
robot_radius=0.8
|
||||||
|
)
|
||||||
|
path = rrt.planning(animation=show_animation)
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
print("Cannot find path")
|
||||||
|
else:
|
||||||
|
print("found path!!")
|
||||||
|
|
||||||
|
# Draw final path
|
||||||
|
if show_animation:
|
||||||
|
rrt.draw_graph() # 画出探索过程
|
||||||
|
plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r') # 画出最终路径
|
||||||
|
plt.grid(True)
|
||||||
|
plt.pause(0.01)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,288 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
Path planning Sample Code with RRT*
|
||||||
|
|
||||||
|
author: Atsushi Sakai(@Atsushi_twi)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import pathlib
|
||||||
|
from rrt import RRT
|
||||||
|
|
||||||
|
sys.path.append(str(pathlib.Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RRTStar(RRT):
|
||||||
|
"""
|
||||||
|
Class for RRT Star planning
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Node(RRT.Node):
|
||||||
|
def __init__(self, x, y):
|
||||||
|
super().__init__(x, y)
|
||||||
|
self.cost = 0.0 # 路径代价
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
rand_area,
|
||||||
|
map,
|
||||||
|
scale_ratio=5,
|
||||||
|
expand_dis=100.0,
|
||||||
|
path_resolution=10.0,
|
||||||
|
goal_sample_rate=10,
|
||||||
|
max_iter=300,
|
||||||
|
connect_circle_dist=50.0, # new
|
||||||
|
search_until_max_iter=False, # new
|
||||||
|
robot_radius=5.0):
|
||||||
|
|
||||||
|
super().__init__(rand_area, map, scale_ratio, expand_dis,
|
||||||
|
path_resolution, goal_sample_rate, max_iter,
|
||||||
|
robot_radius=robot_radius)
|
||||||
|
self.connect_circle_dist = connect_circle_dist
|
||||||
|
self.search_until_max_iter = search_until_max_iter
|
||||||
|
self.node_list = []
|
||||||
|
|
||||||
|
def planning(self, start, goal, path_smoothing=True, animation=False) -> [(float, float), ...]:
|
||||||
|
"""
|
||||||
|
rrt star path planning, return a path list
|
||||||
|
animation: flag for animation on or off .
|
||||||
|
"""
|
||||||
|
(self.start.x, self.start.y) = start
|
||||||
|
(self.goal.x, self.goal.y) = goal
|
||||||
|
# self.update_play_area()
|
||||||
|
self.node_list = [self.start]
|
||||||
|
while len(self.node_list) < self.max_iter:
|
||||||
|
#for i in range(self.max_iter):
|
||||||
|
# print("Iter:", i, ", number of nodes:", len(self.node_list))
|
||||||
|
print("number of nodes:", len(self.node_list))
|
||||||
|
rnd_node = self.get_random_node()
|
||||||
|
nearest_ind = self.get_nearest_node_index(self.node_list, rnd_node)
|
||||||
|
nearest_node = self.node_list[nearest_ind]
|
||||||
|
new_node = self.steer(nearest_node, rnd_node, self.expand_dis)
|
||||||
|
|
||||||
|
if animation:
|
||||||
|
self.draw_graph(new_node)
|
||||||
|
|
||||||
|
if self.check_collision(new_node):
|
||||||
|
new_node.cost = nearest_node.cost + \
|
||||||
|
math.hypot(new_node.x - nearest_node.x,
|
||||||
|
new_node.y - nearest_node.y)
|
||||||
|
near_inds = self.find_near_nodes(new_node)
|
||||||
|
# node_with_updated_parent: 已经找到父节点的new_node
|
||||||
|
node_with_updated_parent = self.choose_parent(new_node, near_inds)
|
||||||
|
if node_with_updated_parent:
|
||||||
|
self.rewire(node_with_updated_parent, near_inds)
|
||||||
|
self.node_list.append(node_with_updated_parent)
|
||||||
|
else:
|
||||||
|
self.node_list.append(new_node) # ??? 不可能发生
|
||||||
|
|
||||||
|
# 目标检测
|
||||||
|
if not self.search_until_max_iter:
|
||||||
|
last_index = self.search_best_goal_node() # 找到目标单步范围内的总距离最短的节点
|
||||||
|
if last_index is not None:
|
||||||
|
path = self.generate_final_course(last_index)
|
||||||
|
if path_smoothing:
|
||||||
|
return self.path_smoothing(path)
|
||||||
|
else:
|
||||||
|
return path
|
||||||
|
|
||||||
|
print("reached max iteration")
|
||||||
|
|
||||||
|
last_index = self.search_best_goal_node()
|
||||||
|
if last_index is not None:
|
||||||
|
path = self.generate_final_course(last_index)
|
||||||
|
if path_smoothing:
|
||||||
|
return self.path_smoothing(path)
|
||||||
|
else:
|
||||||
|
return path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def choose_parent(self, new_node, near_inds):
|
||||||
|
"""
|
||||||
|
为 new_node 选择(从起点开始)总距离最小的父节点
|
||||||
|
Computes the cheapest point to new_node contained in the list
|
||||||
|
near_inds and set such a node as the parent of new_node.
|
||||||
|
Arguments:
|
||||||
|
--------
|
||||||
|
new_node, Node
|
||||||
|
randomly generated node with a path from its neared point
|
||||||
|
There are not coalitions between this node and th tree.
|
||||||
|
near_inds: list
|
||||||
|
Indices of indices of the nodes what are near to new_node
|
||||||
|
|
||||||
|
Returns.
|
||||||
|
------
|
||||||
|
Node, a copy of new_node
|
||||||
|
"""
|
||||||
|
if not near_inds:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# search nearest cost in near_inds
|
||||||
|
costs = []
|
||||||
|
for i in near_inds:
|
||||||
|
near_node = self.node_list[i]
|
||||||
|
t_node = self.steer(near_node, new_node)
|
||||||
|
if t_node and self.check_collision(t_node):
|
||||||
|
costs.append(self.calc_new_cost(near_node, new_node))
|
||||||
|
else:
|
||||||
|
costs.append(float("inf")) # the cost of collision node
|
||||||
|
min_cost = min(costs)
|
||||||
|
|
||||||
|
if min_cost == float("inf"):
|
||||||
|
print("There is no good path.(min_cost is inf)")
|
||||||
|
return None
|
||||||
|
|
||||||
|
min_ind = near_inds[costs.index(min_cost)]
|
||||||
|
new_node = self.steer(self.node_list[min_ind], new_node) # 为new_node设置父节点
|
||||||
|
new_node.cost = min_cost
|
||||||
|
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
def search_best_goal_node(self):
|
||||||
|
'''
|
||||||
|
从可直达目标的节点(单步范围内且中间无障碍物)中,选出从起点到目标距离最短的中间节点
|
||||||
|
'''
|
||||||
|
dist_to_goal_list = [
|
||||||
|
self.calc_dist_to_goal(n.x, n.y) for n in self.node_list
|
||||||
|
]
|
||||||
|
goal_inds = [ # 距离目标单步范围内的节点
|
||||||
|
dist_to_goal_list.index(i) for i in dist_to_goal_list
|
||||||
|
if i <= self.expand_dis
|
||||||
|
]
|
||||||
|
|
||||||
|
safe_goal_inds = [] # 目标单步范围内且中间没有障碍物的节点
|
||||||
|
for goal_ind in goal_inds:
|
||||||
|
t_node = self.steer(self.node_list[goal_ind], self.goal)
|
||||||
|
if self.check_collision(t_node):
|
||||||
|
safe_goal_inds.append(goal_ind)
|
||||||
|
|
||||||
|
if not safe_goal_inds:
|
||||||
|
return None
|
||||||
|
|
||||||
|
safe_goal_costs = [self.node_list[i].cost + # 从起点经过安全节点到目标的距离
|
||||||
|
self.calc_dist_to_goal(self.node_list[i].x, self.node_list[i].y)
|
||||||
|
for i in safe_goal_inds]
|
||||||
|
|
||||||
|
min_cost = min(safe_goal_costs)
|
||||||
|
for i, cost in zip(safe_goal_inds, safe_goal_costs):
|
||||||
|
if cost == min_cost:
|
||||||
|
return i
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_near_nodes(self, new_node):
|
||||||
|
"""
|
||||||
|
找到 new_node 周围一定范围内的树中的节点
|
||||||
|
1) defines a ball centered on new_node
|
||||||
|
2) Returns all nodes of the three that are inside this ball
|
||||||
|
Arguments:
|
||||||
|
---------
|
||||||
|
new_node: Node
|
||||||
|
new randomly generated node, without collisions between
|
||||||
|
its nearest node
|
||||||
|
Returns:
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
List with the indices of the nodes inside the ball of
|
||||||
|
radius r
|
||||||
|
"""
|
||||||
|
nnode = len(self.node_list) + 1
|
||||||
|
r = self.connect_circle_dist * math.sqrt(math.log(nnode) / nnode)
|
||||||
|
# if expand_dist exists, search vertices in a range no more than expand_dist
|
||||||
|
if hasattr(self, 'expand_dis'):
|
||||||
|
r = min(r, self.expand_dis)
|
||||||
|
dist_list = [(node.x - new_node.x) ** 2 + (node.y - new_node.y) ** 2
|
||||||
|
for node in self.node_list]
|
||||||
|
near_inds = [dist_list.index(i) for i in dist_list if i <= r ** 2] #
|
||||||
|
return near_inds
|
||||||
|
|
||||||
|
def rewire(self, new_node, near_inds):
|
||||||
|
"""
|
||||||
|
新加入节点后,为周围的其他节点重新计算最短路径并更新其父节点
|
||||||
|
For each node in near_inds, this will check if it is cheaper to
|
||||||
|
arrive to them from new_node.
|
||||||
|
In such a case, this will re-assign the parent of the nodes in
|
||||||
|
near_inds to new_node.
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
new_node, Node
|
||||||
|
Node randomly added which can be joined to the tree
|
||||||
|
|
||||||
|
near_inds, list of uints
|
||||||
|
A list of indices of the self.new_node which contains
|
||||||
|
nodes within a circle of a given radius.
|
||||||
|
Remark: parent is designated in choose_parent.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for i in near_inds:
|
||||||
|
near_node = self.node_list[i]
|
||||||
|
edge_node = self.steer(new_node, near_node)
|
||||||
|
if not edge_node:
|
||||||
|
continue
|
||||||
|
edge_node.cost = self.calc_new_cost(new_node, near_node)
|
||||||
|
|
||||||
|
no_collision = self.check_collision(edge_node)
|
||||||
|
improved_cost = near_node.cost > edge_node.cost
|
||||||
|
|
||||||
|
if no_collision and improved_cost:
|
||||||
|
for node in self.node_list:
|
||||||
|
if node.parent == near_node:
|
||||||
|
node.parent = edge_node
|
||||||
|
self.node_list[i] = edge_node
|
||||||
|
self.propagate_cost_to_leaves(self.node_list[i])
|
||||||
|
|
||||||
|
def calc_new_cost(self, from_node, to_node):
|
||||||
|
'''
|
||||||
|
从起始位置经过 from_node 到 to_node 的 cost
|
||||||
|
'''
|
||||||
|
d, _ = self.calc_distance_and_angle(from_node, to_node)
|
||||||
|
return from_node.cost + d
|
||||||
|
|
||||||
|
def propagate_cost_to_leaves(self, parent_node):
|
||||||
|
'''
|
||||||
|
(递归算法) 从父节点不断向子节点传播,计算cost
|
||||||
|
'''
|
||||||
|
for node in self.node_list:
|
||||||
|
if node.parent == parent_node:
|
||||||
|
node.cost = self.calc_new_cost(parent_node, node)
|
||||||
|
self.propagate_cost_to_leaves(node)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Start " + __file__)
|
||||||
|
|
||||||
|
# ====Search Path with RRT====
|
||||||
|
obstacle_list = [
|
||||||
|
(5, 5, 1),
|
||||||
|
(3, 6, 2),
|
||||||
|
(3, 8, 2),
|
||||||
|
(3, 10, 2),
|
||||||
|
(7, 5, 2),
|
||||||
|
(9, 5, 2),
|
||||||
|
(8, 10, 1),
|
||||||
|
(6, 12, 1),
|
||||||
|
] # [x,y,size(radius)]
|
||||||
|
|
||||||
|
# Set Initial parameters
|
||||||
|
rrt_star = RRTStar(rand_area=[-2, 15], expand_dis=1, robot_radius=0.8)
|
||||||
|
path = rrt_star.planning(animation=show_animation)
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
print("Cannot find path")
|
||||||
|
else:
|
||||||
|
print("found path!!")
|
||||||
|
|
||||||
|
# Draw final path
|
||||||
|
if show_animation:
|
||||||
|
rrt_star.draw_graph()
|
||||||
|
plt.plot([x for (x, y) in path], [y for (x, y) in path], 'r--')
|
||||||
|
plt.grid(True)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,64 @@
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from scene_utils import control # TODO: 文件名改成Scene.py
|
||||||
|
from navigate import Navigator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
file_name = 'map_5.pkl'
|
||||||
|
if os.path.exists(file_name):
|
||||||
|
with open(file_name, 'rb') as file:
|
||||||
|
map = pickle.load(file)
|
||||||
|
|
||||||
|
control.init_world(1, 3)
|
||||||
|
scene = control.Scene(sceneID=0)
|
||||||
|
scene.reset()
|
||||||
|
|
||||||
|
navigator = Navigator(scene=scene, area_range=[-350, 600, -400, 1450], map=map)
|
||||||
|
goal = (200, 1400)
|
||||||
|
navigator.navigate(goal, animation=False)
|
||||||
|
|
||||||
|
|
||||||
|
# scene.add_walker(1085, 2630, 220)
|
||||||
|
# scene.control_walker([scene.walker_control_generator(0, False, 100, 755, 1900, 180)])
|
||||||
|
|
||||||
|
# print(scene.status)
|
||||||
|
#
|
||||||
|
# control.init_world(1, 3)
|
||||||
|
#
|
||||||
|
# scene = control.Scene(sceneID=0)
|
||||||
|
#
|
||||||
|
# # 实现单顾客领位
|
||||||
|
# scene.reset()
|
||||||
|
# scene.add_walker(0, -200, 220)
|
||||||
|
#
|
||||||
|
# for walker in scene.status.walkers:
|
||||||
|
# print(walker.pose.X)
|
||||||
|
# print('*************************')
|
||||||
|
# time.sleep(3)
|
||||||
|
# for walker in scene.status.walkers:
|
||||||
|
# print(walker.pose)
|
||||||
|
# print('*************************')
|
||||||
|
#
|
||||||
|
# scene.control_walker([scene.walker_control_generator(0, False, 500, 70, 880, 120)])
|
||||||
|
# time.sleep(0.5)
|
||||||
|
# for walker in scene.status.walkers:
|
||||||
|
# print(walker.pose)
|
||||||
|
# print('*************************')
|
||||||
|
# time.sleep(5)
|
||||||
|
# for walker in scene.status.walkers:
|
||||||
|
# print(walker.pose.X)
|
||||||
|
# print('*************************')
|
||||||
|
#
|
||||||
|
# scene.remove_walker(0)
|
||||||
|
# scene.clean_walker()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
|
|
@ -58,3 +58,7 @@ class Bahavior(ptree.behaviour.Behaviour):
|
||||||
|
|
||||||
def terminate(self, new_status: Status) -> None:
|
def terminate(self, new_status: Status) -> None:
|
||||||
return super().terminate(new_status)
|
return super().terminate(new_status)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arg_str(self):
|
||||||
|
return ",".join(self.args)
|
|
@ -3,7 +3,22 @@ from typing import Any
|
||||||
from robowaiter.behavior_lib._base.Act import Act
|
from robowaiter.behavior_lib._base.Act import Act
|
||||||
from robowaiter.llm_client.ask_llm import ask_llm
|
from robowaiter.llm_client.ask_llm import ask_llm
|
||||||
from robowaiter.behavior_tree.utils import load_bt_from_ptml,print_tree_from_root
|
from robowaiter.behavior_tree.utils import load_bt_from_ptml,print_tree_from_root
|
||||||
|
fixed_answers = {
|
||||||
|
"测试VLM:做一杯咖啡":
|
||||||
|
'''
|
||||||
|
测试VLM:做一杯咖啡
|
||||||
|
---
|
||||||
|
{"At(Coffee,Bar)"}
|
||||||
|
'''
|
||||||
|
,
|
||||||
|
"测试VLN:前往桌子":
|
||||||
|
'''
|
||||||
|
测试VLN:前往桌子
|
||||||
|
---
|
||||||
|
{"At(Robot,Table)"}
|
||||||
|
'''
|
||||||
|
,
|
||||||
|
}
|
||||||
class DealChat(Act):
|
class DealChat(Act):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -13,10 +28,16 @@ class DealChat(Act):
|
||||||
chat = self.scene.state['chat_list'].pop()
|
chat = self.scene.state['chat_list'].pop()
|
||||||
|
|
||||||
# 判断是否是测试
|
# 判断是否是测试
|
||||||
if chat =="测试VLN":
|
if chat in fixed_answers.keys():
|
||||||
self.scene.chat_bubble(f"开始测试VLN")
|
sentence,goal = fixed_answers[chat].split("---")
|
||||||
self.scene.state['sub_task_list'].append(("At(Robot, Table)",))
|
sentence = sentence.strip()
|
||||||
|
goal = goal.strip()
|
||||||
|
print(f'机器人回答:{sentence}')
|
||||||
|
goal = eval(goal)
|
||||||
|
print(f'goal:{goal}')
|
||||||
|
|
||||||
|
self.create_sub_task(goal)
|
||||||
|
else:
|
||||||
answer = ask_llm(chat)
|
answer = ask_llm(chat)
|
||||||
print(f"机器人回答:{answer}")
|
print(f"机器人回答:{answer}")
|
||||||
self.scene.chat_bubble(f"机器人回答:{answer}")
|
self.scene.chat_bubble(f"机器人回答:{answer}")
|
||||||
|
@ -24,5 +45,5 @@ class DealChat(Act):
|
||||||
return ptree.common.Status.RUNNING
|
return ptree.common.Status.RUNNING
|
||||||
|
|
||||||
|
|
||||||
def create_sub_task(goal):
|
def create_sub_task(self,goal):
|
||||||
pass
|
self.scene.robot.expand_sub_task_tree(goal)
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import py_trees as ptree
|
||||||
|
from typing import Any
|
||||||
|
from robowaiter.behavior_lib._base.Act import Act
|
||||||
|
from robowaiter.behavior_lib._base.Behavior import Status
|
||||||
|
|
||||||
|
class DelSubTree(Act):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
|
||||||
|
def _update(self) -> ptree.common.Status:
|
||||||
|
sub_task_tree = self.parent
|
||||||
|
print(self.scene.sub_task_seq.children)
|
||||||
|
print(sub_task_tree)
|
||||||
|
self.scene.sub_task_seq.children.remove(sub_task_tree)
|
||||||
|
return Status.RUNNING
|
|
@ -0,0 +1,23 @@
|
||||||
|
import py_trees as ptree
|
||||||
|
from typing import Any
|
||||||
|
from robowaiter.behavior_lib._base.Act import Act
|
||||||
|
from robowaiter.behavior_lib._base.Behavior import Status
|
||||||
|
|
||||||
|
class MakeCoffee(Act):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cond_sets(self):
|
||||||
|
pre = {"At(Robot,Bar)"}
|
||||||
|
add = {"At(Coffee,Bar)"}
|
||||||
|
de = {}
|
||||||
|
return pre,add,de
|
||||||
|
|
||||||
|
def _update(self) -> ptree.common.Status:
|
||||||
|
op_type = 1
|
||||||
|
self.scene.move_task_area(op_type)
|
||||||
|
self.scene.op_task_execute(op_type)
|
||||||
|
self.scene.state["condition_set"].add("At(Coffee,Bar)")
|
||||||
|
return Status.RUNNING
|
|
@ -1,22 +1,15 @@
|
||||||
import py_trees as ptree
|
import py_trees as ptree
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from robowaiter.behavior_lib._base.Act import Act
|
from robowaiter.behavior_lib._base.Act import Act
|
||||||
|
from robowaiter.algos.navigate.DstarLite.navigate import Navigator
|
||||||
class MoveTo(Act):
|
class MoveTo(Act):
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
def setup(self, **kwargs: Any) -> None:
|
|
||||||
return super().setup(**kwargs)
|
|
||||||
|
|
||||||
def initialise(self) -> None:
|
|
||||||
return super().initialise()
|
|
||||||
|
|
||||||
def _update(self) -> ptree.common.Status:
|
def _update(self) -> ptree.common.Status:
|
||||||
print('Start checking IsChatting...')
|
navigator = Navigator(scene=self.scene, area_range=[-350, 600, -400, 1450], map=self.scene.state["map"]["2d"])
|
||||||
return ptree.common.Status.SUCCESS
|
goal = self.scene.state['map']['obj_pos'][self.args[0]]
|
||||||
|
navigator.navigate(goal, animation=False)
|
||||||
def terminate(self, new_status: ptree.common.Status) -> None:
|
self.scene.state['condition_set'].add('At(Robot,Table)')
|
||||||
return super().terminate(new_status)
|
return ptree.common.Status.RUNNING
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,24 @@ class At(Cond):
|
||||||
can_be_expanded = True
|
can_be_expanded = True
|
||||||
num_params = 2
|
num_params = 2
|
||||||
valid_params = '''
|
valid_params = '''
|
||||||
Coffee, Table
|
Coffee, Bar
|
||||||
|
Robot, Bar
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self,*args):
|
def __init__(self,*args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _update(self) -> ptree.common.Status:
|
def _update(self) -> ptree.common.Status:
|
||||||
# if self.scene.status?
|
# if self.scene.status?
|
||||||
if self.scene.state['chat_list'] == []:
|
arg_str = self.arg_str
|
||||||
return ptree.common.Status.FAILURE
|
|
||||||
else:
|
if f'At({arg_str})' in self.scene.state["condition_set"]:
|
||||||
return ptree.common.Status.SUCCESS
|
return ptree.common.Status.SUCCESS
|
||||||
|
else:
|
||||||
|
return ptree.common.Status.FAILURE
|
||||||
|
|
||||||
|
# if self.scene.state['chat_list'] == []:
|
||||||
|
# return ptree.common.Status.FAILURE
|
||||||
|
# else:
|
||||||
|
# return ptree.common.Status.SUCCESS
|
||||||
|
|
|
@ -8,7 +8,7 @@ class HasSubTask(Cond):
|
||||||
|
|
||||||
def _update(self) -> ptree.common.Status:
|
def _update(self) -> ptree.common.Status:
|
||||||
# if self.scene.status?
|
# if self.scene.status?
|
||||||
if self.scene.state['chat_list'] == []:
|
if self.scene.sub_task_seq.children == []:
|
||||||
return ptree.common.Status.FAILURE
|
return ptree.common.Status.FAILURE
|
||||||
else:
|
else:
|
||||||
return ptree.common.Status.SUCCESS
|
return ptree.common.Status.SUCCESS
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import copy
|
import copy
|
||||||
import random
|
import random
|
||||||
from opt_bt_expansion.BehaviorTree import Leaf,ControlBT
|
from robowaiter.behavior_tree.obtea.BehaviorTree import Leaf,ControlBT
|
||||||
|
|
||||||
class CondActPair:
|
class CondActPair:
|
||||||
def __init__(self, cond_leaf,act_leaf):
|
def __init__(self, cond_leaf,act_leaf):
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
from robowaiter.robot.robot import Robot
|
|
||||||
from robowaiter.scene import task_map
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action
|
from robowaiter.behavior_tree.obtea.OptimalBTExpansionAlgorithm import Action
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from robowaiter.behavior_tree.obtea.BehaviorTree import Leaf,ControlBT # 行为结点类:叶子结点和非叶子节点
|
|
||||||
from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm,state_transition # 调用最优行为树扩展算法
|
from robowaiter.behavior_tree.obtea.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm,state_transition # 调用最优行为树扩展算法
|
||||||
from robowaiter.behavior_tree.obtea.tools import print_action_data_table,BTTest
|
|
||||||
from robowaiter.behavior_tree.obtea.examples import MoveBtoB_num,MoveBtoB,Cond2BelongsToCond3 # 导入三个例子
|
|
||||||
from robowaiter.behavior_tree.obtea.examples import *
|
from robowaiter.behavior_tree.obtea.examples import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,7 +99,7 @@ if __name__ == '__main__' :
|
||||||
ptml_string = algo.process(goal)
|
ptml_string = algo.process(goal)
|
||||||
print(ptml_string)
|
print(ptml_string)
|
||||||
|
|
||||||
file_name = "MakeCoffee"
|
file_name = "sub_task"
|
||||||
with open(f'./{file_name}.ptml', 'w') as file:
|
with open(f'./{file_name}.ptml', 'w') as file:
|
||||||
file.write(ptml_string)
|
file.write(ptml_string)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import random
|
import random
|
||||||
from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm
|
from robowaiter.behavior_tree.obtea.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,12 @@ def format_trans_to_bracket(file_path: str) -> str:
|
||||||
if not os.path.exists(file_path):
|
if not os.path.exists(file_path):
|
||||||
raise FileNotFoundError("Given a fault ptml path: {}".format(file_path))
|
raise FileNotFoundError("Given a fault ptml path: {}".format(file_path))
|
||||||
|
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
f = file.read()
|
||||||
|
if "{" in f:
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
|
||||||
def counter_(input:str) -> int:
|
def counter_(input:str) -> int:
|
||||||
length = 0
|
length = 0
|
||||||
for i in range(len(input)):
|
for i in range(len(input)):
|
||||||
|
@ -80,6 +86,7 @@ def format_trans_to_bracket(file_path: str) -> str:
|
||||||
raise TabError('Tab length in ptml file should be 4.')
|
raise TabError('Tab length in ptml file should be 4.')
|
||||||
return length
|
return length
|
||||||
|
|
||||||
|
|
||||||
with open(file_path, 'r') as file:
|
with open(file_path, 'r') as file:
|
||||||
ptml_new = ''
|
ptml_new = ''
|
||||||
ptml_tab = file.readlines()
|
ptml_tab = file.readlines()
|
||||||
|
|
|
@ -14,6 +14,19 @@ def load_bt_from_ptml(scene, ptml_path, behavior_lib_path):
|
||||||
# print(ptree.display.unicode_tree(root=bt.root, show_status=True))
|
# print(ptree.display.unicode_tree(root=bt.root, show_status=True))
|
||||||
return bt
|
return bt
|
||||||
|
|
||||||
|
def load_bt_from_ptml_str(scene, ptml_path, behavior_lib_path):
|
||||||
|
ptml_bt = ptmlCompiler.load(scene, ptml_path, behavior_lib_path)
|
||||||
|
bt = ptree.trees.BehaviourTree(ptml_bt)
|
||||||
|
|
||||||
|
with open(ptml_path, 'r') as f:
|
||||||
|
ptml = f.read()
|
||||||
|
|
||||||
|
print(f'BT loaded:')
|
||||||
|
print_tree_from_root(bt.root)
|
||||||
|
# print(ptree.display.unicode_tree(root=bt.root, show_status=True))
|
||||||
|
return bt
|
||||||
|
|
||||||
|
|
||||||
def print_tree_from_root(node, indent=0):
|
def print_tree_from_root(node, indent=0):
|
||||||
"""
|
"""
|
||||||
Recursively prints the tree, each child with increased indentation.
|
Recursively prints the tree, each child with increased indentation.
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import os
|
|
||||||
import py_trees as ptree
|
|
||||||
|
|
||||||
from robowaiter.behavior_tree.utils import load_bt_from_ptml,find_node_by_name,print_tree_from_root
|
|
||||||
import io
|
import io
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
|
from robowaiter.behavior_tree.utils import load_bt_from_ptml,find_node_by_name,print_tree_from_root
|
||||||
|
from robowaiter.behavior_tree.obtea.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm,state_transition # 调用最优行为树扩展算法
|
||||||
|
from robowaiter.behavior_tree.obtea.opt_bt_exp_main import BTOptExpInterface
|
||||||
|
|
||||||
|
from robowaiter.behavior_lib.act.DelSubTree import DelSubTree
|
||||||
|
from robowaiter.behavior_lib._base.Sequence import Sequence
|
||||||
|
|
||||||
|
|
||||||
class Robot(object):
|
class Robot(object):
|
||||||
scene = None
|
scene = None
|
||||||
response_frequency = 1
|
response_frequency = 1
|
||||||
|
@ -16,6 +20,7 @@ class Robot(object):
|
||||||
self.next_response_time = self.response_frequency
|
self.next_response_time = self.response_frequency
|
||||||
self.step_num = 0
|
self.step_num = 0
|
||||||
self.last_tick_output = ""
|
self.last_tick_output = ""
|
||||||
|
self.action_list = None
|
||||||
|
|
||||||
def set_scene(self,scene):
|
def set_scene(self,scene):
|
||||||
self.scene = scene
|
self.scene = scene
|
||||||
|
@ -27,7 +32,43 @@ class Robot(object):
|
||||||
sub_task_seq = sub_task_place_holder.parent
|
sub_task_seq = sub_task_place_holder.parent
|
||||||
sub_task_seq.children.pop()
|
sub_task_seq.children.pop()
|
||||||
self.scene.sub_task_seq = sub_task_seq
|
self.scene.sub_task_seq = sub_task_seq
|
||||||
|
print(self.scene.sub_task_seq)
|
||||||
|
|
||||||
|
def expand_sub_task_tree(self,goal):
|
||||||
|
if self.action_list is None:
|
||||||
|
self.action_list = self.collect_action_nodes()
|
||||||
|
print(f"首次运行行为树扩展算法,收集到{len(self.action_list)}个有效动作")
|
||||||
|
|
||||||
|
algo = BTOptExpInterface(self.action_list)
|
||||||
|
|
||||||
|
ptml_string = algo.process(goal)
|
||||||
|
|
||||||
|
file_name = "sub_task"
|
||||||
|
file_path = f'./{file_name}.ptml'
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
file.write(ptml_string)
|
||||||
|
|
||||||
|
sub_task_bt = load_bt_from_ptml(self.scene, file_path,self.behavior_lib_path)
|
||||||
|
|
||||||
|
# 加入删除子树的节点
|
||||||
|
seq = Sequence(name="Sequence", memory=False)
|
||||||
|
seq.children.append(sub_task_bt.root)
|
||||||
|
del_sub_tree = DelSubTree()
|
||||||
|
del_sub_tree.set_scene(self.scene)
|
||||||
|
seq.children.append(del_sub_tree)
|
||||||
|
|
||||||
|
self.scene.sub_task_seq.children.append(seq)
|
||||||
|
print("当前行为树为:")
|
||||||
|
print_tree_from_root(self.bt.root)
|
||||||
|
|
||||||
|
def collect_action_nodes(self):
|
||||||
|
action_list = [
|
||||||
|
Action(name='MakeCoffee', pre={'At(Robot,CoffeeMachine)'},
|
||||||
|
add={'At(Coffee,Bar)'}, del_set=set(), cost=1),
|
||||||
|
Action(name='MoveTo(Table)', pre={'At(Robot,Bar)'},
|
||||||
|
add={'At(Robot,Table)'}, del_set=set(), cost=1),
|
||||||
|
]
|
||||||
|
return action_list
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
if self.scene.time > self.next_response_time:
|
if self.scene.time > self.next_response_time:
|
||||||
|
|
|
@ -43,6 +43,7 @@ class Scene:
|
||||||
"chat_list": [], # 未处理的顾客的对话, (顾客的位置,顾客对话的内容)
|
"chat_list": [], # 未处理的顾客的对话, (顾客的位置,顾客对话的内容)
|
||||||
"sub_goal_list": [], # 子目标列表
|
"sub_goal_list": [], # 子目标列表
|
||||||
"status": None, # 仿真器中的观测信息,见下方详细解释
|
"status": None, # 仿真器中的观测信息,见下方详细解释
|
||||||
|
"condition_set": set()
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
status:
|
status:
|
||||||
|
@ -62,13 +63,13 @@ class Scene:
|
||||||
self.use_offset = True
|
self.use_offset = True
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.time = 0
|
self.time = 0
|
||||||
|
self.sub_task_seq = None
|
||||||
|
|
||||||
# init robot
|
# init robot
|
||||||
if robot:
|
if robot:
|
||||||
robot.set_scene(self)
|
robot.set_scene(self)
|
||||||
robot.load_BT()
|
robot.load_BT()
|
||||||
self.robot = robot
|
self.robot = robot
|
||||||
self.sub_task_seq = None
|
|
||||||
|
|
||||||
# myx op
|
# myx op
|
||||||
self.op_dialog = ["","制作咖啡","倒水","夹点心","拖地","擦桌子","关闭窗帘","关筒灯","开大厅灯","搬椅子","打开窗帘","关大厅灯","开筒灯"]
|
self.op_dialog = ["","制作咖啡","倒水","夹点心","拖地","擦桌子","关闭窗帘","关筒灯","开大厅灯","搬椅子","打开窗帘","关大厅灯","开筒灯"]
|
||||||
|
|
|
@ -18,7 +18,7 @@ class SceneOT(Scene):
|
||||||
super().__init__(robot)
|
super().__init__(robot)
|
||||||
# 在这里加入场景中发生的事件
|
# 在这里加入场景中发生的事件
|
||||||
self.event_list = [
|
self.event_list = [
|
||||||
(5,self.give_coffee) # (事件发生的时间,事件函数)
|
(5,self.create_chat_event("给我做一杯咖啡")) # (事件发生的时间,事件函数)
|
||||||
]
|
]
|
||||||
|
|
||||||
def _reset(self):
|
def _reset(self):
|
||||||
|
@ -31,7 +31,3 @@ class SceneOT(Scene):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def give_coffee(self):
|
|
||||||
self.chat_bubble('顾客说:给我一杯咖啡')
|
|
||||||
self.state['chat_list'].append('给我一杯咖啡')
|
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,18 @@ from robowaiter.scene.scene import Scene
|
||||||
class SceneVLM(Scene):
|
class SceneVLM(Scene):
|
||||||
def __init__(self, robot):
|
def __init__(self, robot):
|
||||||
super().__init__(robot)
|
super().__init__(robot)
|
||||||
|
# 在这里加入场景中发生的事件, (事件发生的时间,事件函数)
|
||||||
|
self.event_list = [
|
||||||
|
(5, self.create_chat_event("测试VLM:做一杯咖啡")),
|
||||||
|
]
|
||||||
|
|
||||||
def _reset(self):
|
def _reset(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _run(self, op_type=1):
|
# def _run(self, op_type=1):
|
||||||
|
# self.move_task_area(op_type)
|
||||||
self.move_task_area(op_type)
|
# self.op_task_execute(op_type)
|
||||||
self.op_task_execute(op_type)
|
|
||||||
|
|
||||||
def _step(self):
|
def _step(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
开始条件:监测到顾客靠近
|
开始条件:监测到顾客靠近
|
||||||
结束条件:完成领位,语音:“请问您想喝点什么?”,并等待下一步指令
|
结束条件:完成领位,语音:“请问您想喝点什么?”,并等待下一步指令
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from robowaiter.scene.scene import Scene,init_world # TODO: 文件名改成Scene.py
|
||||||
|
|
||||||
from robowaiter.scene.scene import Scene
|
from robowaiter.scene.scene import Scene
|
||||||
|
|
||||||
|
@ -11,32 +20,15 @@ from robowaiter.scene.scene import Scene
|
||||||
class SceneVLN(Scene):
|
class SceneVLN(Scene):
|
||||||
def __init__(self, robot):
|
def __init__(self, robot):
|
||||||
super().__init__(robot)
|
super().__init__(robot)
|
||||||
|
# 在这里加入场景中发生的事件, (事件发生的时间,事件函数)
|
||||||
|
self.event_list = [
|
||||||
|
(5, self.create_chat_event("测试VLN:前往桌子")),
|
||||||
|
]
|
||||||
|
|
||||||
def _reset(self):
|
def _reset(self):
|
||||||
self.reset_sim()
|
file_name = './robowaiter/algos/navigate/DstarLite/map_5.pkl'
|
||||||
|
with open(file_name, 'rb') as file:
|
||||||
|
map = pickle.load(file)
|
||||||
|
|
||||||
self.add_walker(1085, 2630, 220)
|
self.state['map']['2d'] = map
|
||||||
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
|
self.state['map']['obj_pos']['Table'] = np.array((-100, 700))
|
||||||
|
|
||||||
def _run(self):
|
|
||||||
# 实现单顾客领位
|
|
||||||
self.add_walker(1085, 2630, 220)
|
|
||||||
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
|
|
||||||
|
|
||||||
# todo: 监测到顾客靠近,打招呼,对话,识别获取空闲餐桌位置
|
|
||||||
# 可以使用scene.chat_bubble(message)函数实现对话
|
|
||||||
|
|
||||||
"""
|
|
||||||
scene.walk_to(your_free_table_location)
|
|
||||||
time.sleep(5)
|
|
||||||
scene.control_walker([scene.walker_control_generator(your_free_table_location)])
|
|
||||||
"""
|
|
||||||
|
|
||||||
reach = True
|
|
||||||
if reach:
|
|
||||||
self.chat_bubble("请问您想喝点什么?")
|
|
||||||
|
|
||||||
print(self.status.walkers)
|
|
||||||
|
|
||||||
def _step(self):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from robowaiter import Robot, task_map
|
from robowaiter import Robot, task_map
|
||||||
|
|
||||||
TASK_NAME = 'GQA'
|
TASK_NAME = 'VLN'
|
||||||
|
|
||||||
# create robot
|
# create robot
|
||||||
project_path = "./robowaiter"
|
project_path = "./robowaiter"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
selector{
|
||||||
|
cond At(Robot,Table)
|
||||||
|
selector{
|
||||||
|
cond At(Robot,Bar)
|
||||||
|
act MoveTo(Table)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue