知识库/参考文档/Isaac Sim 仿真到真机迁移

Isaac Sim 仿真到真机迁移

本文总共两个环境:hub环境,放置isaacsim、leisaac;lerobot环境,放置lerobot

注意,后续用到的lerobot文档为我们仓库最新提交的lerobot/dev下的文档,为0.5版本。里面对文件做了一些修改,如果想要正常复现需要下载我们仓库的lerobot代码。并且0.5代码下标定文件从so101_follower变成了so_follower,需要建立一个新的so_follower文件,把之前so101的json复制进去即可。

lerobot0.5版本必须要python>=3.12需要重新建一个conda环境

参考文档:https://lightwheelai.github.io/leisaac/ https://huggingface.co/docs/lerobot/envhub_leisaac

git clone https://github.com/JoyandAI/lerobot.git
git checkout dev

1. ROS2 安装

如果后续开发有需要用到ROS地方建议先装ROS。

先安装ROS2如果是24.0版本的ubuntu装ros2 jazz版本,如果是22.0的装humble版本,不要装rolling版本

这里以jazz版本为例:注意,要按照步骤走,否则可能会出现库版本不兼容的报错

conda create -n envhub python=3.11
conda activate envhub
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list >/dev/null

安装ROS 2 Jazzy的桌面完整版,至此ROS2安装完毕

sudo apt upgrade
sudo apt install ros-jazzy-desktop
# 
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
source ~/.bashrc
#
echo $ROS_DISTRO

2. Isaac-lab 与 Isaac-sim 安装

注意nvdia-smi中的Device Version ≥550。如果当前电脑不支持12.8的CUDA,则按照以下表格安装对应版本的包

Dependency IsaacSim 4.5 IsaacSim 5.0 IsaacSim 5.1
Python 3.10 3.11 3.11
IsaacLab v2.1.1 v2.2.1 v2.3.0
CUDA 11.8 12.8 12.8
PyTorch 2.5.1 2.7.0 2.7.0
conda install -c "nvidia/label/cuda-12.8.1" cuda-toolkit
pip install -U torch==2.7.0 torchvision==0.22.0 --index-url https://download.pytorch.org/whl/cu128
git clone https://github.com/LightwheelAI/leisaac.git
# 先安装这个,否则后续会报错
#pip install "packaging==23.0" --force-reinstall
conda install -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ flatdict=4.0.1

cd /source/leisaac
pip install -e .[isaaclab] --extra-index-url https://pypi.nvidia.com 

# Install with lerobot
pip install -e "source/leisaac[lerobot]"

# Fix numpy version
pip install numpy==1.26.0

如下图所示即为安装完成

img

输入 isaacsim打开仿真软件

3. Isaac-sim 仿真

Sim2Real思路:在仿真平台上进行数据采集,然后加上现实的一些样本进行训练,达到迁移的效果。

3.1 Lerobot 快速实现仿真遥操作

首先在hub的****lerobot 0.4环境下创建文件envhub_teleop_example.py,写入以下代码,注意修改SO101LeaderConfig中的port和id字段,给好端口权限。

# envhub_teleop_example.py

import logging
import time
import gymnasium as gym

from dataclasses import asdict, dataclass
from pprint import pformat

from lerobot.teleoperators import (  # noqa: F401
    Teleoperator,
    TeleoperatorConfig,
    make_teleoperator_from_config,
    so_leader,
    bi_so_leader,
)
from lerobot.utils.robot_utils import precise_sleep
from lerobot.utils.utils import init_logging
from lerobot.envs.factory import make_env


@dataclass
class TeleoperateConfig:
    teleop: TeleoperatorConfig
    env_name: str = "so101_pick_orange"
    fps: int = 60


@dataclass
class EnvWrap:
    env: gym.Env


def make_env_from_leisaac(env_name: str = "so101_pick_orange"):
    envs_dict = make_env(
        f'LightwheelAI/leisaac_env:envs/{env_name}.py',
        n_envs=1,
        trust_remote_code=True
    )
    suite_name = next(iter(envs_dict))
    sync_vector_env = envs_dict[suite_name][0]
    env = sync_vector_env.envs[0].unwrapped

    return env


