这次梳理的是 libero 和 robocasa 这两个仿真框架,而他们都是基于 MuJoCo,所以先读一下 MuJoCo 的文档。
MuJoCo
- Multi-Joint dynamics with Contact,22年由Google DeepMind 开源
- 在现代接触动力学研究中,摩擦接触产生的作用力或冲量,通常被定义为线性互补问题(LCP)或非线性互补问题(NCP)的解,这两类问题均属于 NP 难问题。MuJoCo 采用了一种不同的接触物理建模方式,将问题转化为凸优化问题,支持柔性接触与各类约束,且存在唯一逆映射,这样就方便在前向动力和逆向动力问题中得到唯一解,软件默认使用是可实现二次收敛的牛顿求解器。
LCP
Given a real matrix M and vector q, the linear complementarity problem LCP(q, M) seeks vectors z and w which satisfy the following constraints:
(that is, each component of these two vectors is non-negative)
or equivalently
This is the complementarity condition, since it implies that, for all
, at most one of
and
can be positive.
A sufficient condition for existence and uniqueness of a solution to this problem is that M be symmetric positive-definite.
- 可以通过互补条件建模接触行为,接触速度与接触冲量满足互补关系,至少有一个为0
Model
MuJoCo 有内置的 XML 解析器与编译器,用户通过 MJCF 或 URDF 格式的 XML 文件定义模型,MJCF 格式的 XML 文件可以方便地阅读和编辑。软件能够在不同载体(文件或内存)、不同描述层级(高层或低层)中创建同一模型的多个实例。
| High level | Low level | |
|---|---|---|
| File | MJCF/URDF (XML) | MJB (binary) |
| Memory | mjSpec(C struct) | mjModel (C struct) |
所有运行时计算均依托 mjModel 进行,该结构复杂度极高,无法手动创建。所以设置了两级建模体系,高级模型为方便用户使用而设计,用于编译为可执行计算的低级模型。编译生成的 mjModel 能够加载并保存为二进制文件(MJB),但这类文件与软件版本绑定且无法反编译,因此模型文件建议始终以 XML 格式进行保存和维护。
获取一个 mjModel 的路径:
- (text editor) → MJCF/URDF file → (MuJoCo parser → mjSpec → compiler) → mjModel
- (user code) → mjSpec → (MuJoCo compiler) → mjModel
- MJB file → (model loader) → mjModel
一个 example.xml :
<mujoco model="example">
<default>
<geom rgba=".8 .6 .4 1"/>
</default>
<asset>
<texture type="skybox" builtin="gradient" rgb1="1 1 1" rgb2=".6 .8 1" width="256" height="256"/>
</asset>
<worldbody>
<light pos="0 1 1" dir="0 -1 -1" diffuse="1 1 1"/>
<body pos="0 0 1">
<joint type="ball"/>
<geom type="capsule" size="0.06" fromto="0 0 0 0 0 -.4"/>
<body pos="0 0 -0.4">
<joint axis="0 1 0"/>
<joint axis="1 0 0"/>
<geom type="capsule" size="0.04" fromto="0 0 0 .3 0 0"/>
<body pos=".3 0 0">
<joint axis="0 1 0"/>
<joint axis="0 0 1"/>
<geom pos=".1 0 0" size="0.1 0.08 0.02" type="ellipsoid"/>
<site name="end1" pos="0.2 0 0" size="0.01"/>
</body>
</body>
</body>
<body pos="0.3 0 0.1">
<joint type="free"/>
<geom size="0.07 0.1" type="cylinder"/>
<site name="end2" pos="0 0 0.1" size="0.01"/>
</body>
</worldbody>
<tendon>
<spatial limited="true" range="0 0.6" width="0.005">
<site site="end1"/>
<site site="end2"/>
</spatial>
</tendon>
</mujoco>
MJCF 这个原生格式的一个特点是其默认配置机制,文件中仅包含指定模型所需的最少信息。刚体的位姿由其绑定的几何形体自动推导,在均匀密度前提下,模型会根据几何外形自动计算刚体惯性属性,碰撞规则、摩擦、重力、仿真步长等均使用默认值。
Assets
assets 本身不是 MuJoCo 的模型元素,模型元素可引用 assets,进而改变自身属性,一份资源能被多个模型元素引用。
- assets 必须命名(可以自动沿用文件名),因为引用只能通过名称实现。
Mesh
Mesh 是三角网格模型 ,通常先在 asset 中定义,再由 geom 引用。可从二进制 STL 文件、自定义的 MSH 格式文件和用于存储法向量和纹理坐标的 OBJ 文件中加载,也能够直接内嵌在 XML 文件中。
虽然任意三角形集合都可作为网格进行加载和可视化,但碰撞检测器仅针对凸包生效。网格模型还可通过将自身视作多个三角棱锥的组合,并整合各棱锥的质量与惯性参数,自动推算出惯性属性。需要注意的是,网格本身不具备颜色,其色彩由引用该网格的几何体所对应的材质属性决定,而所有空间属性,则均由网格数据确定。
Skin
蒙皮网格模型是仿真时可形变的网格模型,但仅为视觉展示对象,不会影响物理效果,可大幅提升视觉真实感。Skin 可以从自定义二进制 SKN 文件中加载,也能像 Mesh 一样直接内嵌于 XML 文件中。在自动生成复合柔性物体时,模型编译器也会为这些物体生成对应的 Skin。
Height field
高度场可从 PNG 图片文件(程序内部会将其转换为灰度图)加载,也可从自定义二进制格式 HF 文件中加载。高度场是由高度数据构成的矩形网格。编译器会将这些数据归一化至 0 到 1 的数值区间。高度场实际的空间范围,由引用它的几何体的尺寸参数决定。高度场仅能被绑定在 world body 上的几何体所引用。为满足渲染与碰撞检测的需求,网格矩形会被自动三角化,因此高度场会被视作多个三角棱柱的组合体。对这类组合物体进行碰撞检测时,单个几何体配对理论上会产生大量接触点,若出现该情况,系统仅保留前 64 个接触点。这样设计的原因在于,高度场主要用于构建地形地图,相较于仿真场景内的其他物体,地形的空间特征范围更大,因此设计合理的模型产生的接触点数量本就较少。
Textrue
纹理可从 PNG 文件中加载,也可由编译器根据用户自定义的程序参数生成。在创建模型时可以暂不设置纹理,后续在运行阶段再进行修改,以此在 MuJoCo 仿真中渲染视频或制作其他动态效果。可视化工具支持两种纹理映射方式:二维映射和立方体映射。二维映射适用于平面和高度场。立方体映射能够将纹理紧密贴合在三维物体表面,且无需指定纹理坐标,同时也可用于制作 skybox。立方体贴图的六个面可分别加载独立的图像文件、整合后的单张图像文件,或是通过重复同一张图像生成。其他所有资源都能被模型元素直接引用,但纹理有所不同,它仅能被另一类 asset(Material)引用,再被模型元素调用。
Material
材质用于控制几何体、定位点和肌腱的外观,具体方式是在对应的模型元素中引用该材质。
Kinematic tree
观察上方的 example.xml 不难发现,可运动刚体及其所有子层级构件构成了一个树状结构,而世界基座、静态刚体属于全局架构,不属于任何 Kinematic tree。
Body
Body 仅存储质量、惯性参数,几何外形由附属的Geom 定义。每个 Body 有两个坐标系,本体坐标系用于定位自身及附属元素;另一个惯性坐标系的原点在质心、轴对齐惯性主轴,惯性矩阵在此坐标系下为对角矩阵,简化计算。每步仿真通过递归正运动学,解算出所有刚体在全局笛卡尔坐标系下的位姿,作为后续运算基础;刚体总数由 mjModel.nbody 记录。
Joint
Joint 定义在刚体内部,为刚体与父刚体之间增加运动自由度;无关节时父子刚体固连。这和游戏引擎相反(游戏引擎用关节约束、减少自由度)。共有四种关节:球关节、滑动关节、铰链关节、自由关节(实现漂浮刚体)。单个刚体可挂载多个关节,直接形成复合关节,无需额外辅助刚体。球关节、自由关节的姿态采用单位四元数描述,关节总数由 mjModel.njnt 记录。DOF 与关节不是一一对应,球关节、自由关节包含多个自由度。
Gemo
Gemo 是绑定在刚体上的三维形体。同一个刚体可以绑定多个Gemo,由于 MuJoCo 仅支持凸几何体之间的碰撞检测,而非凸物体只能由多个凸几何体组合构成,因此这一特性十分实用。除了碰撞检测以及后续接触力的计算外,Gemo 还可用于渲染;当未设置刚体质量与转动惯量时,软件也会借助几何体自动推算出对应数值。MuJoCo 支持多种基础几何形体:平面、球体、胶囊体、椭球体、圆柱体、立方体。Gemo 也可以是网格模型或高度场,使用时只需引用对应的资源文件即可。几何体具备多种材质属性,会影响仿真效果与可视化展示。几何体的数量由mjModel.ngeom记录。
Actuator
MuJoCo 提供了灵活的执行器模型,该模型包含三个可独立设置的组成部分,三者共同决定执行器的工作方式。合理搭配设置这三个部分,就可以实现各类常用执行器类型。
- 传动结构 (transmission):定义执行器与仿真系统其他部分的连接方式,支持关节、肌腱、曲柄滑块、定位点、刚体五种连接类型。
- 激活动力学 (activation dynamics):可模拟气缸、液压杆、生物肌肉等部件的内部激活状态,使用这类执行器会让整个系统的动力学特性变为三阶。
- 力生成机制 (force generation):将输入的标量控制信号转为标量力,再结合传动结构的力臂,最终转换成系统广义力。
动力学系统的阶数由系统独立状态变量的个数决定。常规刚体 / 机械系统是二阶动力学:状态仅包含位置 (q) + 速度 (ṙ) 两组变量,运动方程为二阶常微分方程。MuJoCo 的执行器 (actuator) 可引入内部激活状态(气动、液压、生物肌肉等执行器的动态过程)。在原有位置、速度之外,新增执行器内部状态,系统总状态变量增加,整体就变成三阶动力学。
Keyframe
关键帧是仿真状态变量的快照,包含关节位置、关节速度、执行器激活状态、控制参数、动作捕捉躯体姿态以及仿真时间。模型中可以包含 a library of keyframes,它能够将系统状态重置到兴趣点。但是,关键帧并不用于在模型中存储轨迹数据,这类数据应使用外部文件保存。
Divergence
仿真发散指状态量快速趋于无穷大的现象。在 MuJoCo 中,该问题通常会触发 mjWARN_BADQACC 警告。仿真出现发散不一定是模型或者仿真器有问题,可以通过减小仿真步长或更换稳定性更强的积分器来阻止发散。
Simulation
MuJoCo 运行时的仿真参数有两种数据结构(C structs),一种是之前提到的 mjModel ,存储模型描述内容,运行过程中通常保持不变,其内部还嵌套了其他用于设置仿真与可视化参数的结构体。另一种是在程序运行时创建的 mjData,用于存放所有动态变量和中间计算结果,相当于临时数据缓冲区,所有函数均从这里读取输入数据、写入输出数据,并将这些输出数据作为仿真流程下一阶段的输入。这样设计能够便捷地实现多模型仿真和单个模型多种状态与控制方案的分离需要,进而便于基于多线程完成采样计算与有限差分运算。
仿真初始化时不由用户直接分配内存给 mjModel 或 mjData,而是由在仿真的开始就分配所有必须的堆内存,在仿真结束后释放。但是这样的话,MJCF 元素中的内存属性 size 所设定的最大可变内存分配值就必须设置得足够大。若仿真过程中超出该最大容量,系统不会动态扩容,而是直接抛出错误。
simulation loop
如果只是仿真 passive dynamics,可以直接在 while 循环中调用 mj_step(mjModel *m, mjData *d)。mj_step() 具体来说就是根据前向动力学用某个时刻的系统状态和控制量来计算加速度,再在给定的时间步长上积分后加回原状态量来更新系统状态。但是如果对这个系统主动施加力或者控制信号时就需要实现一个 control callback。或者直接设置相应的控制信号,不过需要注意有些控制依赖仿真中间结果,需要先 step 得出需要的中间结果设置完控制信号再计算后面的加速度和积分。
State
stepping 中进行时间积分的主要物理量是广义坐标 qpos,速度 qvel,执行器激活状态 act,存储之前控制量、传感器值的历史缓冲 history。
User input
在 XML 文件的 actuator 部分定义的mjData.ctrl ,要么根据数值直接生成广义力,要么作用于 mjData.act 内的执行器激活量,再由后者生成作用力。辅助控制量 qfrc_applied 与 xfrc_applied ;动作捕捉位姿 mocap_pos 与 mocap_quat,用户可借助它们实时设置静态刚体的位置与姿态,比如从动作捕捉设备传输六维位姿数据时就会用到。等式约束开关eq_active ;用户数据userdata 作为一个用户自定义的内存区域,例如被 callback 使用。
Warmstart
热启动加速度:qacc_warmstart 是上一步仿真得出的加速度,用于作为下一步初值来热启动约束求解器。假设当前求解结果与上一轮结果相差不大,该方式能够减少收敛所需的迭代次数,进而提升仿真速度。默认的牛顿求解器收敛速度很快,通常只需 2-3次迭代,所以对速度没有显著影响。但如果使用其他收敛速度较慢的求解器或对迭代次数有限制导致没能收敛,这时 warmstart 就显得很重要了,以及对严谨的复现而言也是必需的。
Forward dynamic
阶段跳过参数 skipstage 用于指定计算过程中需要跳过的环节,前提是相关数据已提供。
- mjSTAGE_NONE:全量执行,不跳过任何计算。
- mjSTAGE_POS:跳过位置相关运算(正向运动学、碰撞检测、惯性矩阵求解等,CPU 开销最大)
- mjSTAGE_VEL:跳过位置和速度相关运算(科氏力、离心力、阻尼、参考加速度等)
常规仿真无法启用跳过逻辑,但在基于模型优化、机器学习、有限差分求导等场景中,若采样数据仅改动位置 / 速度 / 控制其中一项,借助阶段跳过可复用已有计算结果,性能大致提升一倍。
Inverse dynamic
在早期,逆向动力学计算是 MuJoCo 的独特功能,其他具备接触仿真能力的现代引擎均未实现该功能。完成与正向动力学共用的位置、速度相关计算后,只需借助解析公式,就能根据加速度推导出约束力与外力。该计算过程速度极快,因此我们会利用逆向动力学(采用上一时间步计算出的加速度)warmstart 正向动力学中的迭代约束求解器。
-
在不存在依赖用户自定义状态变量的回调函数时,逆动力学的输入为 mjData 中的以下字段:
(mjData.qpos, mjData.qvel, mjData.qacc, mjData.mocap_pos, mjData.mocap_quat)
-
其主要输出为
mjData.qfrc_inverse,该数值代表系统为产生观测加速度mjData.qacc所必须承受的作用力。mjData.qfrc_inverse的具体计算逻辑为先通过递归牛顿 - 欧拉算法算出系统所受合力,再从中扣除所有内力。 -
若迭代求解器运行至完全收敛,从而精确计算正向动力学,则满足该关节力维度下等式:
mjData.qfrc_inverse = mjData.qfrc_applied + Jacobian’*mjData.xfrc_applied + mjData.qfrc_actuator
其中
mjData.qfrc_actuator是执行器产生的关节空间力,雅可比矩阵则负责实现关节空间到笛卡尔空间的映射。当开启mjModel.opt.enableflags中的 fwdinv 标志位时,系统会利用上述等式校验正向动力学求解结果的精度。具体而言,mjData.solver_fwdinv的两个分量分别被设置为正向求解结果与逆向求解结果在关节力和约束力维度下差值的 L2 范数。
逆向动力学有和正向动力学一致的阶段跳过机制,可用于提升结构化采样的运行速度。在已有实验数据的场景中,逆动力学可作为分析工具,可以计算出使系统沿指定参考轨迹运动所需的关节力矩。在状态估计、系统辨识以及最优控制场景下,可将逆动力学融入优化循环,求解一系列状态量,在降低物理规则违背程度的同时兼顾其他优化目标。而物理规则的违背程度,可通过逆动力学算出的未知外力的范数进行量化。
Data layout
-
Mujoco 中所有矩阵采用行主序格式,沿袭了 c 语言的惯例。
-
Mujoco 会尽可能利用稀疏性来降低计算复杂度,惯性矩阵
mjData.qM及其 LTDL 分解形式mjData.qLD总是以稀疏形式存储。 -
MuJoCo 使用单位四元数来表示 3D 姿态和旋转,也就是用形如 q = (w, x, y, z) 的四维单位向量。其中(x, y, z)是旋转轴单位向量乘以 sin(a/2) 所得的结果,a为以弧度为单位的旋转角度,而w = cos(a/2)。因此,对应零旋转的四元数为(1, 0, 0, 0),这也是 MJCF 中所有四元数的默认取值。
Coordinate frames and transformations
MuJoCo 中使用了多种坐标系,包括 joint coordinates,Cartesian coordinates。其中 Cartesian coordinates 又分为 local coordinates,global coordinates,com-based coordinates(质心坐标系)。
- local coordinates 描述父子刚体间的静态偏移量,如
mjModel.body_pos,mjModel.body_quat就是使用local coordinates表示。正向运动学正是沿着运动学树累加关节变换与静态偏移量,计算出在全局坐标系下所有位姿。 - mjData 中以 “x” 开头的数值均采用全局坐标系表示,如
mjData.xpos、mjData.geom_xpos。位姿由三维位置和单位四元数姿态组合而成,用于表示空间中的位置与姿态。需要注意的是,MuJoCo 中采用四元数描述 orientation,而 OpenGL 使用四阶矩阵来存储同类信息。 - com-based coordinates 用于表示六维空间向量,前半部分为三维角速度、角加速度或力矩,后半部分为三维线速度、线加速度或作用力,通常以”c“开头,如
mjData.cdofandmjData.cacc。
LIBERO
libero共提供了四个数据集,面向lifelong learning decision making(LLDM) 的相关研究
libero_spatial,different objects same layout, with 10 taskslibero_object,different layouts same objects, with 10 taskslibero_goal(libero_10) ,different goals same layout & objects, with 10 taskslibero_100, diverse objects layouts backgrounds, with 100 tasks
现在已经对 MuJoCo 比较熟悉了,我们就直接去看libero codebase 里有哪些文件。
libero/
assets/ # Where all the object mesh files are stored
bddl_files/ # PDDL file definitions for tasks
libero_goal/*.bddl # 10 tasks of LIBERO-Goal suite
libero_spatial/*.bddl # 10 tasks of LIBERO-Spatial suite
libero_object/*.bddl # 10 tasks of LIBERO-Object suite
libero_10/*.bddl # 10 tasks of LIBERO-100 for evaluation (aka LIBERO-LONG)
libero_90/*.bddl # 90 tasks of LIBERO-100 for pretraining
benchmark/ # Task orders for evaluation of all benchmarks
envs/ # Environment definitions for LIBERO tasks
init_files/ # Fixed initializations for benchmark evaluation
utils/ # Miscellaneous utility functions
asset
assets/
articulated_objects/
short_cabinet/
base/
base_col.obj base_vis.mtl base_vis.obj base_vis.xml base.xml dark-wood.png visual
stable_scanned_objects/
turbosquid_objects/
scenes/
stable_hope_objects/
textures/
serving_region.xml wall.xml wall_frames.stl
这里用到的就是MuJoCo Model部分总结的 MJCF格式的 xml模型文件和二进制格式的 stl 网格模型,以及一些会被xml文件引用的 assets。
task
bddl_files/
libero_10/
KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it.bddl
KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it.bddl
KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it.bddl
KITCHEN_SCENE8_put_both_moka_pots_on_the_stove.bddl
LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket.bddl
LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket.bddl
LIVING_ROOM_SCENE2_put_both_the_cream_cheese_box_and_the_butter_in_the_basket.bddl
LIVING_ROOM_SCENE5_put_the_white_mug_on_the_left_plate_and_put_the_yellow_and_white_mug_on_the_right_plate.bddl
LIVING_ROOM_SCENE6_put_the_white_mug_on_the_plate_and_put_the_chocolate_pudding_to_the_right_of_the_plate.bddl
STUDY_SCENE1_pick_up_the_book_and_place_it_in_the_back_compartment_of_the_caddy.bddl
tasks_info.txt
task 文件格式为 .bddl,bddl 是 pddl(Planning Domain Definition Language) 的子集,源自Behavior
task程序化生成流程
-
检索对象列表,确保代码库中存在指定的对象和谓词(对象关系)
-
定义初始状态分布,Scene Register 有哪些 fixtrue、object,指定workspace、region
-
定义任务goal state,语言指令,需要追踪的物体(可选参数),调用
generate_bddl_from_task_info函数快速生成 bddl 任务文件
尝试生成的一个 task 文件 KITCHEN_SCENE1_open_the_wooden_cabinet_and_put_both_plates_inside.bddl如下
点击展开
(define (problem LIBERO_Kitchen_Tabletop_Manipulation)
(:domain robosuite)
(:language Open the wooden cabinet and put both plates inside.)
(:regions
(wooden_cabinet_init_region
(:target kitchen_table)
(:ranges (
(-0.01 -0.31 0.01 -0.29)
)
)
(:yaw_rotation (
(3.141592653589793 3.141592653589793)
)
)
)
(akita_black_bowl_init_region
(:target kitchen_table)
(:ranges (
(-0.025 -0.025 0.025 0.025)
)
)
(:yaw_rotation (
(0.0 0.0)
)
)
)
(plate_1_init_region
(:target kitchen_table)
(:ranges (
(-0.025 0.225 0.025 0.275)
)
)
(:yaw_rotation (
(0.0 0.0)
)
)
)
(plate_2_init_region
(:target kitchen_table)
(:ranges (
(0.095 0.225 0.145 0.275)
)
)
(:yaw_rotation (
(0.0 0.0)
)
)
)
(top_side
(:target wooden_cabinet_1)
)
(top_region
(:target wooden_cabinet_1)
)
(middle_region
(:target wooden_cabinet_1)
)
(bottom_region
(:target wooden_cabinet_1)
)
)
(:fixtures
kitchen_table - kitchen_table
wooden_cabinet_1 - wooden_cabinet
)
(:objects
akita_black_bowl_1 - akita_black_bowl
plate_1 plate_2 - plate
)
(:obj_of_interest
wooden_cabinet_1
plate_1
plate_2
)
(:init
(On akita_black_bowl_1 kitchen_table_akita_black_bowl_init_region)
(On plate_1 kitchen_table_plate_1_init_region)
(On plate_2 kitchen_table_plate_2_init_region)
(On wooden_cabinet_1 kitchen_table_wooden_cabinet_init_region)
)
(:goal
(And (Open wooden_cabinet_1_top_region) (In plate_1 wooden_cabinet_1_top_region) (In plate_2 wooden_cabinet_1_top_region))
))
init_files
init_files/
libero_10/
KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it.init
KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it.pruned_init
KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it.init
KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it.pruned_init
KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it.init
KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it.pruned_init
KITCHEN_SCENE8_put_both_moka_pots_on_the_stove.init
KITCHEN_SCENE8_put_both_moka_pots_on_the_stove.pruned_init
LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket.init
LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket.pruned_init
LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket.init
...用于初始化为对应环境状态
Dataset
以 libero_sptial 为例,demo 文件格式为 .hdf5,记录机器人的轨迹。
libero_spatial/
pick_up_the_black_bowl_between_the_plate_and_the_ramekin_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_from_table_center_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_in_the_top_drawer_of_the_wooden_cabinet_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_next_to_the_cookie_box_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_next_to_the_plate_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_next_to_the_ramekin_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_on_the_cookie_box_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_on_the_ramekin_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_on_the_stove_and_place_it_on_the_plate_demo.hdf5
pick_up_the_black_bowl_on_the_wooden_cabinet_and_place_it_on_the_plate_demo.hdf5
进一步查看 hdf5 文件都提供了哪些数据,随意选取一个demo 使用 h5ls 工具结果如下
/ Group
/data Group
/data/demo_0 Group
/data/demo_0/actions Dataset {98, 7}
/data/demo_0/dones Dataset {98}
/data/demo_0/obs Group
/data/demo_0/obs/agentview_rgb Dataset {98, 128, 128, 3}
/data/demo_0/obs/ee_ori Dataset {98, 3}
/data/demo_0/obs/ee_pos Dataset {98, 3}
/data/demo_0/obs/ee_states Dataset {98, 6}
/data/demo_0/obs/eye_in_hand_rgb Dataset {98, 128, 128, 3}
/data/demo_0/obs/gripper_states Dataset {98, 2}
/data/demo_0/obs/joint_states Dataset {98, 7}
/data/demo_0/rewards Dataset {98}
/data/demo_0/robot_states Dataset {98, 9}
/data/demo_0/states Dataset {98, 92}
...
/data/demo_49 Group
/data/demo_49/actions Dataset {92, 7}
/data/demo_49/dones Dataset {92}
/data/demo_49/obs Group
/data/demo_49/obs/agentview_rgb Dataset {92, 128, 128, 3}
/data/demo_49/obs/ee_ori Dataset {92, 3}
/data/demo_49/obs/ee_pos Dataset {92, 3}
/data/demo_49/obs/ee_states Dataset {92, 6}
/data/demo_49/obs/eye_in_hand_rgb Dataset {92, 128, 128, 3}
/data/demo_49/obs/gripper_states Dataset {92, 2}
/data/demo_49/obs/joint_states Dataset {92, 7}
/data/demo_49/rewards Dataset {92}
/data/demo_49/robot_states Dataset {92, 9}
/data/demo_49/states Dataset {92, 92}
所以里面有 50 条demo,从 /data/demo_0 到 /data/demo_49 ,每条 demo 是一段时间步数不等的机器人轨迹,每个时间步都有图像、机器人状态、环境状态、动作、奖励和终止标记。RGB 图像是128 × 128 resolutions ,动作空间是 7维 - (x, y, z, roll, pitch, yaw, open / close),数据集结构如下所示
/
└── data
├── demo_0
│ ├── actions
│ ├── dones
│ ├── rewards
│ ├── robot_states
│ ├── states
│ └── obs
│ ├── agentview_rgb
│ ├── eye_in_hand_rgb
│ ├── ee_pos
│ ├── ee_ori
│ ├── ee_states
│ ├── gripper_states
│ └── joint_states
├── demo_1
├── demo_2
└── ...
数据流
所以具体而言在使用 libero 进行训练和评估大致有以下的数据流
- 训练时workspace 用 hydra.utils.instantiate(cfg.task.dataset) 创建 Dataset,然后包成 DataLoader。Dataset 从 HDF5 文件读取数据,每次按horizon 采样 batch[“obs”] [“image”]: [B,T,C,H,W],batch[“action”]: [B,T,Da],搬到 GPU 送入模型。
- 评估时 LiberoImageRunner 先从 demo HDF5 里读 env metadata 然后用 metadata 里的指定的 bddl_file 创建环境,再用 init file 设置初始状态,然后 rollout 里反复调用 policy.predict_action(),env.step(action),得到 mean_score。
Robocasa
robocasa 设计了 365 项日常机器人实操任务,仿真库搭载 2500 + 厨房仿真场景、3200 + 三维实物模型,覆盖厨房各类环境与厨具食材,给机器人提供丰富交互环境。以及 2200+小时的数据集(600 + 小时人类实操示范数据、1600 + 小时自动化轨迹生成的机器人数据)兼顾真人行为逻辑与机器运动样本,适配机器人模仿学习。
Task
Atomic tasks
共有 65 个原子任务 ,用于在10 大类宏观技能(开关门、开关盖子、插入、导航、拿取、放置、按钮、旋钮、压杆、滑轨操作)14 种器具(搅拌机、橱柜、咖啡机、洗碗机、抽屉、电热水壶、冰箱、微波炉、烤箱、小烤箱、烤面包机、水槽、厨师机、炉灶)体系下,系统性训练机器人控制策略。
Composite tasks
共有 300 项复合任务,用于面向横跨 6 大类、合计 60 项实操活动做策略的系统化训练,其实基本是atomic task 的简单组合和重复。
Dataset
数据集划分为 预训练集(Pretraining)和目标测试集(Target) 。
| Setting | Num Tasks | Num Scenes | Demos per Task | Dataset Size (hrs) |
|---|---|---|---|---|
| Pretraining (Human) | 300 | 2500 | 100 | 482 |
| Pretraining (MimicGen) | 60 | 2500 | 10,000 | 1615 |
| Target (Human) | 50 | 10 | 500 | 193 |
Pretraining Datasets
由人工遥操作数据 和 MimicGen 合成数据两部分组成,搭建在2500 套厨房场景、300 种任务上,用作机器人前置预训练:
- Human Datasets 覆盖全部 300 项预训练任务(65 atomic tasks + 235 composite tasks),每项任务至少 100 组人工演示;
- Synthetic Datasets 仅包含 60 项原子任务,单任务近 10000 条仿真演示,靠海量合成样本扩充基础动作数据。
Target Datasets
仅10 个全新厨房场景、50 项任务,和预训练厨房环境完全不重合;每项任务 500 条人工演示,用于测试机器人泛化能力,按任务熟悉度分三类:
- Atomic-Seen(18 项):基础原子动作,任务在预训练集中出现过,测基础动作复用能力;
- Composite-Seen(16 项):复合任务,任务组合逻辑预训练见过,测已知技能重组;
- Composite-Unseen(16 项):全新复合任务,任务从未出现在预训练,核心用于检验机器人零样本 / 少样本泛化。
v0.2 的是 hdf5格式,v1.0 的是lerobot格式,这里直接梳理最新的。
lerobot/
├── meta/ # Metadata files describing the dataset
│ ├── info.json # Dataset info (robot type, episodes, frames, fps, features)
│ ├── tasks.jsonl # Language instructions with task indices
│ ├── episodes.jsonl # Per-episode metadata (index, instruction, length)
│ ├── episodes_stats.jsonl # Per-episode statistics for actions/proprioception
│ ├── stats.json # Aggregated statistics across all episodes
│ ├── modality.json # Info contained in observations and action vectors
│ └── embodiment.json # Embodiment information
│
├── data/ # Low-dimensional trajectory data (parquet files)
│ └── chunk-<chunk_id>/
│ └── episode_<episode_id>.parquet # Proprioception, actions, dones, timestamps
│
├── videos/ # MP4 video files for each camera view
│ └── chunk-<chunk_id>/
│ ├── observation.images.robot0_agentview_left/
│ │ └── episode_<episode_id>.mp4 # Left third-person camera
│ ├── observation.images.robot0_agentview_right/
│ │ └── episode_<episode_id>.mp4 # Right third-person camera
│ └── observation.images.robot0_eye_in_hand/
│ └── episode_<episode_id>.mp4 # Eye-in-hand camera
│
└── extras/ # MuJoCo/RoboCasa-specific metadata (non-standard)
├── dataset_meta.json # Environment args and controller configs
└── episode_<episode_id>/ # Per-episode extras
├── ep_meta.json # Episode metadata (layout, style, fixtures, objects)
├── model.xml.gz # Compressed MJCF MuJoCo model XML
└── states.npz # Raw MuJoCo states for replay (not for training)
与 hdf5 把很多东西塞在一个文件里不同,lerobot 格式将数据集 demo 拆成四个部分:
- meta 提供数据集的基本信息,比如robot_type、episode、frames、task的数量、feature的数据类型和形状,例如
点击展开
info.json:
{
"codebase_version": "v2.1",
"robot_type": "PandaOmron",
"total_episodes": 107,
"total_frames": 21328,
"total_tasks": 2,
"total_videos": 321,
"total_chunks": 1,
"chunks_size": 1000,
"fps": 20,
"splits": {
"train": "0:107"
},
"data_path": "data/chunk-{episode_chunk:03d}/episode_{episode_index:06d}.parquet",
"video_path": "videos/chunk-{episode_chunk:03d}/{video_key}/episode_{episode_index:06d}.mp4",
"features": {
"observation.images.robot0_eye_in_hand": {
"dtype": "video",
"shape": [
256,
256,
3
],
"names": [
"height",
"width",
"channel"
],
"video_info": {
"video.fps": 20,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"has_audio": false
},
"info": {
"video.height": 256,
"video.width": 256,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"video.fps": 20,
"video.channels": 3,
"has_audio": false
}
},
"observation.images.robot0_agentview_left": {
"dtype": "video",
"shape": [
256,
256,
3
],
"names": [
"height",
"width",
"channel"
],
"video_info": {
"video.fps": 20,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"has_audio": false
},
"info": {
"video.height": 256,
"video.width": 256,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"video.fps": 20,
"video.channels": 3,
"has_audio": false
}
},
"observation.images.robot0_agentview_right": {
"dtype": "video",
"shape": [
256,
256,
3
],
"names": [
"height",
"width",
"channel"
],
"video_info": {
"video.fps": 20,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"has_audio": false
},
"info": {
"video.height": 256,
"video.width": 256,
"video.codec": "h264",
"video.pix_fmt": "yuv420p",
"video.is_depth_map": false,
"video.fps": 20,
"video.channels": 3,
"has_audio": false
}
},
"annotation.human.task_description": {
"dtype": "int64",
"shape": [
1
]
},
"annotation.human.task_name": {
"dtype": "int64",
"shape": [
1
]
},
"observation.state": {
"dtype": "float64",
"shape": [
16
]
},
"action": {
"dtype": "float64",
"shape": [
12
]
},
"next.reward": {
"dtype": "float32",
"shape": [
1
]
},
"next.done": {
"dtype": "bool",
"shape": [
1
]
},
"timestamp": {
"dtype": "float32",
"shape": [
1
],
"names": null
},
"frame_index": {
"dtype": "int64",
"shape": [
1
],
"names": null
},
"episode_index": {
"dtype": "int64",
"shape": [
1
],
"names": null
},
"index": {
"dtype": "int64",
"shape": [
1
],
"names": null
},
"task_index": {
"dtype": "int64",
"shape": [
1
],
"names": null
}
}
}
episodes.jsonl:
{"episode_index": 0, "tasks": ["Increase the toaster oven temperature."], "length": 234}
{"episode_index": 1, "tasks": ["Increase the toaster oven temperature."], "length": 143}
{"episode_index": 2, "tasks": ["Increase the toaster oven temperature."], "length": 203}
{"episode_index": 3, "tasks": ["Increase the toaster oven temperature."], "length": 156}
{"episode_index": 4, "tasks": ["Increase the toaster oven temperature."], "length": 191}
{"episode_index": 5, "tasks": ["Decrease the toaster oven temperature."], "length": 209}
{"episode_index": 6, "tasks": ["Decrease the toaster oven temperature."], "length": 219}
{"episode_index": 7, "tasks": ["Decrease the toaster oven temperature."], "length": 216}
{"episode_index": 8, "tasks": ["Decrease the toaster oven temperature."], "length": 185}
{"episode_index": 9, "tasks": ["Increase the toaster oven temperature."], "length": 188}
{"episode_index": 10, "tasks": ["Decrease the toaster oven temperature."], "length": 204}
{"episode_index": 11, "tasks": ["Decrease the toaster oven temperature."], "length": 194}
{"episode_index": 12, "tasks": ["Increase the toaster oven temperature."], "length": 199}
{"episode_index": 13, "tasks": ["Decrease the toaster oven temperature."], "length": 210}
{"episode_index": 14, "tasks": ["Decrease the toaster oven temperature."], "length": 139}
{"episode_index": 15, "tasks": ["Decrease the toaster oven temperature."], "length": 122}
{"episode_index": 16, "tasks": ["Decrease the toaster oven temperature."], "length": 119}
{"episode_index": 17, "tasks": ["Increase the toaster oven temperature."], "length": 189}
{"episode_index": 18, "tasks": ["Decrease the toaster oven temperature."], "length": 186}
{"episode_index": 19, "tasks": ["Increase the toaster oven temperature."], "length": 247}
{"episode_index": 20, "tasks": ["Increase the toaster oven temperature."], "length": 157}
{"episode_index": 21, "tasks": ["Increase the toaster oven temperature."], "length": 195}
{"episode_index": 22, "tasks": ["Increase the toaster oven temperature."], "length": 167}
{"episode_index": 23, "tasks": ["Decrease the toaster oven temperature."], "length": 164}
{"episode_index": 24, "tasks": ["Decrease the toaster oven temperature."], "length": 197}
{"episode_index": 25, "tasks": ["Decrease the toaster oven temperature."], "length": 233}
{"episode_index": 26, "tasks": ["Increase the toaster oven temperature."], "length": 194}
{"episode_index": 27, "tasks": ["Decrease the toaster oven temperature."], "length": 207}
{"episode_index": 28, "tasks": ["Increase the toaster oven temperature."], "length": 246}
{"episode_index": 29, "tasks": ["Increase the toaster oven temperature."], "length": 290}
{"episode_index": 30, "tasks": ["Increase the toaster oven temperature."], "length": 153}
{"episode_index": 31, "tasks": ["Increase the toaster oven temperature."], "length": 279}
{"episode_index": 32, "tasks": ["Decrease the toaster oven temperature."], "length": 218}
{"episode_index": 33, "tasks": ["Decrease the toaster oven temperature."], "length": 225}
{"episode_index": 34, "tasks": ["Decrease the toaster oven temperature."], "length": 199}
{"episode_index": 35, "tasks": ["Decrease the toaster oven temperature."], "length": 177}
{"episode_index": 36, "tasks": ["Increase the toaster oven temperature."], "length": 172}
{"episode_index": 37, "tasks": ["Decrease the toaster oven temperature."], "length": 183}
{"episode_index": 38, "tasks": ["Increase the toaster oven temperature."], "length": 232}
{"episode_index": 39, "tasks": ["Decrease the toaster oven temperature."], "length": 263}
{"episode_index": 40, "tasks": ["Decrease the toaster oven temperature."], "length": 272}
{"episode_index": 41, "tasks": ["Decrease the toaster oven temperature."], "length": 240}
{"episode_index": 42, "tasks": ["Increase the toaster oven temperature."], "length": 182}
{"episode_index": 43, "tasks": ["Decrease the toaster oven temperature."], "length": 145}
{"episode_index": 44, "tasks": ["Decrease the toaster oven temperature."], "length": 156}
{"episode_index": 45, "tasks": ["Decrease the toaster oven temperature."], "length": 346}
{"episode_index": 46, "tasks": ["Decrease the toaster oven temperature."], "length": 206}
{"episode_index": 47, "tasks": ["Decrease the toaster oven temperature."], "length": 200}
{"episode_index": 48, "tasks": ["Increase the toaster oven temperature."], "length": 137}
{"episode_index": 49, "tasks": ["Increase the toaster oven temperature."], "length": 242}
{"episode_index": 50, "tasks": ["Decrease the toaster oven temperature."], "length": 196}
{"episode_index": 51, "tasks": ["Increase the toaster oven temperature."], "length": 279}
{"episode_index": 52, "tasks": ["Increase the toaster oven temperature."], "length": 188}
{"episode_index": 53, "tasks": ["Increase the toaster oven temperature."], "length": 171}
{"episode_index": 54, "tasks": ["Decrease the toaster oven temperature."], "length": 238}
{"episode_index": 55, "tasks": ["Increase the toaster oven temperature."], "length": 220}
{"episode_index": 56, "tasks": ["Decrease the toaster oven temperature."], "length": 189}
{"episode_index": 57, "tasks": ["Increase the toaster oven temperature."], "length": 169}
{"episode_index": 58, "tasks": ["Decrease the toaster oven temperature."], "length": 191}
{"episode_index": 59, "tasks": ["Increase the toaster oven temperature."], "length": 229}
{"episode_index": 60, "tasks": ["Increase the toaster oven temperature."], "length": 210}
{"episode_index": 61, "tasks": ["Increase the toaster oven temperature."], "length": 167}
{"episode_index": 62, "tasks": ["Decrease the toaster oven temperature."], "length": 222}
{"episode_index": 63, "tasks": ["Increase the toaster oven temperature."], "length": 319}
{"episode_index": 64, "tasks": ["Increase the toaster oven temperature."], "length": 205}
{"episode_index": 65, "tasks": ["Decrease the toaster oven temperature."], "length": 201}
{"episode_index": 66, "tasks": ["Decrease the toaster oven temperature."], "length": 203}
{"episode_index": 67, "tasks": ["Decrease the toaster oven temperature."], "length": 121}
{"episode_index": 68, "tasks": ["Decrease the toaster oven temperature."], "length": 162}
{"episode_index": 69, "tasks": ["Decrease the toaster oven temperature."], "length": 261}
{"episode_index": 70, "tasks": ["Decrease the toaster oven temperature."], "length": 144}
{"episode_index": 71, "tasks": ["Decrease the toaster oven temperature."], "length": 359}
{"episode_index": 72, "tasks": ["Decrease the toaster oven temperature."], "length": 189}
{"episode_index": 73, "tasks": ["Decrease the toaster oven temperature."], "length": 259}
{"episode_index": 74, "tasks": ["Increase the toaster oven temperature."], "length": 157}
{"episode_index": 75, "tasks": ["Increase the toaster oven temperature."], "length": 257}
{"episode_index": 76, "tasks": ["Decrease the toaster oven temperature."], "length": 189}
{"episode_index": 77, "tasks": ["Decrease the toaster oven temperature."], "length": 240}
{"episode_index": 78, "tasks": ["Decrease the toaster oven temperature."], "length": 175}
{"episode_index": 79, "tasks": ["Decrease the toaster oven temperature."], "length": 184}
{"episode_index": 80, "tasks": ["Increase the toaster oven temperature."], "length": 232}
{"episode_index": 81, "tasks": ["Increase the toaster oven temperature."], "length": 189}
{"episode_index": 82, "tasks": ["Increase the toaster oven temperature."], "length": 138}
{"episode_index": 83, "tasks": ["Increase the toaster oven temperature."], "length": 103}
{"episode_index": 84, "tasks": ["Decrease the toaster oven temperature."], "length": 196}
{"episode_index": 85, "tasks": ["Decrease the toaster oven temperature."], "length": 183}
{"episode_index": 86, "tasks": ["Decrease the toaster oven temperature."], "length": 160}
{"episode_index": 87, "tasks": ["Decrease the toaster oven temperature."], "length": 190}
{"episode_index": 88, "tasks": ["Increase the toaster oven temperature."], "length": 184}
{"episode_index": 89, "tasks": ["Increase the toaster oven temperature."], "length": 165}
{"episode_index": 90, "tasks": ["Increase the toaster oven temperature."], "length": 165}
{"episode_index": 91, "tasks": ["Decrease the toaster oven temperature."], "length": 227}
{"episode_index": 92, "tasks": ["Decrease the toaster oven temperature."], "length": 164}
{"episode_index": 93, "tasks": ["Decrease the toaster oven temperature."], "length": 243}
{"episode_index": 94, "tasks": ["Increase the toaster oven temperature."], "length": 232}
{"episode_index": 95, "tasks": ["Increase the toaster oven temperature."], "length": 156}
{"episode_index": 96, "tasks": ["Decrease the toaster oven temperature."], "length": 196}
{"episode_index": 97, "tasks": ["Decrease the toaster oven temperature."], "length": 212}
{"episode_index": 98, "tasks": ["Decrease the toaster oven temperature."], "length": 200}
{"episode_index": 99, "tasks": ["Decrease the toaster oven temperature."], "length": 253}
{"episode_index": 100, "tasks": ["Decrease the toaster oven temperature."], "length": 131}
{"episode_index": 101, "tasks": ["Increase the toaster oven temperature."], "length": 111}
{"episode_index": 102, "tasks": ["Decrease the toaster oven temperature."], "length": 213}
{"episode_index": 103, "tasks": ["Decrease the toaster oven temperature."], "length": 178}
{"episode_index": 104, "tasks": ["Decrease the toaster oven temperature."], "length": 224}
{"episode_index": 105, "tasks": ["Increase the toaster oven temperature."], "length": 213}
{"episode_index": 106, "tasks": ["Increase the toaster oven temperature."], "length": 146}
tasks.jsonl:
{"task_index": 0, "task": "Increase the toaster oven temperature."}
{"task_index": 1, "task": "Decrease the toaster oven temperature."}
{"task_index": 2, "task": "AdjustToasterOvenTemperature"}
-
data 用 parquet 列式数据存每帧的低维轨迹数据,16维的 proprioceptive state,12维的动作空间
- 3D arm position delta
- 3D arm rotation delta
- 1D gripper control (open/close)
- 4D base control
- 1D mode selection (control base or arm)
-
videos 用 mp4 格式存三个视角下的每帧图像
-
extras 存模型文件、scene 的style、layout、fixtrues等信息
Assets
Scene
每个场景由**(layout、style)** 组合定义。layout 规定厨房的基础s平面格局,确定各类设施(橱柜、微波炉、操作台等)的摆放位置。style 则定义相关材质纹理与设施属性(橱柜拉手款式、柜门样式、咖啡机型号等)。创建环境时会根据参数指定的 layout/style id去找配置文件生成 scene。
objects
objects 有 3,200+ 个,覆盖 150+ 类,来源包括 Objaverse 1.0、LightWheel AI 和 Luma.ai 生成资产;厨房场景里的可交互器具,总共 456 个,覆盖 16 类,即每类都有很多不同 style 的。
数据流
-
训练时workspace 根据 yaml 的 target 创建 LerobotCotrainingDataset 然后包成 PyTorch DataLoader。 LerobotDataset 读取 meta/modality.json,得到有哪些 video/state/action/annotation modality。obs 取过去 n_obs_steps,action 取从过去到未来的 horizon,根据 meta/info.json 里的 data_path 找 parquet,读低维数据 observation.state / action / reward / done,根据 video_path 找 mp4,解码对应帧图像返回一个 data dict,搬到GPU,resize image后送入模型。
-
评估时创建env_runner 用 gym.make(f”robocasa/{env_name}”, split=split, seed=seed) 创建 RoboCasa 环境,包装成 RobomimicImageWrapper -> VideoRecordingWrapper -> MultiStepWrapper。rollout 时从环境实时拿 observation,policy 预测输出 12 维 action。env.step(action) 执行,用 reward / info[“success”] 判断成功,最后算 success rate。
参考资料:
-
[1] LIBERO
-
[2] MuJoCo
-
[3] LCP
-
[5] rlinf_robocasa