Instructions to use kenpeter123/microduck-rl with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Microduck
How to use kenpeter123/microduck-rl with Microduck:
# Replace SLOT with the slot specified in the model card (walk, stand, sitstand, ground_pick, kick_left, kick_right, roulade). sudo robotctl policy load SLOT kenpeter123/microduck-rl
- Notebooks
- Google Colab
- Kaggle
Download scripts/eval_sprint_speed.py from kenpeter123/microduck-rl: direct link, hf CLI and curl.
- Browser
- Download file 4.25 kB
-
https://huggingface.co/kenpeter123/microduck-rl/resolve/main/scripts/eval_sprint_speed.py
- Command line
-
hf download hf://kenpeter123/microduck-rl/scripts/eval_sprint_speed.py
-
curl -L -o eval_sprint_speed.py https://huggingface.co/kenpeter123/microduck-rl/resolve/main/scripts/eval_sprint_speed.py
4.25 kB
| """eval_sprint_speed.py — 在 warp 训练环境内直接测 checkpoint 的真实速度。 | |
| 绕开本地 CPU harness 的所有差异项(BAM/延迟/噪声/归一化烘焙),用训练同款 | |
| 环境 + rsl_rl 推理策略(含 obs normalizer)回答:策略在原生环境里跑多快? | |
| 用法(实例上,工作目录 = repo 根): | |
| uv run --no-sync python scripts/eval_sprint_speed.py \ | |
| --checkpoint logs/rsl_rl/sprint/<run>/model_1999.pt --vx 0.4 1.2 2.0 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from dataclasses import asdict | |
| import numpy as np | |
| import torch | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--task", default="Mjlab-Sprint-Flat-MicroDuck") | |
| ap.add_argument("--checkpoint", default=None) | |
| ap.add_argument("--onnx", default=None, help="测 ONNX(归一化已烘焙)而非 .pt checkpoint") | |
| ap.add_argument("--vx", type=float, nargs="+", default=[0.4, 0.8, 1.2, 1.6, 2.0]) | |
| ap.add_argument("--num-envs", type=int, default=64) | |
| ap.add_argument("--seconds", type=float, default=8.0) | |
| ap.add_argument("--device", default="cuda:0") | |
| args = ap.parse_args() | |
| import mjlab.tasks # noqa: F401 — populate the registry | |
| import mjlab_microduck.tasks # noqa: F401 — microduck registrations | |
| from mjlab.tasks.registry import load_env_cfg, load_rl_cfg, load_runner_cls | |
| from mjlab.envs import ManagerBasedRlEnv | |
| from mjlab.rl import RslRlVecEnvWrapper, MjlabOnPolicyRunner | |
| env_cfg = load_env_cfg(args.task) | |
| agent_cfg = load_rl_cfg(args.task) | |
| env_cfg.scene.num_envs = args.num_envs | |
| env = ManagerBasedRlEnv(cfg=env_cfg, device=args.device) | |
| env = RslRlVecEnvWrapper(env, clip_actions=agent_cfg.clip_actions) | |
| if args.onnx: | |
| import onnxruntime as ort | |
| session = ort.InferenceSession(args.onnx) | |
| def policy(obs): | |
| vec = obs["actor"].detach().cpu().numpy().astype(np.float32) | |
| # 导出的 ONNX batch 维固定为 1,逐 env 推理 | |
| acts = [session.run(None, {session.get_inputs()[0].name: vec[i : i + 1]})[0] | |
| for i in range(vec.shape[0])] | |
| return torch.tensor(np.concatenate(acts, axis=0), | |
| device=args.device, dtype=torch.float32) | |
| else: | |
| assert args.checkpoint, "--checkpoint or --onnx required" | |
| runner_cls = load_runner_cls(args.task) or MjlabOnPolicyRunner | |
| runner = runner_cls(env, asdict(agent_cfg), device=args.device) | |
| runner.load(args.checkpoint, load_cfg={"actor": True}, strict=True, | |
| map_location=args.device) | |
| policy = runner.get_inference_policy(device=args.device) | |
| uw = env.unwrapped | |
| robot = uw.scene["robot"] | |
| cmd_term = uw.command_manager.get_term("twist") | |
| dt = uw.step_dt | |
| print(f"policy: {args.onnx or args.checkpoint}") | |
| print(f"control dt: {dt:.4f}s envs: {args.num_envs} task: {args.task}") | |
| def force_cmd(vx): | |
| cmd_term.vel_command_b[:, 0] = vx | |
| cmd_term.vel_command_b[:, 1] = 0.0 | |
| cmd_term.vel_command_b[:, 2] = 0.0 | |
| cmd_term.is_standing_env[:] = False | |
| obs, _ = env.reset() | |
| for vx in args.vx: | |
| obs, _ = env.reset() | |
| force_cmd(vx) | |
| # 2s 加速/稳定段 | |
| for _ in range(int(2.0 / dt)): | |
| force_cmd(vx) | |
| actions = policy(obs) | |
| obs, _, _, _ = env.step(actions) | |
| x0 = robot.data.root_link_pos_w[:, 0].clone() | |
| y0 = robot.data.root_link_pos_w[:, 1].clone() | |
| n_meas = int(args.seconds / dt) | |
| dones_total = 0 | |
| for _ in range(n_meas): | |
| force_cmd(vx) | |
| actions = policy(obs) | |
| obs, _, dones, _ = env.step(actions) | |
| dones_total += int(dones.sum()) | |
| x1 = robot.data.root_link_pos_w[:, 0] | |
| y1 = robot.data.root_link_pos_w[:, 1] | |
| dist = torch.hypot(x1 - x0, y1 - y0) | |
| speed = dist / args.seconds | |
| alive = (dones_total == 0) | |
| print(f"cmd={vx:4.1f} 实测 speed mean={speed.mean():.3f} " | |
| f"p10={speed.quantile(0.1):.3f} p90={speed.quantile(0.9):.3f} " | |
| f"max={speed.max():.3f} m/s resets={dones_total}") | |
| env.close() | |
| if __name__ == "__main__": | |
| main() | |