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
如下图所示即为安装完成

输入 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,也可以使用键盘控制的方式。


| 输入按键 | 功能说明 |
|---|---|
W / S |
前进 / 后退,对应平移示意图中的红色箭头方向 |
A / D |
左移 / 右移,对应平移示意图中的绿色箭头方向 |
Q / E |
上移 / 下移,对应平移示意图中的蓝色箭头方向 |
J / L |
向左 / 向右旋转(偏航,Yaw),对应旋转示意图中的蓝色箭头方向 |
K / I |
向上 / 向下旋转(俯仰,Pitch),对应旋转示意图中的绿色箭头方向 |
U / O |
夹爪打开 / 夹爪闭合 |
| 参数 | 说明 |
|---|---|
--task |
指定要运行的任务环境名称,例如:LeIsaac-SO101-PickOrange-v0 |
--seed |
指定环境的随机种子,例如:42 |
--teleop_device |
指定遥操作设备类型,例如:so101leader、bi-so101leader、keyboard、gamepad、lekiwi-leader、lekiwi-keyboard、lekiwi-gamepad |
--port |
指定遥操作设备端口,例如:/dev/ttyACM0。仅在 teleop_device 为 so101leader 或 lekiwi-leader 时使用 |
--left_arm_port |
指定左臂端口,例如:/dev/ttyACM0。仅在 teleop_device 为 bi-so101leader 时使用 |
--right_arm_port |
指定右臂端口,例如:/dev/ttyACM1。仅在 teleop_device 为 bi-so101leader 时使用 |
--num_envs |
设置并行仿真环境数量,遥操作时通常为 1 |
--device |
指定计算设备,例如:cpu 或 cuda(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)记录的,需要设置为 keyboard 或 gamepad;否则不要设置,保持默认值 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 = False和concatenate_terms = False是为了让这些信号按原始结构保留,不做额外拼接。
CustomTaskMimicEnvCfg
- 作用:这是 Mimic 环境的主配置类。
- 它继承普通任务配置
CustomTaskEnvCfg,也继承MimicEnvCfg。 - 前者保留普通任务的场景、成功条件和随机化逻辑,后者提供 Mimic 所需的数据生成配置。
datagen_config
- 作用:控制 Mimic 数据生成的基本行为,例如生成次数、是否保留失败样本、是否按相对位姿生成等。
subtask_configs
- 作用:定义这个任务的子任务序列。
- 当前示例里定义了两个子任务:
place_cube_in_box- 一个结尾的
rest子任务
5. Custom Task Mimic 工作流
Mimic 的原理可以简单理解为:
不是重新训练一个生成模型,而是从已有数据集里拆出可复用的动作片段,在新随机场景里重新组合并验证,生成新的示范数据。
核心过程是:
- 先把演示按子任务切开,比如接近、抓取、放置。
- 记录每段动作和物体、末端之间的关系,这样系统知道这段操作是“相对物体怎么做的”。
- 在新场景里复用这些片段,物体位置变了,系统就把原来的操作片段迁移到新的物体位置附近。
- 做插值和平滑连接,保证机器人能从当前状态自然接上这段动作。
- 放到仿真里执行,成功就保留,失败就丢弃。
所以在上述代码中,需要定义好任务过程、完成任务的条件,这样才能让环境自己去判断是否真的成功完成任务。由于目前我们任务比较单一,仅为抓取一个物体到盒子里。如果有三个,那抓取的顺序也会随机,这样可以大量节省类似数据采集的时间。
适用任务:
- 普通任务:
LeIsaac-SO101-CustomTask-v0 - Mimic 任务:
LeIsaac-SO101-CustomTask-Mimic-v0
前提条件:
- 已经录制好原始数据:
./datasets/my_task.hdf5 - 自定义任务已经注册 Mimic 环境
- Mimic 相关依赖已经安装完成
leisaac/scripts/mimic/annotate_demos.py下需要加上以下的代码376-380行

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.py和generate_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 |