Shubham-Rasal Claude Sonnet 4.6 commited on
Commit
07c7d4a
Β·
1 Parent(s): 1dd20f4

add real robot dataset converters and sample CSVs

Browse files

4 datasets: ALOHA (14-DOF), Push-T real (8-DOF), Franka Panda (13-DOF),
Unitree H1 humanoid (40-DOF). Space now has dataset picker with one-click
CSV download in the upload tab.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

__pycache__/app.cpython-313.pyc CHANGED
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
 
app.py CHANGED
@@ -379,7 +379,7 @@ def run_upload(file):
379
  return run_analysis(policy_data)
380
 
381
 
382
- # ── CSV template download ──────────────────────────────────────────────────────
383
  def make_template():
384
  rows = []
385
  for ep in range(3):
@@ -395,6 +395,46 @@ def make_template():
395
  df.to_csv(path, index=False)
396
  return path
397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
 
399
  # ── UI ────────────────────────────────────────────────────────────────────────
400
  CSS = f"""
@@ -501,15 +541,40 @@ def build_ui():
501
 
502
  # ── TAB 2: UPLOAD ─────────────────────────────────────────────────
503
  with gr.Tab("πŸ“‚ Upload Your Own Data"):
 
504
  with gr.Row():
505
  with gr.Column(scale=2):
506
- gr.Markdown(FORMAT_HINT)
 
 
 
 
 
 
 
 
 
 
 
507
  with gr.Column(scale=1):
508
- tmpl_btn = gr.Button("⬇ Download Template CSV")
 
 
 
 
 
 
 
 
 
 
509
  tmpl_file = gr.File(label="Template")
510
  tmpl_btn.click(fn=make_template, outputs=tmpl_file)
511
 
512
- upload = gr.File(label="Upload rollout CSV", file_types=[".csv"])
 
 
 
513
  run_upload_btn = gr.Button("β–Ά Analyse Uploaded Data", variant="primary", size="lg")
514
 
515
  with gr.Row():
 
379
  return run_analysis(policy_data)
380
 
381
 
382
+ # ── CSV template + sample downloads ───────────────────────────────────────────
383
  def make_template():
384
  rows = []
385
  for ep in range(3):
 
395
  df.to_csv(path, index=False)
396
  return path
397
 
398
+ SAMPLE_DATASETS = {
399
+ "ALOHA bimanual β€” cup opening (14-DOF)":
400
+ ("lerobot/aloha_static_cups_open", "observation.state", "action", 20),
401
+ "Push-T real robot β€” tabletop push (8-DOF)":
402
+ ("lerobot/columbia_cairlab_pusht_real", "observation.state", "action", 20),
403
+ "Franka Panda β€” free-play manipulation (13-DOF)":
404
+ ("lerobot/nyu_franka_play_dataset", "observation.state", "action", 20),
405
+ "Unitree H1 humanoid β€” warehouse (19-DOF / 40-DOF action)":
406
+ ("lerobot/unitreeh1_warehouse", "observation.state", "action", 12),
407
+ }
408
+
409
+ def download_sample(choice, progress=gr.Progress()):
410
+ if not choice:
411
+ return None
412
+ progress(0.1, desc=f"Loading {choice}…")
413
+ hf, sc, ac, max_eps = SAMPLE_DATASETS[choice]
414
+ ds = load_dataset(hf, split="train")
415
+ df_raw = ds.to_pandas()
416
+ ep_ids = sorted(df_raw["episode_index"].unique())[:max_eps]
417
+
418
+ rows = []
419
+ policy_name = choice.split("β€”")[0].strip()
420
+ progress(0.4, desc="Extracting episodes…")
421
+ for ei in ep_ids:
422
+ grp = df_raw[df_raw["episode_index"] == ei].reset_index(drop=True)
423
+ success = int(grp["next.reward"].max() > 0) if "next.reward" in grp.columns else 1
424
+ states = np.vstack(grp[sc].values)
425
+ actions = np.vstack(grp[ac].values)
426
+ for fi, (s, a) in enumerate(zip(states, actions)):
427
+ row = {"episode_id": int(ei), "policy_name": policy_name,
428
+ "frame_id": fi, "success": success}
429
+ for i, v in enumerate(s): row[f"state_{i}"] = round(float(v), 6)
430
+ for i, v in enumerate(a): row[f"action_{i}"] = round(float(v), 6)
431
+ rows.append(row)
432
+
433
+ path = f"/tmp/sample_{hf.split('/')[-1]}.csv"
434
+ pd.DataFrame(rows).to_csv(path, index=False)
435
+ progress(1.0, desc="Ready!")
436
+ return path
437
+
438
 
439
  # ── UI ────────────────────────────────────────────────────────────────────────
440
  CSS = f"""
 
541
 
542
  # ── TAB 2: UPLOAD ─────────────────────────────────────────────────
543
  with gr.Tab("πŸ“‚ Upload Your Own Data"):
544
+ gr.Markdown("### Try a real dataset β€” or upload your own rollouts")
545
  with gr.Row():
546
  with gr.Column(scale=2):
547
+ gr.Markdown("""
548
+ **Step 1 β€” pick a real robot dataset to download as a ready-to-use CSV:**
549
+
550
+ | Dataset | Robot | DOF | Task |
551
+ |---|---|---|---|
552
+ | ALOHA bimanual | Stanford ALOHA (2Γ— ViperX) | 14 | Cup opening |
553
+ | Push-T real | Columbia delta robot | 8 | Push block to goal |
554
+ | Franka Panda | NYU Franka Emika Panda | 13 | Free-play manipulation |
555
+ | Unitree H1 | Full-size humanoid | 19 state / 40 action | Warehouse pick-place |
556
+
557
+ Then upload the downloaded CSV below and hit **Analyse**.
558
+ """)
559
  with gr.Column(scale=1):
560
+ sample_picker = gr.Dropdown(
561
+ choices=list(SAMPLE_DATASETS.keys()),
562
+ label="Real robot dataset",
563
+ value=None,
564
+ )
565
+ dl_btn = gr.Button("⬇ Download as CSV", variant="secondary")
566
+ dl_file = gr.File(label="Downloaded CSV (upload below ↓)")
567
+ dl_btn.click(fn=download_sample, inputs=[sample_picker], outputs=[dl_file])
568
+
569
+ gr.Markdown("---")
570
+ tmpl_btn = gr.Button("⬇ Blank template CSV", variant="secondary")
571
  tmpl_file = gr.File(label="Template")
572
  tmpl_btn.click(fn=make_template, outputs=tmpl_file)
573
 
574
+ with gr.Accordion("CSV format reference", open=False):
575
+ gr.Markdown(FORMAT_HINT)
576
+
577
+ upload = gr.File(label="⬆ Upload rollout CSV (your own or downloaded above)", file_types=[".csv"])
578
  run_upload_btn = gr.Button("β–Ά Analyse Uploaded Data", variant="primary", size="lg")
579
 
580
  with gr.Row():
