Files
blackbird/actpred/export_onnx.py
T
qing a2ba797410 feat(pybot×actpred): R2 姿态反馈方案一——MPU6050 独立 IMU token 全链路
- pybot:recorder 逐帧落盘 imu.npy(T×8,roll,pitch,ax,ay,az,gx,gy,gz,与帧/动作同调用对齐);run_loop 对声明 imu_dim 的 predictor 以 predict(..., imu=) 调用(鸭子类型,旧两参调用向后兼容)

- actpred:新增 --imu-dim;model 序列 [img×F, act×H, (IMU), PRED](imu_dim>0 时 F+H+2,IMU token 位于 PRED 前);dataset/train 读 imu.npy(缺失按零向量兜底);predict/ONNX/Hailo 三后端与 export/gen_calibration/dfc_compile/_npu_test 部署链适配 imu 输入

- 姿态按物理量程固定缩放归一化(imu_norm.py,训练/推理共用,零状态)

- 测试:actpred 128 passed / pybot loop+recorder 28 passed
2026-08-14 09:07:24 +08:00

66 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import argparse
import torch
from torch.export import Dim
from config import parse_checkpoint_config
from model import ActionPredictor
def main():
parser = argparse.ArgumentParser(description="Export actpred model to ONNX")
parser.add_argument("--model", required=True, help="checkpoint path (best_inference.pt)")
parser.add_argument("--output", default="model.onnx", help="output ONNX file path")
args = parser.parse_args()
checkpoint = torch.load(args.model, map_location="cpu", weights_only=True)
cfg = parse_checkpoint_config(checkpoint["config"])
data_cfg = cfg.data
model_cfg = cfg.model
model = ActionPredictor(model_cfg, data_cfg, pretrained=False)
model.load_state_dict(checkpoint["model_state_dict"])
model.eval()
dummy_images = torch.randn(
1, data_cfg.frame_len, 3, data_cfg.image_height, data_cfg.image_width)
dummy_history = torch.randn(1, data_cfg.history_len, data_cfg.action_dim)
# R2 方案一:imu_dim>0 时追加归一化姿态输入 (1, imu_dim)
imu_dim = data_cfg.imu_dim
extra_inputs = (torch.randn(1, imu_dim),) if imu_dim > 0 else ()
extra_names = ["imu"] if imu_dim > 0 else []
batch = Dim("batch")
dynamic = {"images": {0: batch}, "action_history": {0: batch}}
if imu_dim > 0:
dynamic["imu"] = {0: batch}
torch.onnx.export(
model, (dummy_images, dummy_history, *extra_inputs),
args.output,
input_names=["images", "action_history"] + extra_names,
output_names=["predicted_action"],
dynamic_shapes=dynamic,
opset_version=18,
)
print(f"ONNX model exported to: {args.output}"
f"{'(含 imu 输入)' if imu_dim > 0 else ''}")
# 可选:验证导出的 ONNX 模型一致性
try:
import onnxruntime as ort
import numpy as np
sess = ort.InferenceSession(args.output)
feeds = {"images": dummy_images.numpy(),
"action_history": dummy_history.numpy()}
for name, t in zip(extra_names, extra_inputs):
feeds[name] = t.numpy()
ort_out = sess.run(None, feeds)[0]
with torch.no_grad():
torch_out = model(dummy_images, dummy_history, *extra_inputs).numpy()
max_diff = np.abs(ort_out - torch_out).max()
print(f"ONNX 一致性验证通过(最大差异: {max_diff:.6e})")
except ImportError:
print("提示: 安装 onnxruntime 可验证导出模型一致性 (pip install onnxruntime)")
if __name__ == "__main__":
main()