参考链接:unitree_IL_lerobot
1. 环境安装
1.1 LeRobot 环境安装
本项的目的是使用LeRobot开源框架训练并测试基于 Unitree 机器人采集的数据。所以首先需要安装 LeRobot 相关依赖。安装步骤如下,也可以参考LeRobot官方进行安装:
# 下载源码
git clone --recurse-submodules https://github.com/unitreerobotics/unitree_IL_lerobot.git
# 已经下载:
git submodule update --init --recursive
# 创建 conda 环境
conda create -y -n unitree_lerobot python=3.10
conda activate unitree_lerobot
# 安装 LeRobot
cd lerobot && pip install -e .
# 安装 unitree_lerobot
cd .. && pip install -e .
1.2 unitree_sdk2_python
针对 Unitree 机器人dds通讯需要安装一些依赖,安装步骤如下:
git clone https://github.com/unitreerobotics/unitree_sdk2_python.git
cd unitree_sdk2_python && pip install -e .
2. 数据采集与转换
2.1 数据加载测试
如果你想加载我们已经录制好的数据集, 你可以从 huggingface上加载
unitreerobotics/G1_ToastedBread_Dataset 数据集, 默认下载到
~/.cache/huggingface/lerobot/unitreerobotics. 如果想从加载本地数据请更改 root 参数
from lerobot.common.datasets.lerobot_dataset import LeRobotDataset
import tqdm
episode_index = 1
dataset = LeRobotDataset(repo_id="unitreerobotics/G1_ToastedBread_Dataset")
from_idx = dataset.episode_data_index["from"][episode_index].item()
to_idx = dataset.episode_data_index["to"][episode_index].item()
for step_idx in tqdm.tqdm(range(from_idx, to_idx)):
step = dataset[step_idx]
可视化
cd unitree_lerobot/lerobot
python lerobot/scripts/visualize_dataset.py \
--repo-id unitreerobotics/G1_ToastedBread_Dataset \
--episode-index 0
2.2 数据采集
如果你想录制自己的数据集, 可以使用开源的遥操作项目avp_teleoperate 对 Unitree G1 人形机器人进行数据采集,具体可参考avp_teleoperate项目。
2.3 数据转换
使用avp_teleoperate采集的数据是采用 JSON 格式进行存储。假如采集的数据存放在$HOME/datasets/task_name 目录中,格式如下:
datasets/ # 数据集文件夹
└── task_name / # 任务名称
├── episode_0001 # 第一条轨迹
│ ├──audios/ # 声音信息
│ ├──colors/ # 图像信息
│ ├──depths/ # 深度图像信息
│ └──data.json # 状态以及动作信息
├── episode_0002
├── episode_...
├── episode_xxx
2.3.1 排序和重命名
生成 lerobot 的数据集时,最好保证数据的episode_0命名是从 0 开始且是连续的,使用下面脚本对数据进行排序处理
python unitree_lerobot/utils/sort_and_rename_folders.py \
--data_dir $HOME/datasets/task_name
python unitree_lerobot/utils/sort_and_rename_folders.py --data_dir ~/Documents/unitrees/unitree_IL_lerobot/datasets/
2.3.2 转换
转换json格式到lerobot格式,你可以根据 ROBOT_CONFIGS 去定义自己的 robot_type
# --raw-dir 对应json的数据集目录
# --repo-id 对应自己的repo-id
# --push_to_hub 是否上传到云端
# --robot_type 对应的机器人类型
python unitree_lerobot/utils/convert_unitree_json_to_lerobot.py
--raw-dir $HOME/datasets
--repo-id your_name/repo_task_name
--robot_type Unitree_G1_Dex3 # Unitree_Z1_Dual, Unitree_G1_Gripper, Unitree_G1_Dex3
--push_to_hub
debug
灵巧手,宇树没有给出对应的代码,需要自己自定义转换格式:
调整
unitree_lerobot/utils/constants.py 最后添加:
# 根据实际数据结构调整配置
G1_INSPIRE_CONFIG = RobotConfig(
motors=[
"kLeftShoulderPitch",
"kLeftShoulderRoll",
"kLeftShoulderYaw",
"kLeftElbow",
"kLeftWristRoll",
"kLeftWristPitch",
"kLeftWristYaw",
"kRightShoulderPitch",
"kRightShoulderRoll",
"kRightShoulderYaw",
"kRightElbow",
"kRightWristRoll",
"kRightWristPitch",
"kRightWristYaw",
# 定义因时灵巧手的 手部电机
'L_pinky_proximal_joint', 'L_ring_proximal_joint', 'L_middle_proximal_joint',
'L_index_proximal_joint', 'L_thumb_proximal_pitch_joint', 'L_thumb_proximal_yaw_joint',
'R_pinky_proximal_joint', 'R_ring_proximal_joint', 'R_middle_proximal_joint',
'R_index_proximal_joint', 'R_thumb_proximal_pitch_joint', 'R_thumb_proximal_yaw_joint'
],
cameras=[
"cam_left_high",
"cam_right_high",
# "cam_left_wrist", # 缺少腕部相机,暂时去掉
# "cam_right_wrist",
],
camera_to_image_key={
'color_0': 'cam_left_high',
'color_1': 'cam_right_high',
# 'color_2': 'cam_left_wrist',
# 'color_3': 'cam_right_wrist'
},
json_state_data_name=['left_arm', 'right_arm', 'left_ee', 'right_ee'], # 对应数据格式修改手部 key
json_action_data_name=['left_arm', 'right_arm', 'left_ee', 'right_ee']
)
ROBOT_CONFIGS = {
"Unitree_Z1_Single": Z1_SINGLE_CONFIG,
"Unitree_Z1_Dual": Z1_CONFIG,
"Unitree_G1_Gripper": G1_GRIPPER_CONFIG,
"Unitree_G1_Dex3": G1_DEX3_CONFIG,
"Unitree_G1_Inspire": G1_INSPIRE_CONFIG,
}
运行:
python convert_unitree_json_to_lerobot.py --raw-dir ~/Documents/unitrees/unitree_IL_lerobot/datasets/ --repo-id my_test/test01 --robot_type Unitree_G1_Inspire
报错:
==> Cached 1 episodes
Map: 100%|█████████████████████████| 1249/1249 [00:00<00:00, 6208.42 examples/s]
Creating parquet from Arrow format: 100%|████████| 2/2 [00:00<00:00, 597.48ba/s]
0%| | 0/1 [00:11<?, ?it/s]
Traceback (most recent call last):
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 396, in <module>
tyro.cli(json_to_lerobot)
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/site-packages/tyro/_cli.py", line 230, in cli
return run_with_args_from_cli()
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 377, in json_to_lerobot
dataset = populate_dataset(
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 351, in populate_dataset
dataset.save_episode()
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/lerobot_dataset.py", line 879, in save_episode
video_paths = self.encode_episode_videos(episode_index)
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/lerobot_dataset.py", line 983, in encode_episode_videos
encode_video_frames(img_dir, video_path, self.fps, overwrite=True)
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/video_utils.py", line 292, in encode_video_frames
subprocess.run(ffmpeg_cmd, check=True, stdin=subprocess.DEVNULL)
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/subprocess.py", line 503, in run
with Popen(*popenargs, **kwargs) as process:
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/subprocess.py", line 971, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/subprocess.py", line 1863, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'ffmpeg'
解决:
sudo apt update
sudo apt install ffmpeg
重新运行
python convert_unitree_json_to_lerobot.py --raw-dir ~/Documents/unitrees/unitree_IL_lerobot/datasets/ --repo-id my_test/test01 --robot_type Unitree_G1_Inspire
报错
Traceback (most recent call last):
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 396, in <module>
tyro.cli(json_to_lerobot)
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/site-packages/tyro/_cli.py", line 230, in cli
return run_with_args_from_cli()
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 377, in json_to_lerobot
dataset = populate_dataset(
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/utils/convert_unitree_json_to_lerobot.py", line 351, in populate_dataset
dataset.save_episode()
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/lerobot_dataset.py", line 879, in save_episode
video_paths = self.encode_episode_videos(episode_index)
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/lerobot_dataset.py", line 983, in encode_episode_videos
encode_video_frames(img_dir, video_path, self.fps, overwrite=True)
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/common/datasets/video_utils.py", line 292, in encode_video_frames
subprocess.run(ffmpeg_cmd, check=True, stdin=subprocess.DEVNULL)
File "/home/ubuntu/anaconda3/envs/unitree_lerobot/lib/python3.10/subprocess.py", line 526, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['ffmpeg', '-f', 'image2', '-r', '30', '-i', '/home/ubuntu/.cache/huggingface/lerobot/my_test/test01/images/observation.images.cam_left_high/episode_000000/frame_%06d.png', '-vcodec', 'libsvtav1', '-pix_fmt', 'yuv420p', '-g', '2', '-crf', '30', '-loglevel', 'error', '-y', '/home/ubuntu/.cache/huggingface/lerobot/my_test/test01/videos/chunk-000/observation.images.cam_left_high/episode_000000.mp4']' returned non-zero exit status 1.
从错误信息来看,ffmpeg 命令执行失败,返回了一个非零退出状态。这通常意味着 ffmpeg 在尝试编码视频时遇到了问题。虽然 ffmpeg 已经被找到并且可以运行,但命令中的某些参数或输入文件可能存在问题。
验证 FFmpeg 编码器支持: 你使用的 -vcodec libsvtav1 参数指定使用 SVT-AV1 编码器。确保你的 ffmpeg 安装版本支持此编码器。你可以通过以下命令检查是否支持 SVT-AV1 编码器:
ffmpeg -encoders | grep libsvtav1
如果没有输出,说明你的 ffmpeg 不支持 SVT-AV1 编码器。你可以考虑更换为其他编码器,如 libx264,这是一个广泛支持的编码器。
修改方法:修改 LeRobot 的视频编码逻辑
你需要修改
lerobot/common/datasets/video_utils.py 文件中的 encode_video_frames 函数,将编码器从 libsvtav1 改为 libx264。
def encode_video_frames(
imgs_dir: Path | str,
video_path: Path | str,
fps: int,
vcodec: str = "libx264",
# vcodec: str = "libsvtav1",
pix_fmt: str = "yuv420p",
g: int | None = 2,
crf: int | None = 30,
fast_decode: int = 0,
log_level: str | None = "error",
overwrite: bool = False,
) -> None:
重新运行,ok
python convert_unitree_json_to_lerobot.py --raw-dir ~/Documents/unitrees/unitree_IL_lerobot/datasets/ --repo-id my_test/test01 --robot_type Unitree_G1_Inspire
可视化
cd unitree_lerobot/lerobot
python lerobot/scripts/visualize_dataset.py --repo-id my_test/test01 --episode-index 0
报错
/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/scripts/visualize_dataset.py:150: DeprecationWarning: Use `set_time(sequence=…)` instead.
See: https://www.rerun.io/docs/reference/migration/migration-0-23 for more details.
rr.set_time_sequence("frame_index", batch["frame_index"][i].item())
/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/scripts/visualize_dataset.py:151: DeprecationWarning: Use `set_time(timestamp=seconds)` or `set_time(duration=seconds)` instead.
See: https://www.rerun.io/docs/reference/migration/migration-0-23 for more details.
rr.set_time_seconds("timestamp", batch["timestamp"][i].item())
0%| | 0/40 [00:00<?, ?it/s]
Traceback (most recent call last):
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/scripts/visualize_dataset.py", line 292, in <module>
main()
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/scripts/visualize_dataset.py", line 288, in main
visualize_dataset(dataset, **vars(args))
File "/home/ubuntu/Documents/unitrees/unitree_IL_lerobot/unitree_lerobot/lerobot/lerobot/scripts/visualize_dataset.py", line 161, in visualize_dataset
rr.log(f"action/{dim_idx}", rr.Scalar(val.item()))
AttributeError: module 'rerun' has no attribute 'Scalar'. Did you mean: 'Scalars'?
k可视化报错
原因
由于 Rerun 可视化库的 API 从 v0.22 升级到 v0.23+ 后发生了不兼容的变更,导致 lerobot 的 visualize_dataset.py 脚本中调用的旧 API 已被废弃或移除。
替代方案:使用 rr.Scalars(注意是复数)
+++++++++++++++一下是分析过程++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
解决方案:修改 visualize_dataset.py
你需要修改
/unitree_lerobot/lerobot/scripts/visualize_dataset.py 文件中的以下部分。
修改日志记录部分(修复 Scalar错误)
原始代码(出错):
python
深色版本
for dim_idx, val in enumerate(batch["action"][i]):
rr.log(f"action/{dim_idx}", rr.Scalar(val.item()))
修改为(使用 Scalars):
python
深色版本
for dim_idx, val in enumerate(batch["action"][i]):
rr.log(f"action/{dim_idx}", rr.Scalars([val.item()]))
注意:rr.Scalars 接收一个列表(即使是单个值也要用 [val] 包裹)。
修改时间设置部分(修复弃用警告)
原始代码:
python
深色版本
rr.set_time_sequence("frame_index", batch["frame_index"][i].item())
rr.set_time_seconds("timestamp", batch["timestamp"][i].item())
修改为(Rerun v0.23+ 新语法):
python
深色版本
rr.set_time_sequence("frame_index", batch["frame_index"][i].item())
rr.set_time_seconds("timestamp", batch["timestamp"][i].item())
等等!看起来代码没变?其实你看到的只是警告(warning),不是错误。但为了兼容未来版本,建议按官方提示更新(虽然目前 set_time_sequence 和 set_time_seconds 仍可用)。
目前可以暂时保留,但未来应迁移到:
python
深色版本
# 新推荐方式(可选,当前非必须)
rr.send_columns(
"log_time",
times=[rr.TimeSecondsColumn("timestamp", batch["timestamp"][i].item())],
sequences=[rr.SequenceColumn("frame_index", batch["frame_index"][i].item())]
)
但为了简单起见,建议先只修复 Scalar 问题。
完整改修建议(只改关键部分)
找到 visualize_dataset.py 中的 visualize_dataset 函数,修改如下:
def visualize_dataset(dataset, num_samples=100, step=1):
# ... 前面的代码不变 ...
for i in range(0, min(num_samples, len(dataset)), step):
batch = dataset[i]
# 更新时间(保留原样,警告可暂时忽略)
rr.set_time_sequence("frame_index", batch["frame_index"][i].item())
rr.set_time_seconds("timestamp", batch["timestamp"][i].item())
# 修复 action 可视化
for dim_idx, val in enumerate(batch["action"][i]):
rr.log(f"action/{dim_idx}", rr.Scalars([val.item()])) # 使用 Scalars
# 如果你还可视化 observation,也需类似修改
if "observation" in batch:
for dim_idx, val in enumerate(batch["observation"][i]):
rr.log(f"observation/{dim_idx}", rr.Scalars([val.item()]))
# 可视化图像(如果有)
if "observation.images" in batch:
for cam_name, img in batch["observation.images"][i].items():
rr.log(f"camera/{cam_name}", rr.Image(img.numpy()))
# ... 后面的代码不变 ...
+++++++++++++++以上是分析过程++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
解决
for batch in tqdm.tqdm(dataloader, total=len(dataloader)):
# iterate over the batch
for i in range(len(batch["index"])):
rr.set_time_sequence("frame_index", batch["frame_index"][i].item())
rr.set_time_seconds("timestamp", batch["timestamp"][i].item())
# display each camera image
for key in dataset.meta.camera_keys:
# TODO(rcadene): add `.compress()`? is it lossless?
rr.log(key, rr.Image(to_hwc_uint8_numpy(batch[key][i])))
# display each dimension of action space (e.g. actuators command)
if "action" in batch:
for dim_idx, val in enumerate(batch["action"][i]):
rr.log(f"action/{dim_idx}", rr.Scalars(val.item()))
# display each dimension of observed state space (e.g. agent position in joint space)
if "observation.state" in batch:
for dim_idx, val in enumerate(batch["observation.state"][i]):
rr.log(f"state/{dim_idx}", rr.Scalars(val.item()))
if "next.done" in batch:
rr.log("next.done", rr.Scalars(batch["next.done"][i].item()))
if "next.reward" in batch:
rr.log("next.reward", rr.Scalars(batch["next.reward"][i].item()))
if "next.success" in batch:
rr.log("next.success", rr.Scalars(batch["next.success"][i].item()))
3. 训练
act
cd unitree_lerobot/lerobot
python lerobot/scripts/train.py \
--dataset.repo_id=unitreerobotics/G1_ToastedBread_Dataset \
--policy.type=act