From 618364921801965758621573ced1b93627b370c2 Mon Sep 17 00:00:00 2001 From: ChenXL97 <908926798@qq.com> Date: Tue, 21 Nov 2023 09:06:50 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E5=B0=86tasks=E7=A7=BB=E5=88=B0=E5=A4=96=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 44 +- robowaiter/behavior_lib/_base/Behavior.py | 2 +- robowaiter/llm_client/data/fix_questions.txt | 2 +- robowaiter/scene/scene.py | 4 +- robowaiter/scene/tasks/VLN.py | 106 ---- robowaiter/utils/draw_bt/Default.ptml | 27 +- robowaiter/utils/draw_bt/Default_bracket.ptml | 23 +- robowaiter/utils/draw_bt/test.dot | 48 +- robowaiter/utils/draw_bt/test.png | Bin 99843 -> 34381 bytes robowaiter/utils/draw_bt/test.svg | 252 +++------ run_robowaiter.py | 16 - sub_task.ptml | 31 -- {robowaiter/scene/tasks => tasks/AEM}/AEM.py | 4 +- .../scene/interact => tasks/AEM}/__init__.py | 0 .../tasks => tasks}/AT/Auto_tasks_light.py | 0 .../scene/tasks => tasks}/AT/__init__.py | 0 .../CafeDailyOperations/CafeOneDay.py | 0 .../CafeDailyOperations/VLN_all.py | 0 .../VLN_greet_and_order.py | 0 .../VLN_greet_and_order_1118.py | 0 .../VLN_greet_and_order_1119.py | 0 .../CafeDailyOperations}/__init__.py | 0 {robowaiter/scene/tasks => tasks}/GQA/GQA.py | 0 .../GQA/GQA_1_ask_order_place.py | 0 .../scene/tasks => tasks}/GQA/GQA_NLP.py | 0 .../scene/tasks/OT => tasks/GQA}/__init__.py | 0 .../tasks/VLM => tasks/Interact}/__init__.py | 0 .../interact => tasks/Interact}/system.py | 11 +- .../OT/OT_coffee_water_dessert.py | 0 .../scene/tasks => tasks}/OT/Open_tasks.py | 0 .../OT/Open_tasks_bright_table.py | 0 .../tasks => tasks}/OT/Open_tasks_test.py | 0 .../tasks => tasks}/OT/Open_tasks_walkers.py | 0 .../scene/tasks => tasks/OT}/__init__.py | 0 {robowaiter/scene/tasks => tasks}/VLM/VLM.py | 0 {robowaiter/scene/tasks => tasks}/VLM/VLM3.py | 0 .../scene/tasks => tasks}/VLM/VLM_1_order.py | 0 .../scene/tasks => tasks}/VLM/VLM_2_AC.py | 0 .../scene/tasks => tasks}/VLM/VLM_AC.py | 0 .../tasks => tasks}/VLM/VLM_cafe_shutdown.py | 0 .../scene/tasks => tasks}/VLM/VLM_order.py | 0 .../tasks => tasks}/VLM/VLN_greet_lead.py | 0 {zoo => tasks/VLM}/__init__.py | 0 .../scene/tasks => tasks}/VLN/VLN_edge_map.py | 2 - .../scene/tasks => tasks}/VLN/VLN_edge_obj.py | 0 .../tasks => tasks}/VLN/VLN_greet_lead.py | 0 .../scene/tasks => tasks}/VLN/VLN_oda_eazy.py | 0 .../scene/tasks => tasks}/VLN/VLN_oda_hard.py | 0 .../tasks => tasks}/VLN/VLN_oda_normal.py | 0 {zoo/behavior_lib => tasks/VLN}/__init__.py | 0 {zoo/behavior_tree => tasks}/__init__.py | 0 zoo/behavior_lib/Behavior.py | 27 - zoo/behavior_lib/Chatting.py | 23 - zoo/behavior_lib/CoffeeCupFound.py | 20 - zoo/behavior_lib/CoffeeCupGrasped.py | 22 - zoo/behavior_lib/CoffeeCupPlaced.py | 21 - zoo/behavior_lib/DealChat.py | 26 - zoo/behavior_lib/DestinationAReached.py | 21 - zoo/behavior_lib/FindCoffeeCup.py | 21 - zoo/behavior_lib/Grasp.py | 21 - zoo/behavior_lib/GraspCoffeeCup.py | 21 - zoo/behavior_lib/Istask.py | 21 - zoo/behavior_lib/MoveTo.py | 21 - zoo/behavior_lib/PlaceCoffeeCup.py | 21 - zoo/behavior_lib/ReachDestinationA.py | 21 - zoo/behavior_lib/SeqTest.py | 21 - zoo/behavior_lib/TestTask.py | 21 - zoo/behavior_tree/behavior_library.py | 124 ----- zoo/behavior_tree/main.py | 66 --- zoo/demo/__init__.py | 0 zoo/demo/关节控制.py | 59 -- zoo/demo/动画控制.py | 94 ---- zoo/demo/场景操作.py | 47 -- zoo/demo/导航寻路.py | 139 ----- zoo/demo/文字冒泡.py | 44 -- zoo/demo/物品操作.py | 103 ---- zoo/demo/相机操作.py | 49 -- zoo/demo/行人控制.py | 86 --- zoo/install_pytorch.sh | 1 - zoo/navigator_DstarLite/__init__.py | 4 - zoo/navigator_DstarLite/dstar_lite.py | 510 ------------------ zoo/navigator_DstarLite/map_5.pkl | Bin 562562 -> 0 bytes zoo/navigator_DstarLite/navigate.py | 78 --- zoo/navigator_DstarLite/readme.md | 4 - zoo/navigator_DstarLite/test.py | 72 --- zoo/opt_bt_expansion/BehaviorTree.py | 95 ---- zoo/opt_bt_expansion/MakeCoffee.ptml | 59 -- .../OptimalBTExpansionAlgorithm.py | 210 -------- .../README.assets/image-20231103191141047.png | Bin 55821 -> 0 bytes zoo/opt_bt_expansion/README.md | 125 ----- zoo/opt_bt_expansion/__init__.py | 0 zoo/opt_bt_expansion/examples.py | 174 ------ zoo/opt_bt_expansion/opt_bt_exp_main.py | 118 ---- zoo/opt_bt_expansion/tools.py | 167 ------ 94 files changed, 131 insertions(+), 3218 deletions(-) delete mode 100644 robowaiter/scene/tasks/VLN.py delete mode 100644 run_robowaiter.py delete mode 100644 sub_task.ptml rename {robowaiter/scene/tasks => tasks/AEM}/AEM.py (98%) rename {robowaiter/scene/interact => tasks/AEM}/__init__.py (100%) rename {robowaiter/scene/tasks => tasks}/AT/Auto_tasks_light.py (100%) rename {robowaiter/scene/tasks => tasks}/AT/__init__.py (100%) rename {robowaiter/scene/tasks => tasks}/CafeDailyOperations/CafeOneDay.py (100%) rename {robowaiter/scene/tasks => tasks}/CafeDailyOperations/VLN_all.py (100%) rename {robowaiter/scene/tasks => tasks}/CafeDailyOperations/VLN_greet_and_order.py (100%) rename {robowaiter/scene/tasks => tasks}/CafeDailyOperations/VLN_greet_and_order_1118.py (100%) rename {robowaiter/scene/tasks => tasks}/CafeDailyOperations/VLN_greet_and_order_1119.py (100%) rename {robowaiter/scene/tasks/GQA => tasks/CafeDailyOperations}/__init__.py (100%) rename {robowaiter/scene/tasks => tasks}/GQA/GQA.py (100%) rename {robowaiter/scene/tasks => tasks}/GQA/GQA_1_ask_order_place.py (100%) rename {robowaiter/scene/tasks => tasks}/GQA/GQA_NLP.py (100%) rename {robowaiter/scene/tasks/OT => tasks/GQA}/__init__.py (100%) rename {robowaiter/scene/tasks/VLM => tasks/Interact}/__init__.py (100%) rename {robowaiter/scene/interact => tasks/Interact}/system.py (54%) rename {robowaiter/scene/tasks => tasks}/OT/OT_coffee_water_dessert.py (100%) rename {robowaiter/scene/tasks => tasks}/OT/Open_tasks.py (100%) rename {robowaiter/scene/tasks => tasks}/OT/Open_tasks_bright_table.py (100%) rename {robowaiter/scene/tasks => tasks}/OT/Open_tasks_test.py (100%) rename {robowaiter/scene/tasks => tasks}/OT/Open_tasks_walkers.py (100%) rename {robowaiter/scene/tasks => tasks/OT}/__init__.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM3.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM_1_order.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM_2_AC.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM_AC.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM_cafe_shutdown.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLM_order.py (100%) rename {robowaiter/scene/tasks => tasks}/VLM/VLN_greet_lead.py (100%) rename {zoo => tasks/VLM}/__init__.py (100%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_edge_map.py (99%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_edge_obj.py (100%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_greet_lead.py (100%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_oda_eazy.py (100%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_oda_hard.py (100%) rename {robowaiter/scene/tasks => tasks}/VLN/VLN_oda_normal.py (100%) rename {zoo/behavior_lib => tasks/VLN}/__init__.py (100%) rename {zoo/behavior_tree => tasks}/__init__.py (100%) delete mode 100644 zoo/behavior_lib/Behavior.py delete mode 100644 zoo/behavior_lib/Chatting.py delete mode 100644 zoo/behavior_lib/CoffeeCupFound.py delete mode 100644 zoo/behavior_lib/CoffeeCupGrasped.py delete mode 100644 zoo/behavior_lib/CoffeeCupPlaced.py delete mode 100644 zoo/behavior_lib/DealChat.py delete mode 100644 zoo/behavior_lib/DestinationAReached.py delete mode 100644 zoo/behavior_lib/FindCoffeeCup.py delete mode 100644 zoo/behavior_lib/Grasp.py delete mode 100644 zoo/behavior_lib/GraspCoffeeCup.py delete mode 100644 zoo/behavior_lib/Istask.py delete mode 100644 zoo/behavior_lib/MoveTo.py delete mode 100644 zoo/behavior_lib/PlaceCoffeeCup.py delete mode 100644 zoo/behavior_lib/ReachDestinationA.py delete mode 100644 zoo/behavior_lib/SeqTest.py delete mode 100644 zoo/behavior_lib/TestTask.py delete mode 100644 zoo/behavior_tree/behavior_library.py delete mode 100644 zoo/behavior_tree/main.py delete mode 100644 zoo/demo/__init__.py delete mode 100644 zoo/demo/关节控制.py delete mode 100644 zoo/demo/动画控制.py delete mode 100644 zoo/demo/场景操作.py delete mode 100644 zoo/demo/导航寻路.py delete mode 100644 zoo/demo/文字冒泡.py delete mode 100644 zoo/demo/物品操作.py delete mode 100644 zoo/demo/相机操作.py delete mode 100644 zoo/demo/行人控制.py delete mode 100644 zoo/install_pytorch.sh delete mode 100644 zoo/navigator_DstarLite/__init__.py delete mode 100644 zoo/navigator_DstarLite/dstar_lite.py delete mode 100644 zoo/navigator_DstarLite/map_5.pkl delete mode 100644 zoo/navigator_DstarLite/navigate.py delete mode 100644 zoo/navigator_DstarLite/readme.md delete mode 100644 zoo/navigator_DstarLite/test.py delete mode 100644 zoo/opt_bt_expansion/BehaviorTree.py delete mode 100644 zoo/opt_bt_expansion/MakeCoffee.ptml delete mode 100644 zoo/opt_bt_expansion/OptimalBTExpansionAlgorithm.py delete mode 100644 zoo/opt_bt_expansion/README.assets/image-20231103191141047.png delete mode 100644 zoo/opt_bt_expansion/README.md delete mode 100644 zoo/opt_bt_expansion/__init__.py delete mode 100644 zoo/opt_bt_expansion/examples.py delete mode 100644 zoo/opt_bt_expansion/opt_bt_exp_main.py delete mode 100644 zoo/opt_bt_expansion/tools.py diff --git a/README.md b/README.md index 0a3eb56..89cfeda 100644 --- a/README.md +++ b/README.md @@ -14,45 +14,29 @@ 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 | 自主任务 | +| 缩写 | 任务 | +|---------------------|-------------| +| AEM | 主动探索和记忆 | +| GQA | 具身多轮对话 | +| VLN | 视觉语言导航 | +| VLM | 视觉语言操作 | +| OT | 复杂开放任务 | +| AT | 自主任务 | +| CafeDailyOperations | 整体展示:咖啡厅的一天 | +| Interact | 命令行自由交互 | ## Scene diff --git a/robowaiter/behavior_lib/_base/Behavior.py b/robowaiter/behavior_lib/_base/Behavior.py index c60ff51..e704d5a 100644 --- a/robowaiter/behavior_lib/_base/Behavior.py +++ b/robowaiter/behavior_lib/_base/Behavior.py @@ -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 diff --git a/robowaiter/llm_client/data/fix_questions.txt b/robowaiter/llm_client/data/fix_questions.txt index 053d559..8962f86 100644 --- a/robowaiter/llm_client/data/fix_questions.txt +++ b/robowaiter/llm_client/data/fix_questions.txt @@ -9,7 +9,7 @@ ok, 有需要您再找我。 做一杯咖啡 好的,我马上做咖啡 create_sub_task -{"goal":"On(Coffee,CoffeeTable)"}00 +{"goal":"On(Coffee,CoffeeTable)"} 不用了。 好的,您有需要再跟我说 diff --git a/robowaiter/scene/scene.py b/robowaiter/scene/scene.py index 5c70b29..0670a3e 100644 --- a/robowaiter/scene/scene.py +++ b/robowaiter/scene/scene.py @@ -306,9 +306,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)) diff --git a/robowaiter/scene/tasks/VLN.py b/robowaiter/scene/tasks/VLN.py deleted file mode 100644 index ce59d79..0000000 --- a/robowaiter/scene/tasks/VLN.py +++ /dev/null @@ -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() diff --git a/robowaiter/utils/draw_bt/Default.ptml b/robowaiter/utils/draw_bt/Default.ptml index 03745e6..55a6f88 100644 --- a/robowaiter/utils/draw_bt/Default.ptml +++ b/robowaiter/utils/draw_bt/Default.ptml @@ -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) +}} \ No newline at end of file diff --git a/robowaiter/utils/draw_bt/Default_bracket.ptml b/robowaiter/utils/draw_bt/Default_bracket.ptml index e18182b..844db49 100644 --- a/robowaiter/utils/draw_bt/Default_bracket.ptml +++ b/robowaiter/utils/draw_bt/Default_bracket.ptml @@ -2,27 +2,28 @@ selector { sequence { - cond CustomerChatting() + cond Chatting() act DealChat() - -} sequence + } sequence { cond HasSubTask() sequence { act SubTaskPlaceHolder() - -} sequence + } } sequence +{ cond FocusingCustomer() act ServeCustomer() - -} sequence + } sequence { - cond NewCustomerComing() + cond NewCustomer() selector { cond At(Robot,Bar) act MoveTo(Bar) - -} act GreetCustomer() -}} \ No newline at end of file + } act GreetCustomer() + } sequence +{ + cond AnomalyDetected() + act ResolveAnomaly() + }} \ No newline at end of file diff --git a/robowaiter/utils/draw_bt/test.dot b/robowaiter/utils/draw_bt/test.dot index bd79eac..cf2b2d7 100644 --- a/robowaiter/utils/draw_bt/test.dot +++ b/robowaiter/utils/draw_bt/test.dot @@ -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"; } diff --git a/robowaiter/utils/draw_bt/test.png b/robowaiter/utils/draw_bt/test.png index 773238dbed479137c4bcb1d36b420be44e68a010..15a318306236680878cf9f06b1cd9ab3e943fdd5 100644 GIT binary patch literal 34381 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%0AQkQ&s* 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

x%+V^Fy+OBb0i88{iS@DL-TJ8npfhLQS^O4ODr+ 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 E0mIncAOHXW diff --git a/robowaiter/utils/draw_bt/test.svg b/robowaiter/utils/draw_bt/test.svg index 2ed2acf..29211a1 100644 --- a/robowaiter/utils/draw_bt/test.svg +++ b/robowaiter/utils/draw_bt/test.svg @@ -4,208 +4,100 @@ - - + + pastafarianism - - + + -a3ac082f-ad46-4cc9-86b4-3e76c3c564d3 - -Selector +c9a8671e-b0b8-4587-a4de-a094fd678334 + +Selector - + -717a1290-56bc-41e1-9b4b-e6df93d40e64 - -Sequence +bc597ff6-b3cb-4573-9dea-944fdb38d045 + +Holding(Nothing) - + -a3ac082f-ad46-4cc9-86b4-3e76c3c564d3->717a1290-56bc-41e1-9b4b-e6df93d40e64 - - +c9a8671e-b0b8-4587-a4de-a094fd678334->bc597ff6-b3cb-4573-9dea-944fdb38d045 + + - - -ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e - -Sequence - - - -a3ac082f-ad46-4cc9-86b4-3e76c3c564d3->ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e - - - - + -b3236b13-d5f2-4643-ae7c-6f27bd4ab6f2 - -CustomerChatting() +a8e26121-f5cc-4ba5-b98e-86bd84de06d6 + +Sequence - + -717a1290-56bc-41e1-9b4b-e6df93d40e64->b3236b13-d5f2-4643-ae7c-6f27bd4ab6f2 - - +c9a8671e-b0b8-4587-a4de-a094fd678334->a8e26121-f5cc-4ba5-b98e-86bd84de06d6 + + - - -28015225-b7a4-4f38-b26f-4dbe5eea4154 - -DealChat() - - - -717a1290-56bc-41e1-9b4b-e6df93d40e64->28015225-b7a4-4f38-b26f-4dbe5eea4154 - - - - + -2f77f976-499c-4601-9d8c-86dd80d66dfa - -HasSubTask() +400b4104-ff96-4d64-89b2-49dc6e4e3cbd + +Sequence - + -ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e->2f77f976-499c-4601-9d8c-86dd80d66dfa - - +c9a8671e-b0b8-4587-a4de-a094fd678334->400b4104-ff96-4d64-89b2-49dc6e4e3cbd + + - + + +676d8bda-2c30-44a5-bca2-ade68761403a + +Is(Curtain,Off) + + + +a8e26121-f5cc-4ba5-b98e-86bd84de06d6->676d8bda-2c30-44a5-bca2-ade68761403a + + + + + +c3d096cf-f848-46a3-9497-5398614798e8 + +Turn(Curtain,On) + + + +a8e26121-f5cc-4ba5-b98e-86bd84de06d6->c3d096cf-f848-46a3-9497-5398614798e8 + + + + -2428792c-3896-443d-8744-f5e286644fad - -Sequence +3fc48afc-1cef-45f5-826d-2571d7979a2f + +Is(AC,Off) - + -ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e->2428792c-3896-443d-8744-f5e286644fad - - +400b4104-ff96-4d64-89b2-49dc6e4e3cbd->3fc48afc-1cef-45f5-826d-2571d7979a2f + + - - -64b2362a-d99e-4e99-8772-ad1419e53a2e - -Sequence - - - -ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e->64b2362a-d99e-4e99-8772-ad1419e53a2e - - - - - -bea12066-ecbd-49b0-8934-efcb2c38b5f5 - -Sequence - - - -ff83c742-a2ba-4aa5-8b3a-39ecd7c03b0e->bea12066-ecbd-49b0-8934-efcb2c38b5f5 - - - - + -4bb7a9c3-521b-408f-b403-1fd25d54b192 - -SubTaskPlaceHolder() +47af4df0-8811-45a5-95e2-f0fc9458ddfd + +Turn(AC,On) - + -2428792c-3896-443d-8744-f5e286644fad->4bb7a9c3-521b-408f-b403-1fd25d54b192 - - - - - -fd7124ba-c9d1-4fde-8063-e8337d335121 - -FocusingCustomer() - - - -64b2362a-d99e-4e99-8772-ad1419e53a2e->fd7124ba-c9d1-4fde-8063-e8337d335121 - - - - - -169ebec9-3645-4fbb-a533-5186a8e5967b - -ServeCustomer() - - - -64b2362a-d99e-4e99-8772-ad1419e53a2e->169ebec9-3645-4fbb-a533-5186a8e5967b - - - - - -a847a0cc-c5af-4757-aa8c-8baef72788dc - -NewCustomerComing() - - - -bea12066-ecbd-49b0-8934-efcb2c38b5f5->a847a0cc-c5af-4757-aa8c-8baef72788dc - - - - - -99354f05-1716-46b2-9151-d88eac0a5b27 - -Selector - - - -bea12066-ecbd-49b0-8934-efcb2c38b5f5->99354f05-1716-46b2-9151-d88eac0a5b27 - - - - - -a3766a34-5152-4f82-8fba-8bf1f6b3830b - -GreetCustomer() - - - -bea12066-ecbd-49b0-8934-efcb2c38b5f5->a3766a34-5152-4f82-8fba-8bf1f6b3830b - - - - - -19de10a3-7554-43a3-b892-34cb2e32ab9a - -At(Robot,Bar) - - - -99354f05-1716-46b2-9151-d88eac0a5b27->19de10a3-7554-43a3-b892-34cb2e32ab9a - - - - - -4286d652-c4ef-4522-a7f1-b5c865dcc4c9 - -MoveTo(Bar) - - - -99354f05-1716-46b2-9151-d88eac0a5b27->4286d652-c4ef-4522-a7f1-b5c865dcc4c9 - - +400b4104-ff96-4d64-89b2-49dc6e4e3cbd->47af4df0-8811-45a5-95e2-f0fc9458ddfd + + diff --git a/run_robowaiter.py b/run_robowaiter.py deleted file mode 100644 index dda629e..0000000 --- a/run_robowaiter.py +++ /dev/null @@ -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() diff --git a/sub_task.ptml b/sub_task.ptml deleted file mode 100644 index 33895cd..0000000 --- a/sub_task.ptml +++ /dev/null @@ -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) -} -} diff --git a/robowaiter/scene/tasks/AEM.py b/tasks/AEM/AEM.py similarity index 98% rename from robowaiter/scene/tasks/AEM.py rename to tasks/AEM/AEM.py index 3ab63f1..e441598 100644 --- a/robowaiter/scene/tasks/AEM.py +++ b/tasks/AEM/AEM.py @@ -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): diff --git a/robowaiter/scene/interact/__init__.py b/tasks/AEM/__init__.py similarity index 100% rename from robowaiter/scene/interact/__init__.py rename to tasks/AEM/__init__.py diff --git a/robowaiter/scene/tasks/AT/Auto_tasks_light.py b/tasks/AT/Auto_tasks_light.py similarity index 100% rename from robowaiter/scene/tasks/AT/Auto_tasks_light.py rename to tasks/AT/Auto_tasks_light.py diff --git a/robowaiter/scene/tasks/AT/__init__.py b/tasks/AT/__init__.py similarity index 100% rename from robowaiter/scene/tasks/AT/__init__.py rename to tasks/AT/__init__.py diff --git a/robowaiter/scene/tasks/CafeDailyOperations/CafeOneDay.py b/tasks/CafeDailyOperations/CafeOneDay.py similarity index 100% rename from robowaiter/scene/tasks/CafeDailyOperations/CafeOneDay.py rename to tasks/CafeDailyOperations/CafeOneDay.py diff --git a/robowaiter/scene/tasks/CafeDailyOperations/VLN_all.py b/tasks/CafeDailyOperations/VLN_all.py similarity index 100% rename from robowaiter/scene/tasks/CafeDailyOperations/VLN_all.py rename to tasks/CafeDailyOperations/VLN_all.py diff --git a/robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order.py b/tasks/CafeDailyOperations/VLN_greet_and_order.py similarity index 100% rename from robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order.py rename to tasks/CafeDailyOperations/VLN_greet_and_order.py diff --git a/robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order_1118.py b/tasks/CafeDailyOperations/VLN_greet_and_order_1118.py similarity index 100% rename from robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order_1118.py rename to tasks/CafeDailyOperations/VLN_greet_and_order_1118.py diff --git a/robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order_1119.py b/tasks/CafeDailyOperations/VLN_greet_and_order_1119.py similarity index 100% rename from robowaiter/scene/tasks/CafeDailyOperations/VLN_greet_and_order_1119.py rename to tasks/CafeDailyOperations/VLN_greet_and_order_1119.py diff --git a/robowaiter/scene/tasks/GQA/__init__.py b/tasks/CafeDailyOperations/__init__.py similarity index 100% rename from robowaiter/scene/tasks/GQA/__init__.py rename to tasks/CafeDailyOperations/__init__.py diff --git a/robowaiter/scene/tasks/GQA/GQA.py b/tasks/GQA/GQA.py similarity index 100% rename from robowaiter/scene/tasks/GQA/GQA.py rename to tasks/GQA/GQA.py diff --git a/robowaiter/scene/tasks/GQA/GQA_1_ask_order_place.py b/tasks/GQA/GQA_1_ask_order_place.py similarity index 100% rename from robowaiter/scene/tasks/GQA/GQA_1_ask_order_place.py rename to tasks/GQA/GQA_1_ask_order_place.py diff --git a/robowaiter/scene/tasks/GQA/GQA_NLP.py b/tasks/GQA/GQA_NLP.py similarity index 100% rename from robowaiter/scene/tasks/GQA/GQA_NLP.py rename to tasks/GQA/GQA_NLP.py diff --git a/robowaiter/scene/tasks/OT/__init__.py b/tasks/GQA/__init__.py similarity index 100% rename from robowaiter/scene/tasks/OT/__init__.py rename to tasks/GQA/__init__.py diff --git a/robowaiter/scene/tasks/VLM/__init__.py b/tasks/Interact/__init__.py similarity index 100% rename from robowaiter/scene/tasks/VLM/__init__.py rename to tasks/Interact/__init__.py diff --git a/robowaiter/scene/interact/system.py b/tasks/Interact/system.py similarity index 54% rename from robowaiter/scene/interact/system.py rename to tasks/Interact/system.py index 6cbd372..fc5517b 100644 --- a/robowaiter/scene/interact/system.py +++ b/tasks/Interact/system.py @@ -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) diff --git a/robowaiter/scene/tasks/OT/OT_coffee_water_dessert.py b/tasks/OT/OT_coffee_water_dessert.py similarity index 100% rename from robowaiter/scene/tasks/OT/OT_coffee_water_dessert.py rename to tasks/OT/OT_coffee_water_dessert.py diff --git a/robowaiter/scene/tasks/OT/Open_tasks.py b/tasks/OT/Open_tasks.py similarity index 100% rename from robowaiter/scene/tasks/OT/Open_tasks.py rename to tasks/OT/Open_tasks.py diff --git a/robowaiter/scene/tasks/OT/Open_tasks_bright_table.py b/tasks/OT/Open_tasks_bright_table.py similarity index 100% rename from robowaiter/scene/tasks/OT/Open_tasks_bright_table.py rename to tasks/OT/Open_tasks_bright_table.py diff --git a/robowaiter/scene/tasks/OT/Open_tasks_test.py b/tasks/OT/Open_tasks_test.py similarity index 100% rename from robowaiter/scene/tasks/OT/Open_tasks_test.py rename to tasks/OT/Open_tasks_test.py diff --git a/robowaiter/scene/tasks/OT/Open_tasks_walkers.py b/tasks/OT/Open_tasks_walkers.py similarity index 100% rename from robowaiter/scene/tasks/OT/Open_tasks_walkers.py rename to tasks/OT/Open_tasks_walkers.py diff --git a/robowaiter/scene/tasks/__init__.py b/tasks/OT/__init__.py similarity index 100% rename from robowaiter/scene/tasks/__init__.py rename to tasks/OT/__init__.py diff --git a/robowaiter/scene/tasks/VLM/VLM.py b/tasks/VLM/VLM.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM.py rename to tasks/VLM/VLM.py diff --git a/robowaiter/scene/tasks/VLM/VLM3.py b/tasks/VLM/VLM3.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM3.py rename to tasks/VLM/VLM3.py diff --git a/robowaiter/scene/tasks/VLM/VLM_1_order.py b/tasks/VLM/VLM_1_order.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM_1_order.py rename to tasks/VLM/VLM_1_order.py diff --git a/robowaiter/scene/tasks/VLM/VLM_2_AC.py b/tasks/VLM/VLM_2_AC.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM_2_AC.py rename to tasks/VLM/VLM_2_AC.py diff --git a/robowaiter/scene/tasks/VLM/VLM_AC.py b/tasks/VLM/VLM_AC.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM_AC.py rename to tasks/VLM/VLM_AC.py diff --git a/robowaiter/scene/tasks/VLM/VLM_cafe_shutdown.py b/tasks/VLM/VLM_cafe_shutdown.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM_cafe_shutdown.py rename to tasks/VLM/VLM_cafe_shutdown.py diff --git a/robowaiter/scene/tasks/VLM/VLM_order.py b/tasks/VLM/VLM_order.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLM_order.py rename to tasks/VLM/VLM_order.py diff --git a/robowaiter/scene/tasks/VLM/VLN_greet_lead.py b/tasks/VLM/VLN_greet_lead.py similarity index 100% rename from robowaiter/scene/tasks/VLM/VLN_greet_lead.py rename to tasks/VLM/VLN_greet_lead.py diff --git a/zoo/__init__.py b/tasks/VLM/__init__.py similarity index 100% rename from zoo/__init__.py rename to tasks/VLM/__init__.py diff --git a/robowaiter/scene/tasks/VLN/VLN_edge_map.py b/tasks/VLN/VLN_edge_map.py similarity index 99% rename from robowaiter/scene/tasks/VLN/VLN_edge_map.py rename to tasks/VLN/VLN_edge_map.py index f2897f0..0a88c3a 100644 --- a/robowaiter/scene/tasks/VLN/VLN_edge_map.py +++ b/tasks/VLN/VLN_edge_map.py @@ -3,8 +3,6 @@ 测试导航算法的动态避障(Obstacle Detection and Avoidance, ODA)能力 - - """ import os import pickle diff --git a/robowaiter/scene/tasks/VLN/VLN_edge_obj.py b/tasks/VLN/VLN_edge_obj.py similarity index 100% rename from robowaiter/scene/tasks/VLN/VLN_edge_obj.py rename to tasks/VLN/VLN_edge_obj.py diff --git a/robowaiter/scene/tasks/VLN/VLN_greet_lead.py b/tasks/VLN/VLN_greet_lead.py similarity index 100% rename from robowaiter/scene/tasks/VLN/VLN_greet_lead.py rename to tasks/VLN/VLN_greet_lead.py diff --git a/robowaiter/scene/tasks/VLN/VLN_oda_eazy.py b/tasks/VLN/VLN_oda_eazy.py similarity index 100% rename from robowaiter/scene/tasks/VLN/VLN_oda_eazy.py rename to tasks/VLN/VLN_oda_eazy.py diff --git a/robowaiter/scene/tasks/VLN/VLN_oda_hard.py b/tasks/VLN/VLN_oda_hard.py similarity index 100% rename from robowaiter/scene/tasks/VLN/VLN_oda_hard.py rename to tasks/VLN/VLN_oda_hard.py diff --git a/robowaiter/scene/tasks/VLN/VLN_oda_normal.py b/tasks/VLN/VLN_oda_normal.py similarity index 100% rename from robowaiter/scene/tasks/VLN/VLN_oda_normal.py rename to tasks/VLN/VLN_oda_normal.py diff --git a/zoo/behavior_lib/__init__.py b/tasks/VLN/__init__.py similarity index 100% rename from zoo/behavior_lib/__init__.py rename to tasks/VLN/__init__.py diff --git a/zoo/behavior_tree/__init__.py b/tasks/__init__.py similarity index 100% rename from zoo/behavior_tree/__init__.py rename to tasks/__init__.py diff --git a/zoo/behavior_lib/Behavior.py b/zoo/behavior_lib/Behavior.py deleted file mode 100644 index 850459a..0000000 --- a/zoo/behavior_lib/Behavior.py +++ /dev/null @@ -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) diff --git a/zoo/behavior_lib/Chatting.py b/zoo/behavior_lib/Chatting.py deleted file mode 100644 index 563c23f..0000000 --- a/zoo/behavior_lib/Chatting.py +++ /dev/null @@ -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) diff --git a/zoo/behavior_lib/CoffeeCupFound.py b/zoo/behavior_lib/CoffeeCupFound.py deleted file mode 100644 index e7130ec..0000000 --- a/zoo/behavior_lib/CoffeeCupFound.py +++ /dev/null @@ -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) diff --git a/zoo/behavior_lib/CoffeeCupGrasped.py b/zoo/behavior_lib/CoffeeCupGrasped.py deleted file mode 100644 index 32c010a..0000000 --- a/zoo/behavior_lib/CoffeeCupGrasped.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/CoffeeCupPlaced.py b/zoo/behavior_lib/CoffeeCupPlaced.py deleted file mode 100644 index d4cdb35..0000000 --- a/zoo/behavior_lib/CoffeeCupPlaced.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/DealChat.py b/zoo/behavior_lib/DealChat.py deleted file mode 100644 index c1fee07..0000000 --- a/zoo/behavior_lib/DealChat.py +++ /dev/null @@ -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) diff --git a/zoo/behavior_lib/DestinationAReached.py b/zoo/behavior_lib/DestinationAReached.py deleted file mode 100644 index 9092023..0000000 --- a/zoo/behavior_lib/DestinationAReached.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/FindCoffeeCup.py b/zoo/behavior_lib/FindCoffeeCup.py deleted file mode 100644 index 6781c50..0000000 --- a/zoo/behavior_lib/FindCoffeeCup.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/Grasp.py b/zoo/behavior_lib/Grasp.py deleted file mode 100644 index 57c5708..0000000 --- a/zoo/behavior_lib/Grasp.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/GraspCoffeeCup.py b/zoo/behavior_lib/GraspCoffeeCup.py deleted file mode 100644 index 285804e..0000000 --- a/zoo/behavior_lib/GraspCoffeeCup.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/Istask.py b/zoo/behavior_lib/Istask.py deleted file mode 100644 index 4a1922c..0000000 --- a/zoo/behavior_lib/Istask.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/MoveTo.py b/zoo/behavior_lib/MoveTo.py deleted file mode 100644 index 516e9dc..0000000 --- a/zoo/behavior_lib/MoveTo.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/PlaceCoffeeCup.py b/zoo/behavior_lib/PlaceCoffeeCup.py deleted file mode 100644 index 7cf0bbd..0000000 --- a/zoo/behavior_lib/PlaceCoffeeCup.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/ReachDestinationA.py b/zoo/behavior_lib/ReachDestinationA.py deleted file mode 100644 index 18fbe03..0000000 --- a/zoo/behavior_lib/ReachDestinationA.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/SeqTest.py b/zoo/behavior_lib/SeqTest.py deleted file mode 100644 index e07ab74..0000000 --- a/zoo/behavior_lib/SeqTest.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_lib/TestTask.py b/zoo/behavior_lib/TestTask.py deleted file mode 100644 index c9c53d7..0000000 --- a/zoo/behavior_lib/TestTask.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/zoo/behavior_tree/behavior_library.py b/zoo/behavior_tree/behavior_library.py deleted file mode 100644 index 113cc49..0000000 --- a/zoo/behavior_tree/behavior_library.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/zoo/behavior_tree/main.py b/zoo/behavior_tree/main.py deleted file mode 100644 index 78cdb99..0000000 --- a/zoo/behavior_tree/main.py +++ /dev/null @@ -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() diff --git a/zoo/demo/__init__.py b/zoo/demo/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/zoo/demo/关节控制.py b/zoo/demo/关节控制.py deleted file mode 100644 index fbbff16..0000000 --- a/zoo/demo/关节控制.py +++ /dev/null @@ -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) # 关节控制测试 \ No newline at end of file diff --git a/zoo/demo/动画控制.py b/zoo/demo/动画控制.py deleted file mode 100644 index 457bbbc..0000000 --- a/zoo/demo/动画控制.py +++ /dev/null @@ -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, "擦桌子失败") \ No newline at end of file diff --git a/zoo/demo/场景操作.py b/zoo/demo/场景操作.py deleted file mode 100644 index cc48eb7..0000000 --- a/zoo/demo/场景操作.py +++ /dev/null @@ -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) # 场景信息测试 \ No newline at end of file diff --git a/zoo/demo/导航寻路.py b/zoo/demo/导航寻路.py deleted file mode 100644 index 21cf412..0000000 --- a/zoo/demo/导航寻路.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/zoo/demo/文字冒泡.py b/zoo/demo/文字冒泡.py deleted file mode 100644 index 370ffb8..0000000 --- a/zoo/demo/文字冒泡.py +++ /dev/null @@ -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, "你好,欢迎光临") \ No newline at end of file diff --git a/zoo/demo/物品操作.py b/zoo/demo/物品操作.py deleted file mode 100644 index f63a4c7..0000000 --- a/zoo/demo/物品操作.py +++ /dev/null @@ -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) # 释放物品测试 \ No newline at end of file diff --git a/zoo/demo/相机操作.py b/zoo/demo/相机操作.py deleted file mode 100644 index 42e400c..0000000 --- a/zoo/demo/相机操作.py +++ /dev/null @@ -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) # 相机操作测试 \ No newline at end of file diff --git a/zoo/demo/行人控制.py b/zoo/demo/行人控制.py deleted file mode 100644 index 32efcb2..0000000 --- a/zoo/demo/行人控制.py +++ /dev/null @@ -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) # 行人控制测试 \ No newline at end of file diff --git a/zoo/install_pytorch.sh b/zoo/install_pytorch.sh deleted file mode 100644 index a2b5b5c..0000000 --- a/zoo/install_pytorch.sh +++ /dev/null @@ -1 +0,0 @@ -conda install pytorch==1.11.0 torchvision==0.12.0 torchaudio==0.11.0 cudatoolkit=11.3 -c pytorch \ No newline at end of file diff --git a/zoo/navigator_DstarLite/__init__.py b/zoo/navigator_DstarLite/__init__.py deleted file mode 100644 index 1ceb32a..0000000 --- a/zoo/navigator_DstarLite/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -from . import navigate -from . import dstar_lite - diff --git a/zoo/navigator_DstarLite/dstar_lite.py b/zoo/navigator_DstarLite/dstar_lite.py deleted file mode 100644 index 6a83108..0000000 --- a/zoo/navigator_DstarLite/dstar_lite.py +++ /dev/null @@ -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是node,u是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) diff --git a/zoo/navigator_DstarLite/map_5.pkl b/zoo/navigator_DstarLite/map_5.pkl deleted file mode 100644 index 7b49370226f1770a974c60a3578f1c44c62dea35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 562562 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 G{C@$1cKMJ1 diff --git a/zoo/navigator_DstarLite/navigate.py b/zoo/navigator_DstarLite/navigate.py deleted file mode 100644 index bbb0a16..0000000 --- a/zoo/navigator_DstarLite/navigate.py +++ /dev/null @@ -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 !!') - - - - - diff --git a/zoo/navigator_DstarLite/readme.md b/zoo/navigator_DstarLite/readme.md deleted file mode 100644 index 3a77674..0000000 --- a/zoo/navigator_DstarLite/readme.md +++ /dev/null @@ -1,4 +0,0 @@ -### dstar_lite.py ——Dstar lite算法文件 -### navigate.py ——导航类 -### test.py ——测试文件 -### map_5.pkl ——离散化地图文件 \ No newline at end of file diff --git a/zoo/navigator_DstarLite/test.py b/zoo/navigator_DstarLite/test.py deleted file mode 100644 index 66ef1e7..0000000 --- a/zoo/navigator_DstarLite/test.py +++ /dev/null @@ -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: 不显示碰撞信息 ??? - - - - - - - - - - - - - - diff --git a/zoo/opt_bt_expansion/BehaviorTree.py b/zoo/opt_bt_expansion/BehaviorTree.py deleted file mode 100644 index 4513a20..0000000 --- a/zoo/opt_bt_expansion/BehaviorTree.py +++ /dev/null @@ -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 - diff --git a/zoo/opt_bt_expansion/MakeCoffee.ptml b/zoo/opt_bt_expansion/MakeCoffee.ptml deleted file mode 100644 index 40fc6a4..0000000 --- a/zoo/opt_bt_expansion/MakeCoffee.ptml +++ /dev/null @@ -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) -} -} diff --git a/zoo/opt_bt_expansion/OptimalBTExpansionAlgorithm.py b/zoo/opt_bt_expansion/OptimalBTExpansionAlgorithm.py deleted file mode 100644 index b886f8d..0000000 --- a/zoo/opt_bt_expansion/OptimalBTExpansionAlgorithm.py +++ /dev/null @@ -1,210 +0,0 @@ -import copy -import random -from robowaiter.behavior_tree.obtea.BehaviorTree import Leaf,ControlBT - -class CondActPair: - def __init__(self, cond_leaf,act_leaf): - self.cond_leaf = cond_leaf - self.act_leaf = act_leaf - -#定义行动类,行动包括前提、增加和删除影响 -class Action: - def __init__(self,name='anonymous action',pre=set(),add=set(),del_set=set(),cost=1): - self.pre=copy.deepcopy(pre) - self.add=copy.deepcopy(add) - self.del_set=copy.deepcopy(del_set) - self.name=name - self.cost=cost - - def __str__(self): - return self.name - -#生成随机状态 -def generate_random_state(num): - result = set() - for i in range(0,num): - if random.random()>0.5: - result.add(i) - return result -#从状态和行动生成后继状态 -def state_transition(state,action): - if not action.pre <= state: - print ('error: action not applicable') - return state - new_state=(state | action.add) - action.del_set - return new_state - - -#本文所提出的完备规划算法 -class OptBTExpAlgorithm: - def __init__(self,verbose=False): - self.bt = None - self.nodes=[] - self.traversed=[] - self.mounted=[] - self.conditions=[] - self.conditions_index=[] - self.verbose=verbose - - def clear(self): - self.bt = None - self.nodes = [] - self.traversed = [] - self.conditions = [] - self.conditions_index = [] - - #运行规划算法,从初始状态、目标状态和可用行动,计算行为树self.bt - def run_algorithm(self,goal,actions): - if self.verbose: - print("\n算法开始!") - - - self.bt = ControlBT(type='cond') - # 初始行为树只包含目标条件 - gc_node = Leaf(type='cond', content=goal,mincost=0) # 为了统一,都成对出现 - ga_node = Leaf(type='act', content=None, mincost=0) - subtree = ControlBT(type='?') - subtree.add_child([copy.deepcopy(gc_node)]) # 子树首先保留所扩展结 - self.bt.add_child([subtree]) - - # self.conditions.append(goal) - cond_anc_pair = CondActPair(cond_leaf=gc_node,act_leaf=ga_node) - self.nodes.append(copy.deepcopy(cond_anc_pair)) # the set of explored but unexpanded conditions - self.traversed = [goal] # the set of expanded conditions - - - while len(self.nodes)!=0: - - # Find the condition for the shortest cost path - pair_node = None - min_cost = float ('inf') - index= -1 - for i,cond_anc_pair in enumerate(self.nodes): - if cond_anc_pair.cond_leaf.mincost < min_cost: - min_cost = cond_anc_pair.cond_leaf.mincost - pair_node = copy.deepcopy(cond_anc_pair) - index = i - break - - if self.verbose: - print("选择扩展条件结点:",pair_node.cond_leaf.content) - # Update self.nodes and self.traversed - self.nodes.pop(index) # the set of explored but unexpanded conditions. self.nodes.remove(pair_node) - c = pair_node.cond_leaf.content # 子树所扩展结点对应的条件(一个文字的set) - - # Mount the action node and extend BT. T = Eapand(T,c,A(c)) - if c!=goal: - sequence_structure = ControlBT(type='>') - sequence_structure.add_child( - [copy.deepcopy(pair_node.cond_leaf), copy.deepcopy(pair_node.act_leaf)]) - subtree.add_child([copy.deepcopy(sequence_structure)]) # subtree 是回不断变化的,它的父亲是self.bt - if self.verbose: - print("完成扩展 a_node= %s,对应的新条件 c_attr= %s,mincost=%d" \ - % (cond_anc_pair.act_leaf.content.name, cond_anc_pair.cond_leaf.content, - cond_anc_pair.cond_leaf.mincost)) - - if self.verbose: - print("遍历所有动作, 寻找符合条件的动作") - # 遍历所有动作, 寻找符合条件的动作 - current_mincost = pair_node.cond_leaf.mincost # 当前的最短路径是多少 - - for i in range(0, len(actions)): - if not c & ((actions[i].pre | actions[i].add) - actions[i].del_set) <= set(): - if (c - actions[i].del_set) == c: - if self.verbose: - print("———— 满足条件可以扩展") - c_attr = (actions[i].pre | c) - actions[i].add - - # 剪枝操作,现在的条件是以前扩展过的条件的超集 - valid = True - for j in self.traversed: # 剪枝操作 - if j <= c_attr: - valid = False - if self.verbose: - print("———— --被剪枝") - break - - if valid: - # 把符合条件的动作节点都放到列表里 - if self.verbose: - print("———— -- %s 符合条件放入列表" % actions[i].name) - c_attr_node = Leaf(type='cond', content=c_attr, mincost=current_mincost + actions[i].cost) - a_attr_node = Leaf(type='act', content=actions[i], mincost=current_mincost + actions[i].cost) - cond_anc_pair = CondActPair(cond_leaf=c_attr_node, act_leaf=a_attr_node) - self.nodes.append(copy.deepcopy(cond_anc_pair)) # condition node list - self.traversed.append(c_attr) # 重点 the set of expanded conditions - - if self.verbose: - print("算法结束!\n") - return True - - def print_solution(self): - print("========= BT ==========") # 树的bfs遍历 - nodes_ls = [] - nodes_ls.append(self.bt) - while len(nodes_ls) != 0: - parnode = nodes_ls[0] - print("Parrent:", parnode.type) - for child in parnode.children: - if isinstance(child, Leaf): - print("---- Leaf:", child.content) - elif isinstance(child, ControlBT): - print("---- ControlBT:", child.type) - nodes_ls.append(child) - print() - nodes_ls.pop(0) - print("========= BT ==========\n") - - # 返回所有能到达目标状态的初始状态 - def get_all_state_leafs(self): - state_leafs=[] - - nodes_ls = [] - nodes_ls.append(self.bt) - while len(nodes_ls) != 0: - parnode = nodes_ls[0] - for child in parnode.children: - if isinstance(child, Leaf): - if child.type == "cond": - state_leafs.append(child.content) - elif isinstance(child, ControlBT): - nodes_ls.append(child) - nodes_ls.pop(0) - - return state_leafs - - - # 树的dfs - def dfs_ptml(self,parnode): - for child in parnode.children: - if isinstance(child, Leaf): - if child.type == 'cond': - self.ptml_string += "cond " - c_set_str = ', '.join(map(str, child.content)) + "\n" - self.ptml_string += c_set_str - elif child.type == 'act': - self.ptml_string += 'act ' + child.content.name + "\n" - elif isinstance(child, ControlBT): - if parnode.type == '?': - self.ptml_string += "selector{\n" - self.dfs_ptml(parnode=child) - elif parnode.type == '>': - self.ptml_string += "sequence{\n" - self.dfs_ptml( parnode=child) - self.ptml_string += '}\n' - - - def get_ptml(self): - self.ptml_string = "selector{\n" - self.dfs_ptml(self.bt.children[0]) - self.ptml_string += '}\n' - return self.ptml_string - - - def save_ptml_file(self,file_name): - self.ptml_string = "selector{\n" - self.dfs_ptml(self.bt.children[0]) - self.ptml_string += '}\n' - with open(f'./{file_name}.ptml', 'w') as file: - file.write(self.ptml_string) - return self.ptml_string diff --git a/zoo/opt_bt_expansion/README.assets/image-20231103191141047.png b/zoo/opt_bt_expansion/README.assets/image-20231103191141047.png deleted file mode 100644 index 6453647b76058878bfa6767963b630e8410b921c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55821 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%0AKf%5_EYX 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 ZoE*=jX`)、动作节点(`act`)或条件节点(`cond`)。 -- 上述两个类都包含 `tick` 方法。 - -### 2. `OptimalBTExpansionAlgorithm.py` 实现最优行为树扩展算法 - -![image-20231103191141047](README.assets/image-20231103191141047.png) - -定义行动类 -```python -#定义行动类,行动包括前提、增加和删除影响 -class Action: - def __init__(self,name='anonymous action',pre=set(),add=set(),del_set=set(),cost=1): - self.pre=copy.deepcopy(pre) - self.add=copy.deepcopy(add) - self.del_set=copy.deepcopy(del_set) - self.name=name - self.cost=cost - - def __str__(self): - return self.name -``` - -调用算法 -```python -algo = OptBTExpAlgorithm(verbose=True) -algo.clear() -algo.run_algorithm(start, goal, actions) # 使用算法得到行为树在 algo.bt -algo.print_solution() # 打印行为树 -val, obj = algo.bt.tick(state) # 执行行为树 -algo.save_ptml_file("bt.ptml") # 保存行为树为 ptml 文件 -``` - -### 3. **`tools.py`** 实现打印数据、行为树测试等模块 - -使用方法 - -```python -print_action_data_table(goal,start,actions) # 打印所有变量 - -# 行为树鲁棒性测试,随机生成规划问题 -# 设置生成规划问题集的超参数:文字数、解深度、迭代次数 -seed=1 -literals_num=10 -depth = 10 -iters= 10 -BTTest(seed=seed,literals_num=literals_num,depth=depth,iters=iters) -``` - -### 4. `example.py` 中设计规划案例 goals, start, actions - -```python -def MoveBtoB (): - actions=[] - a = Action(name="Move(b,ab)") - a.pre={'Free(ab)','WayClear'} - a.add={'At(b,ab)'} - a.del_set= {'Free(ab)','At(b,pb)'} - a.cost = 1 - actions.append(a) - - a=Action(name="Move(s,ab)") - a.pre={'Free(ab)'} - a.add={'Free(ab)','WayClear'} - a.del_set={'Free(ab)','At(s,ps)'} - a.cost = 1 - actions.append(a) - - a=Action(name="Move(s,as)") - a.pre={'Free(as)'} - a.add={'At(s,ps)','WayClear'} - a.del_set={'Free(as)','At(s,ps)'} - a.cost = 1 - actions.append(a) - - start = {'Free(ab)','Free(as)','At(b,pb)','At(s,ps)'} - goal= {'At(b,ab)'} - return goal,start,actions -``` - -### 5. `opt_bt_exp_main.py` 为主函数,在此演示如何调用最优行为树扩展算法得到完全扩展最优行为树 - -初始化的时候:传入 actions (包含 pre,add,del,cost). -调用的时候,传入 goal 状态集合 (set类型),返回完全最优扩展行为树的 ptml 形式 (string类型) - -```python -actions=[ - Action(name='PutDown(Table,Coffee)', pre={'Holding(Coffee)','At(Robot,Table)'}, add={'At(Table,Coffee)','NotHolding'}, del_set={'Holding(Coffee)'}, cost=1) - ………… -] -algo = BTOptExpInterface(actions) - -goal = {'At(Table,Coffee)'} -ptml_string = algo.process(goal,start) -print(ptml_string) - -``` -两种检测方法,用于检测当前状态 `start` 能否到达目标状态 `goal` - -```python -# 判断初始状态能否到达目标状态 -start = {'At(Robot,Bar)', 'Holding(VacuumCup)', 'Available(Table)', 'Available(CoffeeMachine)','Available(FrontDesk)'} - -# 方法一:算法返回所有可能的初始状态,在里面看看有没有对应的初始状态 -right_bt = algo.find_all_leaf_states_contain_start(start) -if not right_bt: - print("ERROR1: The current state cannot reach the goal state!") -else: - print("Right1: The current state can reach the goal state!") - -# 方法二:预先跑一边行为树,看能否到达目标状态 -right_bt2 = algo.run_bt_from_start(goal,start) -if not right_bt2: - print("ERROR2: The current state cannot reach the goal state!") -else: - print("Right2: The current state can reach the goal state!") - -``` - diff --git a/zoo/opt_bt_expansion/__init__.py b/zoo/opt_bt_expansion/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/zoo/opt_bt_expansion/examples.py b/zoo/opt_bt_expansion/examples.py deleted file mode 100644 index 019af9b..0000000 --- a/zoo/opt_bt_expansion/examples.py +++ /dev/null @@ -1,174 +0,0 @@ - -from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action - - - -def MakeCoffee(): - actions=[ - Action(name='Put(Table,Coffee)', pre={'Holding(Coffee)','At(Table)'}, add={'At(Table,Coffee)','NotHolding'}, del_set={'Holding(Coffee)'}, cost=1), - Action(name='Put(Table,VacuumCup)', pre={'Holding(VacuumCup)','At(Table)'}, add={'At(Table,VacuumCup)','NotHolding'}, del_set={'Holding(VacuumCup)'}, cost=1), - - Action(name='Grasp(Coffee)', pre={'NotHolding','At(Coffee)'}, add={'Holding(Coffee)'}, del_set={'NotHolding'}, cost=1), - - Action(name='MoveTo(Table)', pre={'Exist(Table)'}, add={'At(Table)'}, del_set={'At(FrontDesk)','At(Coffee)','At(CoffeeMachine)'}, cost=1), - Action(name='MoveTo(Coffee)', pre={'Exist(Coffee)'}, add={'At(Coffee)'}, del_set={'At(FrontDesk)','At(Table)','At(CoffeeMachine)'}, cost=1), - Action(name='MoveTo(CoffeeMachine)', pre={'Exist(CoffeeMachine)'}, add={'At(CoffeeMachine)'}, del_set={'At(FrontDesk)','At(Coffee)','At(Table)'}, cost=1), - - Action(name='OpCoffeeMachine', pre={'At(CoffeeMachine)','NotHolding'}, add={'Exist(Coffee)','At(Coffee)'}, del_set=set(), cost=1), - ] - - start = {'At(FrontDesk)','Holding(VacuumCup)','Exist(Table)','Exist(CoffeeMachine)','Exist(FrontDesk)'} - goal = {'At(Table,Coffee)'} - return goal,start,actions - -# 本例子中,将 VacuumCup 放到 FrontDesk,比 MoveTo(Table) 再 Put(Table,VacuumCup) 的 cost 要小 -def MakeCoffeeCost(): - actions=[ - Action(name='PutDown(Table,Coffee)', pre={'Holding(Coffee)','At(Robot,Table)'}, add={'At(Table,Coffee)','NotHolding'}, del_set={'Holding(Coffee)'}, cost=1), - Action(name='PutDown(Table,VacuumCup)', pre={'Holding(VacuumCup)','At(Robot,Table)'}, add={'At(Table,VacuumCup)','NotHolding'}, del_set={'Holding(VacuumCup)'}, cost=1), - - Action(name='PickUp(Coffee)', pre={'NotHolding','At(Robot,Coffee)'}, add={'Holding(Coffee)'}, del_set={'NotHolding'}, cost=1), - - Action(name='MoveTo(Table)', pre={'Available(Table)'}, add={'At(Robot,Table)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Coffee)','At(Robot,CoffeeMachine)'}, cost=1), - Action(name='MoveTo(Coffee)', pre={'Available(Coffee)'}, add={'At(Robot,Coffee)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Table)','At(Robot,CoffeeMachine)'}, cost=1), - Action(name='MoveTo(CoffeeMachine)', pre={'Available(CoffeeMachine)'}, add={'At(Robot,CoffeeMachine)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Coffee)','At(Robot,Table)'}, cost=1), - - Action(name='OpCoffeeMachine', pre={'At(Robot,CoffeeMachine)','NotHolding'}, add={'Available(Coffee)','At(Robot,Coffee)'}, del_set=set(), cost=1), - ] - - start = {'At(Robot,Bar)','Holding(VacuumCup)','Available(Table)','Available(CoffeeMachine)','Available(FrontDesk)'} - goal = {'At(Table,Coffee)'} - - return goal,start,actions - - -# test -def Test(): - actions=[ - Action(name='a1', pre={6}, add={0,2,4}, del_set={1,5}, cost=1), - Action(name='a2', pre=set(), add={0,1}, del_set=set(), cost=1), - Action(name='a3', pre={1,6}, add={0,2,3,5}, del_set={1,6}, cost=1), - Action(name='a4', pre={0,2,3}, add={4,5}, del_set={0,6}, cost=1), - Action(name='a5', pre={0,1,4}, add={2,3,6}, del_set={0}, cost=1), - ] - - start = {1,2,6} - goal={0,1,2,4,6} - return goal,start,actions - -# def Test(): -# actions=[ -# Action(name='a1', pre={2}, add={1}, del_set=set(), cost=1), -# Action(name='a2', pre=set(), add={1}, del_set={0,2}, cost=1), -# Action(name='a3', pre={1}, add=set(), del_set={0,2}, cost=1), -# Action(name='a4', pre=set(), add={0}, del_set=set(), cost=1), -# Action(name='a5', pre={1}, add={0,2}, del_set={1}, cost=1), -# Action(name='a6', pre={1}, add=set(), del_set={0,1,2}, cost=1), -# Action(name='a7', pre={1}, add={2}, del_set={0, 2}, cost=1), -# ] -# -# start = {1,2} -# goal={0,1} -# return goal,start,actions - - -# todo: 最原始的例子 -def MoveBtoB_num (): - actions=[] - a = Action(name='a1') - a.pre={1,4} - a.add={"c_goal"} - a.del_set={1,4} - a.cost = 1 - actions.append(a) - - a=Action(name='a2') - a.pre={1,2,3} - a.add={"c_goal"} - a.del_set={1,2,3} - a.cost = 1 - actions.append(a) - - a=Action(name='a3') - a.pre={1,2} - a.add={4} - a.del_set={2} - a.cost = 1 - actions.append(a) - - a=Action(name='a4') - a.pre={"c_start"} - a.add={1,2,3} - a.del_set={"c_start",4} - a.cost = 1 - actions.append(a) - - start = {"c_start"} - goal={"c_goal"} - return goal,start,actions - - -# todo: 最原始的例子 -def MoveBtoB (): - actions=[] - a = Action(name="Move(b,ab)") #'movebtob' - a.pre={'Free(ab)','WayClear'} #{1,2} - a.add={'At(b,ab)'} #{3} - a.del_set= {'Free(ab)','At(b,pb)'} #{1,4} - a.cost = 1 - actions.append(a) - - a=Action(name="Move(s,ab)") #'moveatob' - a.pre={'Free(ab)'} #{1} - a.add={'Free(ab)','WayClear'} #{5,2} - a.del_set={'Free(ab)','At(s,ps)'} #{1,6} - a.cost = 1 - actions.append(a) - - a=Action(name="Move(s,as)") #'moveatoa' - a.pre={'Free(as)'} #{7} - a.add={'At(s,ps)','WayClear'} #{8,2} - a.del_set={'Free(as)','At(s,ps)'} #{7,6} - a.cost = 1 - actions.append(a) - - start = {'Free(ab)','Free(as)','At(b,pb)','At(s,ps)'} #{1,7,4,6} - goal= {'At(b,ab)'} #{3} - return goal,start,actions - - -# 小蔡师兄论文里的例子 -def Cond2BelongsToCond3(): - actions=[] - a = Action(name='a1') - a.pre={1,4} - a.add={"c_goal"} - a.del_set={1,4} - a.cost = 1 - actions.append(a) - - a=Action(name='a2') - a.pre={1,2,3} - a.add={"c_goal"} - a.del_set={1,2,3} - a.cost = 100 - actions.append(a) - - a=Action(name='a3') - a.pre={1,2} - a.add={4} - a.del_set={2} - a.cost = 1 - actions.append(a) - - a=Action(name='a4') - a.pre={"c_start"} - a.add={1,2,3} - a.del_set={"c_start",4} - a.cost = 1 - actions.append(a) - - start = {"c_start"} - goal={"c_goal"} - return goal,start,actions - diff --git a/zoo/opt_bt_expansion/opt_bt_exp_main.py b/zoo/opt_bt_expansion/opt_bt_exp_main.py deleted file mode 100644 index 87329b6..0000000 --- a/zoo/opt_bt_expansion/opt_bt_exp_main.py +++ /dev/null @@ -1,118 +0,0 @@ -from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm,state_transition # 调用最优行为树扩展算法 -from opt_bt_expansion.examples import * - - -# 封装好的主接口 -class BTOptExpInterface: - def __init__(self, action_list): - """ - Initialize the BTOptExpansion with a list of actions. - :param action_list: A list of actions to be used in the behavior tree. - """ - # self.actions = [] - # for act in action_list: - # a = Action(name=act.name) - # a.pre=act['pre'] - # a.add=act['add'] - # a.del_set= act['del_set'] - # a.cost = 1 - # self.actions.append(a) - self.actions = action_list - self.has_processed = False - - def process(self, goal): - """ - Process the input sets and return a string result. - :param input_set: The set of goal states and the set of initial states. - :return: A PTML string representing the outcome of the behavior tree. - """ - self.goal = goal - self.algo = OptBTExpAlgorithm(verbose=False) - self.algo.clear() - self.algo.run_algorithm(self.goal, self.actions) # 调用算法得到行为树保存至 algo.bt - self.ptml_string = self.algo.get_ptml() - self.has_processed = True - # algo.print_solution() # print behavior tree - - return self.ptml_string - - # 方法一:查找所有初始状态是否包含当前状态 - def find_all_leaf_states_contain_start(self,start): - if not self.has_processed: - raise RuntimeError("The process method must be called before find_all_leaf_states_contain_start!") - # 返回所有能到达目标状态的初始状态 - state_leafs = self.algo.get_all_state_leafs() - for state in state_leafs: - if start >= state: - return True - return False - - # 方法二:模拟跑一遍行为树,看 start 能够通过执行一系列动作到达 goal - def run_bt_from_start(self,goal,start): - if not self.has_processed: - raise RuntimeError("The process method must be called before run_bt_from_start!") - # 检查是否能到达目标 - right_bt = True - state = start - steps = 0 - val, obj = self.algo.bt.tick(state) - while val != 'success' and val != 'failure': - state = state_transition(state, obj) - val, obj = self.algo.bt.tick(state) - if (val == 'failure'): - # print("bt fails at step", steps) - right_bt = False - steps += 1 - if not goal <= state: - # print("wrong solution", steps) - right_bt = False - else: - pass - # print("right solution", steps) - return right_bt - - - - -if __name__ == '__main__' : - - # todo: Example Cafe - # todo: Define goal, start, actions - actions=[ - Action(name='PutDown(Table,Coffee)', pre={'Holding(Coffee)','At(Robot,Table)'}, add={'At(Table,Coffee)','NotHolding'}, del_set={'Holding(Coffee)'}, cost=1), - Action(name='PutDown(Table,VacuumCup)', pre={'Holding(VacuumCup)','At(Robot,Table)'}, add={'At(Table,VacuumCup)','NotHolding'}, del_set={'Holding(VacuumCup)'}, cost=1), - - Action(name='PickUp(Coffee)', pre={'NotHolding','At(Robot,Coffee)'}, add={'Holding(Coffee)'}, del_set={'NotHolding'}, cost=1), - - Action(name='MoveTo(Table)', pre={'Available(Table)'}, add={'At(Robot,Table)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Coffee)','At(Robot,CoffeeMachine)'}, cost=1), - Action(name='MoveTo(Coffee)', pre={'Available(Coffee)'}, add={'At(Robot,Coffee)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Table)','At(Robot,CoffeeMachine)'}, cost=1), - Action(name='MoveTo(CoffeeMachine)', pre={'Available(CoffeeMachine)'}, add={'At(Robot,CoffeeMachine)'}, del_set={'At(Robot,FrontDesk)','At(Robot,Coffee)','At(Robot,Table)'}, cost=1), - - Action(name='OpCoffeeMachine', pre={'At(Robot,CoffeeMachine)','NotHolding'}, add={'Available(Coffee)','At(Robot,Coffee)'}, del_set=set(), cost=1), - ] - algo = BTOptExpInterface(actions) - - - goal = {'At(Table,Coffee)'} - ptml_string = algo.process(goal) - print(ptml_string) - - file_name = "MakeCoffee" - with open(f'./{file_name}.ptml', 'w') as file: - file.write(ptml_string) - - - # 判断初始状态能否到达目标状态 - start = {'At(Robot,Bar)', 'Holding(VacuumCup)', 'Available(Table)', 'Available(CoffeeMachine)','Available(FrontDesk)'} - # 方法一:算法返回所有可能的初始状态,在里面看看有没有对应的初始状态 - right_bt = algo.find_all_leaf_states_contain_start(start) - if not right_bt: - print("ERROR1: The current state cannot reach the goal state!") - else: - print("Right1: The current state can reach the goal state!") - # 方法二:预先跑一边行为树,看能否到达目标状态 - right_bt2 = algo.run_bt_from_start(goal,start) - if not right_bt2: - print("ERROR2: The current state cannot reach the goal state!") - else: - print("Right2: The current state can reach the goal state!") diff --git a/zoo/opt_bt_expansion/tools.py b/zoo/opt_bt_expansion/tools.py deleted file mode 100644 index c2e0695..0000000 --- a/zoo/opt_bt_expansion/tools.py +++ /dev/null @@ -1,167 +0,0 @@ - - -from tabulate import tabulate -import numpy as np -import random -from zoo.opt_bt_expansion.OptimalBTExpansionAlgorithm import Action,OptBTExpAlgorithm -import time - - -def print_action_data_table(goal,start,actions): - data = [] - for a in actions: - data.append([a.name , a.pre , a.add , a.del_set , a.cost]) - data.append(["Goal" ,goal ," " ,"Start" ,start]) - print(tabulate(data, headers=["Name", "Pre", "Add" ,"Del" ,"Cost"], tablefmt="fancy_grid")) # grid plain simple github fancy_grid - - -# 从状态随机生成一个行动 -def generate_from_state(act,state,num): - for i in range(0,num): - if i in state: - if random.random() >0.5: - act.pre.add(i) - if random.random() >0.5: - act.del_set.add(i) - continue - if random.random() > 0.5: - act.add.add(i) - continue - if random.random() >0.5: - act.del_set.add(i) - -def print_action(act): - print (act.pre) - print(act.add) - print(act.del_set) - - - -#行为树测试代码 -def BTTest(seed=1,literals_num=10,depth=10,iters=10,total_count=1000): - print("============= BT Test ==============") - random.seed(seed) - # 设置生成规划问题集的超参数:文字数、解深度、迭代次数 - literals_num=literals_num - depth = depth - iters= iters - total_tree_size = [] - total_action_num = [] - total_state_num = [] - total_steps_num=[] - #fail_count=0 - #danger_count=0 - success_count =0 - failure_count = 0 - planning_time_total = 0.0 - # 实验1000次 - for count in range (total_count): - - action_num = 1 - - # 生成一个规划问题,包括随机的状态和行动,以及目标状态 - states = [] - actions = [] - start = generate_random_state(literals_num) - state = start - states.append(state) - #print (state) - for i in range (0,depth): - a = Action() - generate_from_state(a,state,literals_num) - if not a in actions: - a.name = "a"+str(action_num) - action_num+=1 - actions.append(a) - state = state_transition(state,a) - if state in states: - pass - else: - states.append(state) - #print(state) - - goal = states[-1] - state = start - for i in range (0,iters): - a = Action() - generate_from_state(a,state,literals_num) - if not a in actions: - a.name = "a"+str(action_num) - action_num+=1 - actions.append(a) - state = state_transition(state,a) - if state in states: - pass - else: - states.append(state) - state = random.sample(states,1)[0] - - # 选择测试本文算法btalgorithm,或对比算法weakalgorithm - algo = OptBTExpAlgorithm() - #algo = Weakalgorithm() - start_time = time.time() - # print_action_data_table(goal, start, list(actions)) - if algo.run_algorithm(start, goal, actions):#运行算法,规划后行为树为algo.bt - total_tree_size.append( algo.bt.count_size()-1) - # algo.print_solution() # 打印行为树 - else: - print ("error") - end_time = time.time() - planning_time_total += (end_time-start_time) - - #开始从初始状态运行行为树,测试 - state=start - steps=0 - val, obj = algo.bt.tick(state)#tick行为树,obj为所运行的行动 - while val !='success' and val !='failure':#运行直到行为树成功或失败 - state = state_transition(state,obj) - val, obj = algo.bt.tick(state) - if(val == 'failure'): - print("bt fails at step",steps) - steps+=1 - if(steps>=500):#至多运行500步 - break - if not goal <= state:#错误解,目标条件不在执行后状态满足 - #print ("wrong solution",steps) - failure_count+=1 - - else:#正确解,满足目标条件 - #print ("right solution",steps) - success_count+=1 - total_steps_num.append(steps) - algo.clear() - total_action_num.append(len(actions)) - total_state_num.append(len(states)) - - print ("success:",success_count,"failure:",failure_count)#算法成功和失败次数 - print("Total Tree Size: mean=",np.mean(total_tree_size), "std=",np.std(total_tree_size, ddof=1))#1000次测试树大小 - print ("Total Steps Num: mean=",np.mean(total_steps_num),"std=",np.std(total_steps_num,ddof=1)) - print ("Average number of states:",np.mean(total_state_num))#1000次问题的平均状态数 - print ("Average number of actions",np.mean(total_action_num))#1000次问题的平均行动数 - print("Planning Time Total:",planning_time_total,planning_time_total/1000.0) - print("============ End BT Test ===========") - - # xiao cai - # success: 1000 failure: 0 - # Total Tree Size: mean= 35.303 std= 29.71336526001515 - # Total Steps Num: mean= 1.898 std= 0.970844240101644 - # Average number of states: 20.678 - # Average number of actions 20.0 - # Planning Time Total: 0.6280641555786133 0.0006280641555786133 - - # our start - # success: 1000 failure: 0 - # Total Tree Size: mean= 17.945 std= 12.841997192488865 - # Total Steps Num: mean= 1.785 std= 0.8120556843187752 - # Average number of states: 20.678 - # Average number of actions 20.0 - # Planning Time Total: 1.4748523235321045 0.0014748523235321046 - - # our - # success: 1000 failure: 0 - # Total Tree Size: mean= 48.764 std= 20.503626574406358 - # Total Steps Num: mean= 1.785 std= 0.8120556843187752 - # Average number of states: 20.678 - # Average number of actions 20.0 - # Planning Time Total: 3.3271877765655518 0.0033271877765655516 -