Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

V-Modal Robotics / Physical AI SDK

A crash-resilient uplink for Robotics/Physical AI streeaming data flow

Immutable handoff · Checksum-keyed spool · Independent upload lanes · Restart reconciliation

Python 3.10–3.13 LeRobot v3 Linux MIT runtime deps

The network will flap. Power will disappear. Recording must continue.

vmodal-robotics moves finalized LeRobot dataset artifacts off a robot without turning the recording process into a distributed system. The producer closes its files and atomically publishes one .ready.json manifest. The SDK verifies the handoff, copies the exact bytes into a durable local spool, and tracks every artifact until it is durably acknowledged or explicitly blocked.

The base runtime is CPython plus Fire. It does not import LeRobot, PyTorch, pandas, ROS, GStreamer, or a database server. SQLite is provided by the Python standard library.

Robot-grade invariants

InvariantMechanism
Producer data stays producer-ownedAdmission copies files into the spool; rejection never deletes source data
Accepted bytes survive a process crashPayload writes use a temporary file, fsync, atomic rename, and directory fsync
State transitions survive a restartSQLite runs in WAL mode with synchronous=FULL
One artifact maps to one stable identityIDs derive from dataset identity, relative path, and SHA-256
Slow video cannot starve telemetryVideo and auxiliary artifacts run in independent async lanes
Video cannot consume the whole disk budgetA configurable byte reserve is held for telemetry and metadata
Dataset completion cannot race its payloadsA revision becomes publishable only after every artifact is acknowledged
A lost response is recoverableItems left in SENDING are reconciled on startup when the transport supports it
Corrupt or moving inputs never enter the queueSize, checksum, inode, mtime, path containment, and symlink checks gate admission
flowchart LR
    R[LeRobot recorder] -->|close + hash| D[(LeRobot v3 dataset)]
    R -->|atomic rename| M[*.ready.json]
    M --> A[LeRobot adapter]
    D --> A
    A -->|verify + immutable copy| S[(SQLite WAL<br/>checksum-keyed objects)]
    S --> V[video lane]
    S --> X[aux lane<br/>Parquet / metadata / opaque]
    V --> T[Transport]
    X --> T
    T -->|durable receipts| S
    S -->|all artifacts ACK| P[revision publish]

Boot it on a robot

Python 3.10 through 3.13 is supported. Install the tagged public source with the V-Modal cloud transport:

python -m pip install \
  "vmodal-robotics[vmodal] @ git+https://github.com/v-modal/vmodal_sdk_robotics.git@v0.1.0"

For a custom transport, install the lean core:

python -m pip install \
  "vmodal-robotics @ git+https://github.com/v-modal/vmodal_sdk_robotics.git@v0.1.0"

Configure the standard VMODAL_* variables used by the V-Modal Python SDK, then point the daemon at the producer handoff directory and a persistent local disk:

export VMODAL_ROBOT_READY_DIR=/data/lerobot/vmodal-ready
export VMODAL_ROBOT_SPOOL_DIR=/var/lib/vmodal-robot

vmodal-robot run

Inspect the queue or drain it during a controlled shutdown:

vmodal-robot status --spool_dir=/var/lib/vmodal-robot
vmodal-robot flush --spool_dir=/var/lib/vmodal-robot --deadline_seconds=120

run catches SIGINT and SIGTERM, stops admission, and flushes the existing backlog for up to --shutdown_deadline seconds.

The handoff protocol

The ready manifest is a commit record. Publish it only after every referenced file and the metadata snapshot are closed and immutable. Its filename must end in .ready.json.

{
  "contract_version": 1,
  "source_format": "lerobot",
  "source_version": "v3",
  "source_id": "arm-cell-07",
  "dataset_key": "gearbox/insertion",
  "dataset_root": "../dataset",
  "destination": "robot-data/arm-cell-07",
  "source_revision": "capture-000042",
  "complete": true,
  "artifacts": [
    {
      "path": "videos/chunk-000/observation.images.wrist.mp4",
      "kind": "video",
      "content_type": "video/mp4",
      "size_bytes": 73400320,
      "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
      "source_refs": {
        "camera_key": "observation.images.wrist",
        "episodes": [40, 41],
        "video_offsets": [0.0, 8.42]
      },
      "timing": {
        "source_clock": "lerobot.timestamp",
        "start": 0.0,
        "end": 16.81
      }
    },
    {
      "path": "data/chunk-000/file-000.parquet",
      "kind": "telemetry",
      "content_type": "application/vnd.apache.parquet",
      "size_bytes": 8192,
      "sha256": "123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0",
      "source_refs": {
        "episodes": [40, 41],
        "row_ranges": [[0, 252], [252, 504]]
      },
      "timing": {
        "source_clock": "lerobot.timestamp",
        "start": 0.0,
        "end": 16.81
      }
    },
    {
      "path": "meta/info.json",
      "kind": "metadata",
      "content_type": "application/json",
      "size_bytes": 2048,
      "sha256": "23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01",
      "source_refs": {"snapshot": "capture-000042"}
    }
  ]
}

Replace the example sizes and hashes with values computed from the closed files. Every revision requires at least one metadata artifact. Supported artifact kinds are video, telemetry, metadata, and opaque; LeRobot videos must be MP4 and telemetry files must be Parquet.

