FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

docs: refocus repo on the Isaac gripper-drone (manipulation + navigat… · CodCodingCode/SkyVLA@f54c07c · GitHub

Commit f54c07c

Browse files
andcommitted
docs: refocus repo on the Isaac gripper-drone (manipulation + navigation)
The Habitat/OpenFly VLN stack was retired from this branch (commit eef431f); the only tracked code is skyvla_isaac/. Rewrite the front-page docs to match: - README.md: gripper quadrotor doing real-physics pick-and-place + Gaussian-map navigation, with the converged success rates and accurate run commands. Removes the OpenFly/PaliGemma writeup and dead docs/ + video links. - CLAUDE.md: keep the conventions that still apply (W&B-on, tmux long runs, Xid 43 + restart, disk hygiene), retargeted to scripts/train.py. - requirements.txt: drop the PaliGemma VLN deps; Isaac Sim/Lab/rsl_rl come from the Isaac installer, leaving only gsplat + imageio as pip extras. - ci.yml: compileall skyvla_isaac instead of the deleted openfly/vla. - skyvla_isaac/README.md: Status -> Converged (grasp 79 / lift 82 / place 77). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7d2a3de commit f54c07c

5 files changed

Lines changed: 167 additions & 346 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,13 @@ jobs:
1919
with:
2020
python-version: "3.10"
2121

22-
# Compile every Python file we ship. PaliGemma and AirSim imports are
23-
# avoided because the runner has no GPU and no upstream OpenFly-Platform
24-
# checkout; the syntax check still catches `SyntaxError` and
25-
# `IndentationError` across the whole tree.
22+
# Compile every Python file we ship. Isaac Sim / Isaac Lab are not
23+
# installed on the runner (no GPU, no Omniverse), so we don't import the
24+
# modules — compileall still catches `SyntaxError` / `IndentationError`
25+
# across the whole tree without executing any isaaclab imports.
2626
- name: Compile Python sources
2727
run: |
28-
python -m compileall -q \
29-
openfly \
30-
vla/vla_policy.py
31-
32-
# Smoke-import the lightweight RL modules. AirSim, transformers, and
33-
# PaliGemma are not installed on the runner, so we only touch modules
34-
# that do not import the heavy stack at module load.
35-
- name: Import-check RL core
36-
run: |
37-
pip install --quiet numpy 'gymnasium>=1.0'
38-
PYTHONPATH=. python - <<'PY'
39-
import openfly.actions
40-
import openfly.episodes
41-
import openfly.rewards
42-
import openfly.rollout
43-
# envs module triggers gymnasium.register on import
44-
from openfly.envs import AirSimVLNEnv, AirSimVLNEnvConfig # noqa: F401
45-
print("imports ok")
46-
PY
28+
python -m compileall -q skyvla_isaac
4729
4830
markdown-links:
4931
name: Markdown link check

‎CLAUDE.md‎

Lines changed: 71 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,102 @@
11
# SkyVLA — agent conventions
22

3-
Rules for me (the agent) when working in this repo. Short and rule-focused.
3+
Rules for me (the agent) when working in this repo. Short and rule-focused. The
4+
repo is the Isaac Sim / Isaac Lab gripper-drone work in `skyvla_isaac/` — a
5+
free-floating quadrotor with a 4-jaw gripper doing pick-and-place (`tasks/pick_place_env.py`)
6+
and Gaussian-map navigation (`gs/`). Training is PPO via rsl_rl (`scripts/train.py`).
47

5-
## W&B is on by default — don't disable it
6-
7-
The DiT trainer (`openfly/train_subgoal_dit.py`) auto-initializes a W&B run on every launch. The API key lives at `/home/ubuntu/SkyVLA/.wandb_key` (mode 600, gitignored); the shell wrapper exports it as `WANDB_API_KEY` before invoking Python, and the trainer falls back to reading the same file if the env var is missing.
8-
9-
Defaults:
10-
- project: `skyvla-subgoal-dit`
11-
- run name / id: `basename(out_dir)` — same run dir → same W&B run (auto-resumes via `resume="allow"`), so crash-loop relaunches stitch into one continuous series.
12-
- mode: `online`
8+
## Environment
139

14-
**Log only measurable training progress to W&B, nothing else.** No per-step jitter, no operational counters, no constants. The dashboard exists to answer one question: "is training getting better?" Every metric you add must move the needle on that question. Specifically:
10+
All Isaac scripts run in the isolated `isaac` conda env (Python 3.10) and need:
1511

16-
DO log:
17-
- **`val/cos_seen`, `val/cos_ood`** — THE metric. The whole reason we train. Per-epoch.
18-
- **`val/cos_gap_seen_ood`** — generalization gap (seen − ood). Near zero = generalizing. Going up = overfit.
19-
- **`val/best_cos`** — running max of `val/cos_seen`. Easy visual ceiling.
20-
- **`val/loss_seen`, `val/loss_ood`** — secondary, but worth tracking the trend.
21-
- **`epoch/train_cos_mean`** — denoised in-training direction quality (a leading indicator of what val_cos will be).
22-
- **`epoch/train_loss`** — the headline objective.
23-
- **`epoch/nan_skip_ratio`** — health check. > 5% means the run is fighting numerical instability and val numbers are fragile; investigate before drawing conclusions.
24-
- **`train/*_ema`** (per step, EMA-smoothed) — `train/loss_ema`, `train/train_cos_ema`, `train/cos_loss_ema`, `train/repa_loss_ema`. EMA half-life ~50 steps. These are how you watch training "live" — raw per-batch numbers swing too wildly (`train_cos` jitters ±0.5 batch-to-batch).
25-
26-
DO NOT log:
27-
- Raw per-step metrics (use EMA instead).
28-
- `lr` (constant after warmup; not progress).
29-
- `epoch_idx`, `step_in_epoch` (already encoded in gstep on the x-axis).
30-
- `n_valid` per batch (debug noise).
31-
- Operational counters that don't move with training quality (`nan_skip_count` cumulative is debug, `nan_skip_ratio` per epoch is signal).
32-
- Anything with no monotonic / interpretable shape.
12+
```bash
13+
conda activate isaac
14+
export OMNI_KIT_ACCEPT_EULA=YES
15+
export PYTHONPATH=/home/ubuntu/SkyVLA
16+
```
3317

34-
If you're tempted to add a new metric, ask: "would this on a chart change what I'd do next?" If the answer is no, it goes to stdout, not W&B.
18+
## W&B is on by default — don't disable it
3519

36-
To turn it off for a one-shot smoke run: `--wandb_mode disabled` (or `--wandb_project ""`). Don't disable on long runs — we want the dashboard.
20+
`scripts/train.py` sets `logger="wandb"`, project `skyvla-isaac`. The API key
21+
lives at `/home/ubuntu/SkyVLA/.wandb_key` (mode 600, gitignored). **The
22+
`.wandb_key` file must never be committed**`.gitignore` already covers
23+
`.wandb_key` and `.env.local`. If a new credential file is needed, add it to
24+
`.gitignore` first.
3725

38-
The .wandb_key file must never be committed. .gitignore already covers `.wandb_key` and `.env.local`. If a new credential file is needed, add it to .gitignore first.
26+
**Log only measurable training progress to W&B, nothing else** — the dashboard
27+
exists to answer one question: "is training getting better?" For this task the
28+
metrics that matter are the real success rates (`grasp_rate`, `lift_rate`,
29+
`place_success`, `obj_to_goal`) plus mean episode reward. No per-step jitter, no
30+
constants (e.g. `lr` after warmup), no operational counters. If a metric wouldn't
31+
change what I'd do next on a chart, it goes to stdout, not W&B. Don't disable
32+
W&B on long runs — we want the dashboard.
3933

4034
## After launching ANY training or eval run — ALWAYS give tail commands
4135

42-
**Hard rule, no exceptions.** Whenever you launch a training run, eval, ablation, or any background process that writes to a log file, immediately follow the launch confirmation with the user-runnable tail commands. The user has explicitly asked for this — they want to be able to watch progress without having to ask me where the log is.
43-
44-
Default set to surface (adapt grep filters to whatever metrics the run produces):
36+
**Hard rule, no exceptions.** Whenever I launch a training run, eval, or any
37+
background process that writes to a log file, I immediately follow the launch
38+
confirmation with the user-runnable tail commands, in a small code block right
39+
after the "tmux session / log" lines (not buried later). Adapt the grep filters
40+
to whatever the run prints.
4541

4642
```bash
47-
# Live training-step lines
48-
tail -f <LOG> | grep -E "epoch [0-9]+ step"
49-
50-
# Clean tail (warnings filtered)
51-
tail -f <LOG> | grep -vE "UserWarning|tensor_numpy|ascontiguous|FutureWarning"
52-
53-
# Epoch summaries + saves + restarts only (the moments that matter)
54-
tail -f <LOG> | grep -E "epoch [0-9]+ →|saved best|periodic save|==== launch|EXIT reason"
55-
56-
# Attach to live tmux (interactive view; Ctrl-B D to detach)
57-
tmux attach -t <SESS>
43+
tail -f <LOG> # raw progress
44+
tail -f <LOG> | grep -E "Iteration|reward|success" # iteration summaries + rates
45+
tmux attach -t <SESS> # live view (Ctrl-B then D to detach)
46+
tmux ls # find running sessions
5847
```
5948

60-
For eval scripts that don't emit per-step lines, default to a simpler set — just the clean tail + a one-shot "show results so far" pattern.
61-
62-
Phrasing: surface them in a small code block right after the "tmux session: X / log: Y" lines. Don't bury them later in the message; the user reads top-down.
63-
6449
## Long-running training runs
6550

66-
**Always launch long training runs (>15 min) inside a tmux session.** Never use bare `nohup ... &` for SFT / DiT / RL training — tmux is interactively attachable, has cleaner process management, and the user explicitly asked for it so that closing their laptop never matters.
67-
68-
**For any multi-hour DiT run, always use `--run_dir <pinned-path> --auto_resume --ckpt_every_steps 500`** so a Xid 43 / segfault / OOM doesn't lose the entire run. See the Xid 43 section below for why.
69-
70-
Pattern (resilient, with crash-loop wrapper):
51+
**Always launch long training runs (>15 min) inside a tmux session.** Never use
52+
bare `nohup ... &` for PPO training — tmux is interactively attachable, has
53+
cleaner process management, and the user explicitly asked for it so closing
54+
their laptop never matters. Stop a run with `tmux kill-session -t <SESS>`
55+
(preferred over `pkill -9`).
7156

7257
```bash
73-
RUN_DIR=/home/ubuntu/SkyVLA/logs/openfly/subgoal_dit/<descriptive_name>
74-
SESS=<phase>_$(date +%Y%m%d_%H%M%S)
58+
SESS=isaac_pickplace_$(date +%Y%m%d_%H%M%S)
7559
LOG=/tmp/${SESS}.log
76-
77-
# write a relaunch script that retries up to MAX_RESTARTS times on non-clean exit
78-
RELAUNCH=/tmp/${SESS}_loop.sh
79-
cat > "$RELAUNCH" <<EOF
80-
#!/bin/bash
81-
cd /home/ubuntu/SkyVLA
82-
MAX_RESTARTS=8
83-
RESTART=0
84-
while true; do
85-
RESTART=\$((RESTART+1))
86-
echo "==== launch #\$RESTART at \$(date) ====" | tee -a $LOG
87-
./openfly/run_train_<phase>.sh \\
88-
<your args> \\
89-
--ckpt_every_steps 500 \\
90-
--run_dir $RUN_DIR \\
91-
--auto_resume 2>&1 | tee -a $LOG
92-
if tail -200 $LOG | grep -q "EXIT reason=clean"; then
93-
LAST_EPOCH=\$(grep -oE "epoch [0-9]+ →" $LOG | tail -1 | grep -oE "[0-9]+")
94-
[ -n "\$LAST_EPOCH" ] && [ "\$LAST_EPOCH" -ge \$((EPOCHS-1)) ] && break
95-
fi
96-
[ \$RESTART -ge \$MAX_RESTARTS ] && break
97-
sleep 15
98-
done
99-
EOF
100-
chmod +x "$RELAUNCH"
101-
tmux new-session -d -s "$SESS" "$RELAUNCH"
60+
tmux new-session -d -s "$SESS" \
61+
"conda activate isaac && OMNI_KIT_ACCEPT_EULA=YES PYTHONPATH=/home/ubuntu/SkyVLA \
62+
python skyvla_isaac/scripts/train.py --num_envs 2048 --max_iterations 1500 2>&1 | tee $LOG"
10263
```
10364

104-
For a one-off short smoke (<15 min) where a Xid 43 just means rerunning is fine, the simpler one-line tmux is OK:
105-
106-
```bash
107-
tmux new-session -d -s "$SESS" "./openfly/run_train_<phase>.sh <args> 2>&1 | tee $LOG"
108-
```
109-
110-
After launching, tell the user:
111-
- `tmux attach -t <SESS>` to view live (Ctrl-B then D to detach without killing)
112-
- `tail -f <LOG>` for file-based tail
113-
- `tmux ls` to find running sessions
114-
115-
Stopping a run: `tmux kill-session -t <SESS>` (preferred over `pkill -9`).
65+
`scripts/train.py` checkpoints every `save_interval` iterations to
66+
`logs/isaac/drone_pick_place/`, so a crash loses at most that window — resume by
67+
relaunching from the latest `model_*.pt`.
11668

11769
## Xid 43 on this machine
11870

119-
The A100 on this host throws NVIDIA Xid 43 ("GPU stopped processing" / channel reset) errors at a rate of roughly ~50% per hour of sustained training. They surface in Python as `Fatal Python error: Segmentation fault` with no preceding stack and no caught exception. They happen across PyTorch versions, model architectures, and boot cycles — confirmed by multiple entries in `sudo dmesg` / `/var/log/kern.log` dating back days. No ECC errors, no hardware fault. Suspected cause: PyTorch 2.12+cu130 binary on a system with CUDA 12.8 nvcc, causing latent issues in triton JIT or cuDNN algorithm selection.
120-
121-
This means **any multi-hour DiT run will probably crash mid-way.** Don't fight this — design for it:
122-
123-
1. Pin the run directory with `--run_dir`.
124-
2. Save state with `--ckpt_every_steps 500` (mid-epoch save bounds crash-loss to ~3 min on this hardware).
125-
3. Auto-resume from `last.pt` with `--auto_resume`.
126-
4. Wrap launch in a restart loop (see the tmux pattern above).
127-
128-
The trainer's `last.pt` carries `epoch`, `global_step`, `step_in_epoch`, optimizer, EMA — full state. Mid-epoch resume REPLAYS the current epoch from step 0 (preserves optimizer/EMA, redoes already-seen batches; the redo cost is ~10 min and is much simpler than rewinding the DataLoader). Clean epoch boundaries advance to the next epoch.
129-
130-
**Diagnostic output routing:** the trainer's faulthandler heartbeat (`dump_traceback_later(120, repeat=True)`) is routed to `<run_dir>/diagnostics.log`, NOT the main log. If the main log shows a thread-dump-looking block, it's the SIGTERM/SIGINT handler (one-shot at signal time) or an actual fatal — not the periodic heartbeat. Past confusion: a tail of the main log used to show repeating `Timeout (0:02:00)!` blocks every 2 min and looked like a crash; that was just the heartbeat.
131-
132-
## Don't rerun cu128 reinstall without authorization
133-
134-
We diagnosed the Xid 43 issue and considered reinstalling PyTorch with cu128 wheels to match the system CUDA toolkit. The user explicitly chose **not** to do that (preferred crash-loop + auto-resume instead). Don't propose the reinstall again unless the auto-resume strategy starts failing in a new way. The reinstall has a real blast radius (PaliGemma policy, transformers version pinning, accelerate, diffusers).
135-
136-
## Diagnostic logging in long runs
137-
138-
The trainer (`openfly/train_subgoal_dit.py`) already installs:
139-
- `faulthandler.dump_traceback_later(120, repeat=True)` — periodic stack dumps so a hang isn't silent
140-
- `atexit` handler printing `EXIT reason=clean|signal(N)|exception(...)`
141-
- SIGTERM/SIGINT trap that dumps thread stacks before exiting
142-
143-
The shell wrapper (`openfly/run_train_subgoal_dit.sh`) sets `PYTHONUNBUFFERED=1` + `python -u` so prints flush in real time. **Don't pipe long runs through `tail -<N>` without `tee` first**`tail` only emits its output buffer when its input closes, so a SIGTERM kills the pipe before any captured progress reaches the log.
144-
145-
## Val splits — never random_split
146-
147-
For any train/val split: load `train.json` for training, `seen.json` and/or `unseen.json` for val. **Never** `random_split(full_ds, ...)` on a single split — it leaks adjacent frames of the same trajectory across train/val and inflates metrics to look-too-good. See `docs/TRAIN.md` "Gotchas" for the full history.
148-
149-
## val_ddim_steps
150-
151-
Default is **4** in the trainer — matches the policy's inference (`PaliGemmaVLNPolicy.subgoal_sample_steps=4`). Past `val_cos≈0.61` numbers were measured at 20 steps and are deploy-inflated. Use 20 only as a one-off "denoising ceiling" diagnostic.
71+
The A100 on this host throws NVIDIA Xid 43 ("GPU stopped processing" / channel
72+
reset) errors at roughly ~50% per hour of sustained training. They surface in
73+
Python as `Fatal Python error: Segmentation fault` with no stack and no caught
74+
exception, across PyTorch versions and architectures — no ECC errors, no
75+
hardware fault. Suspected cause: a PyTorch cu130 binary on a system with CUDA
76+
12.8 nvcc. **Design for it:** checkpoint often, and wrap long launches in a
77+
restart loop so a mid-run segfault just resumes from the latest checkpoint
78+
rather than losing the run.
79+
80+
**Don't rerun the cu128 reinstall without authorization.** We diagnosed Xid 43
81+
and considered reinstalling PyTorch with cu128 wheels to match the system CUDA
82+
toolkit; the user explicitly chose **not** to (preferred checkpoint + restart).
83+
Don't propose the reinstall again unless the restart strategy starts failing in
84+
a new way — the reinstall has a real blast radius (Isaac Lab / rsl_rl pinning).
15285

15386
## Disk hygiene
15487

155-
Each DiT run writes `best.pt` (~2.5 GB) + `last.pt` (~10 GB). `/dev/vda1` is shared with `/tmp` and 95%+ full on this machine. **Before launching a multi-epoch run**, check `df -h /tmp` has ≥30 GB free. If not, delete old `last.pt` files first:
156-
157-
```bash
158-
find /home/ubuntu/SkyVLA/logs/openfly/subgoal_dit -name "last.pt" -size +5G
159-
# review then:
160-
find /home/ubuntu/SkyVLA/logs/openfly/subgoal_dit -name "last.pt" -size +5G -delete
161-
```
162-
163-
A full root disk also breaks the Claude Code harness (task-output dir can't be written), so this matters more than usual.
164-
165-
## Image data caveat — and why per-env balancing requires the new flag
166-
167-
**Only ~14% of `train.json` steps have local frames** in `~/assets/OpenFly/images/Image`. The dataset's `require_images=True` filter silently drops the rest. **Coverage is wildly uneven across envs** — env_ue_bigcity has 91% coverage (181k steps), env_gs_ecust has 0%, and most others sit at 1-10%. As of 2026-05-29 the breakdown is:
168-
169-
| env | usable steps | coverage |
170-
|---|---|---|
171-
| env_ue_bigcity | 181,792 | 91.4% |
172-
| env_airsim_16 | 17,468 | 9.1% |
173-
| env_airsim_26 | 6,913 | 4.0% |
174-
| env_airsim_23 | 6,779 | 12.7% |
175-
| env_gs_sjtu01 | 6,771 | 6.9% |
176-
| env_airsim_sh | 6,410 | 2.0% |
177-
| env_airsim_18 | 1,613 | 1.0% |
178-
| env_airsim_gz | 1,612 | 0.9% |
179-
| env_gs_nwpu02 | 509 | 0.4% |
180-
| env_gs_nwpu01 | 57 | 0.1% |
181-
| env_gs_ecust | 0 | 0.0% |
182-
183-
The 286k "missing" frames aren't a naming bug — the trajectory directories exist but are empty (download was incomplete). Trying to fix this by changing the path resolution in [openfly/dataset.py](openfly/dataset.py) won't help; the files genuinely aren't on disk.
184-
185-
**Why `--per_env_max_episodes` doesn't actually balance:** it caps EPISODES BEFORE the image-existence filter. Because bigcity has both more episodes AND higher per-episode coverage, any episode cap that's high enough to produce a usable training set is dominated by bigcity. Concretely `--per_env_max_episodes 2000` produces ~95% bigcity samples in the final dataset.
186-
187-
**Use `--per_env_max_index_samples N` for actual balance** — caps usable step-pairs per env AFTER image filtering. With N=10000 the dataset lands at ~50k step-pairs with bigcity at ~20%. Deterministic sampling (seed=0) so configs are reproducible. Always use this when "balanced multi-env training" is what you actually want — `--per_env_max_episodes` alone produces a heavily-skewed bigcity-mostly run.
188-
189-
## Training script gotchas to know about
190-
191-
* **Don't trust `history.json` mid-multi-launch run alone — the log has the truth.** Fixed 2026-05-29: resumes now load prior history.json so summaries from earlier launches survive. Before the fix, a Xid-43 mid-run dropped earlier epoch summaries from the file (they stayed in the stdout log though).
192-
* The wrapper script `/openfly/run_train_subgoal_dit.sh` sets `PYTHONUNBUFFERED=1` and uses `python -u` — don't pipe long runs through `tail -N` without `tee` first, or progress prints get lost on signal.
193-
* `args.json` is persisted to `out_dir` at startup so a mid-training crash still leaves the run config behind.
88+
`/dev/vda1` is shared with `/tmp` and runs 90%+ full on this machine. PPO
89+
checkpoints and rendered mp4s add up. **Before a long run**, check `df -h /tmp`
90+
has headroom; delete stale checkpoints/videos first if not. A full root disk
91+
also breaks the Claude Code harness (task-output dir can't be written), so this
92+
matters more than usual.
19493

19594
## Don't break what works
19695

197-
When making architectural changes to a model that has saved checkpoints, the `load_state_dict(strict=False)` + shape-filter pattern (see `openfly/policies.py:PaliGemmaOpenFlyPolicy.__init__`) keeps old checkpoints loadable with the new architecture. Apply the same pattern when adding/removing layers from `PaliGemmaVLNPolicy` or `PixArtSubgoalDiT`.
96+
The converged pick-place config is the result of careful reward balancing
97+
(strong-but-capped lift gradient, dominant held-only placement reward, start-pose
98+
curriculum). When changing `pick_place_env._get_rewards`, keep that structure —
99+
getting lift vs. placement weights wrong collapses the whole task. When changing
100+
the articulation or observation layout, keep old checkpoints loadable
101+
(`load_state_dict(strict=False)` + shape filter) rather than silently breaking
102+
every saved `model_*.pt`.

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL