Skip to content
JavgilaviPublic

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Liminal — Imagine, then act. Dual-arm manipulation with FLUX 3 Action.

Project page · Live lab

Long-horizon, dual-arm collaboration on SO-101 / XLeRobot, using FLUX 3 Action for manipulation.

One arm changes or holds something in the scene so the other arm can do what it otherwise couldn't. FLUX 3 Action handles the learned manipulation: bounded, text-prompted skills on one arm. Everything above that is this repo: deciding why a skill is needed, when the other arm is ready, which conditions must stay true, and whether the goal really happened.

Hackathon results (ERH Physical AI Hackathon, 18 October 2026)

Team Liminal: Oscar Devos, Javier Gil, Tomas Leggaet, Simone Utili, Stanislaw Sufin. Submission video (2 min) · Slides (PDF) 60 s teasers by Javier Gil: landscape · vertical

The full 4-skill chain, one run (12:05), sped up uniformly 5.71x with no cuts (58 s, then the final frame holds 2 s; operator console: plan, Claude's step checks, head and wrist cameras, FLUX's imagined vs actual frames). Step labels: box = scripted replay, not FLUX; green pen and cutter = FLUX 3 Action LoRAs; the green-pen policy also slotted the red pen on its way home.

Full 4-skill chain on the real Haller, 12:05 run, 5.71x uniform speed-up

  • Full 4-skill chain on the real Haller, planned by Claude with a camera check after every step: 2 of 3 runs finished 4/4 with no human touch during the run (208.6 s and 302.2 s).
    • Box: opened by a scripted replay of a recorded demo, not FLUX (at 12:05 it missed twice; Claude replanned and the third replay opened it).
    • Green pen: placed by its FLUX 3 Action LoRA (about 55 to 58 s).
    • Yellow cutter: placed by its FLUX 3 Action LoRA (at 12:05 on the second attempt, 45.6 s).
    • Red pen: in both 4/4 runs the green-pen policy picked up the red pen and slotted it on its way home, with no human touch. The red-pen step's camera check then found it already in place (2.8 s and 3.8 s), so the red-pen LoRA had nothing left to do. On its own, the red-pen LoRA made 3 of 5 and then 2 of 3 real inserts, plus one HMI insert confirmed by Claude in 33 s.
    • Demo mode: keep-out zones and stall/load stops were off. Joint limits, 1 deg per tick, a 60 % torque cap and the e-stop stayed on.
  • Behavior-tree harness on the real robot (Stanislaw Sufin, Simone Utili): the task-YAML behavior tree drove the Haller through the ws4 safety stack. It skipped the box step when it saw the lid already open, placed the green pen live with its FLUX LoRA, and ran with Claude Opus 5.5 checks. The Opus 5.5 hybrid gate is wired in and was verified in observation mode. See ws4/HARNESS_BRIDGE.md.
  • Live lab and digital twin (Javier Gil): the project page and a paired full-screen live lab that mirrors the real arms' measured joints in a MuJoCo twin of the Haller, also shown inside the orchestrator HMI. See docs/research-ui.md.
  • Per-skill FLUX LoRAs (rank 32, each skill normalized on its own demos): the red pen went 3/5, then 2/3, real inserts. Stock zero-shot FLUX went 0/16.
  • Public models (every checkpoint, under checkpoints/<run>/<step>/{raw,ema}):
  • Public datasets (LeRobot v3, VR teleoperation on the Haller): open the box, green pen, yellow cutter, red pen, take the red pen out.
  • Where things are:

Liminal architecture: a deterministic behavior-tree harness coordinates independent left and right FLUX 3 Action policies adapted with task LoRA. A skill registry selects checkpoint bundles; safety checks gate robot commands. Scene and wrist cameras feed Qwen3-VL-4B-Instruct perception, which updates verified world-state predicates for the harness.

Configured runtime: FLUX 3 Action + task LoRA on each arm; Qwen3-VL-4B-Instruct for perception. The harness, registry and safety checks are deterministic software. Full learned dual-arm hardware performance remains unverified.

Figure details and design references

The figure shows the main learned-skill path. Local hold/release primitives bypass VLA inference; optional task-planning models and the disabled standalone hybrid controller are omitted. Each policy receives its arm's joint state and camera observations; joint states come from the arm drivers. The embodiment panel uses the supplied photograph of our green XLeRobot rig, with its room background removed and the composition adapted for the figure. It illustrates the hardware, not a recorded successful rollout. See the architecture for the complete contracts.

Figure generated and refined with the built-in image-generation tool using our robot photograph; generation and refinement prompts. Visual references: OpenVLA and its GitHub repo, Diffusion Policy and its GitHub repo, π₀, and Nerfies. These informed the layout and visual hierarchy, not the Liminal architecture.

Strategy and key decisions: PLAN.md. Full design: docs/architecture.md. Who does what, and by when: TASK.md. Rules for humans and coding agents: AGENTS.md (CLAUDE.md is an identical copy for Claude Code).