def teleop_loop(teleop: Teleoperator, env: gym.Env, fps: int):
    from leisaac.devices.action_process import preprocess_device_action
    from leisaac.assets.robots.lerobot import SO101_FOLLOWER_MOTOR_LIMITS
    from leisaac.utils.env_utils import dynamic_reset_gripper_effort_limit_sim

    env_wrap = EnvWrap(env=env)

    obs, info = env.reset()
    while True:
        loop_start = time.perf_counter()
        if env.cfg.dynamic_reset_gripper_effort_limit:
            dynamic_reset_gripper_effort_limit_sim(env, 'so101leader')

        raw_action = teleop.get_action()
        processed_action = preprocess_device_action(
            dict(
                so101_leader=True,
                joint_state={
                    k.removesuffix(".pos"): v for k, v in raw_action.items()},
                motor_limits=SO101_FOLLOWER_MOTOR_LIMITS),
            env_wrap
        )
        obs, reward, terminated, truncated, info = env.step(processed_action)
        if terminated or truncated:
            obs, info = env.reset()

        dt_s = time.perf_counter() - loop_start
        precise_sleep(max(1 / fps - dt_s, 0.0))
        loop_s = time.perf_counter() - loop_start
        print(f"\ntime: {loop_s * 1e3:.2f}ms ({1 / loop_s:.0f} Hz)")


def teleoperate(cfg: TeleoperateConfig):
    init_logging()
    logging.info(pformat(asdict(cfg)))

    teleop = make_teleoperator_from_config(cfg.teleop)
    env = make_env_from_leisaac(cfg.env_name)

    teleop.connect()
    if hasattr(env, 'initialize'):
        env.initialize()
    try:
        teleop_loop(teleop=teleop, env=env, fps=cfg.fps)
    except KeyboardInterrupt:
        pass
    finally:
        teleop.disconnect()
        env.close()


def main():
    teleoperate(TeleoperateConfig(
        teleop=so_leader.SO101LeaderConfig(
            port="/dev/ttyACM0",
            id='leader',
            use_degrees=False,
        ),
        env_name="so101_pick_orange",
        fps=60,
    ))


if __name__ == "__main__":
    main()

进入conda环境,运行这个python文件即可完成仿真的demo

python envhub_teleop_example.py

其中可以直接更改env_name字段切换仿真环境,现有的环境如下:

so101_pick_orange
so101_lift_cube
so101_clean_toytable

3.2 遥操作和数据采集

这里数据采集支持lelerobot和lekiwi,需要在一开始指定--task和 --teleop_device。这里先以SO101抓橘子为例

leisaac环境中输入以下指令可以快速开始数据采集,如果只想要纯遥操作,去掉record即可。采集好的数据会保存为hdf5的格式,后续我们需要将这个格式转化为lerobot可以识别的dataset。

键盘:B开始本轮,R重置本轮,N结束本轮,进入下一轮

cd leisaac
python scripts/environments/teleoperation/teleop_se3_agent.py \
    --task=LeIsaac-SO101-PickOrange-v0 \
    --teleop_device=so101leader \
    --port=/dev/ttyACM0 \
    --num_envs=1 \
    --device=cuda \
    --enable_cameras \
    --record \
    --dataset_file=./datasets/dataset.hdf5

在这里还可以选择控制的方式,可以选择单臂lerobot-SO101leader,也可以使用键盘控制的方式。

imgimg

输入按键 功能说明
W / S 前进 / 后退,对应平移示意图中的红色箭头方向
A / D 左移 / 右移,对应平移示意图中的绿色箭头方向
Q / E 上移 / 下移,对应平移示意图中的蓝色箭头方向
J / L 向左 / 向右旋转(偏航,Yaw),对应旋转示意图中的蓝色箭头方向
K / I 向上 / 向下旋转(俯仰,Pitch),对应旋转示意图中的绿色箭头方向
U / O 夹爪打开 / 夹爪闭合
参数 说明
--task 指定要运行的任务环境名称,例如:LeIsaac-SO101-PickOrange-v0
--seed 指定环境的随机种子,例如:42
--teleop_device 指定遥操作设备类型,例如:so101leaderbi-so101leaderkeyboardgamepadlekiwi-leaderlekiwi-keyboardlekiwi-gamepad
--port 指定遥操作设备端口,例如:/dev/ttyACM0。仅在 teleop_deviceso101leaderlekiwi-leader 时使用
--left_arm_port 指定左臂端口,例如:/dev/ttyACM0。仅在 teleop_devicebi-so101leader 时使用
--right_arm_port 指定右臂端口,例如:/dev/ttyACM1。仅在 teleop_devicebi-so101leader 时使用
--num_envs 设置并行仿真环境数量,遥操作时通常为 1
--device 指定计算设备,例如:cpucuda(GPU)
--enable_cameras 启用相机传感器,在遥操作过程中采集视觉数据
--record 启用数据记录,将遥操作数据保存为 HDF5 文件
--dataset_file 记录数据集的保存路径,例如:./datasets/record_data.hdf5
--resume 从已有的数据集文件继续记录数据
--recalibrate 重新校准 SO101-Leader 或 Bi-SO101Leader 设备
--quality 是否启用高质量渲染模式
--use_lerobot_recorder 是否使用 LeRobot recorder 进行数据记录
--lerobot_dataset_repo_id LeRobot 数据集仓库 ID
--lerobot_dataset_fps LeRobot 数据集采集帧率(FPS)

