CoolFace
Datasetpublic

Ahaskar04/viperx-3arm-handover-demo

COLA 3-Arm Sequential Handover — Scripted Demonstrations 1,500 simulated demonstrations of a sequential 3-arm handover task in MuJoCo, generated with a hand-tuned waypoint policy. Three ViperX 300s robot arms (A → B → C) cooperate to lift an object off a table, hand it between two arms, and place it into a box rigidly attached to the third arm. This dataset is intended for multi-agent imitation learning, coordination research, and as the 3-arm extension of the 2-arm… See the full description on the dataset page: https://huggingface.co/datasets/Ahaskar04/viperx-3arm-handover-demo.

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes389downloads
Dataset Card

COLA 3-Arm Sequential Handover — Scripted Demonstrations

1,500 simulated demonstrations of a sequential 3-arm handover task in MuJoCo, generated with a hand-tuned waypoint policy. Three ViperX 300s robot arms (A → B → C) cooperate to lift an object off a table, hand it between two arms, and place it into a box rigidly attached to the third arm.

This dataset is intended for multi-agent imitation learning, coordination research, and as the 3-arm extension of the 2-arm `cola-handover-demos`.


Task description

Three ViperX 300s arms arranged along the Y-axis on a shared table:

ArmBase positionRole
A (giver)(0.0, -0.90, 0.37)Picks up the object from its side of the table
B (relay)(0.0, 0.00, 0.37)Receives from A, rotates, hands to C's tray
C (receiver)(0.0, +0.90, 0.37)Holds an open-topped box at its gripper; never grasps

The chain proceeds through 6 environment phases:

PICKUP → HANDOVER_AB → PLACE_BC → COMPLETE → RETRACT_DONE → RETRACT_C_DONE

A success is a complete chain: A grasps and lifts → A hands to B mid-air → B rotates and drops the object into C's box → both arms retract.


Dataset stats

Episodes1,500
Conditional Successes660 (44%)
Drops (object fell)694 (46.3%)
Timeouts (max steps)261 (17.4%)
Total size~192 GB
Formatone .npz + one .mp4 per episode
Episode length643 – 2000 steps (median 1054, mean 1188)
Control rate50 Hz

Failure mode notes. Most failures occur during the B→C placement phase (object misses the box on drop). The A→B handover stage itself succeeds in nearly all episodes that progress past pickup; pickup-phase timeouts are mostly cases where A grasped successfully but the env's strict is_arm_holding thresholds didn't fire to advance the env phase state. See Notes on success labels below.


File schema

Each episode is a single np.savez_compressed file with these arrays (T = timesteps in that episode):

Per-step observations

KeyShapeDtypeDescription
image_a(T, 256, 256, 3)uint8Frontview camera (RGB)
image_b(T, 256, 256, 3)uint8Topview camera (RGB)
image_c(T, 256, 256, 3)uint8Sideview camera (RGB)
state_a(T, 10)float32A's proprio: 6 joints + gripper qpos + 3D world TCP pos
state_b(T, 10)float32B's proprio (same layout)
state_c(T, 10)float32C's proprio (same layout)

The three cameras are distinct global views (front / top / oblique side), not per-agent egocentric. The arm-to-camera mapping is by convention (A=front, B=top, C=side); all three views show all three arms.

Per-step world state

KeyShapeDtypeDescription
object_positions(T, 3)float32Object center XYZ in world frame
box_positions(T, 3)float32Box (tray) center XYZ

Per-episode metadata

KeyShapeDtypeDescription
initial_object_pos(3,)float32Object spawn XYZ at episode reset
seedscalarint64RNG seed used for that episode
successscalarboolTrue iff env reached RETRACT_C_DONE
statusscalarstr"SUCCESS", "DROPPED", or "TIMEOUT"
final_phasescalarstrLast env phase, e.g. "RETRACT_C_DONE", "PLACE_BC"
total_stepsscalarint64Length T of this episode
strategyscalarstr"scripted"

