Ngseo commited on
Commit
4ca024c
·
verified ·
1 Parent(s): 42a81c6

Add runnable inference example (runs from HF id alone)

Browse files
Files changed (1) hide show
  1. 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()