twanghcmut's picture
|
download
raw
7.85 kB
# Foundation Physics Graph Model — object velocity extraction
Extracts **metric 3D world-frame velocity (m/s)** of the object a robot is
manipulating, from DROID episodes:
```
DROID episode mp4 ──► SAM 3.1 (text prompt derived from the episode's task instruction)
──► mask of the manipulated object
──► query points sampled inside the mask
──► TAPNext++ online point tracking
──► lift 2D tracks to metric 3D (depth from PointWorld scene_flows)
──► camera extrinsics ⇒ robot-base world frame ⇒ velocity (m/s)
```
The output is intended as node/edge features of a physical scene graph, so every
velocity is metric, timestamped, and carries explicit reference-frame provenance
(`VelocityEstimate.frame`).
---
## Why two data sources
**`nvidia/PointWorld-DROID` contains no RGB video** — only a single `initial_rgb`
JPEG per clip. It is a derived-annotation release (~3.91 TB packaged). So the
pipeline joins two sources on the DROID episode uuid
(`AUTOLab+0d4edc83+2023-10-21-19h-07m-04s`):
| Need | Source | Cost |
| --- | --- | --- |
| RGB video + **task instruction** | DROID raw on GCS, anonymous HTTPS | ~16 MB/episode |
| 3D point tracks, intrinsics, extrinsics | PointWorld `*_flows.h5` | 3.5 GB (1 shard ≈ 62 episodes) |
| Camera extrinsics for **every** episode | PointWorld `droid/cameras/` | 19.5 MB (all 42,935) |
`droid/depth_320x180/` is a **1.23 TB archive that cannot be cherry-picked**, so it
is never downloaded. `scene_flows` — which is already `(T, N, 3)` metric 3D point
tracks — serves as the depth source instead.
Total footprint for a 3–5 episode run is roughly **12 GB** including both model
checkpoints.
---
## Setup
### 1. GPU driver shim (required)
This host's NVIDIA kernel module is **570.172.08** while the userspace libraries
were upgraded to **580.173.02**, so a bare `nvidia-smi` fails with
`Failed to initialize NVML: Driver/library version mismatch`. The 570 libraries are
still on disk, so a symlink shim fixes it **without sudo or a reboot**:
```bash
source scripts/nvidia_lib_shim.sh
nvidia-smi # should now list the H200s
```
Everything that touches the GPU must run with that shim active.
`fpgm.utils.gpu.ensure_cuda()` fails fast and repeats this remedy if it is missing.
### 2. Environment
```bash
bash scripts/setup_env.sh
conda activate fpgm
```
This creates a Python 3.12 conda env (SAM 3.1 requires ≥3.12) and installs
**torch 2.10.0 from the cu128 index**. That index is not optional: driver 570 caps
CUDA at 12.8, and the default PyPI wheels are now cu13x, which need driver ≥580 and
will fail at runtime here.
It also clones `sam3` and `tapnet` into `third_party/` (pinned commits recorded in
`third_party/PINNED_COMMITS.txt`) and installs them editable. `tapnet` is installed
with `--no-deps` on purpose: its `pyproject.toml` lists `jax`, `dm-haiku`, `jaxline`
and `optax` as unconditional dependencies, none of which the TAPNext++ PyTorch path
ever imports.
### 3. Data and checkpoints
```bash
export HF_TOKEN=<your token> # facebook/sam3.1 is a gated repo
python scripts/download_checkpoints.py # SAM 3.1 (3.5 GB) + TAPNext++ (2.5 GB)
python scripts/download_pointworld.py --shard shard-000000 --yes
python scripts/download_droid_episodes.py --from-shard shard-000000 --limit 5
```
Checkpoints are resolved through the standard HuggingFace cache, which on this host
already holds 71 GB — nothing is duplicated into the repo.
### 4. Run
```bash
source scripts/nvidia_lib_shim.sh
python scripts/run_pipeline.py --config configs/droid_velocity.yaml
```
Per-clip overlay videos, contact sheets and `run_summary.json` land in `outputs/`.
---
## Layout
```
src/fpgm/
types.py shared dataclasses — the contract every stage speaks
config.py dataclass config schema + YAML loader
data/ episode identity, DROID raw client, PointWorld store
prompting/ task instruction -> SAM 3.1 noun phrase
segmentation/ VideoSegmenter ABC + SAM 3.1 adapter
tracking/ PointTracker ABC + query sampling + TAPNext++ adapter
depth/ DepthSource ABC + scene-flow depth
geometry/ camera, SE3, convention detection, lifting, velocity, validation
pipeline/ clip frame extraction + the orchestrator
viz/ mask / track / velocity overlays
```
Stages communicate only through the dataclasses in `types.py`; no stage reaches
into another's internals. That is what makes each one testable against synthetic
data with neither a GPU nor a download:
```bash
pytest -q
```
---
## Design notes worth knowing before changing things
**Camera resolution is a typed property, not a convention.**
`CameraIntrinsics` carries the `(width, height)` it is valid at, and
`Camera.project`/`unproject` never take a resolution argument. The h5 `intrinsic`
belongs to the annotation resolution while mp4 frames are a different size, so
`Camera.rescaled()` is the single, grep-able place where resolution changes.
**The scene-flow frame convention is detected, never assumed.**
Whether `scene_flows` are stored world- or camera-frame is undocumented. Each clip
is tested empirically by reprojecting into `initial_rgb` and scoring in-bounds
fraction plus colour agreement. An indecisive margin raises
`AmbiguousConventionError`; both hypotheses scoring badly raises
`CameraCalibrationMismatchError` (a different fault — wrong intrinsic/extrinsic/
serial pairing). Guessing here would apply a systematic rigid transform to every
3D position while still looking entirely plausible.
**Depth neighbours are restricted to the object mask.**
Depth at a tracked point is interpolated from projected `scene_flows` points, but
only from those inside the same SAM 3.1 mask. A background point that is *near in
image space* sits on a different surface, and letting it into the interpolation is
the dominant failure mode when tracking a moving object. Delaunay/barycentric
interpolation was considered and rejected: its triangles silently span occlusion
boundaries, producing smoothly wrong depth with no signal that anything went wrong.
**Long occlusions are not bridged.**
Gaps up to `max_gap_frames` are interpolated in position before differentiating.
Longer gaps split a track into independent runs with NaN across the gap, because a
long occlusion is exactly where the object may have changed direction.
**Nothing is invalid without a reason code.**
`DepthResult.method` records *why* a point was rejected. Debugging "why is this
velocity NaN" three stages downstream without provenance is the single biggest time
sink in a pipeline like this.
**The GPUs are shared.** SAM 3.1 holds every frame as a `(T, 3, 1008, 1008)` fp16
tensor (~6.1 MB/frame), so `offload_video_to_cpu` defaults to true and sessions are
always closed in a `finally`. `use_fa3` defaults to false because FlashAttention-3
is a separate install.
---
## Validation
`scene_flows` already contains ground-truth 3D motion for annotated points inside
the mask, so accuracy is checked without any external source: a `Track3D` built
directly from `scene_flows` is run through the *same* `VelocityEstimator` and
compared against the pipeline's own output. Magnitude and direction (cosine) errors
are reported separately, because a magnitude error implies a depth-scale bug while a
direction error implies a convention/axis bug — the report tells you which stage to
fix, not merely that something is wrong.
---
## Licences
DROID and PointWorld-DROID carry their own terms (PointWorld is NVIDIA
research-only); SAM 3.1 is under the SAM License and its HuggingFace repo is gated
with manual approval. This repository does not redistribute any of them.

Xet Storage Details

Size:
7.85 kB
·
Xet hash:
cefc839430a8c71423052ad9b75d191c3cb92092dafeb248dea27037f11fe040

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.