#!/usr/bin/env python3 """Export DreamWaQ CENetActorModel to ONNX for MuJoCo sim2sim. 输入: obs(45) + obs_history(225) → 输出: action(12)(确定性,mean CENet code) 用法: uv run scripts/export_dreamwaq_onnx_new.py uv run scripts/export_dreamwaq_onnx_new.py --checkpoint runs/.../model_2850.pt """ import argparse, os, sys, glob import torch sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from motrix_rl.rslrl.torch.models.cenet_actor import CENetActorModel from tensordict import TensorDict PROJECT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class DwaqInfer(torch.nn.Module): """确定性推理:VAE mean code + Actor MLP。""" def __init__(self, model: CENetActorModel): super().__init__() self.vae = model.vae self.actor_mlp = model.mlp def forward(self, obs, obs_history): """obs: (N,45), obs_history: (N,225) → action: (N,12)""" code = self.vae.deterministic_code(obs_history) # (N,19) latent = torch.cat([code, obs], dim=-1) # (N,64) return self.actor_mlp(latent) def main(): p = argparse.ArgumentParser() p.add_argument("--checkpoint", default=None) p.add_argument("--output", default=None) args = p.parse_args() if args.checkpoint: ckpt = args.checkpoint else: models = sorted(glob.glob(os.path.join(PROJECT, "runs/go1-dreamwaq-walk/rslrl/*/model_*.pt"))) if not models: print("No checkpoints found"); sys.exit(1) ckpt = models[-1] print(f"[Export] checkpoint: {ckpt}") # 加载模型 dummy = TensorDict({ "policy": torch.zeros(1, 45), "obs_history": torch.zeros(1, 225), "privileged_obs": torch.zeros(1, 247), }, batch_size=[1]) model = CENetActorModel( dummy, {"actor": ["policy", "obs_history"]}, "actor", 12, hidden_dims=[512, 256, 128], activation="elu", stochastic=True, init_noise_std=1.0, cenet_in_dim=225, cenet_out_dim=19, ) data = torch.load(ckpt, map_location="cpu") if "actor_state_dict" in data: model.load_state_dict(data["actor_state_dict"]) else: model.load_state_dict(data) model.eval() # 包装为推理模型 infer = DwaqInfer(model) infer.eval() # 导出 ONNX out_path = args.output or os.path.join(os.path.dirname(ckpt), "policy.onnx") dummy_obs = torch.zeros(1, 45) dummy_hist = torch.zeros(1, 225) torch.onnx.export( infer, (dummy_obs, dummy_hist), out_path, input_names=["obs", "obs_history"], output_names=["actions"], opset_version=11, dynamic_axes={"obs": {0: "batch"}, "obs_history": {0: "batch"}, "actions": {0: "batch"}}, ) print(f"[Export] saved: {out_path}") if __name__ == "__main__": main()