This commit is contained in:
2193177243@qq.com 2023-10-25 20:03:30 +08:00
commit e9444e65c0
74 changed files with 635 additions and 192 deletions

View File

@ -1,15 +1,65 @@
# RoboWaiter
大模型具身智能比赛-机器人控制端
### 机器人控制
1. 加载场景
```python
from scene_utils import control
control.init_world(scene_num=1, mapID=3)
```
当前只有一个咖啡馆场景。加载操作只需要执行一遍,当引擎进入相应场景后,可以用`control.reset()`重置场景。
# 项目安装(必看)
## 环境要求
Python=3.10
2. 物品类别
### 安装步骤
```shell
cd RoboWaiter
pip install -e .
```
以上步骤将完成robowaiter项目以及相关依赖库的安装
### 快速入门
1. 安装UE及Harix插件打开默认项目并运行
2. 运行 run_robowaiter.py 文件即可实现机器人控制端与仿真器的交互
# 运行流程介绍
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是任务字典通过任务缩写来返回相应的场景类。
| 缩写 | 任务 |
|----|---------|
| AEM | 主动探索和记忆 |
| GQA | 具身多轮对话 |
| VLN | 视觉语言导航 |
| VLM | 视觉语言操作 |
| OT | 复杂开放任务 |
| AT | 自主任务 |
## Scene
Scene是场景基类task_map返回的任务场景都继承于Scene。
该类实现了一些通用的场景操作接口。
### 场景中物品类别
| ID | Item |
|-----|----------------------|
@ -115,4 +165,11 @@ control.init_world(scene_num=1, mapID=3)
| 252 | Floor |
| 253 | Roof |
| 254 | Wall |
注意78及以后无法使用add_object方法生成
注意78及以后无法使用add_object方法生成
# 调用大模型接口
运行llm_client.py文件调用大模型进行多轮对话。
```shell
python llm_client.py
```
输入字符即可等待回答输入end表示对话结束。

29
llm_clients.py Normal file
View File

@ -0,0 +1,29 @@
import requests
import urllib3
########################################
# 该文件实现了与大模型的简单通信、多轮对话输入end表示对话结束
########################################
# 忽略https的安全性警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url = "https://45.125.46.134:25344/v1/chat/completions"
headers = {"Content-Type": "application/json"}
#在这里输入你的问题
k=input()
data_memory=[]
n=1
while k!='end':
question_now=k
user_dict={"role": "user","content":question_now}
data_memory.append(user_dict)
#print(data_memory)
response = requests.post(url, headers=headers, json={"messages":data_memory, "repetition_penalty": 1.0}, verify=False)
answer=response.json()['choices'][n]['message']['content']
print(answer)
assistant_dict={"role": "assistant","content":answer}
data_memory.append(assistant_dict)
n=n+2
k=input()

View File

@ -1,28 +0,0 @@
import requests
import urllib3
########################################
# 该文件实现了与大模型的简单通信
########################################
# 忽略https的安全性警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
#在这里输入你的问题
question = "假设你是一个咖啡厅的机器人服务员,有一个顾客的请求是'请给我一杯咖啡',请生成对应的行为树来控制机器人完成该动作"
url = "https://45.125.46.134:25344/v1/completions"
headers = {"Content-Type": "application/json"}
data = {
"prompt": question
}
response = requests.post(url, headers=headers, json=data, verify=False)
if response.status_code == 200:
result = response.json()
print(f'问题:{question}')
print('回答:' + result['choices'][0]['text'])
else:
print("请求失败:", response.status_code)

8
requirements.txt Normal file
View File

@ -0,0 +1,8 @@
antlr4-python3-runtime
py_trees
shortuuid
protobuf==3.20.0
gym==0.21.0
grpcio==1.53.0
requests
urllib3

3
robowaiter/__init__.py Normal file
View File

@ -0,0 +1,3 @@
from robowaiter.robot.robot import Robot
from robowaiter.scene import task_map

View File

@ -0,0 +1,22 @@
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 a base behavior node.")
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -1,10 +1,10 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib.Behavior import Bahavior
class CoffeeCupFound(ptree.behaviour.Behaviour):
class CoffeeCupFound(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name)
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)

View File

@ -1,10 +1,11 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib.Behavior import Bahavior
class CoffeeCupGrasped(ptree.behaviour.Behaviour):
class CoffeeCupGrasped(Bahavior):
def __init__(self, name: str, scene):
super().__init__(name)
super().__init__(name, scene)
def setup(self, **kwargs: Any) -> None:
return super().setup(**kwargs)

