Instructions to use Ngseo/hyundai-uiwang-left-flowmatch with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use Ngseo/hyundai-uiwang-left-flowmatch with LeRobot:
- Notebooks
- Google Colab
- Kaggle
Add runnable inference example (runs from HF id alone)
Browse files- inference_example.py +127 -0
inference_example.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""Portable inference example for the Hyundai Uiwang FlowMatch Diffusion Policy.
|
| 4 |
+
|
| 5 |
+
Runs with ONLY the Hugging Face model id — no dataset download, no robot, no
|
| 6 |
+
local checkpoint needed. The uploaded model bundles its normalization stats
|
| 7 |
+
(policy_preprocessor / policy_postprocessor), so `make_pre_post_processors`
|
| 8 |
+
loads everything straight from the Hub.
|
| 9 |
+
|
| 10 |
+
What you need to provide at run time:
|
| 11 |
+
* front_rgb : np.uint8 (H, W, 3) RGB — scene/zivid camera view
|
| 12 |
+
* wrist_rgb : np.uint8 (H, W, 3) RGB — wrist camera view
|
| 13 |
+
* state : np.float32 (26,) — arm joints (6) + hand joints (20)
|
| 14 |
+
|
| 15 |
+
The policy resizes images internally (to 240x320 then center-crops), so the
|
| 16 |
+
input camera resolution does not need to match training exactly — just pass the
|
| 17 |
+
raw RGB frames.
|
| 18 |
+
|
| 19 |
+
Output: np.float32 (26,) action = target arm joints (6) + target hand joints (20), at 30 Hz.
|
| 20 |
+
|
| 21 |
+
Usage:
|
| 22 |
+
# self-contained demo with synthetic frames (verifies the model loads + runs):
|
| 23 |
+
python examples/hyundai_uiwang/inference_example.py
|
| 24 |
+
|
| 25 |
+
# specify a different model / device:
|
| 26 |
+
python examples/hyundai_uiwang/inference_example.py --model-id Ngseo/hyundai-uiwang-left-flowmatch --device cuda
|
| 27 |
+
|
| 28 |
+
Install (once):
|
| 29 |
+
pip install lerobot # or use this repo with PYTHONPATH=src
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
from __future__ import annotations
|
| 33 |
+
|
| 34 |
+
import argparse
|
| 35 |
+
|
| 36 |
+
import numpy as np
|
| 37 |
+
import torch
|
| 38 |
+
|
| 39 |
+
from lerobot.policies.diffusion.modeling_diffusion import DiffusionPolicy
|
| 40 |
+
from lerobot.policies.factory import make_pre_post_processors
|
| 41 |
+
|
| 42 |
+
DEFAULT_MODEL_ID = "Ngseo/hyundai-uiwang-left-flowmatch"
|
| 43 |
+
# Camera feature keys the model was trained with (see the model card / config.json).
|
| 44 |
+
FRONT_KEY = "observation.images.front_rgb"
|
| 45 |
+
WRIST_KEY = "observation.images.wrist_rgb"
|
| 46 |
+
STATE_KEY = "observation.state"
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def load_policy(model_id: str = DEFAULT_MODEL_ID, device: str = "cuda"):
|
| 50 |
+
"""Load the policy + pre/post processors from the Hugging Face Hub."""
|
| 51 |
+
device = device if (device != "cuda" or torch.cuda.is_available()) else "cpu"
|
| 52 |
+
policy = DiffusionPolicy.from_pretrained(model_id)
|
| 53 |
+
policy.config.device = device # saved config pins device=cuda; align it to the runtime device
|
| 54 |
+
policy.to(device)
|
| 55 |
+
policy.eval()
|
| 56 |
+
policy.reset() # clears the internal observation/action queues
|
| 57 |
+
# pretrained_path=model_id -> normalization stats are loaded from the Hub repo.
|
| 58 |
+
# Override the saved device_processor step so preprocessing targets `device` too.
|
| 59 |
+
preprocess, postprocess = make_pre_post_processors(
|
| 60 |
+
policy.config, model_id, preprocessor_overrides={"device_processor": {"device": device}}
|
| 61 |
+
)
|
| 62 |
+
return policy, preprocess, postprocess, device
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@torch.no_grad()
|
| 66 |
+
def predict_action(
|
| 67 |
+
policy,
|
| 68 |
+
preprocess,
|
| 69 |
+
postprocess,
|
| 70 |
+
front_rgb: np.ndarray,
|
| 71 |
+
wrist_rgb: np.ndarray,
|
| 72 |
+
state: np.ndarray,
|
| 73 |
+
device: str = "cuda",
|
| 74 |
+
) -> np.ndarray:
|
| 75 |
+
"""Run one inference step and return a 26-d action as np.float32.
|
| 76 |
+
|
| 77 |
+
Note: the policy keeps an internal queue (n_obs_steps / n_action_steps), so
|
| 78 |
+
call this repeatedly at the control loop rate; `policy.reset()` starts a new
|
| 79 |
+
episode.
|
| 80 |
+
"""
|
| 81 |
+
# Raw frame dict in the format expected by the preprocessor:
|
| 82 |
+
# images: uint8 (H, W, C); state: float32 (D,) — batching/normalization
|
| 83 |
+
# are handled by the processor pipeline.
|
| 84 |
+
obs = {
|
| 85 |
+
FRONT_KEY: torch.from_numpy(front_rgb).to(torch.float32).div(255).permute(2, 0, 1).unsqueeze(0).to(device),
|
| 86 |
+
WRIST_KEY: torch.from_numpy(wrist_rgb).to(torch.float32).div(255).permute(2, 0, 1).unsqueeze(0).to(device),
|
| 87 |
+
STATE_KEY: torch.from_numpy(state).to(torch.float32).unsqueeze(0).to(device),
|
| 88 |
+
"task": "",
|
| 89 |
+
"robot_type": "",
|
| 90 |
+
}
|
| 91 |
+
obs = preprocess(obs)
|
| 92 |
+
action = policy.select_action(obs) # (1, 26), normalized
|
| 93 |
+
action = postprocess(action) # unnormalized
|
| 94 |
+
return action.squeeze(0).float().cpu().numpy()
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def main() -> None:
|
| 98 |
+
ap = argparse.ArgumentParser()
|
| 99 |
+
ap.add_argument("--model-id", default=DEFAULT_MODEL_ID)
|
| 100 |
+
ap.add_argument("--device", default="cuda", choices=["cuda", "cpu", "mps"])
|
| 101 |
+
ap.add_argument("--steps", type=int, default=4, help="number of demo inference steps")
|
| 102 |
+
args = ap.parse_args()
|
| 103 |
+
|
| 104 |
+
print(f"Loading {args.model_id} ...")
|
| 105 |
+
policy, preprocess, postprocess, device = load_policy(args.model_id, args.device)
|
| 106 |
+
|
| 107 |
+
# Report what the model expects (handy when porting to a new robot).
|
| 108 |
+
img_keys = [k for k in policy.config.input_features if "image" in k]
|
| 109 |
+
state_dim = policy.config.input_features[STATE_KEY].shape[0]
|
| 110 |
+
action_dim = policy.config.output_features["action"].shape[0]
|
| 111 |
+
print(f"device={device} | cameras={img_keys} | state_dim={state_dim} | action_dim={action_dim}")
|
| 112 |
+
|
| 113 |
+
# --- demo with synthetic frames (replace these with real camera/robot data) ---
|
| 114 |
+
rng = np.random.default_rng(0)
|
| 115 |
+
for t in range(args.steps):
|
| 116 |
+
front_rgb = rng.integers(0, 256, size=(480, 640, 3), dtype=np.uint8)
|
| 117 |
+
wrist_rgb = rng.integers(0, 256, size=(480, 640, 3), dtype=np.uint8)
|
| 118 |
+
state = rng.standard_normal(state_dim).astype(np.float32)
|
| 119 |
+
|
| 120 |
+
action = predict_action(policy, preprocess, postprocess, front_rgb, wrist_rgb, state, device)
|
| 121 |
+
print(f"step {t}: action[26] = {np.array2string(action, precision=3, max_line_width=120)}")
|
| 122 |
+
|
| 123 |
+
print("\nOK — model runs from the HF id alone. Swap the synthetic frames for your robot's cameras/state.")
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
main()
|