# RDK X5 BPU 量化流程 这个目录用于把 Gym/RobotLab 的 ONNX 策略转成 RDK X5 可运行的 Horizon runtime `.bin`。量化在 Mac 上用 CPU Docker 完成,板端可以做离线测速和实机 部署测试。 参考资料: - D-Robotics 官方 X5 工具链说明:`https://developer.d-robotics.cc/rdk_x_doc/Advanced_development/toolchain_development/overview?v=3.5.0&p=RDK+X5` - D-Robotics 论坛 WTW/Go2/X5 流程:`https://forum.d-robotics.cc/t/topic/28338` 官方工具链对 ONNX 的关键限制是:`ir_version <= 7`、`opset10/11`、固定 4 维输入,且 N 维只能为 1。因此这里不能直接拿原始 ONNX 编译,需要先裁剪 actions-only 输出、降级 opset,再把 `[1, D]` 输入包成固定 4D `NCHW`: Gym 5 帧是 `[1, 1, 1, 225]`,RobotLab 10 帧是 `[1, 1, 1, 450]`。 ## 当前状态 新增 Gym 5 帧策略的一键量化入口,默认目标是: - 原始模型:`../policy_35k.onnx` - 原始输入:`obs [1, 225]` - BPU 编译输入:`obs_4d [1, 1, 1, 225]` - BPU 输出:`actions [1, 12, 1, 1]` - 默认输出:`mapper_output_35k_gemm/policy_35k_int16_gemm.bin` - 默认校准数据:`calibration_data_35k_gym_fast64/` 本机 Docker 已完成一次默认量化: - 浮点 4D/Gemm 图等价对比:64 个真实样本上 `max_abs_diff = 7.15e-7` - `hb_mapper makertbin` 输出:`actions` cosine `0.998524`,L1 `0.014313`,L2 `0.005103`,Chebyshev `0.040004` - 编译估计延迟:`463.9 us` - 产物大小:`2.0M` - 产物路径:`deploy_45dim_rl_gym/bpu_quantization/mapper_output_35k_gemm/policy_35k_int16_gemm.bin` 一键量化命令: ```bash cd /Users/chenyouyuan/cyy_ws/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization ./quantize_policy_x5.sh ``` 切换其他 Gym 轮次时直接指定模型和 round: ```bash ./quantize_policy_x5.sh --policy ../policy_30k.onnx --round 30k ./quantize_policy_x5.sh --policy ../policy_25k.onnx --round 25k ./quantize_policy_x5.sh --policy ../policy_15k.onnx --round 15k ``` 脚本默认只抽 64 个真实 RL 样本做校准和最多 64 个样本做浮点等价对比,避免 之前 512 样本和 batch 回退导致的量化流程过慢。需要更稳的校准时再手动加大: ```bash ./quantize_policy_x5.sh --samples 128 --compare-limit 128 ``` Gym BPU 部署入口支持快速切换轮次: ```bash cd /root/go1_pro_deploy PYTHONPATH=/root/go1_pro_deploy python3 deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py \ --bpu-round 35k \ --kill-sport \ --enable-rl \ --log-dir logs \ --kp 32 --kd 1.0 \ --kp-cal 20 --kd-cal 1.0 \ --power-factor 9 \ --position-protect-limit 0.0 \ --action-clip 6.5 \ --action-trip-limit 8.0 \ --action-hard-trip-limit 16.0 \ --max-target-step 0.0 \ --max-roll-deg 50 \ --max-pitch-deg 50 \ --swap-vy-yaw \ --rc-vx-scale 0.5 \ --rc-vy-scale 0.5 \ --rc-wz-scale 1.0 \ --log-timing ``` 也可以直接指定 bin: ```bash PYTHONPATH=/root/go1_pro_deploy python3 deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py \ --bpu-model deploy_45dim_rl_gym/bpu_quantization/mapper_output_35k_gemm/policy_35k_int16_gemm.bin \ --infer-check --max-steps 1000 --log-timing ``` 板端离线测速: ```bash cd /root/go1_pro_deploy PYTHONPATH=/root/go1_pro_deploy python3 deploy_45dim_rl_gym/bpu_deploy_x5/test_bpu_policy.py \ --bpu-model deploy_45dim_rl_gym/bpu_quantization/mapper_output_35k_gemm/policy_35k_int16_gemm.bin \ --input-bin deploy_45dim_rl_gym/bpu_quantization/calibration_data_35k_gym_fast64/00000.bin \ --repeat 1000 cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_x5/cpp ./bpu_dnn_bench \ /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/mapper_output_35k_gemm/policy_35k_int16_gemm.bin \ /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/calibration_data_35k_gym_fast64/00000.bin \ 1000 ``` 已经完成 `policy_robotlab_15000.onnx` 和 `policy_robotlab_6500.onnx` 的 int16 量化。RobotLab BPU 部署默认仍使用 6500 版本: - 原始模型:`../policy_robotlab_6500.onnx` - 原始输入:`obs [1, 450]` - BPU 编译输入:`obs_4d [1, 1, 1, 450]` - BPU 输出:`actions [1, 12, 1, 1]` - Docker 镜像:`openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8` - `hb_mapper`:`1.24.3` - `hbdk`:`3.49.15` - 当前产物:`mapper_output_6500_gemm/policy_robotlab_6500_int16_gemm.bin` `mapper_output*/`、`.hb_check/`、校准数据、中间 ONNX、`hb_mapper` 日志都被 `.gitignore` 忽略;需要时按下面步骤重新生成。仓库里只保留脚本和 YAML 配置。 6500 量化使用 `calibration_data_fast64/` 的 64 个真实样本。原因是 `hb_mapper` 会先尝试 calibration batch 8,但当前 4D featuremap 包装会被工具链 内部改成固定 batch 的 reshape,batch 8 失败后会退回 batch 1;用 64 样本可以把 校准时间从 512 次 batch1 显著降下来。 ## 1. 生成校准数据 在本机 `deploy_go1_pro` 根目录执行: ```bash /Users/chenyouyuan/miniconda/envs/free_dog_sdk/bin/python3.10 \ deploy_45dim_rl_gym/bpu_quantization/make_calibration_data.py \ --max-samples 512 \ --overwrite ``` 脚本会从 `logs/robotlab_go1_deploy_*/steps.jsonl` 中提取 RL 状态下的真实 `obs_single`,按部署代码相同的 10 帧历史顺序重建 450 维输入,并写成 `float32` feature-map `.bin`。 本次校准数据: - 候选 RL 输入:`62606` - 选中样本:`512` - 单个样本形状:`1x1x1x450` - 元数据:`deploy_45dim_rl_gym/bpu_quantization/calibration_data_metadata.json` ## 2. 生成 opset11 和 4D ONNX 启动 Docker Desktop 后,在 `deploy_go1_pro` 根目录执行: ```bash docker run --rm --platform linux/amd64 \ -v "$PWD:/workspace/deploy_go1_pro" \ openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8 \ bash -lc 'cd /workspace/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization && python3 downgrade_policy_to_opset11.py \ --input ../policy_robotlab_15000.onnx \ --output policy_robotlab_15000_opset11.onnx && python3 make_bpu_4d_onnx.py \ --input policy_robotlab_15000_opset11.onnx \ --output policy_robotlab_15000_bpu4d.onnx && python3 compare_4d_onnx.py \ --flat-onnx ../policy_robotlab_15000.onnx \ --bpu4d-onnx policy_robotlab_15000_bpu4d.onnx \ --limit 256' ``` 本次对比结果:256 个真实输入上 `max_abs_diff = 0`,说明降级和 4D 包装没有 改变浮点 ONNX 输出。 ## 3. 可选:替换 MoE grouped Conv 原始导出的 MoE expert 末端有一个 `group=8, kernel=1` 的 `Conv1d`: ```text [1, 2048] -> Unsqueeze -> Conv(group=8, kernel=1) -> Squeeze -> [1, 256] ``` 在 X5 checker 里,这段会切到 CPU float,导致 BPU 子图被拆开。这个 Conv 等价于一个块对角 `Gemm`,可以不重训直接改图: ```bash docker run --rm --platform linux/amd64 \ -v "$PWD:/workspace/deploy_go1_pro" \ openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8 \ bash -lc 'cd /workspace/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization && python3 replace_group_conv_with_gemm.py \ --input policy_robotlab_15000_bpu4d.onnx \ --output policy_robotlab_15000_bpu4d_gemm.onnx && python3 compare_4d_onnx.py \ --flat-onnx ../policy_robotlab_15000.onnx \ --bpu4d-onnx policy_robotlab_15000_bpu4d_gemm.onnx \ --limit 512' ``` 本次浮点对比结果: - `max_abs_diff = 1.43e-6` - `max_mean_abs_diff = 3.53e-7` 这是浮点舍入级误差,可以认为图变换等价。 注意:这里没有把 `ReduceL2/Pow/Reshape/Sqrt` 这类 norm 算子挪到后处理, 因为它们在 actor 之前,属于策略中间计算,不能当成输出后处理裁掉。当前 `hb_mapper makertbin` 已经能把这些 norm 节点放到 BPU 上。 ## 4. checker 检查 ```bash docker run --rm --platform linux/amd64 \ -v "$PWD:/workspace/deploy_go1_pro" \ openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8 \ bash -lc 'cd /workspace/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization && hb_mapper checker \ --model policy_robotlab_15000_bpu4d_gemm.onnx \ --model-type onnx \ --march bayes-e \ --input-shape obs_4d 1x1x1x450' ``` checker 可以通过。原始模型中 MoE expert 的 `Elu/Reshape/Conv` CPU fallback 会被消掉;checker 阶段仍可能显示 gating `Softmax` 或输出 reshape 的 CPU 适配,正式 int16 编译时 `Softmax` 会被量化拆成 BPU 子算子。 ## 5. 编译 int16 `.bin` ```bash docker run --rm --platform linux/amd64 \ -v "$PWD:/workspace/deploy_go1_pro" \ openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8 \ bash -lc 'cd /workspace/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization && hb_mapper makertbin \ --config policy_robotlab_15000_int16_gemm.yaml \ --model-type onnx' ``` 优化版 `hb_mapper` 输出的量化精度: ```text Output Cosine Similarity L1 Distance L2 Distance Chebyshev Distance actions 0.999910 0.009500 0.003393 0.024229 ``` 优化版正式 int16 编译后,日志中列出的策略节点全部在同一个 BPU 子图 `id(0)` 上,原始模型的 CPU `Elu/Reshape/Conv` fallback 已消失。 优化版 BPU 子图编译估计延迟: - subgraph0:`424.5 us` 原始版 BPU 子图估计延迟是 `284.3 us + 80.8 us`,但带 CPU/hybrid 切换。 优化版单子图的编译估计延迟略高,实际是否更快要以板端 `hrt_model_exec perf` 为准。 ## 6. 板端离线验证 把 `.bin` 和一个校准输入传到板端,例如: ```bash ssh root@192.168.150.167 'mkdir -p /root/go1_pro_deploy/bpu_quant_test' scp \ deploy_45dim_rl_gym/bpu_quantization/mapper_output_gemm/policy_robotlab_15000_int16_gemm.bin \ deploy_45dim_rl_gym/bpu_quantization/calibration_data/00000.bin \ root@192.168.150.167:/root/go1_pro_deploy/bpu_quant_test/ ``` 板端查看模型: ```bash cd /root/go1_pro_deploy/bpu_quant_test hrt_model_exec model_info --model_file policy_robotlab_15000_int16_gemm.bin ``` 板端测速: ```bash hrt_model_exec perf \ --model_file policy_robotlab_15000_int16_gemm.bin \ --model_name policy_robotlab_15000_int16_gemm \ --input_file 00000.bin \ --frame_count 1000 \ --thread_num 1 ``` 原始版板端结果: - 平均延迟:`1.498257 ms` - 最大延迟:`2.942 ms` - FPS:`664.77` 优化版板端结果: - 平均延迟:`0.872819 ms` - 最大延迟:`1.627 ms` - 最小延迟:`0.613 ms` - FPS:`1133.28` 同一板端之前测 CPU ONNX 大约是 `1.63 ms`。原始 BPU hybrid `.bin` 只快了一点; Gemm 优化版移掉 MoE 中间 CPU fallback 后,板端离线平均延迟比 CPU ONNX 快约 46%,比原始 BPU hybrid 快约 42%。 原始版板端单样本输出与本机浮点 ONNX 对比: - `max_abs = 0.02545` - `mean_abs = 0.01040` - `l2 = 0.04584` 优化版板端单样本输出与本机浮点 ONNX 对比: - `max_abs = 0.02528` - `mean_abs = 0.00962` - `l2 = 0.04187` 这个误差对离线验证是可接受的,但还不足以直接上实机。 ## 7. 安全结论 当前建议只做离线推理验证,不要把 `.bin` 接入真实机器人控制循环。原因: - 原始 BPU 端到端速度提升很小,不能解决目前右前腿掉线、力矩保护、上下坡不稳这些核心问题。 - Gemm 优化版已经改善离线推理速度,但还没有做部署代码适配和悬空状态机测试。 - 输出 shape 从 `[1, 12]` 变成 `[1, 12, 1, 1]`,部署代码需要单独适配。 - 接入前至少要做更多 held-out 真实日志对比、悬空状态机测试,再进入地面低速测试。