convert_to_eval_csv.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Converters: real robot datasets β†’ eval harness CSV format.
3
+
4
+ Usage:
5
+ python convert_to_eval_csv.py --dataset pusht --out pusht_eval.csv
6
+ python convert_to_eval_csv.py --dataset franka --out franka_eval.csv
7
+ python convert_to_eval_csv.py --dataset humanoid --out humanoid_eval.csv
8
+ python convert_to_eval_csv.py --dataset aloha --out aloha_eval.csv
9
+
10
+ Output CSV columns:
11
+ episode_id, policy_name, frame_id, timestamp,
12
+ state_0 ... state_N, action_0 ... action_N, success
13
+ """
14
+
15
+ import argparse
16
+ import numpy as np
17
+ import pandas as pd
18
+ from datasets import load_dataset
19
+
20
+ # ── helpers ───────────────────────────────────────────────────────────────────
21
+
22
+ def episode_success(group, reward_col="next.reward", done_col="next.done"):
23
+ """Infer episode success from reward signal or done flag."""
24
+ if reward_col in group.columns:
25
+ return int(group[reward_col].max() > 0)
26
+ # fallback: episode completed normally = success
27
+ return int(group[done_col].iloc[-1]) if done_col in group.columns else 1
28
+
29
+
30
+ def to_eval_csv(hf_dataset_name, policy_name, state_col, action_col,
31
+ max_episodes=None, out_path=None):
32
+ print(f"Loading {hf_dataset_name} …")
33
+ ds = load_dataset(hf_dataset_name, split="train")
34
+ df = ds.to_pandas()
35
+
36
+ ep_ids = sorted(df["episode_index"].unique())
37
+ if max_episodes:
38
+ ep_ids = ep_ids[:max_episodes]
39
+
40
+ rows = []
41
+ for ei in ep_ids:
42
+ grp = df[df["episode_index"] == ei].reset_index(drop=True)
43
+ success = episode_success(grp)
44
+
45
+ states = np.vstack(grp[state_col].values)
46
+ actions = np.vstack(grp[action_col].values) if action_col in grp.columns else states
47
+
48
+ for fi, (s, a) in enumerate(zip(states, actions)):
49
+ row = {
50
+ "episode_id": int(ei),
51
+ "policy_name": policy_name,
52
+ "frame_id": fi,
53
+ "timestamp": round(grp["timestamp"].iloc[fi], 4) if "timestamp" in grp.columns else fi,
54
+ "success": success,
55
+ }
56
+ for i, v in enumerate(s):
57
+ row[f"state_{i}"] = round(float(v), 6)
58
+ for i, v in enumerate(a):
59
+ row[f"action_{i}"] = round(float(v), 6)
60
+ rows.append(row)
61
+
62
+ out = pd.DataFrame(rows)
63
+ if out_path:
64
+ out.to_csv(out_path, index=False)
65
+ print(f"Saved {len(ep_ids)} episodes ({len(out):,} frames) β†’ {out_path}")
66
+ return out
67
+
68
+
69
+ # ── dataset-specific converters ───────────────────────────────────────────────
70
+
71
+ DATASETS = {
72
+ # Real tabletop push-T (Columbia / CAIRLAB)
73
+ # Robot: custom delta robot, 2-DOF end-effector + contact sensors
74
+ # Task: push a T-shaped block to a goal region
75
+ # State: 8-dim (EE pos/vel + block pose estimate)
76
+ "pusht": dict(
77
+ hf="lerobot/columbia_cairlab_pusht_real",
78
+ label="Push-T (Columbia real robot)",
79
+ state_col="observation.state",
80
+ action_col="action",
81
+ max_eps=40,
82
+ note="2-DOF delta robot, tabletop push task, 136 episodes total"
83
+ ),
84
+
85
+ # Franka Panda free-play dataset (NYU)
86
+ # Robot: 7-DOF Franka Emika Panda β€” the most common research arm
87
+ # Task: unstructured manipulation play (no fixed goal)
88
+ # State: 13-dim (7 joint pos + 6 EE pose)
89
+ "franka": dict(
90
+ hf="lerobot/nyu_franka_play_dataset",
91
+ label="Franka Panda Play (NYU)",
92
+ state_col="observation.state",
93
+ action_col="action",
94
+ max_eps=50,
95
+ note="7-DOF Franka Panda, 456 episodes of free-play manipulation"
96
+ ),
97
+
98
+ # Unitree H1 humanoid β€” warehouse task
99
+ # Robot: full-size humanoid, 19-DOF state, 40-DOF action
100
+ # Task: pick and place in warehouse setting
101
+ # No reward signal β€” we treat episode completion as success
102
+ "humanoid": dict(
103
+ hf="lerobot/unitreeh1_warehouse",
104
+ label="Unitree H1 Humanoid (warehouse)",
105
+ state_col="observation.state",
106
+ action_col="action",
107
+ max_eps=24,
108
+ note="19-DOF humanoid state, 40-DOF action, 24 episodes"
109
+ ),
110
+
111
+ # ALOHA bimanual static (cups open) β€” same as demo tab
112
+ "aloha": dict(
113
+ hf="lerobot/aloha_static_cups_open",
114
+ label="ALOHA Bimanual (cups open)",
115
+ state_col="observation.state",
116
+ action_col="action",
117
+ max_eps=50,
118
+ note="14-DOF bimanual ALOHA, 50 episodes, cup-opening task"
119
+ ),
120
+ }
121
+
122
+
123
+ # ── multi-policy comparison helper ───────────────────────────────────────────
124
+
125
+ def make_comparison_csv(datasets_and_names: list[tuple[str, str]],
126
+ max_eps_each: int = 20,
127
+ out_path: str = "comparison_eval.csv"):
128
+ """
129
+ Combine multiple datasets as different 'policies' for A/B comparison.
130
+
131
+ datasets_and_names: list of (dataset_key, policy_label)
132
+ Example:
133
+ make_comparison_csv([("pusht","Push-T"), ("franka","Franka"), ("aloha","ALOHA")])
134
+ """
135
+ dfs = []
136
+ for key, label in datasets_and_names:
137
+ cfg = DATASETS[key]
138
+ df = to_eval_csv(cfg["hf"], label, cfg["state_col"], cfg["action_col"],
139
+ max_episodes=max_eps_each)
140
+ # Truncate to common state dim
141
+ dfs.append(df)
142
+
143
+ # Align state/action columns across datasets (fill missing with 0)
144
+ out = pd.concat(dfs, ignore_index=True).fillna(0.0)
145
+ out.to_csv(out_path, index=False)
146
+ print(f"\nSaved multi-policy comparison CSV β†’ {out_path}")
147
+ print(f"Policies: {out['policy_name'].unique().tolist()}")
148
+ print(f"Total episodes: {out['episode_id'].nunique()}")
149
+ print(f"Total frames: {len(out):,}")
150
+ return out
151
+
152
+
153
+ # ── CLI ───────────────────────────────────────────────────────────────────────
154
+
155
+ if __name__ == "__main__":
156
+ parser = argparse.ArgumentParser()
157
+ parser.add_argument("--dataset", choices=list(DATASETS.keys()) + ["compare"],
158
+ default="pusht", help="Dataset to convert")
159
+ parser.add_argument("--out", default=None, help="Output CSV path")
160
+ parser.add_argument("--max-eps", type=int, default=None,
161
+ help="Max episodes to convert (default: all)")
162
+ args = parser.parse_args()
163
+
164
+ if args.dataset == "compare":
165
+ out = args.out or "comparison_eval.csv"
166
+ make_comparison_csv(
167
+ [("pusht","Push-T"), ("franka","Franka"), ("humanoid","H1-Humanoid")],
168
+ max_eps_each=args.max_eps or 15,
169
+ out_path=out,
170
+ )
171
+ else:
172
+ cfg = DATASETS[args.dataset]
173
+ out = args.out or f"{args.dataset}_eval.csv"
174
+ print(f"\n{cfg['label']}")
175
+ print(f"Note: {cfg['note']}\n")
176
+ to_eval_csv(cfg["hf"], cfg["label"], cfg["state_col"], cfg["action_col"],
177
+ max_episodes=args.max_eps or cfg["max_eps"], out_path=out)
178
+
179
+ print("\nDone. Upload the CSV to the HuggingFace Space:")
180
+ print(" https://huggingface.co/spaces/ShubhamRasal/robot-policy-eval")
sample_comparison.csv ADDED
The diff for this file is too large to render. See raw diff
 
sample_pusht.csv ADDED
The diff for this file is too large to render. See raw diff