View File

@ -0,0 +1,20 @@
import py_trees as ptree
from typing import Any
from robowaiter.behavior_lib.Behavior import Bahavior
class IsChatting(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?
return ptree.common.Status.SUCCESS
def terminate(self, new_status: ptree.common.Status) -> None:
return super().terminate(new_status)

View File

@ -0,0 +1,3 @@
# from robowaiter.behavior_tree.behavior_tree import BehaviorTree
from robowaiter.behavior_tree.behavior_tree import load_bt_from_ptml

View File

@ -0,0 +1,19 @@
import py_trees as ptree
from robowaiter.behavior_tree.ptml import ptmlCompiler
def load_bt_from_ptml(scene, ptml_path, behavior_lib_path):
ptml_bt = ptmlCompiler.load(scene, ptml_path, behavior_lib_path)
bt = ptree.trees.BehaviourTree(ptml_bt)
with open(ptml_path, 'r') as f:
ptml = f.read()
print(f'BT loaded: \n {ptml}')
# print(ptree.display.unicode_tree(root=bt.root, show_status=True))
return bt
# class BehaviorTree(ptree):
# def __init__(self):
# super().__init__()

View File

@ -1,35 +1,35 @@
//sequence:
// act action1()
// act action2(2, 2.3, True)
//
// parallel 2:
// act action3(int a, float b)
// act action4()
sequence{
selector{
cond CoffeeCupFound()
task FindCoffeeCup()
sequence{
cond SeqTest()
task Move(1.2, 2, 2.3, True)
task Grasp()
parallel 3 {
cond Istask()
task TestTask()
}
}
}
selector{
cond CoffeeCupGrasped()
task GraspCoffeeCup()
}
selector{
cond DestinationAReached()
task ReachDestinationA()
}
selector{
cond CoffeeCupPlaced()
task PlaceCoffeeCup()
}
}
//sequence:
// act action1()
// act action2(2, 2.3, True)
//
// parallel 2:
// act action3(int a, float b)
// act action4()
sequence{
selector{
cond CoffeeCupFound()
task FindCoffeeCup()
sequence{
cond SeqTest()
task Move(1.2, 2, 2.3, True)
task Grasp()
parallel 3 {
cond Istask()
task TestTask()
}
}
}
selector{
cond CoffeeCupGrasped()
task GraspCoffeeCup()
}
selector{
cond DestinationAReached()
task ReachDestinationA()
}
selector{
cond CoffeeCupPlaced()
task PlaceCoffeeCup()
}
}

View File

@ -1,16 +1,16 @@
import os
import py_trees as ptree
from scene_utils import control
from scene import scene
from ptmlCompiler import load
if __name__ == '__main__':
project_path = "."
project_path = ""
ptml_path = os.path.join(project_path, 'CoffeeDelivery.ptml')
behavior_lib_path = os.path.join(project_path, 'behaviour_lib')
behavior_lib_path = os.path.join(project_path, '../../behavior_lib')
scene = control.Scene(sceneID=0)
# load

View File

@ -0,0 +1,31 @@
import requests
import urllib3
########################################
# 该文件实现了与大模型的简单通信
########################################
# 忽略https的安全性警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def ask_llm(question):
url = "https://45.125.46.134:25344/v1/completions"
headers = {"Content-Type": "application/json"}
data = {
"prompt": question
}
response = requests.post(url, headers=headers, json=data, verify=False)
if response.status_code == 200:
result = response.json()
return result['choices'][0]['text']
else:
return "大模型请求失败:", response.status_code
if __name__ == '__main__':
question = "假设你是一个咖啡厅的机器人服务员,有一个顾客的请求是'请给我一杯咖啡',请生成对应的行为树来控制机器人完成该动作"
print(ask_llm(question))

View File

@ -0,0 +1,105 @@
//sequence:
// act action1()
// act action2(2, 2.3, True)
//
// parallel 2:
// act action3(int a, float b)
// act action4()
sequence{
selector{
cond CoffeeCupFound()
task FindCoffeeCup()
sequence{
cond SeqTest()
task Move(1.2, 2, 2.3, True)
task Grasp()
parallel 3 {
cond Istask()
task TestTask()
}
}
}
selector{
cond CoffeeCupGrasped()
task GraspCoffeeCup()
}
selector{
cond DestinationAReached()
task ReachDestinationA()
}
selector{
cond CoffeeCupPlaced()
task PlaceCoffeeCup()
}
}
sequence{
cond NotAt(Coffee,Table)
selector{
cond Holding(Coffee)
sequence{
selector{
cond At(Bar)
task MoveTo(Bar)
}
task Grasp(Coffee)
}
}
selector{
cond At(Table)
task MoveTo(Table)
}
Put(Coffee)
}
selector{
sequence{
cond At(Table)
cond Holding(Coffee)
task Put(Table)
}
sequence{
cond NotHolding(Coffee)
task MoveTo(Table)
}
sequence{
cond At(Bar)
task Grasp(Coffee)
}
task MoveTo(Table)
sequence{
cond Coffee()
task FindCoffeeCup()
sequence{
cond SeqTest()
task Move(1.2, 2, 2.3, True)
task Grasp()
parallel 3 {
cond Istask()
task TestTask()
}
}
}
selector{
cond At(Table,Coffee)
task GraspCoffeeCup()
}
selector{
cond DestinationAReached()
task ReachDestinationA()
}
selector{
cond CoffeeCupPlaced()
task PlaceCoffeeCup()
}
}

View File

20
robowaiter/robot/robot.py Normal file
View File

@ -0,0 +1,20 @@
import os
import py_trees as ptree
from robowaiter.behavior_tree import load_bt_from_ptml
class Robot(object):
scene = None
def __init__(self,ptml_path,behavior_lib_path):
self.ptml_path = ptml_path
self.behavior_lib_path = behavior_lib_path
def set_scene(self,scene):
self.scene = scene
def load_BT(self):
self.bt = load_bt_from_ptml(self.scene, self.ptml_path,self.behavior_lib_path)
if __name__ == '__main__':
pass

View File

@ -0,0 +1,17 @@
from .scene import Scene
from robowaiter.scene.tasks.AEM import SceneAEM
from robowaiter.scene.tasks.GQA import SceneGQA
from robowaiter.scene.tasks.VLN import SceneVLN
from robowaiter.scene.tasks.VLM import SceneVLM
from robowaiter.scene.tasks.Open_tasks import SceneOT
from robowaiter.scene.tasks.Auto_tasks import SceneAT
task_map = {
"AEM": SceneAEM,
"GQA": SceneGQA,
"VLN": SceneVLN,
"VLM": SceneVLM,
"OT": SceneOT,
"AT": SceneAT,
}

View File

@ -1,12 +1,10 @@
import time
import gym
import grpc
import numpy as np
from proto import GrabSim_pb2
from proto import GrabSim_pb2_grpc
from robowaiter.proto import GrabSim_pb2
from robowaiter.proto import GrabSim_pb2_grpc
from ptml import ptmlCompiler
channel = grpc.insecure_channel(
"localhost:30001",
@ -21,7 +19,7 @@ animation_step = [4, 5, 7, 3, 3]
loc_offset = [-700, -1400]
def init_world(scene_num, mapID):
def init_world(scene_num=1, mapID=3):
stub.SetWorld(GrabSim_pb2.BatchMap(count=scene_num, mapID=mapID))
time.sleep(3) # wait for the map to load
@ -34,7 +32,21 @@ def image_extract(camera_data):
class Scene:
robot = None
state = {}
"""
# 当前场景的状态
state: {
"chat_pool": [ #未处理的顾客的对话池
{
"pos": 顾客的位置,
"chat": 顾客对话的内容
}
],
"status": # 仿真器中的观测信息,见下方详细解释
}
status:
location: Dict[X: float, Y: float]
rotation: Dict[Yaw: float]
@ -47,18 +59,40 @@ class Scene:
collision: str, info: str
"""
def __init__(self, sceneID=0):
def __init__(self,robot, sceneID=0):
self.sceneID = sceneID
self.use_offset = True
self.BT = None
# init robot
robot.set_scene(self)
robot.load_BT()
self.robot = robot
def run(self):
pass
@property
def status(self):
return stub.Observe(GrabSim_pb2.SceneID(value=self.sceneID))
def reset(self):
def reset_sim(self):
stub.Reset(GrabSim_pb2.ResetParams(scene=self.sceneID))
# reset world
init_world()
# reset state
self.state = {
"chatting_list": []
}
def reset(self):
self.reset_sim()
def walker_control_generator(self, walkerID, autowalk, speed, X, Y, Yaw):
if self.use_offset:
X, Y = X + loc_offset[0], Y + loc_offset[1]
@ -239,5 +273,3 @@ class Scene:
def animation_reset(self):
stub.ControlRobot(GrabSim_pb2.ControlInfo(scene=self.sceneID, type=0, action=0))
def load_BT(self, ptml_path, behaviour_lib_path):
self.BT = ptmlCompiler.load(self, ptml_path, behaviour_lib_path)

View File

@ -0,0 +1,26 @@
"""
环境主动探索和记忆
要求输出探索结果语义地图对环境重点信息记忆生成环境的语义拓扑地图和不少于10个环境物品的识别和位置记忆可以是图片或者文字或者格式化数据
"""
from robowaiter.scene.scene import Scene
class SceneAEM(Scene):
def __init__(self, robot):
super().__init__(robot)
# control.init_world(1, 3)
def reset(self):
self.reset_sim()
self.add_object(0, 570, 1600, 85.5) # type与物品编号对应具体参考README.md
self.add_object(1, 570, 1630, 85.5)
self.add_object(2, 570, 1660, 85.5)
self.add_object(3, 580, 1680, 85.5)
# todo: 探索并获得语义地图
print(self.status.objects) # 全部的物品信息,包括名称、位置等,与获得的语义地图进行对比
def run(self):
pass

View File

@ -0,0 +1,26 @@
"""
在特定环境下机器人发现目标可自主完成任务
1. 打扫地面地面有垃圾机器人主动扫地清理地面垃圾
2. 收拾桌子桌子上的污渍机器人主动擦桌子
3. 摆椅子椅子不正机器人主动摆正椅子
4. 开灯室内光线暗机器人主动打开房屋的灯
"""
# todo: 通过行为树控制自动任务
from robowaiter.scene.scene import Scene
class SceneAT(Scene):
def __init__(self, robot):
super().__init__(robot)
def reset(self):
self.reset_sim()
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
def run(self):
self.chat_bubble("顾客说:请给我一杯咖啡")

View File

@ -1,5 +1,5 @@
"""
具身多轮对话
具身多轮对话 GQA
点餐order的对话咖啡厅服务员可以为客人NPC完成点餐基本对话
场景对话GQA结合场景询问卫生间附近娱乐场所数据来源自主定义
开始条件顾客NPC发出点餐指令
@ -8,3 +8,21 @@
# todo: 使用大模型进行对话,获得指令信息,适时结束对话
# order = {...}
from robowaiter.scene.scene import Scene
class SceneGQA(Scene):
def __init__(self, robot):
super().__init__(robot)
def reset(self):
self.reset_sim()
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
def run(self):
self.chat_bubble("顾客说123546567")

View File

@ -8,12 +8,24 @@
"""
from scene_utils import control
# control.init_world(1, 3)
scene = control.Scene(sceneID=0)
scene.reset()
# todo: 接收点单信息,大模型生成任务规划
from robowaiter.scene.scene import Scene
class SceneOT(Scene):
def __init__(self, robot):
super().__init__(robot)
def reset(self):
self.reset_sim()
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
def run(self):
self.chat_bubble("顾客说:请给我一杯咖啡")

View File

@ -0,0 +1,36 @@
"""
视觉语言操作
机器人根据指令人的指令调节空调自主探索环境导航到目标点通过手臂的运动规划能力操作空调比如开关按钮调温按钮显示面板
"""
import time
from robowaiter.scene.scene import Scene
class SceneVLM(Scene):
def __init__(self, robot):
super().__init__(robot)
def reset(self):
self.reset_sim()
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
def run(self):
# 空调操作
self.walk_to(950, 1260, 90) # 没法转向?
# todo: 手臂操作
time.sleep(5)
self.walk_to(947, 1900, 0)
# 物品挪动
# todo: 视觉导航至目标点,操作手臂至可抓位置
"""
scene.grasp(1, your_objectID)
"""
# todo: 视觉导航至目标点,找准释放位置
"""
scene.release(1)
"""

View File

@ -0,0 +1,39 @@
"""
视觉语言导航
识别顾客NPC靠近打招呼对话领位导航到适合人数的空闲餐桌
开始条件监测到顾客靠近
结束条件完成领位语音请问您想喝点什么并等待下一步指令
"""
from robowaiter.scene.scene import Scene
class SceneVLN(Scene):
def __init__(self, robot):
super().__init__(robot)
def reset(self):
self.reset_sim()
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
def run(self):
# 实现单顾客领位
self.add_walker(1085, 2630, 220)
self.control_walker([self.walker_control_generator(0, False, 100, 755, 1900, 180)])
# todo: 监测到顾客靠近,打招呼,对话,识别获取空闲餐桌位置
# 可以使用scene.chat_bubble(message)函数实现对话
"""
scene.walk_to(your_free_table_location)
time.sleep(5)
scene.control_walker([scene.walker_control_generator(your_free_table_location)])
"""
reach = True
if reach:
self.chat_bubble("请问您想喝点什么?")
print(self.status.walkers)

View File

16
run_robowaiter.py Normal file
View File

@ -0,0 +1,16 @@
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()

View File

@ -1 +0,0 @@
from . import control

22
setup.py Normal file
View File

@ -0,0 +1,22 @@
from setuptools import setup, find_packages
import os
with open('requirements.txt') as f:
required = f.read().splitlines()
setup(
name='robowaiter',
version='0.1.0',
packages=['robowaiter'],
install_requires=required,
author='HPCL-EI',
author_email='hpcl_ei@163.com',
description='RoboWaiter',
url='https://github.com/HPCL-EI/RoboWaiter',
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
)

View File

@ -1,20 +0,0 @@
"""
环境主动探索和记忆
要求输出探索结果语义地图对环境重点信息记忆生成环境的语义拓扑地图和不少于10个环境物品的识别和位置记忆可以是图片或者文字或者格式化数据
"""
from scene_utils import control
# control.init_world(1, 3)
scene = control.Scene(sceneID=0)
scene.reset()
scene.add_object(0, 570, 1600, 85.5) # type与物品编号对应具体参考README.md
scene.add_object(1, 570, 1630, 85.5)
scene.add_object(2, 570, 1660, 85.5)
scene.add_object(3, 580, 1680, 85.5)
# todo: 探索并获得语义地图
print(scene.status.objects) # 全部的物品信息,包括名称、位置等,与获得的语义地图进行对比

View File

@ -1,9 +0,0 @@
"""
在特定环境下机器人发现目标可自主完成任务
1. 打扫地面地面有垃圾机器人主动扫地清理地面垃圾
2. 收拾桌子桌子上的污渍机器人主动擦桌子
3. 摆椅子椅子不正机器人主动摆正椅子
4. 开灯室内光线暗机器人主动打开房屋的灯
"""
# todo: 通过行为树控制自动任务

View File

@ -1,29 +0,0 @@
"""
视觉语言操作
机器人根据指令人的指令调节空调自主探索环境导航到目标点通过手臂的运动规划能力操作空调比如开关按钮调温按钮显示面板
"""
import time
from scene_utils import control
# control.init_world(1, 3)
scene = control.Scene(sceneID=0)
scene.reset()
# 空调操作
scene.walk_to(950, 1260, 90) # 没法转向?
# todo: 手臂操作
time.sleep(5)
scene.walk_to(947, 1900, 0)
# 物品挪动
# todo: 视觉导航至目标点,操作手臂至可抓位置
"""
scene.grasp(1, your_objectID)
"""
# todo: 视觉导航至目标点,找准释放位置
"""
scene.release(1)
"""

View File

@ -1,32 +0,0 @@
"""
视觉语言导航
识别顾客NPC靠近打招呼对话领位导航到适合人数的空闲餐桌
开始条件监测到顾客靠近
结束条件完成领位语音请问您想喝点什么并等待下一步指令
"""
from scene_utils import control
# control.init_world(1, 3)
scene = control.Scene(sceneID=0)
# 实现单顾客领位
scene.reset()
scene.add_walker(1085, 2630, 220)
scene.control_walker([scene.walker_control_generator(0, False, 100, 755, 1900, 180)])
# todo: 监测到顾客靠近,打招呼,对话,识别获取空闲餐桌位置
# 可以使用scene.chat_bubble(message)函数实现对话
"""
scene.walk_to(your_free_table_location)
time.sleep(5)
scene.control_walker([scene.walker_control_generator(your_free_table_location)])
"""
reach = True
if reach:
scene.chat_bubble("请问您想喝点什么?")
print(scene.status.walkers)

0
zoo/__init__.py Normal file
View File

View File

0
zoo/demo/__init__.py Normal file
View File