官方提供了该环境采集好的60组数据,可以直接用于训练。https://huggingface.co/datasets/LightwheelAI/leisaac-pick-orange

3.3 数据格式转换

3.3.1 hdf5 --> LerobotDataset V2 or LerobotDataset V3

注意,采用hdf5->V2需要hub环境下lerobot版本为0.3,hdf5->V3需要本地lerobot环境为0.4以上,先检查自己环境lerobot版本,若版本不满足先安装对应版本。更新完之后需要重新安装numpy

首先进入hub的环境,把我们采集好的数据进行一下格式转换,转换成Lerobot能够识别的数据datasetV2或者V3。看情况转换,如果后续需要对视频进行生成建议先转到V2格式

pip show lerobot
# 更新 / 降级 lerobot
pip install lerobot==0.4.2 
#or
pip install lerobot==0.3.3

# Fix numpy version
pip install numpy==1.26.0

python scripts/convert/isaaclab2lerobot.py \
    --task_name=LeIsaac-SO101-PickOrange-v0 \
    --repo_id=EverNorif/so101_test_orange_pick \
    --hdf5_root=./datasets \
    --hdf5_files=dataset.hdf5
    
python scripts/convert/isaaclab2lerobotv3.py \
    --task_name=LeIsaac-SO101-PickOrange-v0 \
    --repo_id=EverNorif/so101_test_orange_pick \
    --hdf5_root=./datasets \
    --hdf5_files=dataset.hdf5
参数 中文说明
--task_name 任务名称,例如:LeIsaac-SO101-PickOrange-v0
--task_type 指定任务类型。如果数据集是通过键盘或手柄(gamepad)记录的,需要设置为 keyboardgamepad;否则不要设置,保持默认值 None
--repo_id 指定 LeRobot 数据集在 HuggingFace 上的仓库 ID,例如:EverNorif/so101_test_orange_pick
--fps 指定 LeRobot 数据集的帧率(Frames Per Second,每秒帧数)
--hdf5_root HDF5 数据文件所在的根目录
--hdf5_files HDF5 文件列表(用逗号分隔)。如果不提供,则默认使用 hdf5_root 目录下的 dataset.hdf5
--task_description 任务描述。如果不提供,则使用任务中默认定义的描述
--push_to_hub 是否将数据集上传(push)到 HuggingFace Hub

3.3.2 Lerobot-DatasetV2 --> Lerobot-DatasetV3

由于lerobot 0.4后更新了datasetV3版本,官方也提供了转换代码,若使用最新版本的代码建议更新到V3数据集。使用后如果报错huggingface相关可以不用管,只要看到当前文件下有两个文件,一个后缀加了old就表明成功。

若要删除某一轮的数据可以参考我们仓库下的lerobot/example/dataset/delete_episode_demo.py

# lerobot环境下安装
pip install "https://github.com/huggingface/lerobot/archive/33cad37054c2b594ceba57463e8f11ee374fa93c.zip"

# Convert an existing v2.1 dataset hosted on the Hub:
python -m lerobot.datasets.v30.convert_dataset_v21_to_v30 --repo-id=<HF_USER/DATASET_ID>

3.3.3 模型训练

数据收集完成之后进行模型的训练,相关训练代码参考lerobot文档LeRobot SO-ARM101机械臂使用文档-v0.4。训练完后模型可以直接部署在真实机器中,也可以在仿真中推理。

3.3.4 lerobot-policy 异步仿真推理

用我们仓库的代码,异步仿真推理****建议lerobot的版本为0.4以上