First single-arm validation

The first SO101 validation includes official LeRobot FLUX inference on the real robot and MuJoCo task-LoRA training/evaluation. In five matched simulation scenes, the raw adapter achieved bilateral contact in 3/5 and lifts over 3.5 cm in 2/5; all models completed 0/5 placements. Physical inference and motor execution are recorded, with task success still unverified.

Current policy architecture

The initial plan was to fine-tune a unified bimanual XLeRobot embodiment (12-D actions). That plan was superseded by task LoRA fine-tuning of the existing 6-D SO-101 policy, with two independent FLUX instances, one per arm, coordinated by the real skill-runtime harness (behavior tree, checkpoint registry, perception and safety).

The dual-arm profiles select instances: per_arm: separate model state and locks allow concurrent inference when the task permits. LoRA adapters are merged into validated skill bundles for the checkpoint registry. Holding uses a local controller; two policies do not imply both arms always move. See the runtime guide.

The old 12-D configs and group tests remain compatibility scaffolds, with no validated bimanual checkpoint. XLeRobot remains the physical rig. instances: shared is an explicit memory-saving fallback, not the selected dual-arm configuration.

Workstreams

# Workstream Code Brief
WS1 Single-arm FLUX baseline + 50–100 demos + task LoRA policy/, data/, scripts/record_demos.sh 01
WS2 Bimanual: move obstacles away to reach a target bimanual/ 02
WS3 Long-horizon decomposition + orchestration (BT / FSM / LLM / Gemini ER / VLM) orchestration/, perception/ 03
WS4 Simulation + per-arm LoRA evaluation (original embodiment plan superseded) sim/, configs/embodiment/ 04
WS5 Hardware setup, safety, operator UI / triggering hardware/, safety/, ui/ 05

On the Haller (ws4 bridge)

On the green Haller the tree runs through the ws4 stack: each skill becomes one ws4 step (kit keep-outs, start gate, held box arm, stall and load freezes, e-stop that freezes with torque on), FLUX runs on the remote policy server with one LoRA package per skill, and ws4_haiku perception reuses ws4's measured Haiku checks. Command, arm naming and what is verified: ws4/HARNESS_BRIDGE.md.

Quick start (no hardware needed)

uv sync --extra dev
uv run pytest                            # all tests run on mock arms

