Skip to content

Commit 2b8ab2c

Browse files
committed
Add ONNX inference benchmark script
1 parent ac8775e commit 2b8ab2c

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

scripts/bench_inference.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""Benchmark ONNX policy inference speed.
2+
3+
Usage:
4+
python scripts/bench_inference.py controller.policy_path=track.onnx
5+
python scripts/bench_inference.py controller.policy_path=track.onnx controller.device=cuda
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import time
11+
from pathlib import Path
12+
13+
import hydra
14+
import numpy as np
15+
from omegaconf import DictConfig
16+
17+
from teleopit.runtime.cli import add_unitree_sdk_submodule
18+
add_unitree_sdk_submodule(Path(__file__).resolve().parent.parent)
19+
20+
from teleopit.controllers.rl_policy import RLPolicyController
21+
from teleopit.runtime.common import cfg_get
22+
23+
24+
@hydra.main(version_base=None, config_path="../teleopit/configs", config_name="onboard_sim2real")
25+
def main(cfg: DictConfig) -> None:
26+
robot_cfg = cfg_get(cfg, "robot")
27+
ctrl_cfg = cfg_get(cfg, "controller")
28+
num_actions = int(cfg_get(robot_cfg, "num_actions", 29))
29+
30+
# Build policy
31+
policy = RLPolicyController(ctrl_cfg)
32+
obs_dim = policy._expected_obs_dim or 166
33+
print(f"Policy loaded | obs_dim={obs_dim} | multi_input={policy._multi_input}")
34+
if policy._multi_input:
35+
print(f" history_length={policy._history_length} history_obs_dim={policy._history_obs_dim}")
36+
37+
# Detect provider
38+
import onnxruntime as ort
39+
providers = ort.get_available_providers()
40+
print(f"Available ONNX providers: {providers}")
41+
device = str(cfg_get(ctrl_cfg, "device", "cpu"))
42+
print(f"Configured device: {device}")
43+
44+
# Warmup
45+
obs = np.random.randn(obs_dim).astype(np.float32) * 0.1
46+
print("\nWarming up (20 iterations)...")
47+
for _ in range(20):
48+
policy.compute_action(obs)
49+
50+
# Benchmark
51+
for n_iters in [100, 500]:
52+
obs_batch = [np.random.randn(obs_dim).astype(np.float32) * 0.1 for _ in range(n_iters)]
53+
54+
t0 = time.monotonic()
55+
for i in range(n_iters):
56+
action = policy.compute_action(obs_batch[i])
57+
_ = policy.get_target_dof_pos(action)
58+
elapsed = time.monotonic() - t0
59+
60+
avg_ms = elapsed / n_iters * 1000
61+
max_hz = 1000.0 / avg_ms if avg_ms > 0 else float("inf")
62+
print(f"\n--- {n_iters} iterations ---")
63+
print(f" Total: {elapsed:.3f}s")
64+
print(f" Average: {avg_ms:.2f} ms/iter")
65+
print(f" Max throughput: {max_hz:.1f} Hz")
66+
print(f" 50Hz budget (20ms): {'OK' if avg_ms < 20 else 'OVER'} ({avg_ms:.2f}ms)")
67+
68+
# Also benchmark the full observation build + inference pipeline time
69+
print("\n--- Full step simulation (obs build + inference + post-process) ---")
70+
from teleopit.controllers.observation import VelCmdObservationBuilder
71+
from teleopit.interfaces import RobotState
72+
73+
obs_builder = VelCmdObservationBuilder(cfg)
74+
default_angles = np.asarray(cfg_get(robot_cfg, "default_angles"), dtype=np.float32)
75+
76+
# Fake robot state
77+
fake_state = RobotState(
78+
qpos=default_angles.copy(),
79+
qvel=np.zeros(num_actions, dtype=np.float32),
80+
quat=np.array([1, 0, 0, 0], dtype=np.float32),
81+
ang_vel=np.zeros(3, dtype=np.float32),
82+
timestamp=time.time(),
83+
)
84+
# Fake motion qpos
85+
motion_qpos = np.zeros(7 + num_actions, dtype=np.float32)
86+
motion_qpos[3] = 1.0 # identity quat
87+
motion_qpos[7:] = default_angles
88+
last_action = np.zeros(num_actions, dtype=np.float32)
89+
90+
# Warmup
91+
for _ in range(10):
92+
o = obs_builder.build(
93+
fake_state, motion_qpos,
94+
np.zeros(num_actions, dtype=np.float32),
95+
last_action,
96+
np.zeros(3, dtype=np.float32),
97+
np.zeros(3, dtype=np.float32),
98+
)
99+
a = policy.compute_action(o)
100+
policy.get_target_dof_pos(a)
101+
102+
n_iters = 200
103+
t0 = time.monotonic()
104+
for _ in range(n_iters):
105+
o = obs_builder.build(
106+
fake_state, motion_qpos,
107+
np.zeros(num_actions, dtype=np.float32),
108+
last_action,
109+
np.zeros(3, dtype=np.float32),
110+
np.zeros(3, dtype=np.float32),
111+
)
112+
a = policy.compute_action(o)
113+
target = policy.get_target_dof_pos(a)
114+
last_action = a
115+
elapsed = time.monotonic() - t0
116+
avg_ms = elapsed / n_iters * 1000
117+
max_hz = 1000.0 / avg_ms if avg_ms > 0 else float("inf")
118+
119+
print(f" {n_iters} iterations, {elapsed:.3f}s total")
120+
print(f" Average: {avg_ms:.2f} ms/step")
121+
print(f" Max throughput: {max_hz:.1f} Hz")
122+
print(f" 50Hz budget (20ms): {'OK' if avg_ms < 20 else 'OVER'} ({avg_ms:.2f}ms)")
123+
124+
125+
if __name__ == "__main__":
126+
main()

0 commit comments

Comments
 (0)