Preface
关于 LIBERO 这个终身机器人benchmark工具的文档阅读笔记。
Aim:如何使用 LIBERO 跑通实验,及如何外部导入 3D 模型构建本地数据集
网址:https://lifelong-robot-learning.github.io/LIBERO/html/index.html
代码结构
环境:
1 | libero/ |
算法与实验:
1 | lifelong/ # Files for algorithms, models, and training / testing |
创建数据集:
1 | scripts/ |
场景创建
Step 1 准备 mesh 文件
可能有用的资源:obj2mjcf
mesh 文件位置:libero/libero/assets
Step 2 创造问题类
问题类定义了场景的区域。在区域上,初始状态被创建,物体放置于此。
问题类定义文件位置: libero/libero/envs/problems
模板: template
问题
什么是mesh文件,mesh文件定义了什么东西?
mesh文件可以理解为一个物体的3D外壳/几何表面。计算机存储物体时,使用大量三角形近似它的表面。
一个mesh的核心信息包括:
1
2
3
4vertices # 顶点坐标
faces # 哪几个顶点组成一个三角面
normals # 表面朝哪个方向
UV coordinates # 纹理图片怎么贴到表面LIBERO常用的mesh文件格式是
.obj。mesh文件只定义物体形状,关于质量、摩擦系数等这些物理参数,通常由MJCF/XML进一步定义。
obj2mjcf是什么,如何使用?MJCF = MuJoCo XML Configuration Format,就是 MuJoCo 描述仿真物体/场景的 XML 格式。
obj2mjcf是一个命令行工具,专门帮你把.obj模型整理成 MuJoCo 更容易使用的形式。官方说明它主要做三件事:- 把包含多个 material 的复杂 OBJ 拆成若干 sub-mesh;
- 自动生成包含 mesh、material、geom 引用的 MJCF XML;
- 可选地用 CoACD 做 convex decomposition,从而产生更适合作为 collision geometry 的网格。
安装后运行
obj2mjcf --help运行结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66A CLI for processing composite Wavefront OBJ files for use in MuJoCo.
╭─ options ──────────────────────────────────────────╮
│ -h, --help │
│ show this help message and exit │
│ --obj-dir STR │
│ path to a directory containing obj files. │
│ All obj files in the directory will be │
│ converted (required) │
│ --obj-filter {None}|STR │
│ only convert obj files matching this regex │
│ (default: None) │
│ --save-mjcf, --no-save-mjcf │
│ save an example XML (MJCF) file (default: │
│ False) │
│ --compile-model, --no-compile-model │
│ compile the MJCF file to check for errors │
│ (default: False) │
│ --verbose, --no-verbose │
│ print verbose output (default: False) │
│ --decompose, --no-decompose │
│ approximate mesh decomposition using CoACD │
│ (default: False) │
│ --texture-resize-percent FLOAT │
│ resize the texture to this percentage of the │
│ original size (default: 1.0) │
│ --overwrite, --no-overwrite │
│ overwrite previous run output (default: │
│ False) │
│ --add-free-joint, --no-add-free-joint │
│ add a free joint to the root body (default: │
│ False) │
╰────────────────────────────────────────────────────╯
╭─ coacd-args options ───────────────────────────────╮
│ arguments to pass to CoACD │
│ ────────────────────────────────────────────────── │
│ --coacd-args.preprocess-resolution INT │
│ resolution for manifold preprocess (20~100), │
│ default = 50 (default: 50) │
│ --coacd-args.threshold FLOAT │
│ concavity threshold for terminating the │
│ decomposition (0.01~1), default = 0.05 │
│ (default: 0.05) │
│ --coacd-args.max-convex-hull INT │
│ max # convex hulls in the result, -1 for no │
│ maximum limitation (default: -1) │
│ --coacd-args.mcts-iterations INT │
│ number of search iterations in MCTS │
│ (60~2000), default = 100 (default: 100) │
│ --coacd-args.mcts-max-depth INT │
│ max search depth in MCTS (2~7), default = 3 │
│ (default: 3) │
│ --coacd-args.mcts-nodes INT │
│ max number of child nodes in MCTS (10~40), │
│ default = 20 (default: 20) │
│ --coacd-args.resolution INT │
│ sampling resolution for Hausdorff distance │
│ calculation (1e3~1e4), default = 2000 │
│ (default: 2000) │
│ --coacd-args.pca, --coacd-args.no-pca │
│ enable PCA pre-processing, default = false │
│ (default: False) │
│ --coacd-args.seed INT │
│ random seed used for sampling, default = 0 │
│ (default: 0) │
╰────────────────────────────────────────────────────╯一般使用流程:
1
2
3
4
5
6
7
8
9
10
11网上/Blender 找到一个 phone.obj
↓
obj2mjcf
↓
整理 mesh
生成/辅助生成 MJCF XML
生成 collision mesh
↓
放进 LIBERO assets
↓
让 LIBERO 把它实例化成 PhoneObject问题类长什么样,如何创建?
Problem Class 实际上就是 LIBERO 中用来描述“世界怎么搭起来”的 Python 类。
Problem class 的定义根据提供的模板修改,其中需要重点修改的函数有:
__init__:设置一些参数;_load_sites_in_arena:加载场景;_setup_camera:设置摄像头。
对于和现有 LIBERO 场景结构相似的新 Problem Class,通常主要定制
__init__、_load_sites_in_arena和_setup_camera;其余方法可以沿用模板的通用实现。特殊 fixture/object/placement/success logic 才需要额外修改。
任务生成
Step 1 创建场景(previous chapter)
Step 2 布局与初始状态分布
2.1 在codebases里面获取物体和谓词
查看已有数据库中的物体:
1 | from libero.libero.envs.objects import get_object_dict, get_object_fn |
获取物体,并查看所在的类别:
1 | category_name = "moka_pot" |
获取谓词的相关信息:
1 | from libero.libero.envs.predicates import get_predicate_fn_dict, get_predicate_fn |
2.2 定义布局与初始状态分布
1 | import numpy as np |
2.3 定义任务目标
1 | scene_name = "kitchen_scene1" |
任务目标将以命名元组libero.libero.utils.task_ Generation_utils.TaskInfoTuple的格式临时保存在变量libero.libero.utils.task_ Generation_utils.TASK_INFO中。这样的设计目的是为了方便批量创建任务。
查看PDDL文件名称:
1 | # This is the default path to store all the pddl scene files. Here we store the files in the temporary folder. If you want to directly add files into the libero codebase, get the default path use the following commented lines: |
自动生成PDDL文件:
1 | with open(bddl_file_names[0], "r") as f: |
Step 3 指定目标和语言指令
1 | # kitchen_scene_example |
请注意,objects_of_interest是一个可选字段,如果您希望修改 LIBERO 问题类以跟踪某些特定对象的内部模拟状态,则可以使用此字段。
添加外部 3D 物体步骤(以 egg 为例)
从外部网站(如 free3D.com)下载 3D 模型。模型文件格式应为
.obj和模型预览图.jpg。最好是单一物体、非 articulated、面数适中。使用文档给出的工具
obj2mjcf处理.obj文件,确认.obj文件可被 MuJoCo 解析。(此处生成的 XML 文件通常只是参考,不一定能直接拿来当 LIBERO object)1
2
3
4
5obj2mjcf \
--obj-dir playground/custom_assets/egg \
--save-mjcf \
--compile-model \
--verbose检查模型尺寸。运行命令行:
1
2
3
4
5
6
7
8
9
10
11
12python - <<'PY'
import trimesh
mesh = trimesh.load(
"playground/custom_assets/egg/egg/egg.obj",
force="mesh"
)
print("bounds:", mesh.bounds)
print("extents:", mesh.extents)
print("centroid:", mesh.centroid)
PY根据
extents算真实缩放比例。比如一个鸡蛋,原始长轴约5.32个单位,想做成约6cm,故设置:1
scale="0.01127 0.01127 0.01127"
把处理后的 mesh 放进 LIBERO assets,文件位置为:
1
2
3libero/libero/assets/custom_objects/egg/
├── egg.obj
└── egg.xml自己写正式的
egg.xml。这里最好把 visual 和 collision 分开:visual mesh → 外部真实 egg.obj,collision geom → 简单 ellipsoid。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<mesh
name="egg_visual_mesh"
file="egg.obj"
scale="0.01127 0.01127 0.01127"
/>
<geom
type="mesh"
mesh="egg_visual_mesh"
contype="0"
conaffinity="0"
group="1"
/>
<geom
type="ellipsoid"
size="0.0217 0.0217 0.0300"
density="600"
friction="0.8 0.3 0.1"
group="0"
/>
<site name="bottom_site" ... />
<site name="top_site" ... />
<site name="horizontal_radius_site" ... />写 LIBERO object class。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19@register_object
class Egg(MujocoXMLObject):
def __init__(self, name="egg"):
super().__init__(
".../assets/custom_objects/egg/egg.xml",
name=name,
joints=[dict(type="free", damping="0.0005")],
obj_type="all",
duplicate_collision_geoms=False,
)
self.category_name = "egg"
self.rotation = (0.0, 0.0)
self.rotation_axis = "z"
self.object_properties = {
"vis_site_names": {}
}在
libero/libero/envs/objects/__init__.py里加上:1
from .custom_objects import *
验证:
1
2
3
4
5
6python - <<'PY'
from libero.libero.envs.objects import get_object_fn, get_object_dict
print("egg" in get_object_dict())
print(get_object_fn("egg"))
PY理想输出:
1
2True
<class '...Egg'>在 Initial Scene 里声明新物体:
1
2
3
4
5object_num_info = {
"egg": 1,
"tomato_sauce": 1,
"basket": 1,
}在
define_regions()里给 egg 定义初始化区域:1
2
3
4
5
6
7
8self.regions.update(
self.get_region_dict(
region_centroid_xy=[-0.20, -0.10],
region_name="egg_init_region",
target_name=self.workspace_name,
region_half_len=0.08,
)
)在
init_states里明确让 Egg 放到对应 region:1
2
3
4
5
6
7
8
9
def init_states(self):
return [
(
"On",
"egg_1",
"living_room_table_egg_init_region",
),
]在 task 里正常用它:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15register_task_info(
language="pick up the egg and place it in the basket",
scene_name="living_room_scene_custom",
objects_of_interest=[
"egg_1",
"basket_1",
],
goal_states=[
(
"In",
"egg_1",
"basket_1_contain_region",
),
],
)在终端重新生成 BDDL 文件:
1
2rm -rf playground/generated_bddl
python playground/create_custom_tasks.py最后加载模型:
1
python playground/preview_custom_task.py
Comments