# Run the cooperative-holding demo on mock arms
uv run liminal run configs/tasks/lid_hold_insert.yaml
# Check every task file compiles
uv run liminal validate configs/tasks/*.yaml
# Goal -> task tree (template planner; --planner claude|gemini needs `.[llm]` + API key)
uv run liminal plan "hold the lid and insert the block"
# Bimanual obstacle planner (WS2)
uv run python scripts/plan_obstacle.py --strategy hold_away

pyproject.toml declares direct dependencies and uv.lock pins the resolved environment. The committed .python-version selects Python 3.12. Use uv lock --check in CI or before deployment to detect dependency changes that were not locked.

Qwen scene inspection

The real VLM adapter is optional and loads its checkpoint lazily, once per process. Check the device in configs/perception/qwen3_vl_4b.yaml, then inspect one synchronized set of recorded views before enabling any robot motion:

uv sync --extra dev --extra vlm-qwen
uv run liminal inspect --perception configs/perception/qwen3_vl_4b.yaml \
  --image top=path/to/top.jpg \
  --image left_wrist=path/to/left_wrist.jpg \
  --image right_wrist=path/to/right_wrist.jpg

The shipped configuration uses bitsandbytes NF4 quantization with bfloat16 computation. Set quantization: none to measure the unquantized BF16 baseline.

The command prints schema-validated predicates plus load, preprocessing, time-to-first-token, total-latency, and GPU-memory measurements. Pass the same configuration file to liminal run using its --perception option.

Before robot deployment, label synchronized camera views using the manifest format in data/perception_eval/labels.example.yaml, then run repeated offline evaluation:

uv run liminal evaluate-perception data/perception_eval/labels.yaml \
  --perception configs/perception/qwen3_vl_4b.yaml \
  --repeats 3

The JSON report retains raw model responses and parsed evidence, and summarizes accuracy, unknowns, false-positive transitions, repeat agreement, latency, and Qwen preprocessing/token-generation/GPU metrics per call. Treat any false-positive transition as a deployment blocker even if aggregate accuracy looks acceptable.

Once the labeled evaluation passes, verify live camera capture and blackboard updates without connecting to either arm bus or issuing arm commands:

uv run liminal inspect-live \
  --robot configs/robot/so101_dual.yaml \
  --perception configs/perception/qwen3_vl_4b.yaml \
  --samples 3

This command cannot determine whether another controller has left motor torque enabled, so verify that separately. It connects only the perception cameras and writes the captured frames, raw responses, parsed predicates, observation versions, and final blackboard state to runs/.

On real hardware

The connected bimanual XLeRobot has an isolated setup and a local operator UI:

./scripts/setup_robot.sh
./scripts/control_robot.sh           # http://127.0.0.1:8080; starts with torque OFF

Stop other robot controllers first. Support the arm before clicking Engage. Setup, device mapping, calibration, limits, E-stop behavior and the API are in Robot control. This controller does not require Haller, ROS, Node.js, a GPU, or FLUX weights. Scripted and FLUX task backends still use the same safety supervisor; see the guide before running tasks on hardware.

For Haller UI ownership and remote GPU inference, follow the official LeRobot inference guide. It defaults to real-camera inference without motor commands; execution requires a verified rig profile and explicit UI engagement. Liminal supplies SSH transport and execution; LeRobot supplies Flux3Policy and its saved pre/postprocessors.

Every run writes a JSONL event log under runs/ (facts, node start/end, retries, WHILE violations). Use it to count success rates over trials.

Task files

Planners (hand-written, LLM, the bimanual obstacle planner) all emit the same grammar, and one compiler validates it:

tree:
  sequence:                                  # AFTER
    - skill: {arm: left, name: open_lid, prompt: "Lift the lid open", success: lid_open}
    - while:                                 # WHILE: left keeps holding...
        arm: left
        maintain: lid_open                   # ...this must stay true, or the body is cancelled
        until: object_inserted               # UNTIL
        do:
          skill: {arm: right, name: pick_place, prompt: "Place the block inside the open box",
                  success: object_inserted}
    - parallel:
        - {skill: {arm: left, name: release}}
        - {skill: {arm: right, name: home}}
    - check: object_inserted

Grammar reference: src/liminal/orchestration/task_spec.py. Examples: configs/tasks/.

Repo layout

src/liminal/
  core/           types + interface contracts (Arm, Camera, SkillBackend, PredicateDetector)
  orchestration/  behavior tree, task-file compiler, executor, planners (template / LLM)
  perception/     world state + event log, colour-zone detector, VLM verifier
  safety/         arm ownership, zones, joint limits, e-stop
  policy/         FLUX 3 Action backend, scripted fallback, mock
  hardware/       SO-101 via LeRobot (with position hold), cameras, mock arm
  sim/            MuJoCo arms/cameras, embodiment action-space mapping (XLeRobot)
  bimanual/       obstacle-clearing role assignment and plan generation
  data/           demo-collection variation protocol
  ui/             CLI and HTTP operator API
configs/          robot, policy, embodiment, skill catalog, task trees
scripts/          real/GPU inference, simulation collection/training/evaluation, hardware utilities
experiments/      compact validation results, provenance and training recipes
sim/single_arm/   versioned MuJoCo scene/model; external meshes
integrations/     external Haller controller patch and installation guide
docs/             guides, validation reports, architecture and research briefs

Status and caveats

  • The orchestration stack (BT, WHILE/UNTIL, safety, world state, planners, mock and scripted backends) runs and is tested.
  • Single-arm inference is implemented through the dedicated official LeRobot runner. The HF Space normalization profile is experimental on the physical rig. The real skill runtime loads validated bundles through LeRobotEngine and routes skills to independent per-arm backends. The showcase registry still has unstaged opening/green-pen/boxcutter entries; a full learned dual-arm hardware run is unverified. Planning configs retain VERIFY markers and are not commissioned inference profiles.
  • The simulation adapter improves grasp-related measures on a small test set; completed placement, real-task success and bimanual transfer remain unverified.
  • LeRobot imports and CLI flags change between releases. They are isolated in hardware/so101.py and scripts/record_demos.sh.
  • SO-101 holding is position holding, not torque control. Keep loads light.

Public project page and live digital twin lab

Explore the longer interactive project page at https://javgilavi.github.io/liminal/. The separate full-screen lab includes a rotatable recorded MuJoCo scene, scene/wrist views and explicit pairing to a local Haller companion. The project page distinguishes real red-pen insertion highlights, scripted teachers, simulation comparisons and older unlabeled hardware trials.

uv sync --extra research --extra dev
./scripts/research_ui.sh
# Open http://127.0.0.1:18003/pair on the robot-side machine.

Haller should be running at http://127.0.0.1:8000. Viewing both arms and cameras needs no policy profile. Pairing can happen before Haller starts; the companion reconnects automatically. To launch FLUX prompts, add --profile PATH_TO_VERIFIED_PROFILE for the current arm, checkpoint and GPU worker. The lab loads its live MuJoCo scene from that companion, and offers an Open Haller link after pairing.

Liminal is the project; Haller is the hardware/controller. Live cameras and measured joints travel directly from the local companion to the browser. Trials use the existing guarded official runner and save locally under runs/research/. The viewer mirrors arm joints; object poses, contact and physical registration are not automatically tracked. Setup, safety gates and implementation. The static site is deployed independently from the dedicated gh-pages branch.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages