This commit is contained in:
Caiyishuai 2023-11-22 17:58:35 +08:00
commit 50d7110f5f
107 changed files with 752 additions and 3343 deletions

143
README.md
View File

@ -14,162 +14,39 @@ pip install -e .
### 快速入门
1. 安装UE及Harix插件打开默认项目并运行
2. 运行 run_robowaiter.py 文件即可实现机器人控制端与仿真器的交互
2. 运行 tasks 文件夹下的任意场景即可实现机器人控制端与仿真器的交互
# 运行流程介绍
run_robowaiter.py 入口文件如下:
```python
import os
from robowaiter import Robot, task_map
# 代码框架介绍
TASK_NAME = 'GQA'
# create robot
project_path = "./robowaiter"
ptml_path = os.path.join(project_path, 'robot/Default.ptml')
behavior_lib_path = os.path.join(project_path, 'behavior_lib')
robot = Robot(ptml_path,behavior_lib_path)
# create task
task = task_map[TASK_NAME](robot)
task.reset()
task.run()
```
## Robot
Robot是机器人类包括从ptml加载行为树的方法以及执行行为树的方法等
## task_map
task_map是任务字典通过任务缩写来返回相应的场景类
## tasks
tasks文件夹中存放的场景定义及运行代码。
| 缩写 | 任务 |
|----|---------|
|---------------------|-------------|
| AEM | 主动探索和记忆 |
| GQA | 具身多轮对话 |
| VLN | 视觉语言导航 |
| VLM | 视觉语言操作 |
| OT | 复杂开放任务 |
| AT | 自主任务 |
| CafeDailyOperations | 整体展示:咖啡厅的一天 |
| Interact | 命令行自由交互 |
## Scene
Scene是场景基类task_map返回的任务场景都继承于Scene。
该类实现了一些通用的场景操作接口。
### 场景中物品类别
| ID | Item |
|-----|----------------------|
| 0 | Mug |
| 1 | Banana |
| 2 | Toothpaste |
| 3 | Bread |
| 4 | Softdrink |
| 5 | Yogurt |
| 6 | ADMilk |
| 7 | VacuumCup |
| 8 | Bernachon |
| 9 | BottledDrink |
| 10 | PencilVase |
| 11 | Teacup |
| 12 | Caddy |
| 13 | Dictionary |
| 14 | Cake |
| 15 | Date |
| 16 | Stapler |
| 17 | LunchBox |
| 18 | Bracelet |
| 19 | MilkDrink |
| 20 | CocountWater |
| 21 | Walnut |
| 22 | HamSausage |
| 23 | GlueStick |
| 24 | AdhensiveTape |
| 25 | Calculator |
| 26 | Chess |
| 27 | Orange |
| 28 | Glass |
| 29 | Washbowl |
| 30 | Durian |
| 31 | Gum |
| 32 | Towl |
| 33 | OrangeJuice |
| 34 | Cardcase |
| 35 | RubikCube |
| 36 | StickyNotes |
| 37 | NFCJuice |
| 38 | SpringWater |
| 39 | Apple |
| 40 | Coffee |
| 41 | Gauze |
| 42 | Mangosteen |
| 43 | SesameSeedCake |
| 44 | Glove |
| 45 | Mouse |
| 46 | Kettle |
| 47 | Atomize |
| 48 | Chips |
| 49 | SpongeGourd |
| 50 | Garlic |
| 51 | Potato |
| 52 | Tray |
| 53 | Hemomanometer |
| 54 | TennisBall |
| 55 | ToyDog |
| 56 | ToyBear |
| 57 | TeaTray |
| 58 | Sock |
| 59 | Scarf |
| 60 | ToiletPaper |
| 61 | Milk |
| 62 | Soap |
| 63 | Novel |
| 64 | Watermelon |
| 65 | Tomato |
| 66 | CleansingFoam |
| 67 | CocountMilk |
| 68 | SugarlessGum |
| 69 | MedicalAdhensiveTape |
| 70 | SourMilkDrink |
| 71 | PaperCup |
| 72 | Tissue |
| 73 | YogurtDrink |
| 74 | Newspaper |
| 75 | Box |
| 76 | PaperCupStarbucks |
| 77 | CoffeeMachine |
| 78 | GingerLHand |
| 79 | GingerRHand |
| 80 | Straw |
| 81 | Cake |
| 82 | Tray |
| 83 | Bread |
| 84 | Glass |
| 85 | Door |
| 86 | Mug |
| 87 | Machine |
| 88 | Packaged Coffee |
| 89 | Cube Sugar |
| 90 | Apple |
| 91 | Spoon |
| 92 | Drinks |
| 93 | Drink |
| 94 | Take-Away Cup |
| 95 | Saucer |
| 96 | Trash Bin |
| 97 | Knife |
| 251 | Ginger |
| 252 | Floor |
| 253 | Roof |
| 254 | Wall |
注意78及以后无法使用add_object方法生成
# 调用大模型接口
运行llm_client.py文件调用大模型进行多轮对话。
```shell
python llm_client.py
cd robowaiter/llm_client
python multi_rounds.py
```
输入字符即可等待回答输入end表示对话结束。
输入字符即可等待回答

View File

@ -103,7 +103,7 @@ class Bahavior(ptree.behaviour.Behaviour):
# let behavior node interact with the scene
# let behavior node Interact with the scene
def set_scene(self, scene=None):
if scene:
self.scene = scene

View File

@ -64,6 +64,7 @@ class DealChat(Act):
self.scene.robot.expand_sub_task_tree(goal_set)
def get_object_info(self,**args):
try:
obj = args['obj']

View File

@ -19,4 +19,5 @@ class DelSubTree(Act):
sub_task_tree = self.parent
self.scene.sub_task_seq.children.remove(sub_task_tree)
self.scene.draw_current_bt()
return Status.RUNNING

View File

@ -9,7 +9,7 @@ ok, 有需要您再找我。
做一杯咖啡
好的,我马上做咖啡
create_sub_task
{"goal":"On(Coffee,CoffeeTable)"}00
{"goal":"On(Coffee,CoffeeTable)"}
不用了。
好的,您有需要再跟我说

View File

@ -96,6 +96,8 @@ class Robot(object):
# print_tree_from_root(self.bt.root)
print("行为树扩展完成!")
self.scene.draw_current_bt()
# 获取所有action的pre,add,del列表
def collect_action_nodes(self):
action_list = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -18,6 +18,9 @@ from robowaiter.utils import get_root_path
from sklearn.cluster import DBSCAN
from matplotlib import pyplot as plt
from robowaiter.algos.navigator.dstar_lite import euclidean_distance
from PIL import Image
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
@ -56,7 +59,7 @@ def show_image(camera_data):
# matplotlib中的plt方法 对矩阵d 进行图形绘制,如果 深度相机拍摄的带深度的图片图片名字中有depth信息则转换成黑白图即灰度图
plt.imshow(d, cmap="gray" if "depth" in im.name.lower() else None)
# 图像展示在屏幕上
plt.show()
# plt.show()
return d
@ -68,6 +71,9 @@ class Scene:
signal_event_list = []
# show_bubble = True
event_signal = "None"
# step_interval = 1
# camera_interval = 1.5
output_path = os.path.join(os.path.dirname(__file__), "outputs")
default_state = {
"map": {
@ -113,6 +119,7 @@ class Scene:
self.start_time = time.time()
self.time = 0
self.sub_task_seq = None
os.makedirs(self.output_path,exist_ok=True)
self.show_bubble = True
# 图像分割
@ -129,14 +136,13 @@ class Scene:
with open(file_name, 'rb') as file:
self.map_map_real = pickle.load(file)
# init robot
if robot:
robot.set_scene(self)
robot.load_BT()
self.robot = robot
self.init_robot(robot)
self.robot_changed = False
self.last_event_time = 0
self.last_camera_time = -99999
self.last_step_time = -99999
# 1-7 正常执行, 8-10 控灯操作移动到6, 11-12窗帘操作不需要移动,
self.op_dialog = ["", "制作咖啡", "倒水", "夹点心", "拖地", "擦桌子", "开筒灯", "搬椅子", # 1-7
@ -212,6 +218,14 @@ class Scene:
self.init_algos() # 初始化各种算法类
def init_robot(self, robot):
# init robot
self.robot = robot
if robot:
robot.set_scene(self)
return robot.load_BT()
def init_algos(self):
'''
初始化各种各种算法
@ -250,11 +264,13 @@ class Scene:
# 基类step默认执行行为树tick操作
self.time = time.time() - self.start_time
# if self.time - self.last_step_time > self.step_interval:
self.deal_event()
self.deal_new_event()
self.deal_signal_event()
self._step()
self.robot_changed = self.robot.step()
self.last_step_time = self.time
def deal_new_event(self):
if len(self.new_event_list) > 0:
@ -306,9 +322,7 @@ class Scene:
return set_sub_task
def new_set_goal(self, goal):
g = eval("{'" + goal + "'}")
self.state['chat_list'].append(("Goal", g))
def new_set_goal(self,goal):
self.state['chat_list'].append(("Goal",goal))
@ -1162,6 +1176,9 @@ class Scene:
# print("物体世界偏移的坐标: ", world_offest_coordinates)
return world_coordinates
def ui_func(self,args):
pass
def get_obstacle_point(self, db, scene, map_ratio, update_info_count=0):
# if abs(self.last_take_pic_tim - self.time)<
@ -1176,10 +1193,8 @@ class Scene:
not_key_objs_id = {255, 254, 253, 107, 81}
img_data_segment = get_camera([GrabSim_pb2.CameraName.Head_Segment])
img_data_depth = get_camera([GrabSim_pb2.CameraName.Head_Depth])
img_data_color = get_camera([GrabSim_pb2.CameraName.Head_Color])
img_data_segment,img_data_depth,img_data_color = self.get_cameras()
im_segment = img_data_segment.images[0]
im_depth = img_data_depth.images[0]
im_color = img_data_color.images[0]
@ -1191,6 +1206,7 @@ class Scene:
d_color = np.frombuffer(im_color.data, dtype=im_color.dtype).reshape(
(im_color.height, im_color.width, im_color.channels))
items = img_data_segment.info.split(";")
objs_id = {}
for item in items:
@ -1267,6 +1283,10 @@ class Scene:
plt.axis('off')
plt.title("目标检测")
# self.ui_func(("draw_img","img_label_obj",d_color))
for key, value in object_pixels.items():
if key == 0:
continue
@ -1336,7 +1356,25 @@ class Scene:
plt.text(0, 0.3, f'更新语义信息:{update_info_count}', fontsize=10)
# plt.text(0, 0.1, f'已存语义信息:{self.infoCount}', fontsize=10)
plt.show()
output_path = os.path.join(self.output_path,"vision.png")
plt.savefig(output_path)
# canvas = FigureCanvas(plt.gcf())
# fig = plt.gcf() # 获取当前figure
# image = fig.canvas.print_to_buffer()
# width, height = fig.get_size_inches() # 获取图像的宽度和高度(单位:英寸)
# dpi = fig.dpi # 获取图像的DPI
#
# width_px = int(width * dpi) # 转换为像素
# height_px = int(height * dpi) # 转换为像素
#
# image_pil = Image.frombuffer('RGB', (width, height), image, 'raw')
#
# # 转换为numpy数组
# image_np = np.asarray(image_pil)
self.ui_func(("draw_from_file","img_label_canvas",output_path))
plt.close()
# plt.show()
# return cur_obstacle_world_points
def updateMap(self, points):
@ -1369,10 +1407,12 @@ class Scene:
def get_id_object_world(self, id, scene):
pixels = []
world_points = []
img_data_segment = get_camera([GrabSim_pb2.CameraName.Head_Segment])
img_data_segment,img_data_depth,_ = self.get_cameras()
# img_data_segment = get_camera([GrabSim_pb2.CameraName.Head_Segment])
im_segment = img_data_segment.images[0]
img_data_depth = get_camera([GrabSim_pb2.CameraName.Head_Depth])
# img_data_depth = get_camera([GrabSim_pb2.CameraName.Head_Depth])
im_depth = img_data_depth.images[0]
d_segment = np.frombuffer(im_segment.data, dtype=im_segment.dtype).reshape(
@ -1390,3 +1430,14 @@ class Scene:
for pixel in pixels:
world_points.append(self.transform_co(img_data_depth, pixel[0], pixel[1], d_depth[pixel[0]][pixel[1]][0]))
return world_points
def get_cameras(self):
# if self.time - self.last_camera_time > self.camera_interval:
self.img_data_segment = get_camera([GrabSim_pb2.CameraName.Head_Segment])
time.sleep(0.2)
self.img_data_depth = get_camera([GrabSim_pb2.CameraName.Head_Depth])
time.sleep(0.2)
self.img_data_color = get_camera([GrabSim_pb2.CameraName.Head_Color])
self.last_camera_time = self.time
return self.img_data_segment,self.img_data_depth,self.img_data_color

View File

@ -1,106 +0,0 @@
"""
视觉语言导航
识别顾客NPC靠近打招呼对话领位导航到适合人数的空闲餐桌
开始条件监测到顾客靠近
结束条件完成领位语音请问您想喝点什么并等待下一步指令
"""
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.utils import get_root_path
from robowaiter.algos.navigator.navigate import Navigator
from robowaiter.algos.navigator import test
class SceneVLN(Scene):
def __init__(self, robot):
super().__init__(robot)
# 在这里加入场景中发生的事件, (事件发生的时间,事件函数)
self.event_list = [
(5, self.create_chat_event("测试VLN前往2号桌")),
]
def _reset(self):
root_path = get_root_path()
file_name = os.path.join(root_path,'robowaiter/algos/navigator/map_5.pkl')
with open(file_name, 'rb') as file:
map = pickle.load(file)
self.state['map']['2d'] = map
self.state['map']['obj_pos']['Table'] = np.array((-100, 700))
def _run(self):
file_name = '../../algos/navigator/map_5.pkl'
if os.path.exists(file_name):
with open(file_name, 'rb') as file:
map = pickle.load(file)
# self.init_world(1, 11)
# scene = self.status
navigator = Navigator(scene=self, area_range=[-350, 600, -400, 1450], map=map)
'''场景1: 无行人环境 robot到达指定目标'''
# goal = np.array((-100, 700))
'''场景2: 静止行人环境 robot到达指定目标'''
# scene.clean_walker()
# scene.add_walker(50, 300, 0)
# scene.add_walker(-50, 500, 0)
# goal = np.array((-100, 700))
'''场景3: 移动行人环境 robot到达指定目标'''
self.walk_to(100, 0, -90, dis_limit=10)
self.clean_walker()
self.add_walkers([[50, 300], [-50, 500], [0, 700]])
# scene.add_walker(50, 300, 0)
# scene.add_walker(-50, 500, 0)
# scene.add_walker(0, 700, 0)
self.control_walker(
[self.walker_control_generator(walkerID=0, autowalk=False, speed=50, X=-50, Y=600, Yaw=0)])
self.control_walker(
[self.walker_control_generator(walkerID=1, autowalk=False, speed=50, X=100, Y=150, Yaw=0)])
self.control_walker([self.walker_control_generator(walkerID=2, autowalk=False, speed=50, X=0, Y=0, Yaw=0)])
# goal = (-100, 700)
# goal = (-300)
# goal = (340.0, 900.0)
# goal = (240.0, 1000.0)
# goal = (340.0, 900.0)
goal = (240.0, 1160.0)
'''场景4: 行人自由移动 robot到达指定目标'''
# # TODO: autowalk=True仿真器会闪退 ???
# scene.clean_walker()
# scene.add_walker(50, 300, 0)
# scene.add_walker(-50, 500, 0)
# scene.control_walker([scene.walker_control_generator(walkerID=0, autowalk=True, speed=20, X=0, Y=0, Yaw=0)])
# scene.control_walker([scene.walker_control_generator(walkerID=1, autowalk=True, speed=20, X=0, Y=0, Yaw=0)])
# time.sleep(5)
# goal = np.array((-100, 700))
navigator.navigate(goal, animation=False)
self.clean_walker()
print(self.status.collision) # TODO: 不显示碰撞信息 ???
pass
if __name__ == '__main__':
import os
from robowaiter.robot.robot import Robot
robot = Robot()
# create task
task = SceneVLN(robot)
task.reset()
task.run()

View File

@ -279,7 +279,8 @@ def render_dot_tree(root: behaviour.Behaviour,
name: str = None,
target_directory: str = os.getcwd(),
with_blackboard_variables: bool = False,
with_qualified_names: bool = False):
with_qualified_names: bool = False,
png_only = True):
"""
Render the dot tree to .dot, .svg, .png. files in the current
working directory. These will be named with the root behaviour name.
@ -322,10 +323,16 @@ def render_dot_tree(root: behaviour.Behaviour,
filename_wo_extension_to_convert = root.ins_name if name is None else name
filename_wo_extension = utilities.get_valid_filename(filename_wo_extension_to_convert)
filenames = {}
for extension, writer in {"dot": graph.write, "png": graph.write_png, "svg": graph.write_svg}.items():
if png_only:
write_dict = {"png": graph.write_png}
else:
write_dict = {"dot": graph.write, "png": graph.write_png, "svg": graph.write_svg}
for extension, writer in write_dict.items():
filename = filename_wo_extension + '.' + extension
pathname = os.path.join(target_directory, filename)
print("Writing {}".format(pathname))
writer(pathname)
filenames[extension] = pathname
return filenames
return filenames["png"]

View File

@ -1,17 +1,10 @@
selector
sequence
cond CustomerChatting()
act DealChat()
sequence
cond HasSubTask()
sequence
act SubTaskPlaceHolder()
sequence
cond FocusingCustomer()
act ServeCustomer()
sequence
cond NewCustomerComing()
selector
cond At(Robot,Bar)
act MoveTo(Bar)
act GreetCustomer()
sequence{
cond Holding(Nothing)
sequence{
cond Is(Curtain,Off)
act Turn(Curtain,On)
}
sequence{
cond Is(AC,Off)
act Turn(AC,On)
}}

View File

@ -2,27 +2,28 @@ selector
{
sequence
{
cond CustomerChatting()
cond Chatting()
act DealChat()
} sequence
{
cond HasSubTask()
sequence
{
act SubTaskPlaceHolder()
} sequence
} } sequence
{
cond FocusingCustomer()
act ServeCustomer()
} sequence
{
cond NewCustomerComing()
cond NewCustomer()
selector
{
cond At(Robot,Bar)
act MoveTo(Bar)
} act GreetCustomer()
} sequence
{
cond AnomalyDetected()
act ResolveAnomaly()
}}

View File

@ -3,37 +3,19 @@ ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"a3ac082f-ad46-4cc9-86b4-3e76c3c564d3" [fillcolor=cyan, fontcolor=black, fontsize=20, height=0.01, label=Selector, shape=diamond, style=filled, width=0.01];
"717a1290-56bc-41e1-9b4b-e6df93d40e64" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"a3ac082f-ad46-4cc9-86b4-3e76c3c564d3" -> "717a1290-56bc-41e1-9b4b-e6df93d40e64";
"b3236b13-d5f2-4643-ae7c-6f27bd4ab6f2" [fillcolor=yellow, fontcolor=black, fontsize=20, label="CustomerChatting()", shape=ellipse, style=filled];
"717a1290-56bc-41e1-9b4b-e6df93d40e64" -> "b3236b13-d5f2-4643-ae7c-6f27bd4ab6f2";
"28015225-b7a4-4f38-b26f-4dbe5eea4154" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="DealChat()", shape=box, style=filled];
"717a1290-56bc-41e1-9b4b-e6df93d40e64" -> "28015225-b7a4-4f38-b26f-4dbe5eea4154";
"ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"a3ac082f-ad46-4cc9-86b4-3e76c3c564d3" -> "ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e";
"2f77f976-499c-4601-9d8c-86dd80d66dfa" [fillcolor=yellow, fontcolor=black, fontsize=20, label="HasSubTask()", shape=ellipse, style=filled];
"ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e" -> "2f77f976-499c-4601-9d8c-86dd80d66dfa";
"2428792c-3896-443d-8744-f5e286644fad" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e" -> "2428792c-3896-443d-8744-f5e286644fad";
"4bb7a9c3-521b-408f-b403-1fd25d54b192" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="SubTaskPlaceHolder()", shape=box, style=filled];
"2428792c-3896-443d-8744-f5e286644fad" -> "4bb7a9c3-521b-408f-b403-1fd25d54b192";
"64b2362a-d99e-4e99-8772-ad1419e53a2e" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e" -> "64b2362a-d99e-4e99-8772-ad1419e53a2e";
"fd7124ba-c9d1-4fde-8063-e8337d335121" [fillcolor=yellow, fontcolor=black, fontsize=20, label="FocusingCustomer()", shape=ellipse, style=filled];
"64b2362a-d99e-4e99-8772-ad1419e53a2e" -> "fd7124ba-c9d1-4fde-8063-e8337d335121";
"169ebec9-3645-4fbb-a533-5186a8e5967b" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="ServeCustomer()", shape=box, style=filled];
"64b2362a-d99e-4e99-8772-ad1419e53a2e" -> "169ebec9-3645-4fbb-a533-5186a8e5967b";
"bea12066-ecbd-49b0-8934-efcb2c38b5f5" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e" -> "bea12066-ecbd-49b0-8934-efcb2c38b5f5";
"a847a0cc-c5af-4757-aa8c-8baef72788dc" [fillcolor=yellow, fontcolor=black, fontsize=20, label="NewCustomerComing()", shape=ellipse, style=filled];
"bea12066-ecbd-49b0-8934-efcb2c38b5f5" -> "a847a0cc-c5af-4757-aa8c-8baef72788dc";
"99354f05-1716-46b2-9151-d88eac0a5b27" [fillcolor=cyan, fontcolor=black, fontsize=20, height=0.01, label=Selector, shape=diamond, style=filled, width=0.01];
"bea12066-ecbd-49b0-8934-efcb2c38b5f5" -> "99354f05-1716-46b2-9151-d88eac0a5b27";
"19de10a3-7554-43a3-b892-34cb2e32ab9a" [fillcolor=yellow, fontcolor=black, fontsize=20, label="At(Robot,Bar)", shape=ellipse, style=filled];
"99354f05-1716-46b2-9151-d88eac0a5b27" -> "19de10a3-7554-43a3-b892-34cb2e32ab9a";
"4286d652-c4ef-4522-a7f1-b5c865dcc4c9" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="MoveTo(Bar)", shape=box, style=filled];
"99354f05-1716-46b2-9151-d88eac0a5b27" -> "4286d652-c4ef-4522-a7f1-b5c865dcc4c9";
"a3766a34-5152-4f82-8fba-8bf1f6b3830b" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="GreetCustomer()", shape=box, style=filled];
"bea12066-ecbd-49b0-8934-efcb2c38b5f5" -> "a3766a34-5152-4f82-8fba-8bf1f6b3830b";
"c9a8671e-b0b8-4587-a4de-a094fd678334" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Selector, shape=octagon, style=filled, width=0.01];
"bc597ff6-b3cb-4573-9dea-944fdb38d045" [fillcolor=yellow, fontcolor=black, fontsize=20, label="Holding(Nothing)", shape=ellipse, style=filled];
"c9a8671e-b0b8-4587-a4de-a094fd678334" -> "bc597ff6-b3cb-4573-9dea-944fdb38d045";
"a8e26121-f5cc-4ba5-b98e-86bd84de06d6" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"c9a8671e-b0b8-4587-a4de-a094fd678334" -> "a8e26121-f5cc-4ba5-b98e-86bd84de06d6";
"676d8bda-2c30-44a5-bca2-ade68761403a" [fillcolor=yellow, fontcolor=black, fontsize=20, label="Is(Curtain,Off)", shape=ellipse, style=filled];
"a8e26121-f5cc-4ba5-b98e-86bd84de06d6" -> "676d8bda-2c30-44a5-bca2-ade68761403a";
"c3d096cf-f848-46a3-9497-5398614798e8" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="Turn(Curtain,On)", shape=box, style=filled];
"a8e26121-f5cc-4ba5-b98e-86bd84de06d6" -> "c3d096cf-f848-46a3-9497-5398614798e8";
"400b4104-ff96-4d64-89b2-49dc6e4e3cbd" [fillcolor=orange, fontcolor=black, fontsize=20, height=0.01, label=Sequence, shape=octagon, style=filled, width=0.01];
"c9a8671e-b0b8-4587-a4de-a094fd678334" -> "400b4104-ff96-4d64-89b2-49dc6e4e3cbd";
"3fc48afc-1cef-45f5-826d-2571d7979a2f" [fillcolor=yellow, fontcolor=black, fontsize=20, label="Is(AC,Off)", shape=ellipse, style=filled];
"400b4104-ff96-4d64-89b2-49dc6e4e3cbd" -> "3fc48afc-1cef-45f5-826d-2571d7979a2f";
"47af4df0-8811-45a5-95e2-f0fc9458ddfd" [fillcolor=lawngreen, fontcolor=black, fontsize=20, label="Turn(AC,On)", shape=box, style=filled];
"400b4104-ff96-4d64-89b2-49dc6e4e3cbd" -> "47af4df0-8811-45a5-95e2-f0fc9458ddfd";
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -4,208 +4,100 @@
<!-- Generated by graphviz version 9.0.0 (20230911.1827)
-->
<!-- Title: pastafarianism Pages: 1 -->
<svg width="1699pt" height="417pt"
viewBox="0.00 0.00 1699.49 416.87" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 412.87)">
<svg width="716pt" height="220pt"
viewBox="0.00 0.00 715.70 219.87" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 215.87)">
<title>pastafarianism</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-412.87 1695.49,-412.87 1695.49,4 -4,4"/>
<!-- a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3 -->
<polygon fill="white" stroke="none" points="-4,4 -4,-215.87 711.7,-215.87 711.7,4 -4,4"/>
<!-- c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334 -->
<g id="node1" class="node">
<title>a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3</title>
<polygon fill="cyan" stroke="black" points="495.87,-408.87 412.37,-377.62 495.87,-346.37 579.37,-377.62 495.87,-408.87"/>
<text text-anchor="middle" x="495.87" y="-370.24" font-family="Times New Roman,serif" font-size="20.00">Selector</text>
<title>c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334</title>
<polygon fill="orange" stroke="black" points="383.11,-178.04 383.11,-197.85 345.67,-211.87 292.73,-211.87 255.29,-197.85 255.29,-178.04 292.73,-164.03 345.67,-164.03 383.11,-178.04"/>
<text text-anchor="middle" x="319.2" y="-180.57" font-family="Times New Roman,serif" font-size="20.00">Selector</text>
</g>
<!-- 717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64 -->
<!-- bc597ff6&#45;b3cb&#45;4573&#45;9dea&#45;944fdb38d045 -->
<g id="node2" class="node">
<title>717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64</title>
<polygon fill="orange" stroke="black" points="395.38,-276.54 395.38,-296.35 352.9,-310.37 292.83,-310.37 250.35,-296.35 250.35,-276.54 292.83,-262.53 352.9,-262.53 395.38,-276.54"/>
<text text-anchor="middle" x="322.87" y="-279.07" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
<title>bc597ff6&#45;b3cb&#45;4573&#45;9dea&#45;944fdb38d045</title>
<ellipse fill="yellow" stroke="black" cx="114.2" cy="-104.11" rx="114.2" ry="22.1"/>
<text text-anchor="middle" x="114.2" y="-96.74" font-family="Times New Roman,serif" font-size="20.00">Holding(Nothing)</text>
</g>
<!-- a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3&#45;&gt;717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64 -->
<!-- c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;bc597ff6&#45;b3cb&#45;4573&#45;9dea&#45;944fdb38d045 -->
<g id="edge1" class="edge">
<title>a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3&#45;&gt;717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64</title>
<path fill="none" stroke="black" d="M461.27,-358.78C435.8,-345.66 400.81,-327.62 372.38,-312.97"/>
<polygon fill="black" stroke="black" points="374.07,-309.9 363.58,-308.43 370.87,-316.13 374.07,-309.9"/>
<title>c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;bc597ff6&#45;b3cb&#45;4573&#45;9dea&#45;944fdb38d045</title>
<path fill="none" stroke="black" d="M276.22,-169.79C246.35,-157.87 206.15,-141.82 173.26,-128.69"/>
<polygon fill="black" stroke="black" points="174.86,-125.56 164.27,-125.1 172.26,-132.06 174.86,-125.56"/>
</g>
<!-- ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e -->
<g id="node5" class="node">
<title>ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e</title>
<polygon fill="orange" stroke="black" points="742.38,-276.54 742.38,-296.35 699.9,-310.37 639.83,-310.37 597.35,-296.35 597.35,-276.54 639.83,-262.53 699.9,-262.53 742.38,-276.54"/>
<text text-anchor="middle" x="669.87" y="-279.07" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
</g>
<!-- a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3&#45;&gt;ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e -->
<g id="edge4" class="edge">
<title>a3ac082f&#45;ad46&#45;4cc9&#45;86b4&#45;3e76c3c564d3&#45;&gt;ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e</title>
<path fill="none" stroke="black" d="M530.24,-359C556.02,-345.79 591.68,-327.51 620.51,-312.74"/>
<polygon fill="black" stroke="black" points="621.74,-316.04 629.04,-308.37 618.55,-309.81 621.74,-316.04"/>
</g>
<!-- b3236b13&#45;d5f2&#45;4643&#45;ae7c&#45;6f27bd4ab6f2 -->
<!-- a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6 -->
<g id="node3" class="node">
<title>b3236b13&#45;d5f2&#45;4643&#45;ae7c&#45;6f27bd4ab6f2</title>
<ellipse fill="yellow" stroke="black" cx="125.87" cy="-202.61" rx="125.87" ry="22.1"/>
<text text-anchor="middle" x="125.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">CustomerChatting()</text>
<title>a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6</title>
<polygon fill="orange" stroke="black" points="391.72,-94.2 391.72,-114.02 349.24,-128.03 289.16,-128.03 246.68,-114.02 246.68,-94.2 289.16,-80.19 349.24,-80.19 391.72,-94.2"/>
<text text-anchor="middle" x="319.2" y="-96.74" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
</g>
<!-- 717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64&#45;&gt;b3236b13&#45;d5f2&#45;4643&#45;ae7c&#45;6f27bd4ab6f2 -->
<!-- c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6 -->
<g id="edge2" class="edge">
<title>717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64&#45;&gt;b3236b13&#45;d5f2&#45;4643&#45;ae7c&#45;6f27bd4ab6f2</title>
<path fill="none" stroke="black" d="M278.16,-266.88C250.24,-255.28 213.99,-240.22 183.86,-227.7"/>
<polygon fill="black" stroke="black" points="185.46,-224.58 174.88,-223.97 182.77,-231.04 185.46,-224.58"/>
<title>c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6</title>
<path fill="none" stroke="black" d="M319.2,-163.72C319.2,-156.29 319.2,-147.89 319.2,-139.83"/>
<polygon fill="black" stroke="black" points="322.7,-139.92 319.2,-129.92 315.7,-139.92 322.7,-139.92"/>
</g>
<!-- 28015225&#45;b7a4&#45;4f38&#45;b26f&#45;4dbe5eea4154 -->
<g id="node4" class="node">
<title>28015225&#45;b7a4&#45;4f38&#45;b26f&#45;4dbe5eea4154</title>
<polygon fill="lawngreen" stroke="black" points="376.24,-220.61 269.49,-220.61 269.49,-184.61 376.24,-184.61 376.24,-220.61"/>
<text text-anchor="middle" x="322.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">DealChat()</text>
</g>
<!-- 717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64&#45;&gt;28015225&#45;b7a4&#45;4f38&#45;b26f&#45;4dbe5eea4154 -->
<g id="edge3" class="edge">
<title>717a1290&#45;56bc&#45;41e1&#45;9b4b&#45;e6df93d40e64&#45;&gt;28015225&#45;b7a4&#45;4f38&#45;b26f&#45;4dbe5eea4154</title>
<path fill="none" stroke="black" d="M322.87,-262.22C322.87,-252.88 322.87,-242.01 322.87,-232.2"/>
<polygon fill="black" stroke="black" points="326.37,-232.43 322.87,-222.43 319.37,-232.43 326.37,-232.43"/>
</g>
<!-- 2f77f976&#45;499c&#45;4601&#45;9d8c&#45;86dd80d66dfa -->
<!-- 400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd -->
<g id="node6" class="node">
<title>2f77f976&#45;499c&#45;4601&#45;9d8c&#45;86dd80d66dfa</title>
<ellipse fill="yellow" stroke="black" cx="486.87" cy="-202.61" rx="92.45" ry="22.1"/>
<text text-anchor="middle" x="486.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">HasSubTask()</text>
<title>400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd</title>
<polygon fill="orange" stroke="black" points="562.72,-94.2 562.72,-114.02 520.24,-128.03 460.16,-128.03 417.68,-114.02 417.68,-94.2 460.16,-80.19 520.24,-80.19 562.72,-94.2"/>
<text text-anchor="middle" x="490.2" y="-96.74" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
</g>
<!-- ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;2f77f976&#45;499c&#45;4601&#45;9d8c&#45;86dd80d66dfa -->
<!-- c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd -->
<g id="edge5" class="edge">
<title>ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;2f77f976&#45;499c&#45;4601&#45;9d8c&#45;86dd80d66dfa</title>
<path fill="none" stroke="black" d="M626.96,-266.26C600.92,-254.61 567.48,-239.66 539.8,-227.28"/>
<polygon fill="black" stroke="black" points="541.44,-224.18 530.88,-223.3 538.58,-230.57 541.44,-224.18"/>
<title>c9a8671e&#45;b0b8&#45;4587&#45;a4de&#45;a094fd678334&#45;&gt;400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd</title>
<path fill="none" stroke="black" d="M358,-168.38C382.01,-156.89 413.11,-142.01 439.11,-129.56"/>
<polygon fill="black" stroke="black" points="440.29,-132.88 447.8,-125.4 437.27,-126.56 440.29,-132.88"/>
</g>
<!-- 2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad -->
<!-- 676d8bda&#45;2c30&#45;44a5&#45;bca2&#45;ade68761403a -->
<g id="node4" class="node">
<title>676d8bda&#45;2c30&#45;44a5&#45;bca2&#45;ade68761403a</title>
<ellipse fill="yellow" stroke="black" cx="124.2" cy="-22.1" rx="96.17" ry="22.1"/>
<text text-anchor="middle" x="124.2" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">Is(Curtain,Off)</text>
</g>
<!-- a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6&#45;&gt;676d8bda&#45;2c30&#45;44a5&#45;bca2&#45;ade68761403a -->
<g id="edge3" class="edge">
<title>a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6&#45;&gt;676d8bda&#45;2c30&#45;44a5&#45;bca2&#45;ade68761403a</title>
<path fill="none" stroke="black" d="M274.46,-84.75C246.5,-73.28 210.25,-58.41 180.36,-46.14"/>
<polygon fill="black" stroke="black" points="182.06,-43.06 171.48,-42.5 179.4,-49.53 182.06,-43.06"/>
</g>
<!-- c3d096cf&#45;f848&#45;46a3&#45;9497&#45;5398614798e8 -->
<g id="node5" class="node">
<title>c3d096cf&#45;f848&#45;46a3&#45;9497&#45;5398614798e8</title>
<polygon fill="lawngreen" stroke="black" points="399.57,-40.1 238.82,-40.1 238.82,-4.1 399.57,-4.1 399.57,-40.1"/>
<text text-anchor="middle" x="319.2" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">Turn(Curtain,On)</text>
</g>
<!-- a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6&#45;&gt;c3d096cf&#45;f848&#45;46a3&#45;9497&#45;5398614798e8 -->
<g id="edge4" class="edge">
<title>a8e26121&#45;f5cc&#45;4ba5&#45;b98e&#45;86bd84de06d6&#45;&gt;c3d096cf&#45;f848&#45;46a3&#45;9497&#45;5398614798e8</title>
<path fill="none" stroke="black" d="M319.2,-79.97C319.2,-71.24 319.2,-61.2 319.2,-52.01"/>
<polygon fill="black" stroke="black" points="322.7,-52.11 319.2,-42.11 315.7,-52.11 322.7,-52.11"/>
</g>
<!-- 3fc48afc&#45;1cef&#45;45f5&#45;826d&#45;2571d7979a2f -->
<g id="node7" class="node">
<title>2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad</title>
<polygon fill="orange" stroke="black" points="742.38,-192.7 742.38,-212.52 699.9,-226.53 639.83,-226.53 597.35,-212.52 597.35,-192.7 639.83,-178.69 699.9,-178.69 742.38,-192.7"/>
<text text-anchor="middle" x="669.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
<title>3fc48afc&#45;1cef&#45;45f5&#45;826d&#45;2571d7979a2f</title>
<ellipse fill="yellow" stroke="black" cx="490.2" cy="-22.1" rx="72.3" ry="22.1"/>
<text text-anchor="middle" x="490.2" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">Is(AC,Off)</text>
</g>
<!-- ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad -->
<!-- 400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd&#45;&gt;3fc48afc&#45;1cef&#45;45f5&#45;826d&#45;2571d7979a2f -->
<g id="edge6" class="edge">
<title>ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad</title>
<path fill="none" stroke="black" d="M669.87,-262.22C669.87,-254.79 669.87,-246.39 669.87,-238.33"/>
<polygon fill="black" stroke="black" points="673.37,-238.42 669.87,-228.42 666.37,-238.42 673.37,-238.42"/>
<title>400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd&#45;&gt;3fc48afc&#45;1cef&#45;45f5&#45;826d&#45;2571d7979a2f</title>
<path fill="none" stroke="black" d="M490.2,-79.97C490.2,-72.46 490.2,-63.99 490.2,-55.92"/>
<polygon fill="black" stroke="black" points="493.7,-56.06 490.2,-46.06 486.7,-56.06 493.7,-56.06"/>
</g>
<!-- 64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e -->
<g id="node9" class="node">
<title>64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e</title>
<polygon fill="orange" stroke="black" points="905.38,-192.7 905.38,-212.52 862.9,-226.53 802.83,-226.53 760.35,-212.52 760.35,-192.7 802.83,-178.69 862.9,-178.69 905.38,-192.7"/>
<text text-anchor="middle" x="832.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
</g>
<!-- ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e -->
<g id="edge8" class="edge">
<title>ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e</title>
<path fill="none" stroke="black" d="M709.74,-265.43C731.74,-254.38 759.28,-240.56 782.73,-228.78"/>
<polygon fill="black" stroke="black" points="784.12,-232 791.48,-224.39 780.98,-225.75 784.12,-232"/>
</g>
<!-- bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5 -->
<g id="node12" class="node">
<title>bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5</title>
<polygon fill="orange" stroke="black" points="1384.38,-192.7 1384.38,-212.52 1341.9,-226.53 1281.83,-226.53 1239.35,-212.52 1239.35,-192.7 1281.83,-178.69 1341.9,-178.69 1384.38,-192.7"/>
<text text-anchor="middle" x="1311.87" y="-195.24" font-family="Times New Roman,serif" font-size="20.00">Sequence</text>
</g>
<!-- ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5 -->
<g id="edge11" class="edge">
<title>ff83c742&#45;a2ba&#45;4aa5&#45;8b3a&#45;39ecd7c03b0e&#45;&gt;bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5</title>
<path fill="none" stroke="black" d="M742.57,-276.18C862.55,-260.89 1101.31,-230.45 1228.15,-214.28"/>
<polygon fill="black" stroke="black" points="1228.34,-217.79 1237.82,-213.05 1227.46,-210.84 1228.34,-217.79"/>
</g>
<!-- 4bb7a9c3&#45;521b&#45;408f&#45;b403&#45;1fd25d54b192 -->
<!-- 47af4df0&#45;8811&#45;45a5&#45;95e2&#45;f0fc9458ddfd -->
<g id="node8" class="node">
<title>4bb7a9c3&#45;521b&#45;408f&#45;b403&#45;1fd25d54b192</title>
<polygon fill="lawngreen" stroke="black" points="574.49,-129.44 373.24,-129.44 373.24,-93.44 574.49,-93.44 574.49,-129.44"/>
<text text-anchor="middle" x="473.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">SubTaskPlaceHolder()</text>
<title>47af4df0&#45;8811&#45;45a5&#45;95e2&#45;f0fc9458ddfd</title>
<polygon fill="lawngreen" stroke="black" points="707.7,-40.1 580.7,-40.1 580.7,-4.1 707.7,-4.1 707.7,-40.1"/>
<text text-anchor="middle" x="644.2" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">Turn(AC,On)</text>
</g>
<!-- 2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad&#45;&gt;4bb7a9c3&#45;521b&#45;408f&#45;b403&#45;1fd25d54b192 -->
<!-- 400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd&#45;&gt;47af4df0&#45;8811&#45;45a5&#45;95e2&#45;f0fc9458ddfd -->
<g id="edge7" class="edge">
<title>2428792c&#45;3896&#45;443d&#45;8744&#45;f5e286644fad&#45;&gt;4bb7a9c3&#45;521b&#45;408f&#45;b403&#45;1fd25d54b192</title>
<path fill="none" stroke="black" d="M627.82,-182.48C596.86,-168.4 554.7,-149.22 522.33,-134.49"/>
<polygon fill="black" stroke="black" points="524.12,-131.46 513.57,-130.51 521.22,-137.83 524.12,-131.46"/>
</g>
<!-- fd7124ba&#45;c9d1&#45;4fde&#45;8063&#45;e8337d335121 -->
<g id="node10" class="node">
<title>fd7124ba&#45;c9d1&#45;4fde&#45;8063&#45;e8337d335121</title>
<ellipse fill="yellow" stroke="black" cx="720.87" cy="-111.44" rx="128.52" ry="22.1"/>
<text text-anchor="middle" x="720.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">FocusingCustomer()</text>
</g>
<!-- 64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e&#45;&gt;fd7124ba&#45;c9d1&#45;4fde&#45;8063&#45;e8337d335121 -->
<g id="edge9" class="edge">
<title>64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e&#45;&gt;fd7124ba&#45;c9d1&#45;4fde&#45;8063&#45;e8337d335121</title>
<path fill="none" stroke="black" d="M803.73,-178.42C789.1,-166.77 771.27,-152.57 755.9,-140.33"/>
<polygon fill="black" stroke="black" points="758.36,-137.82 748.36,-134.33 754,-143.3 758.36,-137.82"/>
</g>
<!-- 169ebec9&#45;3645&#45;4fbb&#45;a533&#45;5186a8e5967b -->
<g id="node11" class="node">
<title>169ebec9&#45;3645&#45;4fbb&#45;a533&#45;5186a8e5967b</title>
<polygon fill="lawngreen" stroke="black" points="1020.49,-129.44 867.24,-129.44 867.24,-93.44 1020.49,-93.44 1020.49,-129.44"/>
<text text-anchor="middle" x="943.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">ServeCustomer()</text>
</g>
<!-- 64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e&#45;&gt;169ebec9&#45;3645&#45;4fbb&#45;a533&#45;5186a8e5967b -->
<g id="edge10" class="edge">
<title>64b2362a&#45;d99e&#45;4e99&#45;8772&#45;ad1419e53a2e&#45;&gt;169ebec9&#45;3645&#45;4fbb&#45;a533&#45;5186a8e5967b</title>
<path fill="none" stroke="black" d="M861.74,-178.42C877.67,-165.62 897.45,-149.73 913.6,-136.76"/>
<polygon fill="black" stroke="black" points="915.36,-139.83 920.97,-130.84 910.98,-134.37 915.36,-139.83"/>
</g>
<!-- a847a0cc&#45;c5af&#45;4757&#45;aa8c&#45;8baef72788dc -->
<g id="node13" class="node">
<title>a847a0cc&#45;c5af&#45;4757&#45;aa8c&#45;8baef72788dc</title>
<ellipse fill="yellow" stroke="black" cx="1186.87" cy="-111.44" rx="148.14" ry="22.1"/>
<text text-anchor="middle" x="1186.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">NewCustomerComing()</text>
</g>
<!-- bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;a847a0cc&#45;c5af&#45;4757&#45;aa8c&#45;8baef72788dc -->
<g id="edge12" class="edge">
<title>bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;a847a0cc&#45;c5af&#45;4757&#45;aa8c&#45;8baef72788dc</title>
<path fill="none" stroke="black" d="M1280,-178.88C1263.4,-167.04 1242.97,-152.46 1225.48,-139.99"/>
<polygon fill="black" stroke="black" points="1227.51,-137.14 1217.34,-134.18 1223.44,-142.84 1227.51,-137.14"/>
</g>
<!-- 99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27 -->
<g id="node14" class="node">
<title>99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27</title>
<polygon fill="cyan" stroke="black" points="1436.87,-142.69 1353.37,-111.44 1436.87,-80.19 1520.37,-111.44 1436.87,-142.69"/>
<text text-anchor="middle" x="1436.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">Selector</text>
</g>
<!-- bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27 -->
<g id="edge13" class="edge">
<title>bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27</title>
<path fill="none" stroke="black" d="M1343.73,-178.88C1360.85,-166.67 1382.05,-151.54 1399.9,-138.81"/>
<polygon fill="black" stroke="black" points="1401.55,-141.94 1407.65,-133.28 1397.48,-136.24 1401.55,-141.94"/>
</g>
<!-- a3766a34&#45;5152&#45;4f82&#45;8fba&#45;8bf1f6b3830b -->
<g id="node17" class="node">
<title>a3766a34&#45;5152&#45;4f82&#45;8fba&#45;8bf1f6b3830b</title>
<polygon fill="lawngreen" stroke="black" points="1691.49,-129.44 1538.24,-129.44 1538.24,-93.44 1691.49,-93.44 1691.49,-129.44"/>
<text text-anchor="middle" x="1614.87" y="-104.07" font-family="Times New Roman,serif" font-size="20.00">GreetCustomer()</text>
</g>
<!-- bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;a3766a34&#45;5152&#45;4f82&#45;8fba&#45;8bf1f6b3830b -->
<g id="edge16" class="edge">
<title>bea12066&#45;ecbd&#45;49b0&#45;8934&#45;efcb2c38b5f5&#45;&gt;a3766a34&#45;5152&#45;4f82&#45;8fba&#45;8bf1f6b3830b</title>
<path fill="none" stroke="black" d="M1369.59,-187.32C1413.74,-176.07 1475.98,-159.54 1529.87,-142.69 1538.78,-139.91 1548.16,-136.78 1557.3,-133.61"/>
<polygon fill="black" stroke="black" points="1558.17,-137.02 1566.45,-130.4 1555.85,-130.41 1558.17,-137.02"/>
</g>
<!-- 19de10a3&#45;7554&#45;43a3&#45;b892&#45;34cb2e32ab9a -->
<g id="node15" class="node">
<title>19de10a3&#45;7554&#45;43a3&#45;b892&#45;34cb2e32ab9a</title>
<ellipse fill="yellow" stroke="black" cx="1347.87" cy="-22.1" rx="95.64" ry="22.1"/>
<text text-anchor="middle" x="1347.87" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">At(Robot,Bar)</text>
</g>
<!-- 99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27&#45;&gt;19de10a3&#45;7554&#45;43a3&#45;b892&#45;34cb2e32ab9a -->
<g id="edge14" class="edge">
<title>99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27&#45;&gt;19de10a3&#45;7554&#45;43a3&#45;b892&#45;34cb2e32ab9a</title>
<path fill="none" stroke="black" d="M1414.41,-88.4C1403.11,-77.32 1389.28,-63.74 1377.16,-51.84"/>
<polygon fill="black" stroke="black" points="1379.76,-49.5 1370.17,-44.99 1374.86,-54.49 1379.76,-49.5"/>
</g>
<!-- 4286d652&#45;c4ef&#45;4522&#45;a7f1&#45;b5c865dcc4c9 -->
<g id="node16" class="node">
<title>4286d652&#45;c4ef&#45;4522&#45;a7f1&#45;b5c865dcc4c9</title>
<polygon fill="lawngreen" stroke="black" points="1589.74,-40.1 1461.99,-40.1 1461.99,-4.1 1589.74,-4.1 1589.74,-40.1"/>
<text text-anchor="middle" x="1525.87" y="-14.72" font-family="Times New Roman,serif" font-size="20.00">MoveTo(Bar)</text>
</g>
<!-- 99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27&#45;&gt;4286d652&#45;c4ef&#45;4522&#45;a7f1&#45;b5c865dcc4c9 -->
<g id="edge15" class="edge">
<title>99354f05&#45;1716&#45;46b2&#45;9151&#45;d88eac0a5b27&#45;&gt;4286d652&#45;c4ef&#45;4522&#45;a7f1&#45;b5c865dcc4c9</title>
<path fill="none" stroke="black" d="M1459.32,-88.4C1471.74,-76.21 1487.23,-61.02 1500.13,-48.35"/>
<polygon fill="black" stroke="black" points="1502.42,-51.01 1507.1,-41.51 1497.51,-46.02 1502.42,-51.01"/>
<title>400b4104&#45;ff96&#45;4d64&#45;89b2&#45;49dc6e4e3cbd&#45;&gt;47af4df0&#45;8811&#45;45a5&#45;95e2&#45;f0fc9458ddfd</title>
<path fill="none" stroke="black" d="M529.46,-82.71C551.25,-71.39 578.4,-57.28 600.82,-45.64"/>
<polygon fill="black" stroke="black" points="602.15,-48.89 609.41,-41.17 598.92,-42.68 602.15,-48.89"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -0,0 +1,171 @@
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QTimer, QCoreApplication
import sys
import os
from robowaiter.utils.ui.window import Ui_MainWindow
from robowaiter.utils.basic import get_root_path
from PyQt5.QtCore import QThread
import queue
import numpy as np
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import Qt
# 创建线程安全的队列
scene_queue = queue.Queue()
ui_queue = queue.Queue()
root_path = get_root_path()
class TaskThread(QThread):
def __init__(self, task, scene_cls,robot_cls, *args, **kwargs):
super(TaskThread, self).__init__(*args, **kwargs)
self.task = task
self.scene_cls = scene_cls
self.robot_cls = robot_cls
def run(self):
self.task(self.scene_cls,self.robot_cls)
def run_scene(scene_cls,robot_cls):
scene = scene_cls(robot_cls(),scene_queue,ui_queue)
scene.reset()
scene.run()
#
# try:
# # 机器人系统的主循环
#
# except Exception as e:
# # 打印异常信息到命令行
# print("Robot system error:", str(e))
class UI(QMainWindow, Ui_MainWindow):
scene = None
def __init__(self,scene_cls,robot_cls):
app = QApplication(sys.argv)
MainWindow = QMainWindow()
super(UI, self).__init__(MainWindow)
self.scene_cls = scene_cls
self.robot_cls = robot_cls
self.setupUi(MainWindow) # 初始化UI
# 初始化
self.btn_say.clicked.connect(self.btn_say_on_click)
# 设置一个定时器每隔100ms检查一次队列
timer = QTimer()
timer.setInterval(100) # 设置检查队列的间隔时间
timer.timeout.connect(self.handle_queue_messages) # 当定时器超时时,处理队列中的消息
timer.start() # 启动定时器
MainWindow.show()
thread = TaskThread(run_scene,scene_cls,robot_cls)
thread.start()
sys.exit(app.exec_())
def btn_say_on_click(self):
question = self.edit_say.text()
if question[-1] == ")":
print(f"设置目标:{question}")
self.scene_func(("new_set_goal",question))
# self.scene.new_set_goal(question)
else:
self.scene_func(("customer_say","System",question))
# self.scene.customer_say("System", question)
def scene_func(self,args):
scene_queue.put(args)
def handle_queue_messages(self):
while not ui_queue.empty():
message = ui_queue.get()
function_name = message[0]
function = getattr(self, function_name, None)
args = []
if len(message)>1:
args = message[1:]
result = function(*args)
def setupUi(self, MainWindow):
Ui_MainWindow.setupUi(self,MainWindow)
# dir_path = os.path.dirname(__file__)
# 加载并显示图片
# img_path = os.path.join(root_path, "robowaiter/utils/draw_bt/test.png")
# self.draw_from_file("img_label_bt",img_path)
# self.label.setAlignment(Qt.AlignCenter) # 图片居中显示
def draw_from_file(self,control_name,file_name):
control = getattr(self,control_name,None)
# 加载并显示图片
pixmap = QPixmap(file_name) # 替换为你的图片路径
# self.label.setPixmap(pixmap)
control.setPixmap(self.scale_pixmap_to_label(pixmap, control))
def draw_img(self,control_name,img):
control = getattr(self,control_name,None)
# 加载并显示图片
print(img)
img = self.ndarray_to_qimage(img)
print(img)
pixmap = QPixmap.fromImage(img) # 替换为你的图片路径
# self.label.setPixmap(pixmap)
control.setPixmap(self.scale_pixmap_to_label(pixmap, control))
def draw_canvas(self,control_name,canvas):
control = getattr(self,control_name,None)
# 加载并显示图片
pixmap = QApplication.grabWidget(canvas)
# self.label.setPixmap(pixmap)
control.setPixmap(self.scale_pixmap_to_label(pixmap, control))
def ndarray_to_qimage(self,array: np.ndarray) -> QImage:
# 假设你的ndarray是uint8类型的且是RGB格式三个通道
height, width, channels = array.shape
if channels ==3:
bytes_per_line = channels * width
image = QImage(array.data, width, height, bytes_per_line, QImage.Format_RGB888)
else:
# 对于灰度图像
image = QImage(array.data, width, height, QImage.Format_Grayscale8)
return image
def scale_pixmap_to_label(self, pixmap, label):
# 获取QLabel的大小
label_width = label.width()
label_height = label.height()
# 保持图片的长宽比同时确保图片的一个维度适应QLabel的大小
scaled_pixmap = pixmap.scaledToWidth(label_width, Qt.SmoothTransformation)
if scaled_pixmap.height() > label_height:
scaled_pixmap = pixmap.scaledToHeight(label_height, Qt.SmoothTransformation)
return scaled_pixmap
def set_scene(self, scene):
self.scene = scene
if __name__ == "__main__":
# app = QApplication(sys.argv)
# MainWindow = QMainWindow()
ui = UI()
# ui.setupUi(MainWindow)
# MainWindow.show()
# sys.exit(app.exec_())

View File

@ -0,0 +1,84 @@
"""
交互式场景输入
"""
import tkinter as tk
from robowaiter.utils.ui.pyqt5 import UI
import os
# todo: 接收点单信息,大模型生成任务规划
from robowaiter.scene.scene import Scene
from robowaiter.utils.ui.ui import RobotInterface
from robowaiter.utils.bt.draw import render_dot_tree
class SceneUI(Scene):
scene_queue = None
ui_queue = None
# camera_interval = 4
def __init__(self, robot,scene_queue,ui_queue):
self.scene_queue = scene_queue
self.ui_queue = ui_queue
super().__init__(robot)
# 在这里加入场景中发生的事件
# while True:
# if not self.scene_queue.empty():
# param = self.scene_queue.get()
# # 处理参数...
# self.ui_queue.put(('say',"test"))
def init_robot(self, robot):
# init robot
self.robot = robot
if robot:
robot.set_scene(self)
robot.load_BT()
self.draw_current_bt()
def draw_current_bt(self):
render_dot_tree(self.robot.bt.root,target_directory=self.output_path,name="current_bt")
self.ui_queue.put(('draw_from_file',"img_label_bt", f"{self.output_path}/current_bt.png"))
def ui_func(self,args):
self.ui_queue.put(args)
def _reset(self):
pass
def _step(self):
# print("已运行")
self.handle_queue_messages()
# if len(self.sub_task_seq.children) == 0:
# question = input("请输入指令:")
# if question[-1] == ")":
# print(f"设置目标:{question}")
# self.new_set_goal(question)
# else:
# self.customer_say("System",question)
def handle_queue_messages(self):
while not self.scene_queue.empty():
message = self.scene_queue.get()
function_name = message[0]
function = getattr(self, function_name, None)
args = []
if len(message)>1:
args = message[1:]
result = function(*args)
if __name__ == '__main__':
from robowaiter.robot.robot import Robot
robot = Robot()
ui = UI(SceneUI, Robot)
# create task
# task = SceneUI(robot,ui)

110
robowaiter/utils/ui/ui.py Normal file
View File

@ -0,0 +1,110 @@
import tkinter as tk
from robowaiter.utils.basic import get_root_path
from tkinter import PhotoImage
import os
root_path = get_root_path()
class Robot:
def __init__(self):
self.is_running = False
def start(self):
if not self.is_running:
self.is_running = True
# 在这里添加启动机器人的逻辑代码
print("机器人正在启动...")
def stop(self):
if self.is_running:
self.is_running = False
# 在这里添加停止机器人的逻辑代码
print("机器人正在停止...")
def get_status(self):
if self.is_running:
return "运行中"
else:
return "已停止"
class RobotInterface:
def __init__(self, master):
self.master = master
# self.robot = Robot() # 假设这里有一个名为Robot的类表示机器人系统
# 创建启动机器人按钮
self.start_button = tk.Button(master, text="启动机器人", command=self.start_robot)
self.start_button.pack()
# 创建停止机器人按钮
self.stop_button = tk.Button(master, text="停止机器人", command=self.stop_robot)
self.stop_button.pack()
# 创建显示机器人状态的标签
self.status_label = tk.Label(master, text="机器人状态:")
self.status_label.pack()
# 创建用于显示图片的三个标签
self.image_label1 = tk.Label(master)
self.image_label1.pack()
self.image_label2 = tk.Label(master)
self.image_label2.pack()
self.image_label3 = tk.Label(master)
self.image_label3.pack()
# dir_path = os.path.dirname(__file__)
# # 加载图片
# self.image1 = PhotoImage(file=os.path.join(dir_path,"1.png")) # 替换为实际图片路径
# self.image2 = PhotoImage(file=os.path.join(dir_path,"2.png")) # 替换为实际图片路径
# self.image3 = PhotoImage(file=os.path.join(dir_path,"3.png")) # 替换为实际图片路径
#
# # 显示第一张图片
# self.show_image(1)
def display_binary_image(self,data):
# 创建一个Tkinter窗口
# 创建一个像素画布
canvas = tk.Canvas(self.master, width=len(data[0]), height=len(data), bg='white')
canvas.pack()
# 在画布上绘制像素点
for i in range(len(data)):
for j in range(len(data[0])):
if data[i][j] == 1:
canvas.create_rectangle(j, i, j + 1, i + 1, fill='black')
def show_image(self, image_number):
if image_number == 1:
self.image_label1.config(image=self.image1)
elif image_number == 2:
self.image_label2.config(image=self.image2)
elif image_number == 3:
self.image_label3.config(image=self.image3)
else:
print("无效的图片编号")
def start_robot(self):
self.robot.start() # 假设Robot类有一个start方法来启动机器人
self.update_status("机器人已启动。")
def stop_robot(self):
self.robot.stop() # 假设Robot类有一个stop方法来停止机器人
self.update_status("机器人已停止。")
def update_status(self, status):
self.status_label.config(text="机器人状态: " + status)
if __name__ == '__main__':
root = tk.Tk()
interface = RobotInterface(root)
binary_image = [
[0, 1, 0],
[1, 1, 1],
[0, 1, 0]
]
interface.display_binary_image(binary_image)
root.mainloop()

View File

@ -0,0 +1 @@
pyuic5 -x window.ui -o window.py

View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'window.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(960, 1080)
MainWindow.setAutoFillBackground(True)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.edit_say = QtWidgets.QLineEdit(self.centralwidget)
self.edit_say.setGeometry(QtCore.QRect(450, 30, 221, 31))
self.edit_say.setObjectName("edit_say")
self.btn_say = QtWidgets.QPushButton(self.centralwidget)
self.btn_say.setGeometry(QtCore.QRect(680, 40, 75, 23))
self.btn_say.setObjectName("btn_say")
self.img_label_canvas = QtWidgets.QLabel(self.centralwidget)
self.img_label_canvas.setGeometry(QtCore.QRect(0, 160, 811, 441))
self.img_label_canvas.setStyleSheet("border: 2px solid black;")
self.img_label_canvas.setText("")
self.img_label_canvas.setObjectName("img_label_canvas")
self.img_label_bt = QtWidgets.QLabel(self.centralwidget)
self.img_label_bt.setGeometry(QtCore.QRect(0, 670, 811, 311))
self.img_label_bt.setStyleSheet("border: 2px solid black;")
self.img_label_bt.setText("")
self.img_label_bt.setObjectName("img_label_bt")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(400, 630, 111, 16))
self.label_5.setObjectName("label_5")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 960, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.edit_say.setText(_translate("MainWindow", "做一杯咖啡"))
self.btn_say.setText(_translate("MainWindow", "说话"))
self.label_5.setText(_translate("MainWindow", "语义地图"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</width>
<height>1080</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QLineEdit" name="edit_say">
<property name="geometry">
<rect>
<x>450</x>
<y>30</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>做一杯咖啡</string>
</property>
</widget>
<widget class="QPushButton" name="btn_say">
<property name="geometry">
<rect>
<x>680</x>
<y>40</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>说话</string>
</property>
</widget>
<widget class="QLabel" name="img_label_canvas">
<property name="geometry">
<rect>
<x>0</x>
<y>160</y>
<width>811</width>
<height>441</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border: 2px solid black;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="img_label_bt">
<property name="geometry">
<rect>
<x>0</x>
<y>670</y>
<width>811</width>
<height>311</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border: 2px solid black;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>400</x>
<y>630</y>
<width>111</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>语义地图</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>960</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,16 +0,0 @@
import os
from robowaiter import Robot, task_map
TASK_NAME = 'VLM'
# create robot
project_path = "./robowaiter"
ptml_path = os.path.join(project_path, 'robot/Default.ptml')
behavior_lib_path = os.path.join(project_path, 'behavior_lib')
robot = Robot(ptml_path, behavior_lib_path)
# create task
task = task_map[TASK_NAME](robot)
task.reset()
task.run()

View File

@ -1,31 +0,0 @@
selector{
sequence{
cond On(Coffee,CoffeeTable)
cond On(Coffee,CoffeeTable)
}
sequence{
cond Holding(Nothing)
act Make(Coffee)
}
sequence{
cond At(Robot,CoffeeTable)
cond Holding(Coffee)
act PutDown(Coffee,CoffeeTable)
}
sequence{
cond At(Robot,Bar)
cond Holding(Coffee)
act PutDown(Coffee,Bar)
}
sequence{
cond At(Robot,WaterTable)
cond Holding(Coffee)
act PutDown(Coffee,WaterTable)
}
sequence{
cond Holding(Coffee)
act MoveTo(CoffeeTable)
}
}

View File

@ -40,7 +40,7 @@ class SceneAEM(Scene):
map_ratio = self.map_ratio
db = DBSCAN(eps=map_ratio, min_samples=int(map_ratio / 2))
file_name = '../../proto/map_1.pkl'
file_name = '../../robowaiter/proto/map_1.pkl'
if os.path.exists(file_name):
with open(file_name, 'rb') as file:
map = pickle.load(file)
@ -117,7 +117,7 @@ class SceneAEM(Scene):
{"id": f"{i}", "name": f"{cur_objs[i].name}", "location": f"{cur_objs[i].location}",
"height": f"{cur_objs[i].location.Z}"})
with open('../../proto/objs.json', 'w') as file:
with open('../../robowaiter/proto/objs.json', 'w') as file:
json.dump(obj_json_data, file)
# for i in range(-350, 600):

View File

@ -1,10 +1,5 @@
"""
人提出请求机器人完成任务
1. 做咖啡固定动画接收到做咖啡指令走到咖啡机拿杯子操作咖啡机取杯子送到客人桌子上
2. 倒水
3. 夹点心
具体描述设计一套点单规则如菜单包含咖啡点心等按照规则拟造随机的订单在收到订单后通过大模型让机器人输出合理的备餐计划并尝试在模拟环境中按照这个规划实现任务
交互式场景输入
"""
@ -27,9 +22,9 @@ class SubScene(Scene):
question = input("请输入指令:")
if question[-1] == ")":
print(f"设置目标:{question}")
self.set_goal(question)()
self.new_set_goal(question)
else:
self.state['chat_list'].append(question)
self.customer_say("System",question)

View File

@ -3,8 +3,6 @@
测试导航算法的动态避障(Obstacle Detection and Avoidance, ODA)能力
"""
import os
import pickle

View File

@ -1,27 +0,0 @@
import py_trees as ptree
from typing import Any
# _base Behavior
class Bahavior(ptree.behaviour.Behaviour):
scene = None
def __init__(self, name: str, scene):
super().__init__(name)
self.scene = scene
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def _update(self) -> ptree.common.Status:
print("this is just a _base behavior node.")
def update(self) -> ptree.common.Status:
re = self._update()
print(f"{self.__class__.__name__}: {re.value}")
return re
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,23 +0,0 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib._base.Behavior import Bahavior
class Chatting(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def _update(self) -> ptree.common.Status:
# if self.scene.status?
if self.scene.state['chat_list'] == []:
return ptree.common.Status.FAILURE
else:
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,20 +0,0 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib._base.Behavior import Bahavior
class CoffeeCupFound(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print("Start checking IsChatting...")
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,22 +0,0 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib._base.Behavior import Bahavior
class CoffeeCupGrasped(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class CoffeeCupPlaced(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,26 +0,0 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib._base.Behavior import Bahavior
from robowaiter.llm_client.ask_llm import ask_llm
class DealChat(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def _update(self) -> ptree.common.Status:
# if self.scene.status?
chat = self.scene.state['chat_list'].pop()
answer = ask_llm(chat)
print(f"机器人回答:{answer}")
self.scene.chat_bubble(f"机器人回答:{answer}")
return ptree.common.Status.RUNNING
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class DestinationAReached(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class FindCoffeeCup(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class Grasp(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def _update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class GraspCoffeeCup(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class Istask(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class MoveTo(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene, a, b, c, d):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def _update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class PlaceCoffeeCup(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class ReachDestinationA(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class SeqTest(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,21 +0,0 @@
import py_trees as ptree
from typing import Any
class TestTask(ptree.behaviour.Behaviour):
def __init__(self, name: str, scene):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> ptree.common.Status:
print('Start checking IsChatting...')
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,124 +0,0 @@
"""
顶层行为树中的动作与条件节点
"""
from typing import *
import py_trees
from py_trees import common
from py_trees.common import Status
##############################################################
# 条件节点
##############################################################
class IsChatting(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start checking IsChatting...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)
class IsTakingAction(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start checking IsTakingAction...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)
class IsSomethingMore(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start checking IsSomethingMore...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)
##############################################################
# 动作节点
##############################################################
class Chatting(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start executing Chatting...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)
class TakingAction(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start executing TakingAction...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)
class TakingMoreAction(py_trees.behaviour.Behaviour):
def __init__(self, name: str = ""):
super().__init__(name)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)
def initialise(self) -> None:
return super().initialise()
def update(self) -> Status:
print('Start executing TakingMoreAction...')
return common.Status.SUCCESS
def terminate(self, new_status: Status) -> None:
return super().terminate(new_status)

View File

@ -1,66 +0,0 @@
import py_trees
from behavior_library import *
def LoadMainTree() -> py_trees.trees.BehaviourTree:
"""
此方法用于加载固定的顶层行为树不包括实际执行
Args: None
"""
seq_subtree_0 = py_trees.composites.Sequence(
name='seq_subtree_0',
memory=False,
children=[IsChatting(), Chatting()]
)
seq_subtree_1 = py_trees.composites.Sequence(
name='seq_subtree_1',
memory=False,
children=[IsTakingAction(), TakingAction()]
)
seq_subtree_2 = py_trees.composites.Sequence(
name='seq_subtree_2',
memory=False,
children=[IsSomethingMore(), TakingMoreAction()]
)
root = py_trees.composites.Selector(
name='selector_root',
memory=False,
children=[seq_subtree_0, seq_subtree_1, seq_subtree_2]
)
return py_trees.trees.BehaviourTree(root)
def LoadSubTree(path: str) -> py_trees.behaviour.Behaviour:
"""
此方法用于从ptml文件中加载行为树不包括实际执行
Args:
-- path: ptml文件的路径
"""
# TODO
pass
if __name__ == '__main__':
btree = LoadMainTree()
def print_tree(tree):
print(py_trees.display.unicode_tree(root=tree.root, show_status=True))
try:
btree.tick_tock(
period_ms=500,
number_of_iterations=py_trees.trees.CONTINUOUS_TICK_TOCK,
pre_tick_handler=None,
post_tick_handler=print_tree
)
except KeyboardInterrupt:
btree.interrupt()

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def joint_test(scene_id=0):
print('------------------joint_test----------------------')
action_list = [[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 36.0, -39.37, 37.2, -92.4, 4.13, -0.62, 0.4],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 36.0, -39.62, 34.75, -94.80, 3.22, -0.26, 0.85],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 32.63, -32.80, 15.15, -110.70, 6.86, 2.36, 0.40],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 28.18, -27.92, 6.75, -115.02, 9.46, 4.28, 1.35],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 4.09, -13.15, -11.97, -107.35, 13.08, 8.58, 3.33]]
for value in action_list:
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.RotateJoints, values=value)
scene = sim_client.Do(action)
for i in range(8, 21): # arm
print(
f"{scene.joints[i].name}:{scene.joints[i].angle} location:{scene.joints[i].location.X},{scene.joints[i].location.Y},{scene.joints[i].location.Z}"
)
print('')
for i in range(5, 10): # Right hand
print(
f"{scene.fingers[i].name} angle:{scene.fingers[i].angle} location:{scene.fingers[i].location[0].X},{scene.fingers[i].location[0].Y},{scene.fingers[i].location[0].Z}"
)
print('----------------------------------------')
time.sleep(0.03)
time.sleep(1)
if __name__ == '__main__':
map_id = 3 # 地图编号: 3: 咖啡厅
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
for i in range(scene_num):
print("------------------", i, "----------------------")
joint_test(i) # 关节控制测试

View File

@ -1,94 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# enconding = utf8
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def control_robot_action(scene_id=0, type=0, action=0, message="你好"):
scene = sim_client.ControlRobot(GrabSim_pb2.ControlInfo(scene=scene_id, type=type, action=action, content=message))
if (scene.info == "action success"):
return True
else:
return False
if __name__ == '__main__':
map_id = 3 # 地图编号: 0空房间 1室内 2:咖啡厅1.0 3: 咖啡厅2.0 4:餐厅 5:养老院 6会议室
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
# 制作咖啡
control_robot_action(0, 0, 1, "开始制作咖啡")
result = control_robot_action(0, 1, 1)
if (result):
control_robot_action(0, 1, 2)
control_robot_action(0, 1, 3)
control_robot_action(0, 1, 4)
else:
control_robot_action(0, 0, 1, "制作咖啡失败")
# 倒水
control_robot_action(0, 0, 1, "开始倒水")
result = control_robot_action(0, 2, 1)
if (result):
control_robot_action(0, 2, 2)
control_robot_action(0, 2, 3)
control_robot_action(0, 2, 4)
control_robot_action(0, 2, 5)
else:
control_robot_action(0, 0, 1, "倒水失败")
# 夹点心
control_robot_action(0, 0, 1, "开始夹点心")
result = control_robot_action(0, 3, 1)
if (result):
control_robot_action(0, 3, 2)
control_robot_action(0, 3, 3)
control_robot_action(0, 3, 4)
control_robot_action(0, 3, 5)
control_robot_action(0, 3, 6)
control_robot_action(0, 3, 7)
else:
control_robot_action(0, 0, 1, "夹点心失败")
# 拖地
control_robot_action(0, 0, 1, "开始拖地")
result = control_robot_action(0, 4, 1)
if (result):
control_robot_action(0, 4, 2)
control_robot_action(0, 4, 3)
control_robot_action(0, 4, 4)
else:
control_robot_action(0, 0, 1, "拖地失败")
# 擦桌子
control_robot_action(0, 0, 1, "开始擦桌子")
result = control_robot_action(0, 5, 1)
if (result):
control_robot_action(0, 5, 2)
control_robot_action(0, 5, 3)
else:
control_robot_action(0, 0, 1, "擦桌子失败")

View File

@ -1,47 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def reset(scene_id=0):
scene = sim_client.Reset(GrabSim_pb2.ResetParams(scene=scene_id))
def show_env_info(scene_id=0):
scene = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
print('------------------show_env_info----------------------')
print(
f"location:{[scene.location.X, scene.location.Y]}, rotation:{scene.rotation.Yaw}\n",
f"joints number:{len(scene.joints)}, fingers number:{len(scene.fingers)}\n", f"objects number: {len(scene.objects)}\n"
f"rotation:{scene.rotation}, timestep:{scene.timestep}\n"
f"timestamp:{scene.timestamp}, collision:{scene.collision}, info:{scene.info}")
if __name__ == '__main__':
map_id = 3 # 地图编号: 3: 咖啡厅
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
for i in range(scene_num):
print("------------------", i, "----------------------")
reset(i) # 场景重置测试
show_env_info(i) # 场景信息测试

View File

@ -1,139 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# enconding = utf8
import sys
import time
import grpc
sys.path.append('./')
sys.path.append('../')
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from robowaiter.proto import GrabSim_pb2_grpc,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(scene_id=0, map_id=0):
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, 600, 100]
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 # 场景数量
print('------------ 初始化加载场景 ------------')
Init()
AcquireAvailableMaps()
SetWorld(map_id, scene_num)
time.sleep(5.0)
for i in range(scene_num):
print('------------ 场景操作 ------------')
Observe(i)
Reset(i)
print('------------ 导航移动 ------------')
navigation_move(i, map_id)

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# enconding = utf8
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def control_robot_action(scene_id=0, type=0, action=0, message="你好"):
scene = sim_client.ControlRobot(GrabSim_pb2.ControlInfo(scene=scene_id, type=type, action=action, content=message))
if (scene.info == "action success"):
return True
else:
return False
if __name__ == '__main__':
map_id = 3 # 地图编号: 0空房间 1室内 2:咖啡厅1.0 3: 咖啡厅2.0 4:餐厅 5:养老院 6会议室
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
# 文字冒泡
control_robot_action(0, 0, 1, "你好,欢迎光临")

View File

@ -1,103 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def joint_test(scene_id=0):
print('------------------joint_test----------------------')
action_list = [[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 36.0, -39.37, 37.2, -92.4, 4.13, -0.62, 0.4],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 36.0, -39.62, 34.75, -94.80, 3.22, -0.26, 0.85],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 32.63, -32.80, 15.15, -110.70, 6.86, 2.36, 0.40],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 28.18, -27.92, 6.75, -115.02, 9.46, 4.28, 1.35],
[0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 4.09, -13.15, -11.97, -107.35, 13.08, 8.58, 3.33]]
for value in action_list:
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.RotateJoints, values=value)
scene = sim_client.Do(action)
for i in range(8, 21): # arm
print(
f"{scene.joints[i].name}:{scene.joints[i].angle} location:{scene.joints[i].location.X},{scene.joints[i].location.Y},{scene.joints[i].location.Z}"
)
print('')
for i in range(5, 10): # Right hand
print(
f"{scene.fingers[i].name} angle:{scene.fingers[i].angle} location:{scene.fingers[i].location[0].X},{scene.fingers[i].location[0].Y},{scene.fingers[i].location[0].Z}"
)
print('----------------------------------------')
time.sleep(0.03)
time.sleep(1)
def gen_obj(scene_id, h=80):
print('------------------gen objs----------------------')
scene = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
ginger_loc = [scene.location.X, scene.location.Y, scene.location.Z]
obj_list = [
GrabSim_pb2.ObjectList.Object(x=ginger_loc[0] - 90, y=ginger_loc[1] + 30, yaw=10, z=h, type=4),
GrabSim_pb2.ObjectList.Object(x=ginger_loc[0] - 80, y=ginger_loc[1] + 31, z=h, type=5),
GrabSim_pb2.ObjectList.Object(x=ginger_loc[0] - 33, y=ginger_loc[1] - 10.5, z=h+20, type=7),
GrabSim_pb2.ObjectList.Object(x=ginger_loc[0] - 70, y=ginger_loc[1] + 33, z=h, type=9),
GrabSim_pb2.ObjectList.Object(x=ginger_loc[0] - 60, y=ginger_loc[1] + 34, z=h, type=13)
]
scene = sim_client.AddObjects(GrabSim_pb2.ObjectList(objects=obj_list, scene=scene_id))
print(scene.collision)
time.sleep(5)
def remove_obj(scene_id=0, id_list=[1]):
print('------------------remove objs----------------------')
remove_obj_list = id_list
scene = sim_client.RemoveObjects(GrabSim_pb2.RemoveList(IDs=remove_obj_list, scene=scene_id))
print(f"remove objects {id_list}. current obj:")
time.sleep(1)
def clean_obj(scene_id=0):
print('------------------clean objs----------------------')
scene = sim_client.CleanObjects(GrabSim_pb2.SceneID(value=scene_id))
def obj_test(scene_id=0):
gen_obj(scene_id)
# remove_obj(scene_id, id_list=[0])
# clean_obj(scene_id)
def grasp_test(hand_id, obj_scene_id, scene_id=0):
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.Grasp, values=[hand_id, obj_scene_id])
scene = sim_client.Do(action)
def release_test(hand_id, scene_id=0):
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.Release, values=[hand_id])
scene = sim_client.Do(action)
if __name__ == '__main__':
map_id = 3 # 地图编号: 3: 咖啡厅
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
for i in range(scene_num):
print("------------------", i, "----------------------")
joint_test(i) # 关节控制测试
obj_test(i) # 物品生成测试
grasp_test(1, 2) # 抓取物品测试
joint_test(i) # 关节控制测试
release_test(1) # 释放物品测试

View File

@ -1,49 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def get_camera(part, scene_id=0):
action = GrabSim_pb2.CameraList(cameras=part, scene=scene_id)
return sim_client.Capture(action)
def show_image(img_data):
im = img_data.images[0]
d = np.frombuffer(im.data, dtype=im.dtype).reshape((im.height, im.width, im.channels))
plt.imshow(d, cmap="gray" if "depth" in im.name.lower() else None)
plt.show()
def camera_test(scene_id=0):
for camera_name in [GrabSim_pb2.CameraName.Head_Color, GrabSim_pb2.CameraName.Head_Depth, GrabSim_pb2.CameraName.Head_Segment]:
img_data = get_camera([camera_name], scene_id)
show_image(img_data)
if __name__ == '__main__':
map_id = 3 # 地图编号: 0空房间 1室内 2:咖啡厅1.0 3: 咖啡厅2.0 4:餐厅 5:养老院 6会议室
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
for i in range(scene_num):
print("------------------", i, "----------------------")
camera_test(i) # 相机操作测试

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import sys
import time
import grpc
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
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 map_test(map_id=0, scene_num=1):
initworld = sim_client.Init(GrabSim_pb2.NUL())
print(sim_client.AcquireAvailableMaps(GrabSim_pb2.NUL()))
initworld = sim_client.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=map_id))
def add_walker(scene_id=0):
"""pose:表示行人的初始位置姿态"""
print('------------------add walker----------------------')
s = sim_client.Observe(GrabSim_pb2.SceneID(value=0))
walker_loc = [[0, 880], [250, 1200], [-55, 750], [70, -200]]
walker_list = []
for i in range(len(walker_loc)):
loc = walker_loc[i] + [0, 600, 100]
action = GrabSim_pb2.Action(scene=scene_id, action=GrabSim_pb2.Action.ActionType.WalkTo, values=loc)
scene = sim_client.Do(action)
print(scene.info)
# 只有可达的位置才能成功初始化行人显示unreachable的地方不能初始化行人
walker_list.append(GrabSim_pb2.WalkerList.Walker(id=i, pose=GrabSim_pb2.Pose(X=loc[0], Y=loc[1], Yaw=90)))
scene = sim_client.AddWalker(GrabSim_pb2.WalkerList(walkers=walker_list, scene=scene_id))
return scene
def control_walkers(scene_id=0):
"""pose:表示行人的终止位置姿态"""
s = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
walker_loc = [[-55, 750], [70, -200], [250, 1200], [0, 880]]
controls = []
for i in range(len(s.walkers)):
loc = walker_loc[i]
is_autowalk = 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 = sim_client.ControlWalkers(GrabSim_pb2.WalkerControls(controls=controls, scene=scene_id))
time.sleep(10)
return scene
def remove_walkers(scene_id=0):
s = sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id))
scene = sim_client.RemoveWalkers(GrabSim_pb2.RemoveList(IDs=[1, 3], scene=scene_id))
time.sleep(2)
return
def clean_walkers(scene_id=0):
scene = sim_client.CleanWalkers(GrabSim_pb2.SceneID(value=scene_id))
return scene
def walker_test(scene_id=0):
add_walker(scene_id)
control_walkers(scene_id)
print(sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id)).walkers)
remove_walkers(scene_id)
print(sim_client.Observe(GrabSim_pb2.SceneID(value=scene_id)).walkers)
clean_walkers(scene_id)
return
if __name__ == '__main__':
map_id = 3 # 地图编号: 3: 咖啡厅
scene_num = 1 # 场景数量
map_test(map_id, scene_num) # 场景加载测试
time.sleep(5)
for i in range(scene_num):
print("------------------", i, "----------------------")
walker_test(i) # 行人控制测试

View File

@ -1 +0,0 @@
conda install pytorch==1.11.0 torchvision==0.12.0 torchaudio==0.11.0 cudatoolkit=11.3 -c pytorch

View File

@ -1,4 +0,0 @@
from . import navigate
from . import dstar_lite

View File

@ -1,510 +0,0 @@
'''
基于两个D* lite的实现
按照原始算法的伪代码
自己写的D* lite
'''
import math
import queue
from functools import partial
import numpy as np
import heapq
from matplotlib import pyplot as plt
def diagonal_distance(start, end): #
return max(abs(start[0] - end[0]), abs(start[1] - end[1]))
def manhattan_distance(start, end): # 曼哈顿距离
return abs(start[0] - end[0]) + abs(start[1] - end[1])
def euclidean_distance(start, end): # 欧式距离
# return np.linalg.norm(start-end)
return math.sqrt((start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2)
def heuristic(start, end, name='euclidean'):
if name == 'diagonal':
return diagonal_distance(start, end)
elif name == 'euclidean':
return euclidean_distance(start, end)
elif name == 'manhattan':
return manhattan_distance(start, end)
else:
raise Exception('Error heuristic name!')
class Priority:
'''
优先级类
'''
def __init__(self, k1, k2):
self.k1 = k1
self.k2 = k2
def __lt__(self, other): # 定义对象的 < 运算
return self.k1 < other.k1 or (self.k1 == other.k1 and self.k2 < other.k2)
def __le__(self, other): # 定于对象的 <= 运算
return self.k1 < other.k1 or (self.k1 == other.k1 and self.k2 <= other.k2)
class Node:
'''
节点类
'''
def __init__(self, pos: (int, int), priority: Priority):
self.pos = pos # X Y
self.priority = priority
def __lt__(self, other): # 定义对象的 < 运算
return self.priority < other.priority
def __le__(self, other): # 定于对象的 <= 运算
return self.priority <= other.priority
class PriorityQueue:
def __init__(self):
self.queue = [] # 节点队列
self.nodes = [] # 节点位置列表
def is_empty(self):
# 队列判空
return len(self.queue) == 0
def top(self):
return self.queue[0].pos
def top_key(self):
if self.is_empty():
return Priority(float('inf'), float('inf'))
return self.queue[0].priority
def pop(self):
# 取出第一个节点
node = heapq.heappop(self.queue)
self.nodes.remove(node.pos)
return node
def insert(self, pos, priority):
# 创建并添加节点
node = Node(pos, priority)
heapq.heappush(self.queue, node)
self.nodes.append(pos)
def remove(self, pos):
# 删除指定节点并重新排序
self.queue = [n for n in self.queue if n.pos != pos]
heapq.heapify(self.queue) # 重排序
self.nodes.remove(pos)
def update(self, pos, priority):
# 更新指定位置的priority值
for n in self.queue:
if n.pos == pos:
n.priority = priority
break
class DStarLite:
def __init__(self,
map: np.array([int, int]), # [X, Y]
area_range, # [x_min, x_max, y_min, y_max] 实际坐标范围
scale_ratio=5, # 地图缩放率
dyna_obs_radius=20 # dyna_obs实际身位半径
):
# self.area_bounds = area
self.map = map
self.background = map
self.X = map.shape[0]
self.Y = map.shape[1]
(self.x_min, self.x_max, self.y_min, self.y_max) = area_range
self.scale_ratio = scale_ratio
self.dyna_obs_list = [] # 动态障碍物位置列表( 当前地图 ) [(x, y)]
self.dyna_obs_radius = math.ceil(dyna_obs_radius/scale_ratio) # dyna_obs缩放后身位半径
# free:0, obs:1, dyna_obs:2
self.idx_to_object = {
0: "free",
1: "obstacle",
2: "dynamic obstacle"
}
self.object_to_idx = dict(zip(self.idx_to_object.values(), self.idx_to_object.keys()))
self.object_to_cost = {
"free": 0,
"obstacle": float('inf'),
"dynamic obstacle": 50
}
self.compute_cost_map()
self.s_start = None # (int,int) 必须是元组(元组可以直接当作矩阵索引)
self.s_goal = None # (int,int)
self.s_last = None # (int,int)
self.U = PriorityQueue()
self.k_m = 0
self.rhs = np.ones((self.X, self.Y)) * np.inf
self.g = self.rhs.copy()
self.path = []
def set_map(self, map_):
'''
设置map cost_map
'''
self.map = map_
self.X = map_.shape[0]
self.Y = map_.shape[1]
self.compute_cost_map()
def reset(self):
'''
(完成一次导航后)
重置 1.环境变量
2.dstar_lite变量
'''
# env reset
self.map = self.background.copy()
self.compute_cost_map()
self.dyna_obs_list.clear()
self.path.clear()
# dstar_lite reset
self.s_start = None
self.s_goal = None
self.s_last = None
self.U = PriorityQueue()
self.k_m = 0
self.rhs = np.ones((self.X, self.Y)) * np.inf
self.g = self.rhs.copy()
def calculate_key(self, s: (int, int)):
'''
计算 位置s key1, key2
:returns
Priority(k1, k2): 可比较的对象
'''
k1 = min(self.g[s], self.rhs[s]) + heuristic(self.s_start, s) + self.k_m
k2 = min(self.g[s], self.rhs[s])
return Priority(k1, k2)
def c(self, u: (int, int), v: (int, int), v_old=None) -> float:
'''
计算节点间的 路径代价 目标位置代价 (目标位置代价为0时采用路径代价)
(因为是终点->起点的扩展方向因此v是nodeu是v扩展的neighbor)
Args:
u: from pos
v: to pos
v_old: 指定的v的类型
'''
if v_old:
obj_cost = self.object_to_cost[v_old]
else:
obj_cost = self.cost_map[v]
if obj_cost > 0:
return obj_cost
return heuristic(u, v)
def contain(self, u: (int, int)):
'''
判断 节点u 是否在队列中
'''
return u in self.U.nodes
def update_vertex(self, u: (int, int)):
'''
判定节点状态, 更新队列
不一致且在队列 --> 更新key
不一致且不在队列 --> 计算key并加入队列
一致且在队列 --> 移出队列
'''
if self.g[u] != self.rhs[u] and self.contain(u):
self.U.update(u, self.calculate_key(u))
elif self.g[u] != self.rhs[u] and not self.contain(u):
self.U.insert(u, self.calculate_key(u))
elif self.g[u] == self.rhs[u] and self.contain(u):
self.U.remove(u)
def compute_shortest_path(self):
'''
计算最短路径
'''
while self.U.top_key() < self.calculate_key(self.s_start) or self.rhs[self.s_start] > self.g[self.s_start]:
u = self.U.top()
k_old = self.U.top_key()
k_new = self.calculate_key(u)
if k_old < k_new:
self.U.update(u, k_new)
elif self.g[u] > self.rhs[u]: # 过一致
self.g[u] = self.rhs[u]
self.U.remove(u)
pred = self.get_neighbors(u)
for s in pred:
if s != self.s_goal:
self.rhs[s] = min(self.rhs[s], self.c(s, u) + self.g[u])
self.update_vertex(s)
else: # 欠一致
g_old = self.g[u]
self.g[u] = float('inf')
pred = self.get_neighbors(u)
for s in pred + [u]:
if self.rhs[s] == self.c(s, u) + g_old:
if s != self.s_goal:
succ = self.get_neighbors(s)
self.rhs[s] = min([self.c(s, s_) + self.g[s_] for s_ in succ])
self.update_vertex(s)
def _planning(self, s_start, s_goal, dyna_obs, step_num=None, debug=False):
'''
规划路径(实际实现)
Args:
dyna_obs: 动态障碍物位置列表
step_num: 单次移动步数
'''
# 确保目标合法
if not self.in_bounds_without_obstacle(s_goal):
return None
# 第一次规划需要初始化rhs并将goal加入队列计算最短路径
if self.s_goal is None:
self.s_start = tuple(s_start)
self.s_goal = tuple(s_goal)
self.s_last = tuple(s_start)
self.rhs[tuple(s_goal)] = 0
self.U.insert(tuple(s_goal), Priority(k1=heuristic(tuple(s_start), tuple(s_goal)), k2=0))
self.compute_shortest_path() # 计算最短路径
self.path = self.get_path()
# 后续规划只更新起点,直接使用原路径(去掉已走过部分)
else:
self.s_start = tuple(s_start)
self.path = self.path[step_num:]
# 根据obs更新map, cost_map, edge_cost
changed_pos = self.update_map(dyna_obs=dyna_obs)
if changed_pos:
self.update_cost_map(changed_pos)
self.update_edge_costs(changed_pos)
# 若地图改变,重新计算最短路径
self.compute_shortest_path()
self.path = self.get_path()
# TODO: 误差抖动使robot没有到达路径上的点导致新起点的rhs=∞可能导致get_path失败 ( 当前版本没有该问题 )
# assert (self.rhs[self.s_start] != float('inf')), "There is no known path!"
# self.path = self.get_path(step_num)
# # debug
# if debug:
# pass
return self.path
def planning(self, s_start, s_goal, dyna_obs, step_num=None, debug=False):
'''
路径规划(供外部调用处理实际坐标和地图坐标的转换)
'''
# 实际坐标 -> 地图坐标
s_start = self.real2map(s_start)
s_goal = self.real2map(s_goal)
for i in range(len(dyna_obs)):
dyna_obs[i] = self.real2map(dyna_obs[i])
self._planning(s_start, s_goal, dyna_obs, step_num, debug)
# 地图坐标->实际坐标
path = [self.map2real(node) for node in self.path]
return path
def get_path(self):
'''
得到路径
Args:
step_num: 路径步数 (None表示返回完整路径)
return:
path: [(x, y), ...]
'''
if self.s_start is None or self.s_goal == self.s_start:
return []
path = []
cur = self.s_start
# if step_num is None: # 得到完整路径
while cur != self.s_goal:
succ = self.get_neighbors(cur)
cur = succ[np.argmin([self.c(cur, s_) + self.g[s_] for s_ in succ])]
path.append(cur)
# else:
# for i in range(step_num):
# if cur == self.s_goal:
# break
# succ = self.get_neighbors(cur)
# cur = succ[np.argmin([self.c(cur, s_) + self.g[s_] for s_ in succ])]
# path.append(cur)
return path
def in_bounds_without_obstacle(self, pos):
'''
判断位置在地图范围内 不是静态障碍物
'''
(x, y) = pos
return 0 <= x < self.X and 0 <= y < self.Y and self.map[x, y] != self.object_to_idx["obstacle"]
def get_neighbors(self, pos, mode=8):
'''
获取邻居节点, 地图范围内
'''
(x_, y_) = pos
# results = [(x_+1,y_), (x_-1,y_), (x_, y_+1), (x_,y_-1)]
# if mode == 8:
neighbors = [(x_ + 1, y_), (x_ - 1, y_), (x_, y_ + 1), (x_, y_ - 1), (x_ + 1, y_ + 1), (x_ - 1, y_ + 1),
(x_ + 1, y_ - 1), (x_ - 1, y_ - 1)]
neighbors = filter(self.in_bounds_without_obstacle, neighbors) # 确保位置在地图范围内 且 不是静态障碍物
return list(neighbors)
def compute_cost_map(self):
# 计算当前地图的cost_map
self.cost_map = np.zeros(self.map.shape)
for idx, obj in self.idx_to_object.items():
self.cost_map[self.map == idx] = self.object_to_cost[obj]
def update_map(self, dyna_obs):
'''
更新地图 动态障碍物列表
Args:
dyna_obs: 当前动态障碍物位置列表 [(x,y), ...]
return:
update_obj: 改变的位置列表 [(x, y, obj_idx, obj_idx_old), ...]
'''
# dyna_obs没有变化 (集合set可以忽略元素在列表中的位置)
if set(dyna_obs) == set(self.dyna_obs_list):
return []
# 新旧dyna_obs占用位置列表
old_obs_occupy = []
new_obs_occupy = []
for pos in self.dyna_obs_list:
old_obs_occupy.extend(self.get_occupy_pos(pos))
for pos in dyna_obs:
new_obs_occupy.extend(self.get_occupy_pos(pos))
old_obs_occupy = [pos for i, pos in enumerate(old_obs_occupy) if pos not in old_obs_occupy[:i]] # 去除重复位置
new_obs_occupy = [pos for i, pos in enumerate(new_obs_occupy) if pos not in new_obs_occupy[:i]] # 去除重复位置
# 转变为free 和 转变为obs的位置列表
changed_free = [pos for pos in old_obs_occupy if pos not in new_obs_occupy]
changed_obs = [pos for pos in new_obs_occupy if pos not in old_obs_occupy]
# 更新地图计算changed_pos
changed_pos = []
for (x, y) in changed_free:
self.map[x, y] = self.object_to_idx['free']
changed_pos.append((x, y, self.object_to_idx["free"], self.object_to_idx["dynamic obstacle"]))
for (x, y) in changed_obs:
self.map[x, y] = self.object_to_idx['dynamic obstacle']
changed_pos.append((x, y, self.object_to_idx["dynamic obstacle"], self.object_to_idx["free"]))
# 更新动态障碍物列表
self.dyna_obs_list = dyna_obs
return changed_pos
def get_occupy_pos(self, obs_pos):
'''
根据dyna_obs中心位置计算其占用的所有网格位置
'''
(x, y) = obs_pos
occupy_pos = []
for i in range(x-self.dyna_obs_radius, x+self.dyna_obs_radius+1):
for j in range(y-self.dyna_obs_radius, y+self.dyna_obs_radius+1):
occupy_pos.append((i, j))
occupy_pos = filter(self.in_bounds_without_obstacle, occupy_pos) # 确保位置在地图范围内 且 不是静态障碍物
return list(occupy_pos)
def update_cost_map(self, changed_pos):
'''
更新cost_map
Args:
changed_pos: 改变的位置列表 [x, y, idx]
'''
for (x, y, idx, _) in changed_pos:
self.cost_map[x, y] = self.object_to_cost[self.idx_to_object[idx]]
def update_edge_costs(self, changed_pos):
'''
重新计算edge_cost更新受影响节点的rhs
Args:
changed_pos: 改变的位置列表 [(x, y, obj_idx_new, obj_idx_old)]
'''
if not changed_pos: return
self.k_m += heuristic(self.s_last, self.s_start, name='euclidean') # 使用欧式距离 更新km
self.s_last = self.s_start
for pos in changed_pos:
v = (pos[0], pos[1]) # to pos
v_old = self.idx_to_object[pos[3]] # 位置v的旧类型
pred_v = self.get_neighbors(v)
for u in pred_v:
c_old = self.c(u, v, v_old=v_old)
c_new = self.c(u, v)
if c_old > c_new and u != self.s_goal:
self.rhs[u] = min(self.rhs[u], self.c(u, v) + self.g[v])
elif self.rhs[u] == c_old + self.g[v] and u != self.s_goal:
succ_u = self.get_neighbors(u)
self.rhs[u] = min([self.c(u, s_) + self.g[s_] for s_ in succ_u])
self.update_vertex(u)
def map2real(self, pos):
'''
地图坐标->实际坐标
'''
x = pos[0] * self.scale_ratio + self.x_min
y = pos[1] * self.scale_ratio + self.y_min
return tuple((x, y))
def real2map(self, pos, approximation='round'):
'''
实际坐标->地图坐标
'''
if approximation == 'round': # 四舍五入
x = round((pos[0] - self.x_min) / self.scale_ratio)
y = round((pos[1] - self.y_min) / self.scale_ratio)
elif approximation == 'low': # 向下取整
x = math.floor((pos[0] - self.x_min) / self.scale_ratio)
y = math.floor((pos[1] - self.y_min) / self.scale_ratio)
else:
raise Exception("Wrong approximation!")
return tuple((x, y))
def draw_graph(self, step_num):
# 清空当前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])
# 缩放坐标偏移量
offset = (self.x_min / self.scale_ratio, self.x_max / self.scale_ratio,
self.y_min / self.scale_ratio, self.y_max / self.scale_ratio,)
# 画地图: X行Y列第一行在下面
# 范围: 横向Y[-80,290] 纵向X[-70,120]
plt.imshow(self.map, cmap='binary', alpha=0.5, origin='lower',
extent=(offset[2], offset[3],
offset[0], offset[1]))
# 画起点和目标
plt.plot(self.s_start[1] + offset[2], self.s_start[0] + offset[0], "xr")
plt.plot(self.s_goal[1] + offset[2], self.s_goal[0] + offset[0], "xr")
# 画搜索路径
plt.plot([y + offset[2] for (x, y) in self.path],
[x + offset[0] for (x, y) in self.path], "-g")
# 画移动路径
next_step = min(step_num, len(self.path))
# plt.plot([self.s_start[1] + offset[2], self.path[next_step-1][1] + offset[2]],
# [self.s_start[0] + offset[0], self.path[next_step-1][0] + offset[0]], "-r")
plt.plot([y + offset[2] for (x, y) in self.path[:next_step]],
[x + offset[0] for (x, y) in self.path[:next_step]], "-r")
plt.xlabel('y', loc='right')
plt.ylabel('x', loc='top')
plt.grid(color='black', linestyle='-', linewidth=0.5)
plt.pause(0.3)

Binary file not shown.

View File

@ -1,78 +0,0 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import math
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 Scene
from dstar_lite import DStarLite
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.step_num = self.step_length // self.scale_ratio # 单次移动地图格数
self.v = 200 # 速度
self.step_time = self.step_length/self.v + 0.1 # 单步移动时长
self.planner = DStarLite(area_range=area_range, map=map, scale_ratio=scale_ratio)
@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), animation=True):
'''
单次导航直到到达目标
'''
pos = np.array((self.scene.status.location.X, self.scene.status.location.Y)) # 机器人当前: 位置 和 朝向
yaw = self.scene.status.rotation.Yaw
print('------------------navigation_start----------------------')
while not self.is_reached(pos, goal):
dyna_obs = [(walker.pose.X, walker.pose.Y) for walker in self.scene.status.walkers] # 动态障碍物(顾客)位置列表
path = self.planner.planning(pos, goal, dyna_obs, step_num=self.step_num)
if path:
next_step = min(self.step_num, len(path))
(next_x, next_y) = path[next_step-1]
print('plan pos:', (next_x, next_y), end=' ')
self.scene.walk_to(next_x, next_y, yaw, velocity=self.v)
if animation:
self.planner.draw_graph(self.step_num) # 画出搜索路径
time.sleep(self.step_time)
pos = np.array((self.scene.status.location.X, self.scene.status.location.Y))
print('reach pos:', pos)
self.planner.reset() # 完成一轮导航,重置变量
if self.is_reached(pos, goal):
print('The robot has achieved goal !!')

View File

@ -1,4 +0,0 @@
### dstar_lite.py ——Dstar lite算法文件
### navigate.py ——导航类
### test.py ——测试文件
### map_5.pkl ——离散化地图文件

View File

@ -1,72 +0,0 @@
import os
import pickle
import time
import random
import matplotlib.pyplot as plt
import numpy as np
from scene_utils import Scene # 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)
Scene.init_world(1, 3)
scene = Scene.Scene(sceneID=0)
navigator = Navigator(scene=scene, area_range=[-350, 600, -400, 1450], map=map)
'''场景1: 无行人环境 robot到达指定目标'''
# goal = np.array((-100, 700))
'''场景2: 静止行人环境 robot到达指定目标'''
# scene.clean_walker()
# scene.add_walker(50, 300, 0)
# scene.add_walker(-50, 500, 0)
# goal = np.array((-100, 700))
'''场景3: 移动行人环境 robot到达指定目标'''
scene.clean_walker()
scene.add_walker(50, 300, 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=1, autowalk=False, speed=20, X=100, Y=150, Yaw=0)])
goal = np.array((-100, 700))
'''场景4: 行人自由移动 robot到达指定目标'''
# # TODO: autowalk=True仿真器会闪退 ???
# scene.clean_walker()
# scene.add_walker(50, 300, 0)
# scene.add_walker(-50, 500, 0)
# scene.control_walker([scene.walker_control_generator(walkerID=0, autowalk=True, speed=20, X=0, Y=0, Yaw=0)])
# scene.control_walker([scene.walker_control_generator(walkerID=1, autowalk=True, speed=20, X=0, Y=0, Yaw=0)])
# time.sleep(5)
# goal = np.array((-100, 700))
navigator.navigate(goal, animation=False)
scene.clean_walker()
print(scene.status.collision) # TODO: 不显示碰撞信息 ???

View File

@ -1,95 +0,0 @@
#叶结点
class Leaf:
def __init__(self,type,content,mincost):
self.type=type
self.content=content #conditionset or action
self.parent=None
self.parent_index=0
self.mincost=mincost
# tick 叶节点返回返回值以及对应的条件或行动对象self.content
def tick(self,state):
if self.type=='cond':
if self.content <= state:
return 'success',self.content
else:
return 'failure',self.content
if self.type=='act':
if self.content.pre<=state:
return 'running',self.content #action
else:
return 'failure',self.content
def __str__(self):
print( self.content)
return ''
def print_nodes(self):
print(self.content)
def count_size(self):
return 1
#可能包含控制结点的行为树
class ControlBT:
def __init__(self,type):
self.type=type
self.children=[]
self.parent=None
self.parent_index=0
def add_child(self,subtree_list):
for subtree in subtree_list:
self.children.append(subtree)
subtree.parent=self
subtree.parent_index=len(self.children)-1
# tick行为树根据不同控制结点逻辑tick子结点
def tick(self,state):
if len(self.children) < 1:
print("error,no child")
if self.type =='?':#选择结点,即或结点
for child in self.children:
val,obj=child.tick(state)
if val=='success':
return val,obj
if val=='running':
return val,obj
return 'failure','?fails'
if self.type =='>':#顺序结点,即与结点
for child in self.children:
val,obj=child.tick(state)
if val=='failure':
return val,obj
if val=='running':
return val,obj
return 'success', '>success'
if self.type =='act':#行动结点
return self.children[0].tick(state)
if self.type =='cond':#条件结点
return self.children[0].tick(state)
def getFirstChild(self):
return self.children[0]
def __str__(self):
print(self.type+'\n')
for child in self.children:
print (child)
return ''
def print_nodes(self):
print(self.type)
for child in self.children:
child.print_nodes()
# 递归统计树中结点数
def count_size(self):
result=1
for child in self.children:
result+= child.count_size()
return result

View File

@ -1,59 +0,0 @@
selector{
cond At(Table,Coffee)
selector{
cond At(Robot,Table), Holding(Coffee)
act PutDown(Table,Coffee)
}
selector{
cond At(Robot,Coffee), NotHolding, At(Robot,Table)
act PickUp(Coffee)
}
selector{
cond Available(Table), Holding(Coffee)
act MoveTo(Table)
}
selector{
cond At(Robot,Coffee), At(Robot,Table), Holding(VacuumCup)
act PutDown(Table,VacuumCup)
}
selector{
cond At(Robot,CoffeeMachine), NotHolding, At(Robot,Table)
act OpCoffeeMachine
}
selector{
cond At(Robot,Coffee), Available(Table), NotHolding
act PickUp(Coffee)
}
selector{
cond At(Robot,CoffeeMachine), At(Robot,Table), Holding(VacuumCup)
act PutDown(Table,VacuumCup)
}
selector{
cond Available(Table), Available(Coffee), NotHolding
act MoveTo(Coffee)
}
selector{
cond Available(Table), At(Robot,CoffeeMachine), NotHolding
act OpCoffeeMachine
}
selector{
cond Available(Table), Available(Coffee), At(Robot,Table), Holding(VacuumCup)
act PutDown(Table,VacuumCup)
}
selector{
cond Available(Table), NotHolding, Available(CoffeeMachine)
act MoveTo(CoffeeMachine)
}
selector{
cond Available(Table), Available(Coffee), Holding(VacuumCup)
act MoveTo(Table)
}
selector{
cond Available(Table), Available(CoffeeMachine), At(Robot,Table), Holding(VacuumCup)
act PutDown(Table,VacuumCup)
}
selector{
cond Available(Table), Available(CoffeeMachine), Holding(VacuumCup)
act MoveTo(Table)
}
}

Some files were not shown because too many files have changed in this diff Show More