Skip to content

Advanced Data Generation

在这个章节中,我们会深入 GenManip Config 的更多的自定义内容,这些内容部分是复杂的,但是你不需要全部理解,事实上最后大多数的内容你可以通过修改我们已经提供的预定义 Config 来实现,更进一步的需求也可以向我们发送邮件来获得帮助。

我们将首先介绍 GenManip 数据生成的全部选项,这些内容事实上可以跳过。然后结合那些令人兴奋的实例介绍如何使用这些选项。

程序传参

demogen_V4

demogen_V4 的全部参数如下:

  • -cfg/--config:Config 文件路径
  • --eval:是否生成测试用例
  • -l/--local:是否本地运行。本地运行时会读取 local 而非 default 的 anygrasp 配置,并且开启 GUI。
  • --record:仅作为传参,可以用来记录你的用户名或者进程名,如 python demogen_V4.py --record demogen_000,从而你可以在如 nvitop 中区分你的进程。
  • -wop/--without_planning:不进行规划,仅保留第一帧。假如你需要 scaling up VLM 的数据,而只利用 demogen 的 randomization 功能,可以加上这个参数。

同时,demogen_V4 支持读取环境变量中的 GMPDEBUG 环境变量,当变量为 1 时,在 planning 之后的 physics step 中会同时执行 render,以在 GUI 中查看运行效果。

render_V3

render_V3 的全部参数如下:

  • -cfg/--config:Config 文件路径
  • --eval:是否生成测试用例
  • -l/--local:是否本地运行。本地运行时会读取 local 而非 default 的 anygrasp 配置,并且开启 GUI。
  • --record:仅作为传参,可以用来记录你的用户名或者进程名,如 python demogen_V4.py --record demogen_000,从而你可以在如 nvitop 中区分你的进程。
  • -r/--render_first_frame:是否仅渲染第一帧,与 -wop/--without_planning 在相同需求下使用。
  • -wod/--without_depth: 是否渲染深度信息,激活后忽略 camera config 中 depth 的配置,均不保存 depth 信息。在数据不需要深度信息且为了节约空间的时候使用。
  • -a/--add_random_position_camera: 是否添加一个随机位置的相机,默认不添加。
  • -d/--downsample n(int): 是否进行 downsample,默认不进行。2 即进行两倍的降采样。

Config 设置

一切 GenManip Config 都从随机化的角度出发,旨在通过尽可能少的内容实现尽可能多可控的随机化。

Object Config

在 GenManip 中,Object Config 描述了一种名为 Meta Object 的抽象层,这一抽象层可以在 Generation Config 以及 Layout Config 中被引用,从而达成更加灵活的场景构建。

本质上,Object Config 是一个 Dict,每一项的 Key 是 Meta Object 的 Name,而 Value 是一个 Dict,其中包含了该 Meta Object 的配置信息。

object_config:
apple:
type: existed_object
uid_list:
- 1fdc84a7be2c4348b281490c89d76062
banana:
filter_rule: []
max_cached_num: 50
option: []
path: object_usds/bananas
type: load_object_from_path
container:
option: []
path: object_usds/objaverse_usd/f8a2cdc9970846da95585a428697d173.usd
type: add_additional_object_from_path

在上述示例中展示了 Meta Object 的三种类型,我们将分别讲解这三种不同类型的 Meta Object 如何使用。

Existed Object

Existed Object 表示一个已经存在于场景中的对象,通常在 GenManip 中用于描述一个已经存在的物体。对于这种类型的 Meta Object,你需要提供一个 uid_list,其中包含了所有需要被引用的对象的 UID,这意味着这些物体位于 /scene_uid/obj_<uid> 下。

值得注意的是,UID list 是一个列表,这意味着其实际上可以同时表示若干物体。通过在 Existed Object 中展示这个概念,我们可以直观理解 Meta Object 到实际对象的映射关系。

apple:
type: existed_object
uid_list:
- 1fdc84a7be2c4348b281490c89d76062
- 1fdc84a7be2c4348b281490c89d76063
- 1fdc84a7be2c4348b281490c89d76064

对于彼此不同的三个 UID,我们撰写以下的 Goal:

goal:
- - obj1_uid:
- apple
obj2_uid:
- container
position:
- top

在每一次实际执行的时候,在一切的开始,系统会实现 meta to fine 映射,对于 existed object 来说,这意味着我们每次会将 apple 对应到 uid list 中的一个 UID,然后将其放置到 container 的顶部。从而实现 将桌子上苹果放一个到让容器中。假如说你希望实现任选两个苹果,那么可以改变 object config 为:

apple1:
type: existed_object
uid_list:
- 1fdc84a7be2c4348b281490c89d76062
- 1fdc84a7be2c4348b281490c89d76063
- 1fdc84a7be2c4348b281490c89d76064
apple2:
type: existed_object
uid_list:
- 1fdc84a7be2c4348b281490c89d76062
- 1fdc84a7be2c4348b281490c89d76063
- 1fdc84a7be2c4348b281490c89d76064

在每次的映射过程中,程序会避免不同的 meta object 映射到同一 fine object。

之后使用 Goal:

- - obj1_uid:
- apple1
obj2_uid:
- container
position:
- top
- obj1_uid:
- apple2
obj2_uid:
- container
position:
- top

即可完成你的目标。

Load Object from Path

Load Object from Path 主要实现了将一个文件夹里面的若干对象加载到场景中,并且在每一次仅激活一个。

banana:
filter_rule: []
max_cached_num: 50
option: []
path: object_usds/bananas
type: load_object_from_path

上述的内容会将 max_cached_num 个 banana 在场景初始化的时候被预加载,之后在每次采集数据的过程中,系统会随机激活其中的一个 banana 到场景中。其中,假如 max_cached_num 大于文件夹中的物体数量,那么会加载全部的物体。

因为一个文件夹中的物体,如从 Objaverse 中导出的物体,他们并不具有统一的尺寸,从而在加载进场景之后需要率先进行 resize。这一部分参见 Domain Randomization 中对于 Object Annotation 的介绍。具体来说,对于标注中的 scale 字段,这是一个长度为 2 的 list,分别代表了物体的最小和最大尺寸,那么物体会被随机缩放到这个范围之内。

Filter Rule

Filter Rule 用于过滤掉一些物体,具体来说,Filter Rule 是一个列表,其中每一项是一个字符,用于根据你的物体标注中的 shape material color can_grasp is_container category_list scale字段进行过滤,其中列表表示了且关系。

apple:
filter_rule:
- can_grasp
- retrieve_category_food
- retrieve_shape_sphere
- retrieve_not_material_metal
- retrieve_not_material_plastic
- retrieve_color_red
- retrieve_scale_less_than_0.5
- retrieve_scale_greater_than_0.3
max_cached_num: 50
option: []
path: object_usds/apples
type: load_object_from_path

可以帮助你在 object_usds/apples 文件夹中加载到可以被抓取的苹果,并且其尺寸在 0.3 到 0.5 之间,颜色为红色,形状为球体,材质不是金属和塑料,并且其类别为食物(以避免如苹果手机)。另外,对于 is_container 的字段,对应的 filter_rule 为 is_container

Scales

另外,GenManip 除了通过 Object Annotation 中的 scale 字段进行 resize,还支持非常多的方法对于覆盖 annotation 中的信息,进行额外的 resize。

这些 key 直接发生在 object config 中,如:

apple:
clip_range:
- 0.3
- 0.5

这些内容包括:

  • clip_range: 一个长度为 2 的 list,会根据这个范围对于原始的 scale 信息进行 clip。如,对于 scale[0.3, 0.5] 的物体,如果 clip_range[0.2, 0.4],那么物体会被缩放到 [0.3, 0.4] 之间。
  • fixed_size: 一个长度为 3 的列表,表明写死的单位为 m 的 size。如 [0.1, 0.1, 0.1] 表示物体会被缩放到长宽高均为 0.1 米的 size。
  • fixed_scale: 一个浮点数,表明对于物体直接进行的缩放比例。如 0.5 表示物体会被缩放到原始大小的 0.5 倍。

Add Additional Object from Path

Add Additional Object from Path 主要实现了将一个文件夹里面或者某个特定的物体加载到场景中,并且就像是 existed object 一样对待他们。这一 Config 可以让你在 GenManip 里面组织一个 Scene,而不是提前制作,从而复用这些 USD 信息。我们会在后续给出其一个最佳实践。

container:
option: []
path: object_usds/objaverse_usd/f8a2cdc9970846da95585a428697d173.usd
type: add_additional_object_from_path

上述的 Config 可以简单让一个容器被加载到场景中。

而当 path 为文件夹的时候,你同样可以使用 max_cached_num 来限制加载的物体数量,这些物体会被同时加载到场景中。

例如你希望实现等价于 Exist Object 的效果:

apple:
type: existed_object
uid_list:
- 1fdc84a7be2c4348b281490c89d76062
- 1fdc84a7be2c4348b281490c89d76063
- 1fdc84a7be2c4348b281490c89d76064

你可以将以上的三个苹果放入文件夹中,并且通过:

apple:
max_cached_num: 3
option: []
path: object_usds/apples
type: add_additional_object_from_path

来实现完全相同的效果。

对于 scale 相关的内容,在 Load Object from Path 支持的内容对于 Add Additional Object from Path 同样支持。

Layout Config

Domain Randomization

Domain Randomization 部分用于随机化相机、环境、机械臂、桌子、墙面等元素。

Camera Config

在 Domain Randomization 中包括了相机参数配置,你可以通过 config_path 指定一个相机配置文件,同时其中 type 只支持 fixed 类型。

cameras:
config_path: configs/cameras/fixed_camera.yml
type: fixed

对于 Camera Config,两种不同风格的 Config 如下。

GenManip Style

以下是一个 GenManip Style 的 camera config:

camera_name:
clipping_range_max: 10000.0 # 相机的远截面,保持默认即可
clipping_range_min: 0.001 # 相机的近截面,保持默认即可
exists: false # 是否是存在于场景内的相机,如为 false,相机创建在 /Cameras<prim_path> 下,否则创建在 /World/<scene_uuid><prim_path> 下
focal_length: 5.0 # 相机的焦距
frequency: 60 # 相机的频率,保持默认即可
horizontal_aperture: 10.0 # 相机的水平孔径,与焦距共同决定相机视场角
name: camera_name # 相机的名字
orientation: # 相机相较于自己的父节点的相对朝向
- -0.0071865058262247095 # x
- -0.5792785229595211 # y
- -0.005107428795172445 # z
- 0.815081996576386 # w
position: # 相机相较于自己的父节点的相对位置
- 0.28070902824401855 # x
- -0.02326910011470318 # y
- 1.6857764720916748 # z
prim_path: /camera_name # 相机的 prim_path
resolution: # 相机的分辨率
- 1280
- 720
vertical_aperture: 5.625 # 相机的垂直孔径,与焦距共同决定相机视场角
with_distance: true # 是否渲染深度信息
with_semantic: true # 是否渲染语义掩码信息
with_bbox2d: true # 是否渲染 2D 边界框信息
with_bbox3d: true # 是否渲染 3D 边界框信息
with_motion_vector: true # 是否渲染运动向量信息

同时支持 camera_param 作为参数,会覆盖 focal_length, horizontal_aperture 以及 vertical_aperture

camera_params:
- 387.2585754394531 # fx
- 386.81646728515625 # fy
- 324.53442382812 # cx
- 244.35198974609375 # cy
- 640 # width
- 480 # height
Simbox Style

下面是一个 Simbox Style 的 camera config:

obs_camera:
exists: false # 是否是存在于场景内的相机,如为 false,相机创建在 /Cameras<prim_path> 下,否则创建在 /World/<scene_uuid><prim_path> 下
frequency: 60 # 相机的频率,保持默认即可
name: obs_camera # 相机的名字
camera_params:
- 605.451 # fx
- 605.137 # fy
- 320.778 # cx
- 255.816 # cy
orientation: [0.83980864, 0.4807688, -0.10643996, -0.2285899] # 相机相较于自己的父节点的相对朝向
position: # 相机相较于自己的父节点的相对位置
- -0.54772 # x
- -0.79264 # y
- 1.64032 # z
prim_path: /obs_camera # 相机的 prim_path
resolution:
- 640
- 480
pixel_size: 3.0 # 相机的像素大小,保持默认即可
f_number: 2.0 # 相机的光圈数,保持默认即可
focus_distance: 0.5 # 相机的焦距,保持默认即可
with_distance: false # 是否渲染深度信息
with_semantic: false # 是否渲染语义掩码信息
with_bbox2d: false # 是否渲染 2D 边界框信息
with_bbox3d: false # 是否渲染 3D 边界框信息
with_motion_vector: false # 是否渲染运动向量信息
camera_axes: usd # 相机坐标系,保持默认即可

大多数时候,对于已经定义的相机,你只需要使用 GenManip 中提供的相机即可,如对于 Franka with Panda hand,直接使用 fixed_camera_s2r_3L-align_twoObs.yml

Random Environment

在 Random Environment 中我们定义了一系列参数可供选择:

random_environment:
has_wall: false # 是否拥有四周墙面
hdr: false # 是否随机化 dome light,使用的资产位于 `saved/assets/miscs/hdrs/*.exr`
robot_base_position: false # 是否随机化机械臂基座位置
robot_eepose: false # 是否随机化机械臂末端位置
table_texture: false # 是否随机化桌子纹理,使用的贴图资产位于 `saved/assets/textures/*.jpg`
table_type: false # 是否随机化桌子,使用的桌子资产来自 InternUtopia,位于 `saved/assets/object_usds/grutopia_usd/Table/tabl/*.usd`,当激活 table_type 的时候,table_texture 的贴图资产使用 `saved/assets/object_usds/grutopia_usd/Table/Materials/*.mdl`
wall_texture: false # 是否随机化墙面纹理,使用的贴图资产位于 `saved/assets/textures/*.jpg`
camera_randomization: # 相机位置随机化,可以不写,暂时仅对 simbox style 的 camera 生效,key 为相机名字,value 为随机化参数
realsense:
max_translation_noise: 0.02
max_orientation_noise: 2.5
obs_camera:
max_translation_noise: 0.05
max_orientation_noise: 10.0

Rewrite Instruction and Object Annotation

object_data_path 一般在 scaling 任务中出现,如 object_data_path: objaverse_annotation_refined_container_selection,表明读取 assets/objects/objaverse_annotation_refined_container_selection.pkl 的物体描述文件,这个文件中包含了每个物体的标注信息,一些是必须的。以下是一个简单的生成 pkl 的程序:

new_data = {}
for key in data.keys():
new_data[key] = {}
new_data[key]["caption"] = data[key]["caption"] # 物体的描述,此项为必须
new_data[key]["scale"] = data[key]["scale"] # 物体的尺寸,[min, max],单位为 m,此项为必须
# 以下内容仅在特殊的 scaling filter rule 中被调用,否则可以随意设置
new_data[key]["color"] = [] # 物体的颜色 list[str]
new_data[key]["materials"] = [] # 物体的材质 list[str]
new_data[key]["shape"] = [] # 物体的形状 list[str]
new_data[key]["category_list"] = [] # 物体的类别 list[str], 依次从宏观类别到微观类别,如 ['food', 'fruit', 'banana']
new_data[key]["can_grasp"] = True # 物体是否可以被抓取
new_data[key]["is_container"] = True # 物体是否可以作为容器
pickle.dump(
new_data,
open("assets/objects/objaverse_annotation_refined_pnp.pickle", "wb"),
)

rewrite_instruction 用于重写任务描述,支持 falsetrue 两个值。当为 true 时,instruction 可以被替换为 put {obj10_name} to the {position0} of {obj20_name}, put {obj11_name} to the {position1} of {obj21_name} and put {obj12_name} to the {position2} of {obj22_name} 这样的格式。

Generation Config

Generation Config 主要用于控制数据的生成过程,目前在本质上只支持 Pick and Place 任务,在这里与之前章节的内容保持一致,再复制一次。

Generation Config 定义了任务生成的各种参数。

其中最核心的是 Goal,它通过 Scene Graph 定义目标布局,当前主要用于描述 Pick-and-Place 任务。 goal 是一个嵌套列表:

  • 外层列表表示逻辑 OR
  • 内层列表表示逻辑 AND

例如:

  • [[{banana → left of plate}], [{banana → right of plate}]] 表示香蕉可以放在盘子的左边 右边
  • [[{banana → left of plate}, {cup → right of plate}]] 表示香蕉必须在左边并且杯子必须在右边

每个目标项包含 obj1_uidobj2_uidposition,表示 obj1 应相对于 obj2 放在指定位置。

抓取姿态通常由 AnyGrasp 服务器 提供(详见 安装 AnyGrasp)。 但如果启用了 allow_fixed_graspforce_fixed_grasp,GenManip 会直接从物体的网格和位置计算一个俯视抓取姿态。 同样,fixed_position 会将目标位置约束为 obj2 的中心。

最简形式只需要提供 obj1_uidobj2_uidposition,GenManip 会向 AnyGrasp 请求一个随机抓取姿态,并将物体放置在符合目标要求的随机位置。

除此之外,事实上 Goal 支持三层列表,这是一种拓展用法,用于将不同的任务整合到一个 Config 中并且进行随机的采样,在这里描述为 [normal_goal_1, normal_goal_2]。比如说在相同的场景中用户想要包含两个任务,将香蕉放到盘子里和将苹果放到盘子里,这本质上可以通过建立三层列表来实现。指的注意的是,这与 [[{banana -> plate}, {apple -> plate}]] 完全不等价。三层列表本身描述的任务是独立的香蕉放到盘子里和将苹果放到盘子里,而二层列表描述的是香蕉 苹果放到盘子里。

  • Action Path:通常设为 autorobot: 0。GenManip 会自动根据目标推断动作序列。
  • articulationmodeplanner:建议保持默认。虽然之前支持 mplib,但 curobo 性能更佳,目前已作为默认选项。

其他内容

除此以外的其他内容也与之前介绍的一致,在这里同样复制。