A robust producer handoff is deliberately boring:

  1. Close the MP4, Parquet, and metadata snapshot.
  2. Compute each byte length and SHA-256.
  3. Write the manifest under a temporary filename in the ready directory.
  4. Flush it, fsync it, and atomically rename it to *.ready.json.
  5. Leave the referenced bytes immutable.

Absolute artifact paths, .. escapes, symlinks, duplicate paths, missing files, changing files, invalid checksums, and unsupported dataset versions are rejected and recorded in the spool database.

State machine and failure semantics

                         transient failure
                    ┌────────────────────────┐
                    ▼                        │
READY ──claim──▶ SENDING ──ACK────────▶ ACKNOWLEDGED
                    │
                    ├──retry──────────▶ RETRY_WAIT
                    │                     │
                    │                     └──backoff elapsed──▶ SENDING
                    │
                    └──terminal / max attempts──────────────▶ BLOCKED

startup: SENDING ──reconcile──▶ ACKNOWLEDGED | READY | BLOCKED

Retries use exponential backoff with jitter: one second initially, capped at five minutes, with ten attempts by default. A transport receipt is accepted only when it carries the expected item identity, ACKNOWLEDGED state, a stable remote reference, and a non-conflicting checksum.

EventResult
Robot loses power while a source is being copiedThe incomplete temporary object is removed on the next start
Source changes during admissionThe entire handoff is rejected and its source remains untouched
Spool byte or record quota is reachedNew handoffs are rejected; the durable backlog remains intact
Upload succeeds but the response is lostStartup reconciliation asks the transport for the remote outcome
Network request times outThe item enters RETRY_WAIT with bounded exponential backoff
Artifact receipt conflicts with the local checksumThe artifact enters BLOCKED immediately
Process receives SIGTERMAdmission stops and the accepted backlog drains until the shutdown deadline

Scheduling and storage bounds

The video lane and auxiliary lane each claim one item per cycle and execute concurrently. Dataset revisions are serialized behind their artifacts. Default limits are intentionally finite:

ControlDefaultCLI / environment
Total spool bytes10 GiB--max_bytes / VMODAL_ROBOT_MAX_BYTES
Reserved auxiliary bytes512 MiB--aux_reserve_bytes / VMODAL_ROBOT_AUX_RESERVE_BYTES
Spool records10,000--max_records / VMODAL_ROBOT_MAX_RECORDS
Single video bytes100 MiBSpoolConfig.max_video_bytes
Discovery poll1 s--poll_seconds
Request timeout120 s--request_timeout_seconds
Shutdown drain30 s--shutdown_deadline

Use a spool path on persistent storage. status emits machine-readable JSON with queue depth and bytes per lane, oldest item age, disk headroom, retry and rejection counts, blocked items, and the latest durable receipt:

vmodal-robot status --spool_dir=/var/lib/vmodal-robot | python -m json.tool

Transport boundary

The runner depends on three async transport operations:

class Transport:
    async def deliver(self, artifact, destination): ...
    async def reconcile(self, artifact, destination): ...
    async def publish_revision(self, revision): ...

This narrow seam makes S3, R2, an on-prem object store, or a lab receiver easy to integrate without importing those clients into the recorder. A successful operation returns a Receipt with a stable remote_ref and status ACKNOWLEDGED.

Current V-Modal cloud support is explicit:

OperationBuilt-in VmodalTransport
MP4 deliveryQualified through collections.video_upload
Telemetry, metadata, and opaque deliveryRequires a generic artifact_api implementation
Remote reconciliationRequires a generic artifact_api implementation
Dataset revision publicationRequires a generic artifact_api implementation

Until the generic artifact and revision endpoints are qualified, the built-in transport places those operations in BLOCKED. It never routes original Parquet or metadata bytes through a video-description endpoint.

Hack on it

Clone the public repository and run the complete fake-data suite:

git clone https://github.com/v-modal/vmodal_sdk_robotics.git
cd vmodal_sdk_robotics
bash test.sh test

The suite uses tiny synthetic MP4, Parquet, metadata, and opaque payloads. It exercises byte-for-byte delivery, multi-camera and multi-episode references, lane independence, quota rejection, retry backoff, lost responses, process restart reconciliation, cleanup, the custom adapter seam, packaging, and a clean public install. No robot, camera, ROS graph, or cloud account is needed.

src/vmodal_robot/
├── adapters/lerobot.py      # strict LeRobot v3 ready-manifest parser
├── contracts.py             # adapter, artifact, revision, receipt, transport
├── runner.py                # scheduler, retries, recovery, graceful drain
├── spool.py                 # durable state machine + content-addressed objects
├── transports/vmodal.py     # optional V-Modal cloud bridge
└── utils.py                 # hashing, safe paths, atomic durable copies

Useful entry points:

vmodal-robot --help
vmodal-robot run --help
vmodal-robot status --help
vmodal-robot flush --help
bash test.sh package

If you are integrating a new recorder or transport, keep the invariant that matters most: the recorder owns mutable files; the uploader owns immutable bytes.

关于 About

Robotics vision SDK
multimodalmultimodal-aimultimodal-datamultimodal-fusionmultimodal-llmphysical-airoboticsrobotics-algorithmsrobotics-controlrobotics-librariesrobotics-programmingrobotics-simulationsearchsearch-apisearch-enginesearch-interfacevisionvision-aivisual-language-models

语言 Languages

Python100.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
5
Total Commits
峰值: 5次/周
Less
More

核心贡献者 Contributors