Huggingface.js documentation

@huggingface/lerobot

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

@huggingface/lerobot

用于解析托管在 Hugging Face Hub 上的 LeRobot 机器人数据集的解析器。

所有内容均通过范围 HTTP 请求读取,因此可在浏览器和 Node 中直接运行,且无需下载整个数据集即可回答关于该数据集的问题。

为什么

LeRobot 数据集有两种布局。在 v2.0 / v2.1 中,每个 episode 都有独立的视频和 parquet 文件,且 episode 索引是一个 JSON Lines 文件。在 v3.0 中,episode 被拼接进共享文件,索引为 parquet 格式,因此一个 episode 是较大文件中的一个字节范围和时间范围。

该包通过统一的 LeRobotEpisode 结构隐藏了这一差异。

安装

npm install @huggingface/lerobot

hyparquet 随附提供,用于读取 v3.0 剧集索引和帧数据。fzstd 在 hyparquet 内置的 SNAPPY 之外增加了 ZSTD 支持。两者均按需导入,因此仅需调用 info() 或列出 v2 剧集的调用方永远不会加载它们。

用法

import { LeRobotDataset } from "@huggingface/lerobot";

const dataset = new LeRobotDataset("lerobot/svla_so101_pickplace");

const info = await dataset.info();
// {
//   codebaseVersion: "v3.0",
//   robotType: "so100_follower",
//   fps: 30,
//   totalEpisodes: 50,
//   totalFrames: 11939,
//   cameras: [{ key: "observation.images.up", width: 640, height: 480, codec: "av1" }, ...],
//   joints: { "observation.state": ["shoulder_pan.pos", "shoulder_lift.pos", ...] },
//   ...
// }

const episodes = await dataset.episodes({ limit: 10 });
// [
//   {
//     index: 0,
//     length: 303,
//     durationSec: 10.1,
//     tasks: ["pink lego brick into the transparent box"],
//     videos: [{ cameraKey: "observation.images.up", url: "https://...", fromSec: 0, toSec: 10.1 }, ...],
//     data: { url: "https://.../data/chunk-000/file-000.parquet", fromRow: 0, toRow: 303 },
//   },
//   ...
// ]

const episode = await dataset.episode(42);
// The episode whose `index` is 42, or undefined when there is none

offset 统计的是索引中的位置,只有当数据集按 0、1、2、… 对剧集编号时,这些位置才与剧集索引一致。有些数据集从 10 开始编号,或跳过了已删除的剧集,因此请使用 episode(index) 来打开指定的剧集。对于按 0、1、2、… 编号的数据集,其开销与 episodes({ offset: index, limit: 1 }) 相同。

videos[].fromSec / toSec 是剧集在 url 中的偏移量。在 v3.0 中,多个剧集共享同一个文件,因此需要定位到 fromSec 并在 toSec 处停止;而在 v2.x 中,每个剧集都有独立文件,该范围恰好覆盖整个文件。

data 指向该 episode 的帧行,供希望自行绘制 observation.state 或 action 的调用方使用。fromRow 为包含边界,toRow 为不包含边界;两者均为 data.url 内部的行偏移量,而非整个数据集的索引。对于 v3,当某一页从数据文件中间开始读取时,列出 episodes 也会读取该文件的 parquet footer,以确定其起始位置。如果该文件无法读取或与 episode 元数据不一致,则 data 字段将不存在。

在分页浏览 episode 时,只需保留一个 LeRobotDataset:与 info() 类似,它会记住已遍历的 v3.0 索引文件,因此后续的翻页或 episode() 查找会直接定位到包含该数据的文件。

固定修订版本

在结果被存储或缓存到任何位置时,始终传入 commit sha,这样后续的 push 操作就无法更改你解析的内容:

const dataset = new LeRobotDataset("lerobot/svla_so101_pickplace", {
	revision: "f641879e22172be7e8161d5e6c1503c2d2feb657",
});

私有和受控数据集

传入您自己的 fetch 以添加凭据:

const dataset = new LeRobotDataset("my-org/private-dataset", {
	additionalFetchHeaders: { Authorization: `Bearer ${accessToken}` },
});

在 huggingface.co 的浏览器中,会话 Cookie 就足够了:

const dataset = new LeRobotDataset(repoId, {
	fetch: (url, init) => fetch(url, { ...init, credentials: "same-origin" }),
});

不获取数据的情况下进行解析

parseInfo 是纯函数,适用于已经拥有字节数据的调用方:

import { parseInfo } from "@huggingface/lerobot";

const info = parseInfo(await readFile("meta/info.json", "utf8"));

对于任何无法使用的信息文件,包括 LFS 指针、截断的读取内容,或本包未实现的 codebase_version,它都会抛出异常。

Update on GitHub