git checkout 099af72601b39cc6b0752b96737f62c03bbca776
pip install -e ".[async]"

lerobot端口启动policy_server代理

# 0.3版本
python -m  lerobot.scripts.server.policy_server \
     --host=localhost \
     --port=5858

# 0.4以上版本
python -m lerobot.async_inference.policy_server \
     --host=localhost \
     --port=5858

启动isaacsim的conda环境,开启isaacsim-client代理,之后会开始异步推理。R键重置当前推理,N键进入下一条。这里以smolvla为例

python scripts/evaluation/policy_inference.py \
    --task=LeIsaac-SO101-PickOrange-v0 \
    --eval_rounds=10 \
    --policy_type=lerobot-act\
    --policy_host=localhost \
    --policy_port=5858 \
    --policy_timeout_ms=5000 \
    --policy_action_horizon=32 \
    --policy_language_instruction="Pick up the orange and place it on the plate" \
    --device=cuda \
    --policy_checkpoint_path=/home/joyandai/leisaac/tmp/pretrained_model \
    --enable_cameras
参数 说明
--task 要运行的任务环境名称
--seed 环境的随机种子
--episode_length_s 每个 episode 的时长(单位:秒)
--eval_rounds 评估轮数。0 表示不添加超时终止条件,策略会一直运行直到成功或手动重置
--policy_type 使用的策略类型,lerobot-act、lerobot-smolvla,以lerobot-XXX为格式
--policy_host 策略服务器地址,server方指定
--policy_port 策略服务器端口,server方指定
--policy_timeout_ms 策略服务器超时时间(毫秒)
--policy_action_horizon 每次服务器推理的action数量,需要调整,原理见blog:https://huggingface.co/blog/async-robot-inference
--policy_language_instruction 提供给策略的语言指令(自然语言任务描述)
--policy_checkpoint_path 策略模型 checkpoint 绝对路径,到pretrained下
--device cuda

4. 如何创建自己的任务工作流

这篇教程以 Custom_Task 为例,说明如何在 LeIsaac 中搭建一条完整的自定义任务工作流。

4.1 Custom_Task 环境代码

先创建文件:

source/leisaac/leisaac/tasks/custom_task/custom_task_env_cfg.py
import torch

from isaaclab.assets import AssetBaseCfg, RigidObject
from isaaclab.managers import SceneEntityCfg
from isaaclab.managers import TerminationTermCfg as DoneTerm
from isaaclab.utils import configclass

from leisaac.assets.scenes.custom_scene import CUSTOM_SCENE_CFG, CUSTOM_SCENE_USD_PATH
from leisaac.utils.general_assets import parse_usd_and_create_subassets
from leisaac.utils.domain_randomization import domain_randomization, randomize_object_uniform

from ..template import (
    SingleArmObservationsCfg,
    SingleArmTaskEnvCfg,
    SingleArmTaskSceneCfg,
    SingleArmTerminationsCfg,
)


@configclass
class CustomTaskSceneCfg(SingleArmTaskSceneCfg):
    """Scene configuration for the custom task."""

    scene: AssetBaseCfg = CUSTOM_SCENE_CFG.replace(prim_path="{ENV_REGEX_NS}/Scene")


def cube_in_box(
    env,
    cube_cfg: SceneEntityCfg,
    box_cfg: SceneEntityCfg,
    x_range: tuple[float, float],
    y_range: tuple[float, float],
    height_threshold: float,
):
    """Termination condition for the object in the box."""
    done = torch.ones(env.num_envs, dtype=torch.bool, device=env.device)

    box: RigidObject = env.scene[box_cfg.name]
    box_x = box.data.root_pos_w[:, 0] - env.scene.env_origins[:, 0]
    box_y = box.data.root_pos_w[:, 1] - env.scene.env_origins[:, 1]

    cube: RigidObject = env.scene[cube_cfg.name]
    cube_x = cube.data.root_pos_w[:, 0] - env.scene.env_origins[:, 0]
    cube_y = cube.data.root_pos_w[:, 1] - env.scene.env_origins[:, 1]
    cube_z = cube.data.root_pos_w[:, 2] - env.scene.env_origins[:, 2]

    done = torch.logical_and(done, cube_x < box_x + x_range[1])
    done = torch.logical_and(done, cube_x > box_x + x_range[0])
    done = torch.logical_and(done, cube_y < box_y + y_range[1])
    done = torch.logical_and(done, cube_y > box_y + y_range[0])
    done = torch.logical_and(done, cube_z < height_threshold)

    return done


@configclass
class TerminationsCfg(SingleArmTerminationsCfg):
    """Termination configuration for the custom task."""

    success = DoneTerm(
        func=cube_in_box,
        params={
            "cube_cfg": SceneEntityCfg("cube"),
            "box_cfg": SceneEntityCfg("box"),
            "x_range": (-0.05, 0.05),
            "y_range": (-0.05, 0.05),
            "height_threshold": 0.10,
        },
    )


@configclass
class CustomTaskEnvCfg(SingleArmTaskEnvCfg):
    """Configuration for the custom task environment."""

    scene: CustomTaskSceneCfg = CustomTaskSceneCfg(env_spacing=8.0)

    observations: SingleArmObservationsCfg = SingleArmObservationsCfg()

    terminations: TerminationsCfg = TerminationsCfg()

    task_description: str = "pick up the red cube and place it into the box."

    def __post_init__(self) -> None:
        super().__post_init__()

        self.viewer.eye = (-0.2, -1.0, 0.5)
        self.viewer.lookat = (0.6, 0.0, -0.2)

        self.scene.robot.init_state.pos = (0.35, -0.64, 0.01)

        parse_usd_and_create_subassets(CUSTOM_SCENE_USD_PATH, self)

        domain_randomization(
            self,
            random_options=[
                randomize_object_uniform(
                    "cube",
                    pose_range={
                        "x": (-0.05, 0.05),
                        "y": (-0.05, 0.05),
                        "z": (0.0, 0.0),
                    },
                ),
                randomize_object_uniform(
                    "box",
                    pose_range={
                        "x": (-0.05, 0.05),
                        "y": (-0.05, 0.05),
                        "z": (0.0, 0.0),
                    },
                ),
            ],
        )
CustomTaskSceneCfg
  • 作用:定义这个任务使用哪个场景。
  • 放在这个文件里,是因为它属于当前任务的场景配置。
cube_in_box
  • 作用:定义成功条件。
  • 它检查 cube 是否进入 box 的范围内,并且高度是否低于阈值。
TerminationsCfg
  • 作用:把 cube_in_box 注册成任务的成功终止条件。
  • 这样环境在运行时就知道什么时候任务算完成。
CustomTaskEnvCfg
  • 作用:这是整个普通任务环境的主配置类。
  • 它把场景、观测、终止条件、任务描述都组织到一起。
  • __post_init__ 里进一步设置了视角、机器人初始位姿和物体随机化。

4.2 Custom_Task 注册代码

创建文件:

source/leisaac/leisaac/tasks/custom_task/__init__.py
import gymnasium as gym

gym.register(
    id="LeIsaac-SO101-CustomTask-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    disable_env_checker=True,
    kwargs={
        "env_cfg_entry_point": f"{__name__}.custom_task_env_cfg:CustomTaskEnvCfg",
    },
)

gym.register(
    id="LeIsaac-SO101-CustomTask-Mimic-v0",
    entry_point="leisaac.enhance.envs:ManagerBasedRLLeIsaacMimicEnv",
    disable_env_checker=True,
    kwargs={
        "env_cfg_entry_point": f"{__name__}.custom_task_mimic_env_cfg:CustomTaskMimicEnvCfg",
    },
)

gym.register(...)

  • 注册普通任务:
  • 任务名是 LeIsaac-SO101-CustomTask-v0
  • 使用普通环境入口 ManagerBasedRLEnv
gym.register(...)
  • 注册 Mimic 任务:
  • 任务名是 LeIsaac-SO101-CustomTask-Mimic-v0
  • 使用 Mimic 环境入口 ManagerBasedRLLeIsaacMimicEnv

Mimic环境用于后续生成数据用

4.3 运行 Custom_Task 并录制原始数据

启动普通任务环境验证。可以直接运行:

python scripts/environments/teleoperation/teleop_se3_agent.py \
    --task=LeIsaac-SO101-CustomTask-v0 \
    --teleop_device=so101leader \
    --num_envs=1 \
    --device=cuda \
    --enable_cameras

最后得到的hd5文件用于转化成lerobot数据集和进行Minmic生成

4.4 Mimic 环境配置代码

创建文件:

source/leisaac/leisaac/tasks/custom_task/custom_task_mimic_env_cfg.py
from isaaclab.envs.mimic_env_cfg import MimicEnvCfg, SubTaskConfig
from isaaclab.managers import ObservationGroupCfg as ObsGroup
from isaaclab.managers import ObservationTermCfg as ObsTerm
from isaaclab.managers import SceneEntityCfg
from isaaclab.utils import configclass

from .custom_task_env_cfg import CustomTaskEnvCfg, cube_in_box
from ..template import SingleArmObservationsCfg


@configclass
class ObservationsCfg(SingleArmObservationsCfg):
    """Extend the base observation config with subtask completion signals for Mimic."""

    @configclass
    class SubtaskCfg(ObsGroup):
        """Observations for mimic subtask boundary signals."""

        place_cube_in_box = ObsTerm(
            func=cube_in_box,
            params={
                "cube_cfg": SceneEntityCfg("cube"),
                "box_cfg": SceneEntityCfg("box"),
                "x_range": (-0.05, 0.05),
                "y_range": (-0.05, 0.05),
                "height_threshold": 0.10,
            },
        )

        def __post_init__(self):
            self.enable_corruption = False
            self.concatenate_terms = False

    subtask_terms: SubtaskCfg = SubtaskCfg()


@configclass
class CustomTaskMimicEnvCfg(CustomTaskEnvCfg, MimicEnvCfg):
    """Custom task config augmented with Mimic-specific datagen settings."""

    observations: ObservationsCfg = ObservationsCfg()

    def __post_init__(self):
        super().__post_init__()

        self.datagen_config.name = "custom_task_leisaac_task_v0"
        self.datagen_config.generation_guarantee = True
        self.datagen_config.generation_keep_failed = True
        self.datagen_config.generation_num_trials = 10
        self.datagen_config.generation_select_src_per_subtask = True
        self.datagen_config.generation_transform_first_robot_pose = False
        self.datagen_config.generation_interpolate_from_last_target_pose = True
        self.datagen_config.generation_relative = True
        self.datagen_config.max_num_failures = 25
        self.datagen_config.seed = 42

        self.subtask_configs["so101_follower"] = [
            SubTaskConfig(
                object_ref="box",
                subtask_term_signal="place_cube_in_box",
                subtask_term_offset_range=(10, 20),
                selection_strategy="nearest_neighbor_object",
                selection_strategy_kwargs={"nn_k": 3},
                action_noise=0.003,
                num_interpolation_steps=5,
                num_fixed_steps=0,
                apply_noise_during_interpolation=False,
                description="Place cube into box",
                next_subtask_description="Rest robot",
            ),
            SubTaskConfig(
                object_ref=None,
                subtask_term_signal=None,
                subtask_term_offset_range=(0, 0),
                selection_strategy="random",
                selection_strategy_kwargs={},
                action_noise=0.0001,
                num_interpolation_steps=5,
                num_fixed_steps=0,
                apply_noise_during_interpolation=False,
            ),
        ]
ObservationsCfg
  • 作用:在普通观测配置基础上,增加 Mimic 需要的 subtask_terms
  • 这里新增了 place_cube_in_box,作为子任务完成信号。
SubtaskCfg
  • 作用:把子任务信号整理成一个单独的 observation group。
  • enable_corruption = Falseconcatenate_terms = False 是为了让这些信号按原始结构保留,不做额外拼接。
CustomTaskMimicEnvCfg
  • 作用:这是 Mimic 环境的主配置类。
  • 它继承普通任务配置 CustomTaskEnvCfg,也继承 MimicEnvCfg
  • 前者保留普通任务的场景、成功条件和随机化逻辑,后者提供 Mimic 所需的数据生成配置。
datagen_config
  • 作用:控制 Mimic 数据生成的基本行为,例如生成次数、是否保留失败样本、是否按相对位姿生成等。
subtask_configs
  • 作用:定义这个任务的子任务序列。
  • 当前示例里定义了两个子任务:
    • place_cube_in_box
    • 一个结尾的 rest 子任务

5. Custom Task Mimic 工作流

Mimic 的原理可以简单理解为:

不是重新训练一个生成模型,而是从已有数据集里拆出可复用的动作片段,在新随机场景里重新组合并验证,生成新的示范数据。

