#### NOTE
This is a **Hugging Face dataset**. For large datasets, ensure `huggingface_hub>=1.1.3` to avoid rate limits. Learn more in the <a href="https://docs.voxel51.com/integrations/huggingface.html#loading-datasets-from-the-hub" target="_blank">Hugging Face integration docs</a>.

<a href="https://huggingface.co/datasets/Voxel51/intern-shiguan-lerobot-51ep" target="_blank">![Hugging Face](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Dataset-yellow)</a>

# Dataset Card for Intern Shiguan Tactile (51 episodes)

![Intern Shiguan Tactile preview](https://huggingface.co/datasets/Voxel51/intern-shiguan-lerobot-51ep/resolve/main/intern_shiguan_tactile.gif)

This is a [FiftyOne](https://github.com/voxel51/fiftyone) dataset with 51 samples. Each sample is one teleoperated episode.

## Installation

```bash
pip install -U fiftyone
```

## Usage

### Load from the Hub

```python
import fiftyone as fo
from fiftyone.utils.huggingface import load_from_hub

dataset = load_from_hub("Voxel51/intern-shiguan-lerobot-51ep")

session = fo.launch_app(dataset)
```

### Download a snapshot and load it locally

```python
import fiftyone as fo
from huggingface_hub import snapshot_download

snapshot_download(
    repo_id="Voxel51/intern-shiguan-lerobot-51ep",
    repo_type="dataset",
    local_dir="intern-shiguan-lerobot-51ep",
)

dataset = fo.Dataset.from_dir(
    dataset_dir="intern-shiguan-lerobot-51ep",
    dataset_type=fo.types.LeRobotDataset,
    name="intern_shiguan_tactile",
)

session = fo.launch_app(dataset)
```

## Dataset Details

### Dataset Description

51 GELLO-teleoperated demonstrations (26,665 frames, 30 fps) of one instruction (“Pick up the test tube from the left side of the rack and insert it into the hole at the right end of the rack.”) on the `intern_gello_7dof_robotiq` robot schema. The source is a community LeRobot v3.0 repo with two RGB views, two tactile image streams, and force/torque channels. All source episodes are imported, with no success filtering (per the source README).

- **Shared by:** `cloudfan` on Hugging Face
- **Language(s):** en
- **License:** None declared in the source repo

### Dataset Sources

- **Repository:** https://huggingface.co/datasets/cloudfan/intern-shiguan-lerobot

## Uses

### Direct Use

Per the source README, training with the InternVLA A-series project (two RGB views mapped; tactile/force channels retained as auxiliary data).

## Dataset Structure

**Topology.** `media_type: multimodal`, 51 samples, one per episode, 30 fps, 329-781 frames (11.0-26.0 s), 26,665 frames total. Samples hold a `media_reference` to the per-frame parquet data and 4 video streams.

| Field             | FiftyOne type             | Description                                           |
|-------------------|---------------------------|-------------------------------------------------------|
| `media_reference` | `LeRobotEpisodeReference` | Pointers into `data/` parquet and the 4 video streams |
| `episode_index`   | `IntField`                | Episode index (0-50)                                  |
| `task`            | `StringField`             | The single task instruction                           |
| `tasks`           | `ListField(StringField)`  | All task strings of the episode                       |
| `length`          | `IntField`                | Frames in the episode                                 |
| `duration`        | `FloatField`              | Episode duration in seconds                           |
| `robot_type`      | `StringField`             | `intern_gello_7dof_robotiq`                           |
| `fps`             | `FloatField`              | 30                                                    |

**Data behind `media_reference`** (per-frame, from `meta/info.json`):

| Feature                                                              | Shape        | Description                                                                         |
|----------------------------------------------------------------------|--------------|-------------------------------------------------------------------------------------|
| `observation.state`                                                  | (8,)         | 7 measured joint angles + Robotiq knuckle angle (rad)                               |
| `action`                                                             | (8,)         | 7 absolute GELLO joint targets + commanded gripper open fraction (0 closed, 1 open) |
| `observation.eef_pose`                                               | (6,)         | Original six-value pose, no coordinate conversion                                   |
| `observation.wrench`                                                 | (6,)         | Force/torque                                                                        |
| `observation.wrench_valid`                                           | (6,)         | Per-axis validity flags for `observation.wrench`                                    |
| `observation.gripper_command`                                        | (1,)         | Recorded follower gripper command                                                   |
| `source_timestamp`                                                   | (1,) float64 | Original timestamps                                                                 |
| `observation.images.cam_high`, `cam_front`                           | 480x640x3    | RGB views (H.264 after re-encode; MJPEG in the source)                              |
| `observation.images.tactile_left_aug_diff`, `tactile_right_aug_diff` | 700x400x3    | Tactile touch images (H.264 after re-encode; MJPEG in the source)                   |

**Label types and why.** The importer produces episode-level samples with a `media_reference`; numeric streams (state, action, wrench) are read through the viewer and are not stored as FiftyOne label fields. The source has a single task, so `task` is constant. No custom fields were added, so the FiftyOne dataset matches what the LeRobot export contains.

**`dataset.info`.** `lerobot` entry with `format`, `format_major`, `episode_count`, `imported_episode_count`, `skipped_episodes` (empty).

**Parsing decisions.**

- All episodes imported from the full repo (3.5 GB).
- Metadata repair: `meta/tasks.parquet` stored the task text as the pandas index (`task_index` column only). It was rewritten with real `task_index` and `task` columns before import.
- Video transcode: the source videos are MJPEG in MP4 (remuxed from original JPEG packets, per the source README), which browsers cannot decode, so they did not play in the FiftyOne App. All 204 files (51 episodes x 4 streams) were re-encoded in place with ffmpeg to H.264 (`libx264`, `-crf 18`, `-g 2`, yuv420p; full-range yuvj420p converted to limited range). This is a lossy re-encode of the source pixels. Summed frame counts of `cam_high` and `tactile_left_aug_diff` match `total_frames` in `meta/info.json`, and `video.codec` / `video.pix_fmt` in `meta/info.json` were updated to `h264` / `yuv420p`. The original MJPEG files are not part of this dataset.

## Dataset Creation

### Source Data

#### Data Collection and Processing

The source README describes the original upload, before the H.264 re-encode covered under Parsing decisions. JPEG packets were remuxed into MJPEG MP4 with no resize or re-encode, and every recorded frame is kept. Timestamps are `frame_index / 30`, not wall-clock resampled; the capture FPS is in the source manifest. Numeric fields were compared to source values, and JPEG bytes were SHA256-checked per stream. Validation records are in `meta/validation.json`, `meta/source_manifest.json` and `meta/conversion.json`.

### Annotations

#### Annotation process

A single natural-language instruction states the intended goal. Per the source README, it does not mean every demonstration succeeded.