Status is also encoded in the filename suffix: episode_{NNNN}_{success|dropped|timeout}.npz.

Videos

Alongside each .npz is a matching .mp4 showing an HD (720×1280) oblique side-view render of the same episode. Useful for visual debugging and dataset previews.


Loading

python
import numpy as np

ep = np.load("episode_0042_success.npz", allow_pickle=True)

T = int(ep["total_steps"])
images = {
    "front": ep["image_a"],   # (T, 256, 256, 3) uint8
    "top":   ep["image_b"],
    "side":  ep["image_c"],
}
proprio = {
    "a": ep["state_a"],   # (T, 10)
    "b": ep["state_b"],
    "c": ep["state_c"],
}
actions = {
    "a": ep["action_a"],  # (T, 7), in [-1, 1]
    "b": ep["action_b"],
    "c": ep["action_c"],
}
print(f"episode {ep['seed']}: {ep['status']}, {T} steps")

Streaming via the datasets library:

python
from huggingface_hub import snapshot_download

local_dir = snapshot_download(
    repo_id="Ahaskar04/viperx-3arm-handover-demo",
    repo_type="dataset",
    allow_patterns=["episode_*_success.npz"],   # e.g. only successes
)

Notes on success labels

The success flag is conservative. The env requires the full chain to complete before declaring success: A holds AND lifts → B holds → box contains object → both arms retract. Observed dataset characteristics:

  • —A's pickup actually succeeds in nearly all "PICKUP timeout" episodes. The 261 timeouts marked with final_phase=PICKUP are mostly cases where A grasped and lifted the object, but the env's strict is_arm_holding predicate (gripper closed AND TCP within 5 cm of object) didn't latch firmly enough to advance the phase machine. The policy then proceeds through its own internal phases out of sync with the env, eventually opens A's gripper for the handover, and the object falls.
  • —The B→C placement is the dominant real-failure mode (694 drops). Most of these are the object missing the box on drop, or B knocking the box on retraction.
  • —The 545 _success.npz episodes are unambiguous: env confirmed full chain + retraction.

If you only care about "did A actually grasp and lift," you'll want to re-label with a more generous threshold using object_positions[:,2].max() and the gripper qpos in state_a[:,6].


Environment & data generation

Generated using the MuJoCo environment and waypoint policy in `cola-research/3arm-handover/` (private repo). Key components:

  • —three_arm_handover_scene.xml: MuJoCo scene (3 ViperX arms, table, object, box rigidly attached to C's gripper, 4 global cameras + per-arm wrist cameras)
  • —three_arm_handover_env.py: gym-style env with phase state machine, staged dense rewards, observation dict
  • —scripted_policy.py: 14-phase finite-state-machine policy with reactive Cartesian tracking during handover and placement
  • —run_scripted_policy.py: rollout driver

The collection ran on NSCC ASPIRE 2A (1× GPU, ~14 h walltime, 50 Hz control with mj_step substepping at the model timestep).

Object spawn distribution

Objects are spawned in a small box near A:

  • —X: uniform in [-0.08, +0.08] m
  • —Y: uniform in [-0.47, -0.43] m (close to A's reach)
  • —Z: 0.40 m (constant, on table)

Quaternion is fixed identity (no spawn rotation). Both initial_object_pos (spawn) and object_positions[0] (after a 50-step settle) are recorded; they differ by the small drop during settling.


License

Released under MIT. The MuJoCo physics models for ViperX 300s come from the Trossen Robotics MuJoCo Menagerie (BSD-3).

Citation

If you use this dataset, please cite:

@misc{cola_3arm_handover_2026,
  title  = {COLA 3-Arm Sequential Handover Demonstrations},
  author = {Ahaskar Kashyap},
  year   = {2026},
  url    = {https://huggingface.co/datasets/Ahaskar04/viperx-3arm-handover-demo},
}

Related datasets