核心过程是:

  1. 先把演示按子任务切开,比如接近、抓取、放置。
  2. 记录每段动作和物体、末端之间的关系,这样系统知道这段操作是“相对物体怎么做的”。
  3. 在新场景里复用这些片段,物体位置变了,系统就把原来的操作片段迁移到新的物体位置附近。
  4. 做插值和平滑连接,保证机器人能从当前状态自然接上这段动作。
  5. 放到仿真里执行,成功就保留,失败就丢弃。

所以在上述代码中,需要定义好任务过程、完成任务的条件,这样才能让环境自己去判断是否真的成功完成任务。由于目前我们任务比较单一,仅为抓取一个物体到盒子里。如果有三个,那抓取的顺序也会随机,这样可以大量节省类似数据采集的时间。

适用任务:

  • 普通任务:LeIsaac-SO101-CustomTask-v0
  • Mimic 任务:LeIsaac-SO101-CustomTask-Mimic-v0

前提条件:

  • 已经录制好原始数据:./datasets/my_task.hdf5
  • 自定义任务已经注册 Mimic 环境
  • Mimic 相关依赖已经安装完成

leisaac/scripts/mimic/annotate_demos.py下需要加上以下的代码376-380行

img

5.1 步骤 1:把原始动作转成 IK 动作

python scripts/mimic/eef_action_process.py \
    --input_file ./datasets/my_task.hdf5 \
    --output_file ./datasets/my_task_mimic.hdf5 \
    --to_ik --headless

5.2 步骤 2:标注演示数据

python scripts/mimic/annotate_demos.py \
  --device cuda \
  --task LeIsaac-SO101-CustomTask-Mimic-v0 \
  --input_file ./datasets/my_task_mimic.hdf5 \
  --output_file ./datasets/annotated_my_task_mimic.hdf5 \
  --enable_cameras \
  --headless \
  --auto

说明:

  • --auto 表示自动标注,任务简单建议自动
  • 如果不使用 --auto,则进入人工标注流程

5.3 步骤 3:生成新数据

python scripts/mimic/generate_dataset.py \
  --device cpu \
  --task LeIsaac-SO101-CustomTask-Mimic-v0 \
  --num_envs 1 \
  --generation_num_trials 10 \
  --input_file ./datasets/annotated_my_task_mimic.hdf5 \
  --output_file ./datasets/generated_my_task_mimic.hdf5 \
  --enable_cameras

说明:

  • 日志里的 1/5 (20.0%) successful demos generated by mimic 表示当前累计生成成功率
  • device先用cpu,cuda

5.4 步骤 4:把生成结果转回关节动作

python scripts/mimic/eef_action_process.py \
    --input_file ./datasets/generated_my_task_mimic.hdf5 \
    --output_file ./datasets/final_generated_my_task_mimic.hdf5 \
    --to_joint --headless

5.5 注意事项

  • annotate_demos.pygenerate_dataset.py 必须使用 Mimic 任务 ID
  • 普通 replay 一般使用普通任务环境,不直接使用 Mimic 任务环境

6. Sim2Real 数据提供

摄像机参数:640*360

注意:如果需要更改相机分辨率参数,要设置leisaac代码中的相机参数,保证真实数据和仿真数据的分辨率要相同,否则无法合并视频。我们leisaac仓库和lerobot教程中默认的分辨率均为640 * 360。

my_task:仿真采集数据 67组

real_data_blue-block_red-box:真实环境下蓝色物块放到红色盒子 10组

real_data_green-block_red-box:真实环境下绿色物块放到红色盒子 10组

real_data_orange-block_red-box:真实环境下橘色物块放到红色盒子 10组

6.1 模型与数据提供

https://huggingface.co/datasets/sherardliu/isaac-custom-task

我们提供了可供复现的数据和模型,如果想要不同的组合,可以使用/example/dataset/merge_dataset_demo.py文件

my_task 仿真数据67组
my_task_mimic mimic生成数据10组
real_data_blue-block_red-box 真实场景蓝色物块放红盒子10组
real_data_green-block_red-box 真实场景绿色物块放红盒子10组
real_data_orange-block_red-box 真实场景橘色物块放红盒子10组
sim2real 上述5组数据之和

我们对不同的场景进行了测试,训练了两个模型,smolvla模型需要加上微调的额外库

pip install -e ".[peft]"
模型 数据集 仿真成功率 真实成功率
act sim2real 90% ≈70%
peft64 smolvla-500M my_task+my_task_mimic+real_data_green-block_red-box