Compare commits
5 Commits
071c576bfe
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22a13d6659 | ||
|
|
8c1b2aecae | ||
|
|
8a44857314 | ||
|
|
8e2f5d6de5 | ||
|
|
a729020c78 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,6 +7,8 @@ deploy_45dim_rl_gym/bpu_quantization/calibration_data/
|
|||||||
deploy_45dim_rl_gym/bpu_quantization/calibration_data_fast*/
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_fast*/
|
||||||
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_gym_fast*/
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_gym_fast*/
|
||||||
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_gym_fast*_metadata.json
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_gym_fast*_metadata.json
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_robotlab_fast*/
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_*_robotlab_fast*_metadata.json
|
||||||
deploy_45dim_rl_gym/bpu_quantization/mapper_output/
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output/
|
||||||
deploy_45dim_rl_gym/bpu_quantization/mapper_output_gemm/
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_gemm/
|
||||||
deploy_45dim_rl_gym/bpu_quantization/mapper_output_*_gemm/
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_*_gemm/
|
||||||
@@ -16,6 +18,7 @@ deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_actions.onnx
|
|||||||
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_bpu4d.onnx
|
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_bpu4d.onnx
|
||||||
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_bpu4d_gemm.onnx
|
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_bpu4d_gemm.onnx
|
||||||
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_int16_gemm.yaml
|
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_int16_gemm.yaml
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_int8_gemm.yaml
|
||||||
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_opset11.onnx
|
deploy_45dim_rl_gym/bpu_quantization/policy_[0-9]*_opset11.onnx
|
||||||
deploy_45dim_rl_gym/bpu_quantization/policy_robotlab_15000_bpu4d.onnx
|
deploy_45dim_rl_gym/bpu_quantization/policy_robotlab_15000_bpu4d.onnx
|
||||||
deploy_45dim_rl_gym/bpu_quantization/policy_robotlab_15000_bpu4d_gemm.onnx
|
deploy_45dim_rl_gym/bpu_quantization/policy_robotlab_15000_bpu4d_gemm.onnx
|
||||||
|
|||||||
239
deploy_45dim_rl_gym/bpu_deploy_s100/README.md
Normal file
239
deploy_45dim_rl_gym/bpu_deploy_s100/README.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# S100 BPU 部署测试
|
||||||
|
|
||||||
|
这个目录是 S100 平台的隔离部署路径,不覆盖现有 X5 BPU 脚本。
|
||||||
|
|
||||||
|
当前默认模型:
|
||||||
|
|
||||||
|
```text
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm
|
||||||
|
```
|
||||||
|
|
||||||
|
S100 量化参数:
|
||||||
|
|
||||||
|
- 原始模型:`deploy_45dim_rl_gym/policy_robotlab_26000.onnx`
|
||||||
|
- 历史长度:RobotLab 10 帧
|
||||||
|
- 输入:`obs_4d [1, 1, 1, 450]`,float32 featuremap
|
||||||
|
- 输出:`actions [1, 12, 1, 1]`
|
||||||
|
- `march`:`nash-e`
|
||||||
|
- Docker 镜像:`registry.d-robotics.cc/deliver/ai_toolchain_ubuntu_22_s100_s600_cpu:v3.7.0`
|
||||||
|
|
||||||
|
## 本机量化
|
||||||
|
|
||||||
|
在 Mac 的仓库根目录执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/chenyouyuan/cyy_ws/deploy_go1_pro
|
||||||
|
bash deploy_45dim_rl_gym/bpu_quantization/quantize_policy_s100.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
等价显式命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/chenyouyuan/cyy_ws/deploy_go1_pro
|
||||||
|
bash deploy_45dim_rl_gym/bpu_quantization/quantize_policy_s100.sh \
|
||||||
|
--policy ../policy_robotlab_26000.onnx \
|
||||||
|
--round 26000 \
|
||||||
|
--name policy_robotlab_26000 \
|
||||||
|
--history-len 10 \
|
||||||
|
--samples 64 \
|
||||||
|
--min-samples 32 \
|
||||||
|
--log-prefix robotlab_go1_deploy \
|
||||||
|
--cal-tag robotlab \
|
||||||
|
--march nash-e
|
||||||
|
```
|
||||||
|
|
||||||
|
输出文件:
|
||||||
|
|
||||||
|
```text
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm
|
||||||
|
```
|
||||||
|
|
||||||
|
## 同步到 S100
|
||||||
|
|
||||||
|
S100 板端地址:
|
||||||
|
|
||||||
|
```text
|
||||||
|
root@192.168.11.144
|
||||||
|
```
|
||||||
|
|
||||||
|
如果仓库已经通过 git 同步,直接在板端拉取即可。如果只同步产物,可以从 Mac 执行;Docker 只在 Mac 上用于量化,S100 板端不运行 Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp \
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm \
|
||||||
|
root@192.168.11.144:/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/
|
||||||
|
```
|
||||||
|
|
||||||
|
同时确保校准输入存在,离线测速会用到:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp \
|
||||||
|
deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
|
||||||
|
root@192.168.11.144:/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 板端安装 hbm_runtime
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.11.144
|
||||||
|
cd /usr/hobot/lib/hbm_runtime
|
||||||
|
./build.sh install
|
||||||
|
```
|
||||||
|
|
||||||
|
S100 使用官方 `hbm_runtime` Python 绑定加载 `.hbm`,不复用 X5 的
|
||||||
|
`/usr/include/dnn/hb_dnn.h` C++ wrapper。
|
||||||
|
|
||||||
|
## 离线推理测速
|
||||||
|
|
||||||
|
先用官方 `hrt_model_exec` 看模型信息:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
/usr/hobot/bin/hrt_model_exec model_info \
|
||||||
|
--model_file deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm
|
||||||
|
```
|
||||||
|
|
||||||
|
官方 `hrt_model_exec` 稳态测速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
/usr/hobot/bin/hrt_model_exec perf \
|
||||||
|
--model_file deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm \
|
||||||
|
--model_name policy_robotlab_26000_s100_int16_gemm \
|
||||||
|
--input_file deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
|
||||||
|
--frame_count 1000 \
|
||||||
|
--thread_num 1
|
||||||
|
```
|
||||||
|
|
||||||
|
当前板端 `root@192.168.11.144` 已验证:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Average latency: 0.394 ms
|
||||||
|
FPS: 2442.456
|
||||||
|
```
|
||||||
|
|
||||||
|
部署脚本使用的 Python `hbm_runtime` wrapper:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_s100/test_bpu_policy.py \
|
||||||
|
--repeat 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
当前板端结果:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Backend: hbm_runtime_s100
|
||||||
|
Input: obs_4d (1, 1, 1, 450)
|
||||||
|
Output: actions (1, 12)
|
||||||
|
repeat=1000 avg_ms=0.733020
|
||||||
|
```
|
||||||
|
|
||||||
|
纯 C++ BPU wrapper/bench,不经过 Python 推理路径:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_s100/cpp
|
||||||
|
bash build_board.sh
|
||||||
|
|
||||||
|
./s100_bpu_bench \
|
||||||
|
/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm \
|
||||||
|
/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
|
||||||
|
1000 \
|
||||||
|
-1
|
||||||
|
```
|
||||||
|
|
||||||
|
参数含义:
|
||||||
|
|
||||||
|
- 第 1 个参数:S100 `.hbm` 模型。
|
||||||
|
- 第 2 个参数:float32 输入样本,当前 RobotLab 10 帧模型应为 450 个 float。
|
||||||
|
- 第 3 个参数:重复推理次数。
|
||||||
|
- 第 4 个参数:BPU core,`-1` 表示自动选择,`0..3` 表示固定单核。
|
||||||
|
|
||||||
|
这个 C++ wrapper 只做离线推理:模型加载和 tensor 内存分配只初始化一次,循环里只做输入拷贝、cache flush、`hbDNNInferV2`、`hbUCPSubmitTask`、等待和输出拷贝。它不会连接机器人,也不会发送电机指令。
|
||||||
|
|
||||||
|
当前板端纯 C++ 结果:
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend=cpp_dnn_api_s100
|
||||||
|
input_floats=450 output_floats=12 bpu_core=0
|
||||||
|
action [0.544585 -1.108296 0.916061 -0.837005 0.074006 -0.434765 -0.858212 -2.766010 1.127831 0.422954 1.424179 -0.016353]
|
||||||
|
action_max_abs 2.766010
|
||||||
|
repeat=5000 cpp_avg_ms=0.426015
|
||||||
|
```
|
||||||
|
|
||||||
|
## 离线推理检查
|
||||||
|
|
||||||
|
这一步会连接 MCU 读取状态,但不会发送电机指令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py \
|
||||||
|
--infer-check \
|
||||||
|
--log-dir logs \
|
||||||
|
--print-every 50 \
|
||||||
|
--max-steps 500
|
||||||
|
```
|
||||||
|
|
||||||
|
## 悬空状态机测试
|
||||||
|
|
||||||
|
先不要加 `--enable-rl`,确认 R2 只能推进到 `INFER_TEST`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py \
|
||||||
|
--kill-sport \
|
||||||
|
--log-dir logs \
|
||||||
|
--kp 28 --kd 0.7 \
|
||||||
|
--kp-cal 20 --kd-cal 1.0 \
|
||||||
|
--power-factor 7 \
|
||||||
|
--position-protect-limit 0.0 \
|
||||||
|
--action-clip 5.0 \
|
||||||
|
--action-trip-limit 8.0 \
|
||||||
|
--action-hard-trip-limit 16.0 \
|
||||||
|
--max-target-step 0.025 \
|
||||||
|
--max-roll-deg 35 \
|
||||||
|
--max-pitch-deg 35 \
|
||||||
|
--swap-vy-yaw \
|
||||||
|
--rc-vx-scale 0.3 \
|
||||||
|
--rc-vy-scale 0.3 \
|
||||||
|
--rc-wz-scale 0.6 \
|
||||||
|
--log-timing
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实际 RL 启动
|
||||||
|
|
||||||
|
只有悬空测试正常后,再启用 RL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/go1_pro_deploy
|
||||||
|
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py \
|
||||||
|
--kill-sport \
|
||||||
|
--enable-rl \
|
||||||
|
--log-dir logs \
|
||||||
|
--kp 28 --kd 0.7 \
|
||||||
|
--kp-cal 20 --kd-cal 1.0 \
|
||||||
|
--power-factor 7 \
|
||||||
|
--position-protect-limit 0.0 \
|
||||||
|
--action-clip 5.0 \
|
||||||
|
--action-trip-limit 8.0 \
|
||||||
|
--action-hard-trip-limit 16.0 \
|
||||||
|
--max-target-step 0.025 \
|
||||||
|
--max-roll-deg 35 \
|
||||||
|
--max-pitch-deg 35 \
|
||||||
|
--swap-vy-yaw \
|
||||||
|
--rc-vx-scale 0.3 \
|
||||||
|
--rc-vy-scale 0.3 \
|
||||||
|
--rc-wz-scale 0.6 \
|
||||||
|
--log-timing
|
||||||
|
```
|
||||||
|
|
||||||
|
如果要临时指定其它 S100 `.hbm`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py \
|
||||||
|
--bpu-model /absolute/path/to/model.hbm
|
||||||
|
```
|
||||||
77
deploy_45dim_rl_gym/bpu_deploy_s100/bpu_policy.py
Normal file
77
deploy_45dim_rl_gym/bpu_deploy_s100/bpu_policy.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""S100 HBM policy runtime wrapper."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class BpuInferLibPolicy:
|
||||||
|
"""S100 backend: official hbm_runtime Python binding."""
|
||||||
|
|
||||||
|
backend_name = "hbm_runtime_s100"
|
||||||
|
|
||||||
|
def __init__(self, model_path, priority=0, bpu_cores=(0,), cpp_lib=None):
|
||||||
|
del cpp_lib
|
||||||
|
self.model_path = Path(model_path).expanduser().resolve()
|
||||||
|
if not self.model_path.exists():
|
||||||
|
raise FileNotFoundError(f"S100 HBM model not found: {self.model_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from hbm_runtime import HB_HBMRuntime
|
||||||
|
except ImportError as exc:
|
||||||
|
raise RuntimeError(
|
||||||
|
"hbm_runtime is required on S100. Install it on the board with: "
|
||||||
|
"cd /usr/hobot/lib/hbm_runtime && ./build.sh install"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
self.priority = int(priority)
|
||||||
|
self.bpu_cores = tuple(int(core) for core in bpu_cores)
|
||||||
|
self.runtime = HB_HBMRuntime(str(self.model_path))
|
||||||
|
self.version = getattr(self.runtime, "version", "")
|
||||||
|
|
||||||
|
model_names = list(self.runtime.model_names)
|
||||||
|
if len(model_names) != 1:
|
||||||
|
raise RuntimeError(f"expected one model in {self.model_path}, got {model_names}")
|
||||||
|
self.model_name = model_names[0]
|
||||||
|
|
||||||
|
input_names = list(self.runtime.input_names[self.model_name])
|
||||||
|
output_names = list(self.runtime.output_names[self.model_name])
|
||||||
|
if len(input_names) != 1 or len(output_names) != 1:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"expected 1 input and 1 output, got {input_names} / {output_names}"
|
||||||
|
)
|
||||||
|
self.input_name = input_names[0]
|
||||||
|
self.output_name = output_names[0]
|
||||||
|
|
||||||
|
self.input_shape = tuple(int(x) for x in self.runtime.input_shapes[self.model_name][self.input_name])
|
||||||
|
self.output_shape = tuple(int(x) for x in self.runtime.output_shapes[self.model_name][self.output_name])
|
||||||
|
self.input_size = int(np.prod(self.input_shape))
|
||||||
|
self.output_size = int(np.prod(self.output_shape))
|
||||||
|
|
||||||
|
print(f"[INFO] BPU model: {self.model_path}")
|
||||||
|
print(f"[INFO] Backend: {self.backend_name}")
|
||||||
|
print(f"[INFO] Runtime: {self.version}")
|
||||||
|
print(f"[INFO] Input : {self.input_name} {self.input_shape}")
|
||||||
|
print(f"[INFO] Output : {self.output_name} {self.output_shape}")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.runtime = None
|
||||||
|
|
||||||
|
def __call__(self, flat_input):
|
||||||
|
arr = np.asarray(flat_input, dtype=np.float32)
|
||||||
|
if arr.size != self.input_size:
|
||||||
|
raise ValueError(f"S100 policy input has {arr.size} values, expected {self.input_size}")
|
||||||
|
input_tensor = np.ascontiguousarray(arr.reshape(self.input_shape), dtype=np.float32)
|
||||||
|
outputs = self.runtime.run(input_tensor)
|
||||||
|
action = np.asarray(outputs[self.model_name][self.output_name], dtype=np.float32).reshape(-1)
|
||||||
|
if action.size != self.output_size:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"S100 policy output has {action.size} values, expected {self.output_size}"
|
||||||
|
)
|
||||||
|
if not np.all(np.isfinite(action)):
|
||||||
|
raise RuntimeError(f"S100 policy output is not finite: {action}")
|
||||||
|
return action.copy()
|
||||||
|
|
||||||
|
|
||||||
|
BpuInferLibPythonPolicy = BpuInferLibPolicy
|
||||||
33
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/build_board.sh
Executable file
33
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/build_board.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
CXX="${CXX:-g++}"
|
||||||
|
CXXFLAGS=(
|
||||||
|
-O3
|
||||||
|
-DNDEBUG
|
||||||
|
-std=c++17
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-fPIC
|
||||||
|
-I/usr/include
|
||||||
|
)
|
||||||
|
LDFLAGS=(
|
||||||
|
-L/usr/hobot/lib
|
||||||
|
-ldnn
|
||||||
|
-lhbucp
|
||||||
|
-Wl,-rpath,/usr/hobot/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
"${CXX}" "${CXXFLAGS[@]}" -shared s100_bpu_policy.cpp \
|
||||||
|
"${LDFLAGS[@]}" \
|
||||||
|
-o libs100_bpu_policy.so
|
||||||
|
|
||||||
|
"${CXX}" "${CXXFLAGS[@]}" s100_bpu_bench.cpp \
|
||||||
|
-L. -ls100_bpu_policy -Wl,-rpath,'$ORIGIN' \
|
||||||
|
"${LDFLAGS[@]}" \
|
||||||
|
-o s100_bpu_bench
|
||||||
|
|
||||||
|
echo "[OK] built $(pwd)/libs100_bpu_policy.so"
|
||||||
|
echo "[OK] built $(pwd)/s100_bpu_bench"
|
||||||
129
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_bench.cpp
Normal file
129
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_bench.cpp
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void *rlgym_s100_bpu_create(const char *model_path, int bpu_core, int priority,
|
||||||
|
char *err, int err_len);
|
||||||
|
int rlgym_s100_bpu_infer(void *handle, const float *input, float *output, char *err,
|
||||||
|
int err_len);
|
||||||
|
int rlgym_s100_bpu_input_floats(void *handle);
|
||||||
|
int rlgym_s100_bpu_output_floats(void *handle);
|
||||||
|
void rlgym_s100_bpu_destroy(void *handle);
|
||||||
|
const char *rlgym_s100_bpu_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool read_f32_file(const std::string &path, std::vector<float> *data) {
|
||||||
|
std::ifstream ifs(path, std::ios::binary);
|
||||||
|
if (!ifs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ifs.seekg(0, std::ios::end);
|
||||||
|
const auto size = ifs.tellg();
|
||||||
|
ifs.seekg(0, std::ios::beg);
|
||||||
|
if (size <= 0 || size % static_cast<std::streamoff>(sizeof(float)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data->resize(static_cast<size_t>(size) / sizeof(float));
|
||||||
|
ifs.read(reinterpret_cast<char *>(data->data()), size);
|
||||||
|
return ifs.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(const char *argv0) {
|
||||||
|
std::cerr << "Usage: " << argv0 << " [model.hbm] [input.bin] [repeat] [bpu_core]\n"
|
||||||
|
<< " bpu_core: -1 means any core; 0..3 pins one BPU core\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
const char *model =
|
||||||
|
"/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/"
|
||||||
|
"mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm";
|
||||||
|
const char *input =
|
||||||
|
"/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/"
|
||||||
|
"calibration_data_26000_robotlab_fast64/00000.bin";
|
||||||
|
int repeat = 1000;
|
||||||
|
int bpu_core = -1;
|
||||||
|
if (argc > 1) model = argv[1];
|
||||||
|
if (argc > 2) input = argv[2];
|
||||||
|
if (argc > 3) repeat = std::atoi(argv[3]);
|
||||||
|
if (argc > 4) bpu_core = std::atoi(argv[4]);
|
||||||
|
if (argc > 5 || repeat <= 0) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char err[2048] = {};
|
||||||
|
void *handle = rlgym_s100_bpu_create(model, bpu_core, 0, err, sizeof(err));
|
||||||
|
if (handle == nullptr) {
|
||||||
|
std::cerr << err << "\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int input_floats = rlgym_s100_bpu_input_floats(handle);
|
||||||
|
const int output_floats = rlgym_s100_bpu_output_floats(handle);
|
||||||
|
if (input_floats <= 0 || output_floats <= 0) {
|
||||||
|
std::cerr << "invalid tensor sizes from S100 BPU runtime\n";
|
||||||
|
rlgym_s100_bpu_destroy(handle);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> obs;
|
||||||
|
if (!read_f32_file(input, &obs) || static_cast<int>(obs.size()) != input_floats) {
|
||||||
|
std::cerr << "failed to read " << input_floats << " float32 input: " << input
|
||||||
|
<< "\n";
|
||||||
|
rlgym_s100_bpu_destroy(handle);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> out(static_cast<size_t>(output_floats), 0.0f);
|
||||||
|
if (rlgym_s100_bpu_infer(handle, obs.data(), out.data(), err, sizeof(err)) != 0) {
|
||||||
|
std::cerr << err << "\n";
|
||||||
|
rlgym_s100_bpu_destroy(handle);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto t0 = std::chrono::steady_clock::now();
|
||||||
|
for (int i = 0; i < repeat; ++i) {
|
||||||
|
if (rlgym_s100_bpu_infer(handle, obs.data(), out.data(), err, sizeof(err)) != 0) {
|
||||||
|
std::cerr << err << "\n";
|
||||||
|
rlgym_s100_bpu_destroy(handle);
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto t1 = std::chrono::steady_clock::now();
|
||||||
|
const double elapsed_ms =
|
||||||
|
std::chrono::duration<double, std::milli>(t1 - t0).count();
|
||||||
|
|
||||||
|
const auto max_it = std::max_element(out.begin(), out.end(), [](float a, float b) {
|
||||||
|
return std::fabs(a) < std::fabs(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::cout << "backend=" << rlgym_s100_bpu_version() << "\n";
|
||||||
|
std::cout << "input_floats=" << input_floats << " output_floats=" << output_floats
|
||||||
|
<< " bpu_core=" << bpu_core << "\n";
|
||||||
|
std::cout << std::fixed << std::setprecision(6);
|
||||||
|
std::cout << "action [";
|
||||||
|
for (int i = 0; i < output_floats; ++i) {
|
||||||
|
if (i != 0) {
|
||||||
|
std::cout << ' ';
|
||||||
|
}
|
||||||
|
std::cout << out[static_cast<size_t>(i)];
|
||||||
|
}
|
||||||
|
std::cout << "]\n";
|
||||||
|
std::cout << "action_max_abs " << (max_it == out.end() ? 0.0f : std::fabs(*max_it))
|
||||||
|
<< "\n";
|
||||||
|
std::cout << "repeat=" << repeat << " cpp_avg_ms=" << (elapsed_ms / repeat) << "\n";
|
||||||
|
|
||||||
|
rlgym_s100_bpu_destroy(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
349
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_policy.cpp
Normal file
349
deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_policy.cpp
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <hobot/dnn/hb_dnn.h>
|
||||||
|
#include <hobot/dnn/hb_dnn_status.h>
|
||||||
|
#include <hobot/hb_ucp.h>
|
||||||
|
#include <hobot/hb_ucp_status.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void set_error(char *err, int err_len, const std::string &msg) {
|
||||||
|
if (err == nullptr || err_len <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::snprintf(err, static_cast<size_t>(err_len), "%s", msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string api_error(const std::string &where, int32_t code) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << where << " failed: " << code;
|
||||||
|
const char *desc = hbDNNGetErrorDesc(code);
|
||||||
|
if (desc == nullptr) {
|
||||||
|
desc = hbUCPGetErrorDesc(code);
|
||||||
|
}
|
||||||
|
if (desc != nullptr) {
|
||||||
|
oss << " (" << desc << ")";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_api(int32_t code, const std::string &where) {
|
||||||
|
if (code != 0) {
|
||||||
|
throw std::runtime_error(api_error(where, code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t element_count(const hbDNNTensorShape &shape) {
|
||||||
|
int64_t count = 1;
|
||||||
|
for (int i = 0; i < shape.numDimensions; ++i) {
|
||||||
|
if (shape.dimensionSize[i] <= 0) {
|
||||||
|
throw std::runtime_error("dynamic or invalid tensor shape is not supported");
|
||||||
|
}
|
||||||
|
count *= shape.dimensionSize[i];
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int element_size(int tensor_type) {
|
||||||
|
switch (tensor_type) {
|
||||||
|
case HB_DNN_TENSOR_TYPE_BOOL8:
|
||||||
|
case HB_DNN_TENSOR_TYPE_S8:
|
||||||
|
case HB_DNN_TENSOR_TYPE_U8:
|
||||||
|
return 1;
|
||||||
|
case HB_DNN_TENSOR_TYPE_F16:
|
||||||
|
case HB_DNN_TENSOR_TYPE_S16:
|
||||||
|
case HB_DNN_TENSOR_TYPE_U16:
|
||||||
|
return 2;
|
||||||
|
case HB_DNN_TENSOR_TYPE_F32:
|
||||||
|
case HB_DNN_TENSOR_TYPE_S32:
|
||||||
|
case HB_DNN_TENSOR_TYPE_U32:
|
||||||
|
return 4;
|
||||||
|
case HB_DNN_TENSOR_TYPE_F64:
|
||||||
|
case HB_DNN_TENSOR_TYPE_S64:
|
||||||
|
case HB_DNN_TENSOR_TYPE_U64:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unsupported tensor type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t compact_tail_bytes(const int32_t *dims, int dim_count, int elem_bytes) {
|
||||||
|
int64_t bytes = elem_bytes;
|
||||||
|
for (int i = 0; i < dim_count; ++i) {
|
||||||
|
bytes *= dims[i];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_compact_to_strided(char *dst, const char *src, const hbDNNTensorProperties &props,
|
||||||
|
int dim, int elem_bytes) {
|
||||||
|
const auto &shape = props.validShape;
|
||||||
|
if (dim + 1 == shape.numDimensions) {
|
||||||
|
std::memcpy(dst, src, static_cast<size_t>(shape.dimensionSize[dim] * elem_bytes));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t src_step =
|
||||||
|
compact_tail_bytes(shape.dimensionSize + dim + 1, shape.numDimensions - dim - 1,
|
||||||
|
elem_bytes);
|
||||||
|
const int64_t dst_step = props.stride[dim];
|
||||||
|
for (int i = 0; i < shape.dimensionSize[dim]; ++i) {
|
||||||
|
copy_compact_to_strided(dst + dst_step * i, src + src_step * i, props, dim + 1,
|
||||||
|
elem_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_strided_to_compact(char *dst, const char *src, const hbDNNTensorProperties &props,
|
||||||
|
int dim, int elem_bytes) {
|
||||||
|
const auto &shape = props.validShape;
|
||||||
|
if (dim + 1 == shape.numDimensions) {
|
||||||
|
std::memcpy(dst, src, static_cast<size_t>(shape.dimensionSize[dim] * elem_bytes));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t dst_step =
|
||||||
|
compact_tail_bytes(shape.dimensionSize + dim + 1, shape.numDimensions - dim - 1,
|
||||||
|
elem_bytes);
|
||||||
|
const int64_t src_step = props.stride[dim];
|
||||||
|
for (int i = 0; i < shape.dimensionSize[dim]; ++i) {
|
||||||
|
copy_strided_to_compact(dst + dst_step * i, src + src_step * i, props, dim + 1,
|
||||||
|
elem_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t core_mask_from_arg(int bpu_core) {
|
||||||
|
if (bpu_core < 0) {
|
||||||
|
return HB_UCP_BPU_CORE_ANY;
|
||||||
|
}
|
||||||
|
if (bpu_core > 3) {
|
||||||
|
throw std::runtime_error("bpu_core must be -1 or 0..3");
|
||||||
|
}
|
||||||
|
return 1ULL << static_cast<uint64_t>(bpu_core);
|
||||||
|
}
|
||||||
|
|
||||||
|
class S100BpuPolicy {
|
||||||
|
public:
|
||||||
|
S100BpuPolicy(const char *model_path, int bpu_core, int priority)
|
||||||
|
: bpu_core_mask_(core_mask_from_arg(bpu_core)), priority_(priority) {
|
||||||
|
if (model_path == nullptr || model_path[0] == '\0') {
|
||||||
|
throw std::runtime_error("empty model path");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *model_files[] = {model_path};
|
||||||
|
check_api(hbDNNInitializeFromFiles(&packed_handle_, model_files, 1),
|
||||||
|
"hbDNNInitializeFromFiles");
|
||||||
|
|
||||||
|
const char **model_names = nullptr;
|
||||||
|
int32_t model_count = 0;
|
||||||
|
check_api(hbDNNGetModelNameList(&model_names, &model_count, packed_handle_),
|
||||||
|
"hbDNNGetModelNameList");
|
||||||
|
if (model_count <= 0 || model_names == nullptr || model_names[0] == nullptr) {
|
||||||
|
throw std::runtime_error("model has no names");
|
||||||
|
}
|
||||||
|
model_name_ = model_names[0];
|
||||||
|
check_api(hbDNNGetModelHandle(&dnn_handle_, packed_handle_, model_names[0]),
|
||||||
|
"hbDNNGetModelHandle");
|
||||||
|
|
||||||
|
int32_t input_count = 0;
|
||||||
|
int32_t output_count = 0;
|
||||||
|
check_api(hbDNNGetInputCount(&input_count, dnn_handle_), "hbDNNGetInputCount");
|
||||||
|
check_api(hbDNNGetOutputCount(&output_count, dnn_handle_), "hbDNNGetOutputCount");
|
||||||
|
if (input_count != 1 || output_count != 1) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "expected 1 input and 1 output, got " << input_count << " inputs and "
|
||||||
|
<< output_count << " outputs";
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
input_tensors_.resize(1);
|
||||||
|
output_tensors_.resize(1);
|
||||||
|
check_api(hbDNNGetInputTensorProperties(&input_tensors_[0].properties, dnn_handle_, 0),
|
||||||
|
"hbDNNGetInputTensorProperties");
|
||||||
|
check_api(hbDNNGetOutputTensorProperties(&output_tensors_[0].properties, dnn_handle_, 0),
|
||||||
|
"hbDNNGetOutputTensorProperties");
|
||||||
|
|
||||||
|
validate_float_tensor(input_tensors_[0].properties, "input");
|
||||||
|
validate_float_tensor(output_tensors_[0].properties, "output");
|
||||||
|
input_floats_ = checked_float_count(input_tensors_[0].properties, "input");
|
||||||
|
output_floats_ = checked_float_count(output_tensors_[0].properties, "output");
|
||||||
|
|
||||||
|
alloc_tensor_mem(input_tensors_[0]);
|
||||||
|
alloc_tensor_mem(output_tensors_[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
~S100BpuPolicy() {
|
||||||
|
release_tensor_mem(input_tensors_);
|
||||||
|
release_tensor_mem(output_tensors_);
|
||||||
|
if (packed_handle_ != nullptr) {
|
||||||
|
hbDNNRelease(packed_handle_);
|
||||||
|
packed_handle_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void infer(const float *input, float *output) {
|
||||||
|
if (input == nullptr || output == nullptr) {
|
||||||
|
throw std::runtime_error("null input/output pointer");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &input_tensor = input_tensors_[0];
|
||||||
|
const auto &input_props = input_tensor.properties;
|
||||||
|
std::memset(input_tensor.sysMem.virAddr, 0,
|
||||||
|
static_cast<size_t>(input_props.alignedByteSize));
|
||||||
|
copy_compact_to_strided(reinterpret_cast<char *>(input_tensor.sysMem.virAddr),
|
||||||
|
reinterpret_cast<const char *>(input), input_props, 0,
|
||||||
|
sizeof(float));
|
||||||
|
check_api(hbUCPMemFlush(&input_tensor.sysMem, HB_SYS_MEM_CACHE_CLEAN),
|
||||||
|
"hbUCPMemFlush(input)");
|
||||||
|
|
||||||
|
hbUCPTaskHandle_t task_handle = nullptr;
|
||||||
|
check_api(hbDNNInferV2(&task_handle, output_tensors_.data(), input_tensors_.data(),
|
||||||
|
dnn_handle_),
|
||||||
|
"hbDNNInferV2");
|
||||||
|
|
||||||
|
hbUCPSchedParam sched_param{};
|
||||||
|
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
|
||||||
|
sched_param.priority = priority_;
|
||||||
|
sched_param.backend = bpu_core_mask_;
|
||||||
|
try {
|
||||||
|
check_api(hbUCPSubmitTask(task_handle, &sched_param), "hbUCPSubmitTask");
|
||||||
|
check_api(hbUCPWaitTaskDone(task_handle, 0), "hbUCPWaitTaskDone");
|
||||||
|
|
||||||
|
auto &output_tensor = output_tensors_[0];
|
||||||
|
check_api(hbUCPMemFlush(&output_tensor.sysMem, HB_SYS_MEM_CACHE_INVALIDATE),
|
||||||
|
"hbUCPMemFlush(output)");
|
||||||
|
copy_strided_to_compact(reinterpret_cast<char *>(output),
|
||||||
|
reinterpret_cast<const char *>(output_tensor.sysMem.virAddr),
|
||||||
|
output_tensor.properties, 0, sizeof(float));
|
||||||
|
} catch (...) {
|
||||||
|
hbUCPReleaseTask(task_handle);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_api(hbUCPReleaseTask(task_handle), "hbUCPReleaseTask");
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_floats() const { return input_floats_; }
|
||||||
|
int output_floats() const { return output_floats_; }
|
||||||
|
const std::string &model_name() const { return model_name_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void validate_float_tensor(const hbDNNTensorProperties &props, const char *name) {
|
||||||
|
if (props.tensorType != HB_DNN_TENSOR_TYPE_F32) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << name << " tensor type " << props.tensorType << " != "
|
||||||
|
<< HB_DNN_TENSOR_TYPE_F32;
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
if (props.alignedByteSize <= 0) {
|
||||||
|
throw std::runtime_error(std::string(name) + " alignedByteSize <= 0");
|
||||||
|
}
|
||||||
|
if (props.validShape.numDimensions <= 0) {
|
||||||
|
throw std::runtime_error(std::string(name) + " has invalid dimensions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int checked_float_count(const hbDNNTensorProperties &props, const char *name) {
|
||||||
|
const int64_t floats = element_count(props.validShape);
|
||||||
|
const int64_t bytes = floats * element_size(props.tensorType);
|
||||||
|
if (bytes > props.alignedByteSize) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << name << " compact bytes " << bytes << " > alignedByteSize "
|
||||||
|
<< props.alignedByteSize;
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
if (floats > static_cast<int64_t>(std::numeric_limits<int>::max())) {
|
||||||
|
throw std::runtime_error(std::string(name) + " tensor is too large");
|
||||||
|
}
|
||||||
|
return static_cast<int>(floats);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void alloc_tensor_mem(hbDNNTensor &tensor) {
|
||||||
|
std::memset(&tensor.sysMem, 0, sizeof(tensor.sysMem));
|
||||||
|
check_api(hbUCPMallocCached(&tensor.sysMem,
|
||||||
|
static_cast<uint64_t>(tensor.properties.alignedByteSize), 0),
|
||||||
|
"hbUCPMallocCached");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_tensor_mem(std::vector<hbDNNTensor> &tensors) {
|
||||||
|
for (auto &tensor : tensors) {
|
||||||
|
if (tensor.sysMem.virAddr != nullptr) {
|
||||||
|
hbUCPFree(&tensor.sysMem);
|
||||||
|
std::memset(&tensor.sysMem, 0, sizeof(tensor.sysMem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hbDNNPackedHandle_t packed_handle_{nullptr};
|
||||||
|
hbDNNHandle_t dnn_handle_{nullptr};
|
||||||
|
std::string model_name_;
|
||||||
|
std::vector<hbDNNTensor> input_tensors_;
|
||||||
|
std::vector<hbDNNTensor> output_tensors_;
|
||||||
|
int input_floats_{0};
|
||||||
|
int output_floats_{0};
|
||||||
|
uint64_t bpu_core_mask_{HB_UCP_BPU_CORE_ANY};
|
||||||
|
int priority_{HB_UCP_PRIORITY_LOWEST};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void *rlgym_s100_bpu_create(const char *model_path, int bpu_core, int priority,
|
||||||
|
char *err, int err_len) {
|
||||||
|
try {
|
||||||
|
set_error(err, err_len, "");
|
||||||
|
return new S100BpuPolicy(model_path, bpu_core, priority);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
set_error(err, err_len, e.what());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rlgym_s100_bpu_infer(void *handle, const float *input, float *output, char *err,
|
||||||
|
int err_len) {
|
||||||
|
try {
|
||||||
|
set_error(err, err_len, "");
|
||||||
|
if (handle == nullptr) {
|
||||||
|
throw std::runtime_error("null policy handle");
|
||||||
|
}
|
||||||
|
static_cast<S100BpuPolicy *>(handle)->infer(input, output);
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
set_error(err, err_len, e.what());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rlgym_s100_bpu_input_floats(void *handle) {
|
||||||
|
if (handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<S100BpuPolicy *>(handle)->input_floats();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rlgym_s100_bpu_output_floats(void *handle) {
|
||||||
|
if (handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<S100BpuPolicy *>(handle)->output_floats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rlgym_s100_bpu_destroy(void *handle) {
|
||||||
|
delete static_cast<S100BpuPolicy *>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *rlgym_s100_bpu_version() {
|
||||||
|
return "cpp_dnn_api_s100";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
55
deploy_45dim_rl_gym/bpu_deploy_s100/test_bpu_policy.py
Normal file
55
deploy_45dim_rl_gym/bpu_deploy_s100/test_bpu_policy.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Offline BPU policy smoke test for S100.
|
||||||
|
|
||||||
|
This does not connect to the robot. It loads a S100 BPU .hbm and one raw
|
||||||
|
float32 input file, then runs the hbm_runtime backend repeatedly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from bpu_policy import BpuInferLibPolicy
|
||||||
|
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
DEFAULT_MODEL = (
|
||||||
|
HERE.parent / "bpu_quantization" / "mapper_output_26000_s100_gemm" /
|
||||||
|
"policy_robotlab_26000_s100_int16_gemm.hbm"
|
||||||
|
)
|
||||||
|
DEFAULT_INPUT = (
|
||||||
|
HERE.parent / "bpu_quantization" /
|
||||||
|
"calibration_data_26000_robotlab_fast64" / "00000.bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Offline BPU policy smoke test")
|
||||||
|
parser.add_argument("--bpu-model", default=str(DEFAULT_MODEL))
|
||||||
|
parser.add_argument("--input-bin", default=str(DEFAULT_INPUT))
|
||||||
|
parser.add_argument("--repeat", type=int, default=1000)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
input_path = Path(args.input_bin).expanduser().resolve()
|
||||||
|
data = np.fromfile(input_path, dtype=np.float32)
|
||||||
|
policy = BpuInferLibPolicy(args.bpu_model)
|
||||||
|
if data.size != policy.input_size:
|
||||||
|
raise ValueError(
|
||||||
|
f"{input_path} has {data.size} float32 values, "
|
||||||
|
f"but model expects {policy.input_size}"
|
||||||
|
)
|
||||||
|
action = policy(data)
|
||||||
|
print("action", np.array2string(action, precision=6))
|
||||||
|
print("action_max_abs", float(np.max(np.abs(action))))
|
||||||
|
|
||||||
|
repeats = max(1, int(args.repeat))
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
for _ in range(repeats):
|
||||||
|
policy(data)
|
||||||
|
elapsed_ms = (time.perf_counter() - t0) * 1000.0
|
||||||
|
print(f"repeat={repeats} avg_ms={elapsed_ms / repeats:.6f}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
- `bpu_policy.py`:BPU policy wrapper,默认使用 `cpp_dnn_api_x5` C++ DNN API 后端。
|
- `bpu_policy.py`:BPU policy wrapper,默认使用 `cpp_dnn_api_x5` C++ DNN API 后端。
|
||||||
- `cpp/`:C++ DNN API 推理库源码、板端编译脚本和纯 C++ benchmark。
|
- `cpp/`:C++ DNN API 推理库源码、板端编译脚本和纯 C++ benchmark。
|
||||||
- `deploy_go1_robotlab_bpu_x5_fastcpp.py`:基于现有 `lab_fastcpp` 状态机的 BPU 版本。
|
- `deploy_go1_robotlab_bpu_x5_fastcpp.py`:基于现有 `lab_fastcpp` 状态机的 BPU 版本,默认 26000。
|
||||||
|
|
||||||
## 重要限制
|
## 重要限制
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ bash build_board.sh
|
|||||||
默认模型:
|
默认模型:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
deploy_45dim_rl_gym/bpu_quantization/mapper_output_6500_gemm/policy_robotlab_6500_int16_gemm.bin
|
deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin
|
||||||
```
|
```
|
||||||
|
|
||||||
输入输出:
|
输入输出:
|
||||||
@@ -66,12 +66,12 @@ deploy_45dim_rl_gym/bpu_quantization/mapper_output_6500_gemm/policy_robotlab_650
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /root/go1_pro_deploy
|
cd /root/go1_pro_deploy
|
||||||
ls deploy_45dim_rl_gym/bpu_quantization/mapper_output_6500_gemm/policy_robotlab_6500_int16_gemm.bin
|
ls deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin
|
||||||
ls deploy_45dim_rl_gym/bpu_quantization/calibration_data/00000.bin
|
ls deploy_45dim_rl_gym/bpu_quantization/calibration_data/00000.bin
|
||||||
ls deploy_45dim_rl_gym/bpu_deploy_x5/cpp/libbpu_dnn_policy.so
|
ls deploy_45dim_rl_gym/bpu_deploy_x5/cpp/libbpu_dnn_policy.so
|
||||||
```
|
```
|
||||||
|
|
||||||
如果 `mapper_output_6500_gemm/` 没有同步到 git,需要手动把 `.bin` 放到对应路径,
|
如果 `mapper_output_26000_gemm/` 没有同步到 git,需要手动把 `.bin` 放到对应路径,
|
||||||
或用 `--bpu-model` 指定绝对路径。
|
或用 `--bpu-model` 指定绝对路径。
|
||||||
|
|
||||||
## 纯离线 BPU 自检
|
## 纯离线 BPU 自检
|
||||||
@@ -82,38 +82,32 @@ ls deploy_45dim_rl_gym/bpu_deploy_x5/cpp/libbpu_dnn_policy.so
|
|||||||
cd /root/go1_pro_deploy
|
cd /root/go1_pro_deploy
|
||||||
PYTHONPATH=/root/go1_pro_deploy \
|
PYTHONPATH=/root/go1_pro_deploy \
|
||||||
python3 deploy_45dim_rl_gym/bpu_deploy_x5/test_bpu_policy.py \
|
python3 deploy_45dim_rl_gym/bpu_deploy_x5/test_bpu_policy.py \
|
||||||
--check-reference-00000 \
|
--bpu-model deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin \
|
||||||
|
--input-bin deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
|
||||||
--repeat 1000
|
--repeat 1000
|
||||||
```
|
```
|
||||||
|
|
||||||
通过时应看到:
|
|
||||||
|
|
||||||
```text
|
|
||||||
reference_max_abs_diff 0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
当前板端 `root@192.168.150.167` 已验证:
|
当前板端 `root@192.168.150.167` 已验证:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
reference_max_abs_diff 4.76837158203125e-07
|
repeat=1000 avg_ms=... # Python ctypes -> C++ DNN API
|
||||||
reference_mean_abs_diff 3.071812386679085e-07
|
|
||||||
repeat=1000 avg_ms=1.019592 # Python ctypes -> C++ DNN API
|
|
||||||
duplicate_errors=0
|
|
||||||
```
|
```
|
||||||
|
|
||||||
纯 C++ benchmark:
|
纯 C++ benchmark:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_x5/cpp
|
cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_x5/cpp
|
||||||
./bpu_dnn_bench
|
./bpu_dnn_bench \
|
||||||
|
/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin \
|
||||||
|
/root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
|
||||||
|
1000
|
||||||
```
|
```
|
||||||
|
|
||||||
当前板端结果:
|
当前板端结果:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
reference_max_abs_diff 0
|
input_floats=450 output_floats=12
|
||||||
repeat=1000 cpp_avg_ms=0.711932
|
repeat=1000 cpp_avg_ms=...
|
||||||
duplicate_errors=0
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 离线推理检查
|
## 离线推理检查
|
||||||
@@ -132,6 +126,18 @@ python3 deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py
|
|||||||
|
|
||||||
这一步仍会连接 MCU 读取状态,但不会发送电机指令。
|
这一步仍会连接 MCU 读取状态,但不会发送电机指令。
|
||||||
|
|
||||||
|
如果要切回旧轮次:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PYTHONPATH=/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py \
|
||||||
|
--bpu-round 6500
|
||||||
|
|
||||||
|
PYTHONPATH=/root/go1_pro_deploy \
|
||||||
|
python3 deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py \
|
||||||
|
--bpu-round 15000
|
||||||
|
```
|
||||||
|
|
||||||
## 悬空状态机测试
|
## 悬空状态机测试
|
||||||
|
|
||||||
只有 `--infer-check` 日志确认 action 正常后,再悬空测试状态机。先不加
|
只有 `--infer-check` 日志确认 action 正常后,再悬空测试状态机。先不加
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Deploy the RoboGauge Go1 45-dim RobotLab BPU policy on Unitree Go1 PRO.
|
|||||||
This uses go1_pro_sdk direct MCU control, not LCM or the official Unitree SDK.
|
This uses go1_pro_sdk direct MCU control, not LCM or the official Unitree SDK.
|
||||||
|
|
||||||
Policy:
|
Policy:
|
||||||
- policy_robotlab_6500_int16_gemm.bin
|
- policy_robotlab_26000_int16_gemm.bin by default
|
||||||
- single-frame obs: 45 dims
|
- single-frame obs: 45 dims
|
||||||
- BPU input: 10-frame history, 1x1x1x450 featuremap, stacked by observation terms
|
- BPU input: 10-frame history, 1x1x1x450 featuremap, stacked by observation terms
|
||||||
- command scale: [1.0, 1.0, 1.0]
|
- command scale: [1.0, 1.0, 1.0]
|
||||||
@@ -32,6 +32,7 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -43,6 +44,31 @@ from pathlib import Path
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
DEPLOY_ROOT = HERE.parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_paths():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
fast_root = sdk_root / "fast_lowcmd_cpp"
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
if fast_root.exists():
|
||||||
|
sys.path.insert(0, str(fast_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_paths()
|
||||||
|
SDK_FAST_LOW_CMD = SDK_ROOT / "fast_lowcmd_cpp" if SDK_ROOT is not None else None
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
@@ -50,12 +76,6 @@ from go1_pro_sdk import (
|
|||||||
from bpu_policy import BpuInferLibPolicy
|
from bpu_policy import BpuInferLibPolicy
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
DEPLOY_ROOT = HERE.parents[1]
|
|
||||||
WORKSPACE_ROOT = HERE.parents[2]
|
|
||||||
SDK_FAST_LOW_CMD = WORKSPACE_ROOT / "go1_pro_sdk" / "fast_lowcmd_cpp"
|
|
||||||
if SDK_FAST_LOW_CMD.exists():
|
|
||||||
sys.path.insert(0, str(SDK_FAST_LOW_CMD))
|
|
||||||
try:
|
try:
|
||||||
from fast_lowcmd import FastLowCmdBuilder
|
from fast_lowcmd import FastLowCmdBuilder
|
||||||
from fast_mcu import FastMCUClient
|
from fast_mcu import FastMCUClient
|
||||||
@@ -66,11 +86,17 @@ except ImportError as exc:
|
|||||||
"PYTHONPATH=/root/go1_pro_sdk python3 setup.py build_ext --inplace"
|
"PYTHONPATH=/root/go1_pro_sdk python3 setup.py build_ext --inplace"
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
DEFAULT_BPU_MODEL = (
|
BPU_MODEL_REGISTRY = {
|
||||||
HERE.parent / "bpu_quantization" / "mapper_output_6500_gemm" /
|
"6500": HERE.parent / "bpu_quantization" / "mapper_output_6500_gemm" /
|
||||||
"policy_robotlab_6500_int16_gemm.bin"
|
"policy_robotlab_6500_int16_gemm.bin",
|
||||||
)
|
"15000": HERE.parent / "bpu_quantization" / "mapper_output_gemm" /
|
||||||
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate"
|
"policy_robotlab_15000_int16_gemm.bin",
|
||||||
|
"26000": HERE.parent / "bpu_quantization" / "mapper_output_26000_gemm" /
|
||||||
|
"policy_robotlab_26000_int16_gemm.bin",
|
||||||
|
}
|
||||||
|
DEFAULT_BPU_ROUND = "26000"
|
||||||
|
DEFAULT_BPU_MODEL = BPU_MODEL_REGISTRY[DEFAULT_BPU_ROUND]
|
||||||
|
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_cpp_udp"
|
||||||
SPORT_KILL_CMD = (
|
SPORT_KILL_CMD = (
|
||||||
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
||||||
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
||||||
@@ -217,6 +243,38 @@ def validate_joint_order():
|
|||||||
print(f" [{i:02d}] {sdk_name:4s} -> {policy_name:8s} default={q0:+.3f}")
|
print(f" [{i:02d}] {sdk_name:4s} -> {policy_name:8s} default={q0:+.3f}")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_bpu_model(args):
|
||||||
|
if args.bpu_model:
|
||||||
|
return Path(args.bpu_model).expanduser().resolve()
|
||||||
|
key = str(args.bpu_round)
|
||||||
|
if key not in BPU_MODEL_REGISTRY:
|
||||||
|
choices = ", ".join(sorted(BPU_MODEL_REGISTRY))
|
||||||
|
raise ValueError(f"Unknown --bpu-round {args.bpu_round!r}; choices: {choices}")
|
||||||
|
return BPU_MODEL_REGISTRY[key].expanduser().resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def make_policy(args):
|
||||||
|
model_path = resolve_bpu_model(args)
|
||||||
|
policy = BpuInferLibPolicy(
|
||||||
|
model_path,
|
||||||
|
priority=args.bpu_priority,
|
||||||
|
bpu_cores=args.bpu_cores,
|
||||||
|
)
|
||||||
|
if policy.input_size != POLICY_INPUT_DIM:
|
||||||
|
policy.close()
|
||||||
|
raise ValueError(
|
||||||
|
f"BPU model input has {policy.input_size} values, expected {POLICY_INPUT_DIM}. "
|
||||||
|
"Use a RobotLab 10-frame/450-dim .bin."
|
||||||
|
)
|
||||||
|
if policy.output_size != NUM_ACTIONS:
|
||||||
|
policy.close()
|
||||||
|
raise ValueError(
|
||||||
|
f"BPU model output has {policy.output_size} values, expected exactly {NUM_ACTIONS}. "
|
||||||
|
"Compile an actions-only ONNX for deployment."
|
||||||
|
)
|
||||||
|
return policy
|
||||||
|
|
||||||
|
|
||||||
def apply_deadzone(value, deadzone):
|
def apply_deadzone(value, deadzone):
|
||||||
if deadzone <= 0.0:
|
if deadzone <= 0.0:
|
||||||
return float(value)
|
return float(value)
|
||||||
@@ -346,7 +404,8 @@ class JsonlLogger:
|
|||||||
"num_obs": NUM_OBS,
|
"num_obs": NUM_OBS,
|
||||||
"history_len": HISTORY_LEN,
|
"history_len": HISTORY_LEN,
|
||||||
"policy_backend": BpuInferLibPolicy.backend_name,
|
"policy_backend": BpuInferLibPolicy.backend_name,
|
||||||
"bpu_model": str(Path(args.bpu_model).expanduser()),
|
"bpu_model": str(resolve_bpu_model(args)),
|
||||||
|
"bpu_round": args.bpu_round,
|
||||||
"policy_input_dim": POLICY_INPUT_DIM,
|
"policy_input_dim": POLICY_INPUT_DIM,
|
||||||
"bpu_input_shape": BPU_INPUT_SHAPE,
|
"bpu_input_shape": BPU_INPUT_SHAPE,
|
||||||
"bpu_output_shape": BPU_OUTPUT_SHAPE,
|
"bpu_output_shape": BPU_OUTPUT_SHAPE,
|
||||||
@@ -750,7 +809,7 @@ def run_infer_check(args):
|
|||||||
logger = JsonlLogger(args.log_dir, args)
|
logger = JsonlLogger(args.log_dir, args)
|
||||||
client = None
|
client = None
|
||||||
try:
|
try:
|
||||||
policy = BpuInferLibPolicy(args.bpu_model, priority=args.bpu_priority, bpu_cores=args.bpu_cores)
|
policy = make_policy(args)
|
||||||
client, state = connect_client(args)
|
client, state = connect_client(args)
|
||||||
obs_builder = ObsHistoryBuilder()
|
obs_builder = ObsHistoryBuilder()
|
||||||
cmd_filter = CommandFilter(args)
|
cmd_filter = CommandFilter(args)
|
||||||
@@ -845,7 +904,7 @@ def run_deploy(args):
|
|||||||
print(STARTUP_BANNER)
|
print(STARTUP_BANNER)
|
||||||
input("Press Enter when ready...")
|
input("Press Enter when ready...")
|
||||||
|
|
||||||
policy = BpuInferLibPolicy(args.bpu_model, priority=args.bpu_priority, bpu_cores=args.bpu_cores)
|
policy = make_policy(args)
|
||||||
logger = JsonlLogger(args.log_dir, args)
|
logger = JsonlLogger(args.log_dir, args)
|
||||||
client = None
|
client = None
|
||||||
state = None
|
state = None
|
||||||
@@ -1162,8 +1221,11 @@ def run_deploy(args):
|
|||||||
|
|
||||||
def build_arg_parser():
|
def build_arg_parser():
|
||||||
parser = argparse.ArgumentParser(description="Deploy RoboGauge Go1 RobotLab BPU on Go1 PRO")
|
parser = argparse.ArgumentParser(description="Deploy RoboGauge Go1 RobotLab BPU on Go1 PRO")
|
||||||
parser.add_argument("--bpu-model", default=str(DEFAULT_BPU_MODEL),
|
parser.add_argument("--bpu-model", default="",
|
||||||
help="Path to policy_robotlab_6500_int16_gemm.bin")
|
help="Path to a compiled RobotLab 10-frame BPU .bin; overrides --bpu-round")
|
||||||
|
parser.add_argument("--bpu-round", default=DEFAULT_BPU_ROUND,
|
||||||
|
choices=sorted(BPU_MODEL_REGISTRY),
|
||||||
|
help="Quick-select RobotLab BPU model round")
|
||||||
parser.add_argument("--bpu-priority", type=int, default=0)
|
parser.add_argument("--bpu-priority", type=int, default=0)
|
||||||
parser.add_argument("--bpu-cores", type=int, nargs="+", default=[0],
|
parser.add_argument("--bpu-cores", type=int, nargs="+", default=[0],
|
||||||
help="Reserved BPU core ids for future runtime scheduling")
|
help="Reserved BPU core ids for future runtime scheduling")
|
||||||
|
|||||||
@@ -55,6 +55,30 @@ cd /Users/chenyouyuan/cyy_ws/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization
|
|||||||
./quantize_policy_x5.sh --samples 128 --compare-limit 128
|
./quantize_policy_x5.sh --samples 128 --compare-limit 128
|
||||||
```
|
```
|
||||||
|
|
||||||
|
速度优先 int8 对照:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./quantize_policy_x5.sh --quant int8
|
||||||
|
```
|
||||||
|
|
||||||
|
本次 `policy_35k` int8 结果不建议直接实机使用:
|
||||||
|
|
||||||
|
- 产物:`mapper_output_35k_int8_gemm/policy_35k_int8_gemm.bin`
|
||||||
|
- `actions` cosine:`0.981343`
|
||||||
|
- L1/L2/Chebyshev:`0.053474 / 0.018129 / 0.113345`
|
||||||
|
- mapper 编译估计延迟:`314.2 us + 49.7 us`
|
||||||
|
- 板端 `hrt_model_exec perf` 实测:`1.583870 ms`
|
||||||
|
- 板端 C++ bench:`1.56478 ms`
|
||||||
|
|
||||||
|
对比当前 int16:
|
||||||
|
|
||||||
|
- `actions` cosine:`0.998524`
|
||||||
|
- 板端 `hrt_model_exec perf` 实测:`1.401526 ms`
|
||||||
|
- 板端 C++ bench:`1.37792 ms`
|
||||||
|
|
||||||
|
结论:这个模型上默认 int8 既更慢,输出误差也更大。当前继续用
|
||||||
|
`policy_35k_int16_gemm.bin` 更合理。
|
||||||
|
|
||||||
Gym BPU 部署入口支持快速切换轮次:
|
Gym BPU 部署入口支持快速切换轮次:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -105,25 +129,31 @@ cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_x5/cpp
|
|||||||
1000
|
1000
|
||||||
```
|
```
|
||||||
|
|
||||||
已经完成 `policy_robotlab_15000.onnx` 和 `policy_robotlab_6500.onnx` 的 int16
|
已经完成 `policy_robotlab_15000.onnx`、`policy_robotlab_6500.onnx` 和
|
||||||
量化。RobotLab BPU 部署默认仍使用 6500 版本:
|
`policy_robotlab_26000.onnx` 的 int16 量化。RobotLab BPU 部署当前默认使用
|
||||||
|
26000 版本:
|
||||||
|
|
||||||
- 原始模型:`../policy_robotlab_6500.onnx`
|
- 原始模型:`../policy_robotlab_26000.onnx`
|
||||||
- 原始输入:`obs [1, 450]`
|
- 原始输入:`obs [1, 450]`
|
||||||
- BPU 编译输入:`obs_4d [1, 1, 1, 450]`
|
- BPU 编译输入:`obs_4d [1, 1, 1, 450]`
|
||||||
- BPU 输出:`actions [1, 12, 1, 1]`
|
- BPU 输出:`actions [1, 12, 1, 1]`
|
||||||
- Docker 镜像:`openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8`
|
- Docker 镜像:`openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8`
|
||||||
- `hb_mapper`:`1.24.3`
|
- `hb_mapper`:`1.24.3`
|
||||||
- `hbdk`:`3.49.15`
|
- `hbdk`:`3.49.15`
|
||||||
- 当前产物:`mapper_output_6500_gemm/policy_robotlab_6500_int16_gemm.bin`
|
- 当前产物:`mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin`
|
||||||
|
|
||||||
`mapper_output*/`、`.hb_check/`、校准数据、中间 ONNX、`hb_mapper` 日志都被
|
`mapper_output*/`、`.hb_check/`、校准数据、中间 ONNX、`hb_mapper` 日志都被
|
||||||
`.gitignore` 忽略;需要时按下面步骤重新生成。仓库里只保留脚本和 YAML 配置。
|
`.gitignore` 忽略;需要时按下面步骤重新生成。仓库里只保留脚本和 YAML 配置。
|
||||||
|
|
||||||
6500 量化使用 `calibration_data_fast64/` 的 64 个真实样本。原因是
|
26000 量化使用 `calibration_data_26000_robotlab_fast64/` 的 64 个真实样本。原因是
|
||||||
`hb_mapper` 会先尝试 calibration batch 8,但当前 4D featuremap 包装会被工具链
|
`hb_mapper` 会先尝试 calibration batch 8,但当前 4D featuremap 包装会被工具链
|
||||||
内部改成固定 batch 的 reshape,batch 8 失败后会退回 batch 1;用 64 样本可以把
|
内部改成固定 batch 的 reshape,batch 8 失败后会退回 batch 1;用 64 样本可以把
|
||||||
校准时间从 512 次 batch1 显著降下来。
|
校准时间压到可接受范围。
|
||||||
|
|
||||||
|
如果你要回退到旧版:
|
||||||
|
|
||||||
|
- `mapper_output_6500_gemm/policy_robotlab_6500_int16_gemm.bin`
|
||||||
|
- `mapper_output_gemm/policy_robotlab_15000_int16_gemm.bin`
|
||||||
|
|
||||||
## 1. 生成校准数据
|
## 1. 生成校准数据
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
|||||||
|
model_parameters:
|
||||||
|
onnx_model: "./policy_robotlab_26000_bpu4d_gemm.onnx"
|
||||||
|
march: "bayes-e"
|
||||||
|
layer_out_dump: false
|
||||||
|
working_dir: "mapper_output_26000_gemm"
|
||||||
|
output_model_file_prefix: "policy_robotlab_26000_int16_gemm"
|
||||||
|
|
||||||
|
input_parameters:
|
||||||
|
input_name: "obs_4d"
|
||||||
|
input_shape: "1x1x1x450"
|
||||||
|
input_type_rt: "featuremap"
|
||||||
|
input_layout_rt: "NCHW"
|
||||||
|
input_type_train: "featuremap"
|
||||||
|
input_layout_train: "NCHW"
|
||||||
|
norm_type: "no_preprocess"
|
||||||
|
|
||||||
|
calibration_parameters:
|
||||||
|
cal_data_dir: "./calibration_data_26000_robotlab_fast64"
|
||||||
|
cal_data_type: "float32"
|
||||||
|
calibration_type: "default"
|
||||||
|
optimization: "set_all_nodes_int16"
|
||||||
|
per_channel: true
|
||||||
|
|
||||||
|
compiler_parameters:
|
||||||
|
compile_mode: "latency"
|
||||||
|
debug: false
|
||||||
|
optimize_level: "O3"
|
||||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
|||||||
|
model_parameters:
|
||||||
|
onnx_model: "./policy_robotlab_26000_bpu4d_gemm.onnx"
|
||||||
|
march: "nash-e"
|
||||||
|
layer_out_dump: false
|
||||||
|
working_dir: "mapper_output_26000_s100_gemm"
|
||||||
|
output_model_file_prefix: "policy_robotlab_26000_s100_int16_gemm"
|
||||||
|
|
||||||
|
input_parameters:
|
||||||
|
input_name: "obs_4d"
|
||||||
|
input_shape: "1x1x1x450"
|
||||||
|
input_type_rt: "featuremap"
|
||||||
|
input_type_train: "featuremap"
|
||||||
|
input_layout_train: "NCHW"
|
||||||
|
norm_type: "no_preprocess"
|
||||||
|
separate_batch: false
|
||||||
|
|
||||||
|
calibration_parameters:
|
||||||
|
cal_data_dir: "./calibration_data_26000_robotlab_fast64"
|
||||||
|
cal_data_type: "float32"
|
||||||
|
calibration_type: "max"
|
||||||
|
quant_config:
|
||||||
|
model_config:
|
||||||
|
all_node_type: int16
|
||||||
|
activation:
|
||||||
|
calibration_type: max
|
||||||
|
per_channel: true
|
||||||
|
|
||||||
|
compiler_parameters:
|
||||||
|
compile_mode: "latency"
|
||||||
|
optimize_level: "O2"
|
||||||
|
core_num: 1
|
||||||
|
jobs: 8
|
||||||
|
cache_mode: "disable"
|
||||||
229
deploy_45dim_rl_gym/bpu_quantization/quantize_policy_s100.sh
Executable file
229
deploy_45dim_rl_gym/bpu_quantization/quantize_policy_s100.sh
Executable file
@@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||||
|
|
||||||
|
POLICY="../policy_robotlab_26000.onnx"
|
||||||
|
ROUND="26000"
|
||||||
|
NAME=""
|
||||||
|
HISTORY_LEN=10
|
||||||
|
FLAT_DIM=""
|
||||||
|
SAMPLES=64
|
||||||
|
MIN_SAMPLES=32
|
||||||
|
LOG_PREFIX="robotlab_go1_deploy"
|
||||||
|
CAL_TAG="robotlab"
|
||||||
|
DOCKER_IMAGE="registry.d-robotics.cc/deliver/ai_toolchain_ubuntu_22_s100_s600_cpu:v3.7.0"
|
||||||
|
MARCH="nash-e"
|
||||||
|
COMPARE_LIMIT=64
|
||||||
|
RUN_CHECKER=1
|
||||||
|
QUANT="int16"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
./quantize_policy_s100.sh [options]
|
||||||
|
|
||||||
|
Default: quantize RobotLab policy_robotlab_26000.onnx as 10-frame/450-dim
|
||||||
|
S100 int16 Gemm BPU model.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--policy PATH ONNX policy path, relative to this directory or absolute
|
||||||
|
--round NAME round label used in output paths, e.g. 15k/25k/30k/35k
|
||||||
|
--name NAME model basename; default is policy filename without .onnx
|
||||||
|
--history-len N observation history length; Gym=5, RobotLab=10
|
||||||
|
--flat-dim N flat input dim; default 45 * history-len
|
||||||
|
--samples N calibration sample count; default 64 for faster mapping
|
||||||
|
--min-samples N minimum valid samples required; default 32
|
||||||
|
--log-prefix PREFIX log dir prefix below logs/, default rlgym_go1_deploy
|
||||||
|
--cal-tag TAG calibration dir tag, default gym
|
||||||
|
--docker-image IMAGE D-Robotics S100/S600 CPU toolchain image
|
||||||
|
--march MARCH S100 march, default nash-e
|
||||||
|
--compare-limit N float ONNX equivalence sample count, default 64
|
||||||
|
--quant int16|int8 int16 uses all_node_type int16; int8 uses default S100 PTQ
|
||||||
|
--skip-checker accepted for parity with X5 script; hb_compile path ignores it
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--policy) POLICY="$2"; shift 2 ;;
|
||||||
|
--round) ROUND="$2"; shift 2 ;;
|
||||||
|
--name) NAME="$2"; shift 2 ;;
|
||||||
|
--history-len) HISTORY_LEN="$2"; shift 2 ;;
|
||||||
|
--flat-dim) FLAT_DIM="$2"; shift 2 ;;
|
||||||
|
--samples) SAMPLES="$2"; shift 2 ;;
|
||||||
|
--min-samples) MIN_SAMPLES="$2"; shift 2 ;;
|
||||||
|
--log-prefix) LOG_PREFIX="$2"; shift 2 ;;
|
||||||
|
--cal-tag) CAL_TAG="$2"; shift 2 ;;
|
||||||
|
--docker-image) DOCKER_IMAGE="$2"; shift 2 ;;
|
||||||
|
--march) MARCH="$2"; shift 2 ;;
|
||||||
|
--compare-limit) COMPARE_LIMIT="$2"; shift 2 ;;
|
||||||
|
--quant) QUANT="$2"; shift 2 ;;
|
||||||
|
--skip-checker) RUN_CHECKER=0; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "${FLAT_DIM}" ]]; then
|
||||||
|
FLAT_DIM=$((45 * HISTORY_LEN))
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${QUANT}" in
|
||||||
|
int16|int8) ;;
|
||||||
|
*) echo "--quant must be int16 or int8, got: ${QUANT}" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "${POLICY}" = /* ]]; then
|
||||||
|
POLICY_ABS="${POLICY}"
|
||||||
|
else
|
||||||
|
POLICY_ABS="${SCRIPT_DIR}/${POLICY}"
|
||||||
|
fi
|
||||||
|
POLICY_ABS="$(cd "$(dirname "${POLICY_ABS}")" && pwd)/$(basename "${POLICY_ABS}")"
|
||||||
|
|
||||||
|
if [[ ! -f "${POLICY_ABS}" ]]; then
|
||||||
|
echo "Policy not found: ${POLICY_ABS}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
case "${POLICY_ABS}" in
|
||||||
|
"${REPO_ROOT}"/*) POLICY_REL="${POLICY_ABS#${REPO_ROOT}/}" ;;
|
||||||
|
*) echo "Policy must be inside repo root ${REPO_ROOT}: ${POLICY_ABS}" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ -z "${NAME}" ]]; then
|
||||||
|
NAME="$(basename "${POLICY_ABS}" .onnx)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CAL_DIR="calibration_data_${ROUND}_${CAL_TAG}_fast${SAMPLES}"
|
||||||
|
if [[ "${QUANT}" = "int16" ]]; then
|
||||||
|
OUTPUT_DIR="mapper_output_${ROUND}_s100_gemm"
|
||||||
|
OUTPUT_PREFIX="${NAME}_s100_int16_gemm"
|
||||||
|
else
|
||||||
|
OUTPUT_DIR="mapper_output_${ROUND}_s100_int8_gemm"
|
||||||
|
OUTPUT_PREFIX="${NAME}_s100_int8_gemm"
|
||||||
|
fi
|
||||||
|
YAML_FILE="${OUTPUT_PREFIX}.yaml"
|
||||||
|
|
||||||
|
echo "[INFO] repo : ${REPO_ROOT}"
|
||||||
|
echo "[INFO] policy : ${POLICY_REL}"
|
||||||
|
echo "[INFO] name/round : ${NAME} / ${ROUND}"
|
||||||
|
echo "[INFO] history/shape : ${HISTORY_LEN} / 1x1x1x${FLAT_DIM}"
|
||||||
|
echo "[INFO] calibration : ${CAL_DIR} (${SAMPLES} samples, prefix ${LOG_PREFIX})"
|
||||||
|
echo "[INFO] output : ${OUTPUT_DIR}/${OUTPUT_PREFIX}.hbm"
|
||||||
|
echo "[INFO] quant : ${QUANT}"
|
||||||
|
echo "[INFO] march : ${MARCH}"
|
||||||
|
echo "[INFO] docker image : ${DOCKER_IMAGE}"
|
||||||
|
|
||||||
|
docker run --rm --platform linux/amd64 \
|
||||||
|
-e POLICY_REL="${POLICY_REL}" \
|
||||||
|
-e NAME="${NAME}" \
|
||||||
|
-e HISTORY_LEN="${HISTORY_LEN}" \
|
||||||
|
-e FLAT_DIM="${FLAT_DIM}" \
|
||||||
|
-e SAMPLES="${SAMPLES}" \
|
||||||
|
-e MIN_SAMPLES="${MIN_SAMPLES}" \
|
||||||
|
-e LOG_PREFIX="${LOG_PREFIX}" \
|
||||||
|
-e CAL_DIR="${CAL_DIR}" \
|
||||||
|
-e OUTPUT_DIR="${OUTPUT_DIR}" \
|
||||||
|
-e OUTPUT_PREFIX="${OUTPUT_PREFIX}" \
|
||||||
|
-e YAML_FILE="${YAML_FILE}" \
|
||||||
|
-e COMPARE_LIMIT="${COMPARE_LIMIT}" \
|
||||||
|
-e RUN_CHECKER="${RUN_CHECKER}" \
|
||||||
|
-e QUANT="${QUANT}" \
|
||||||
|
-e MARCH="${MARCH}" \
|
||||||
|
-v "${REPO_ROOT}:/workspace/deploy_go1_pro" \
|
||||||
|
"${DOCKER_IMAGE}" \
|
||||||
|
bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
cd /workspace/deploy_go1_pro/deploy_45dim_rl_gym/bpu_quantization
|
||||||
|
|
||||||
|
POLICY="/workspace/deploy_go1_pro/${POLICY_REL}"
|
||||||
|
ACTIONS_ONNX="${NAME}_actions.onnx"
|
||||||
|
OPSET_ONNX="${NAME}_opset11.onnx"
|
||||||
|
BPU4D_ONNX="${NAME}_bpu4d.onnx"
|
||||||
|
GEMM_ONNX="${NAME}_bpu4d_gemm.onnx"
|
||||||
|
|
||||||
|
python3 make_calibration_data.py \
|
||||||
|
--logs-root ../../logs \
|
||||||
|
--log-prefix "${LOG_PREFIX}" \
|
||||||
|
--history-len "${HISTORY_LEN}" \
|
||||||
|
--output-dir "${CAL_DIR}" \
|
||||||
|
--max-samples "${SAMPLES}" \
|
||||||
|
--min-samples "${MIN_SAMPLES}" \
|
||||||
|
--overwrite
|
||||||
|
|
||||||
|
python3 keep_actions_output.py \
|
||||||
|
--input "${POLICY}" \
|
||||||
|
--output "${ACTIONS_ONNX}"
|
||||||
|
|
||||||
|
python3 downgrade_policy_to_opset11.py \
|
||||||
|
--input "${ACTIONS_ONNX}" \
|
||||||
|
--output "${OPSET_ONNX}"
|
||||||
|
|
||||||
|
python3 make_bpu_4d_onnx.py \
|
||||||
|
--input "${OPSET_ONNX}" \
|
||||||
|
--output "${BPU4D_ONNX}" \
|
||||||
|
--flat-dim "${FLAT_DIM}"
|
||||||
|
|
||||||
|
python3 replace_group_conv_with_gemm.py \
|
||||||
|
--input "${BPU4D_ONNX}" \
|
||||||
|
--output "${GEMM_ONNX}"
|
||||||
|
|
||||||
|
python3 compare_4d_onnx.py \
|
||||||
|
--flat-onnx "${ACTIONS_ONNX}" \
|
||||||
|
--bpu4d-onnx "${GEMM_ONNX}" \
|
||||||
|
--calibration-dir "${CAL_DIR}" \
|
||||||
|
--flat-dim "${FLAT_DIM}" \
|
||||||
|
--limit "${COMPARE_LIMIT}"
|
||||||
|
|
||||||
|
if [[ "${QUANT}" = "int16" ]]; then
|
||||||
|
QUANT_CONFIG=$(cat <<EOF
|
||||||
|
quant_config:
|
||||||
|
model_config:
|
||||||
|
all_node_type: int16
|
||||||
|
activation:
|
||||||
|
calibration_type: max
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
else
|
||||||
|
QUANT_CONFIG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "${YAML_FILE}" <<YAML
|
||||||
|
model_parameters:
|
||||||
|
onnx_model: "./${GEMM_ONNX}"
|
||||||
|
march: "${MARCH}"
|
||||||
|
layer_out_dump: false
|
||||||
|
working_dir: "${OUTPUT_DIR}"
|
||||||
|
output_model_file_prefix: "${OUTPUT_PREFIX}"
|
||||||
|
|
||||||
|
input_parameters:
|
||||||
|
input_name: "obs_4d"
|
||||||
|
input_shape: "1x1x1x${FLAT_DIM}"
|
||||||
|
input_type_rt: "featuremap"
|
||||||
|
input_type_train: "featuremap"
|
||||||
|
input_layout_train: "NCHW"
|
||||||
|
norm_type: "no_preprocess"
|
||||||
|
separate_batch: false
|
||||||
|
|
||||||
|
calibration_parameters:
|
||||||
|
cal_data_dir: "./${CAL_DIR}"
|
||||||
|
cal_data_type: "float32"
|
||||||
|
calibration_type: "max"
|
||||||
|
${QUANT_CONFIG}
|
||||||
|
per_channel: true
|
||||||
|
|
||||||
|
compiler_parameters:
|
||||||
|
compile_mode: "latency"
|
||||||
|
optimize_level: "O2"
|
||||||
|
core_num: 1
|
||||||
|
jobs: 8
|
||||||
|
cache_mode: "disable"
|
||||||
|
YAML
|
||||||
|
|
||||||
|
hb_compile -c "${YAML_FILE}"
|
||||||
|
|
||||||
|
find "${OUTPUT_DIR}" -maxdepth 1 -type f \( -name "${OUTPUT_PREFIX}.hbm" -o -name "${OUTPUT_PREFIX}.bin" \) -print -exec ls -lh {} \;
|
||||||
|
'
|
||||||
|
|
||||||
|
echo "[INFO] Done: deploy_45dim_rl_gym/bpu_quantization/${OUTPUT_DIR}/${OUTPUT_PREFIX}.hbm"
|
||||||
@@ -12,9 +12,11 @@ FLAT_DIM=""
|
|||||||
SAMPLES=64
|
SAMPLES=64
|
||||||
MIN_SAMPLES=32
|
MIN_SAMPLES=32
|
||||||
LOG_PREFIX="rlgym_go1_deploy"
|
LOG_PREFIX="rlgym_go1_deploy"
|
||||||
|
CAL_TAG="gym"
|
||||||
DOCKER_IMAGE="openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8"
|
DOCKER_IMAGE="openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8"
|
||||||
COMPARE_LIMIT=64
|
COMPARE_LIMIT=64
|
||||||
RUN_CHECKER=1
|
RUN_CHECKER=1
|
||||||
|
QUANT="int16"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
@@ -32,8 +34,10 @@ Options:
|
|||||||
--samples N calibration sample count; default 64 for faster mapping
|
--samples N calibration sample count; default 64 for faster mapping
|
||||||
--min-samples N minimum valid samples required; default 32
|
--min-samples N minimum valid samples required; default 32
|
||||||
--log-prefix PREFIX log dir prefix below logs/, default rlgym_go1_deploy
|
--log-prefix PREFIX log dir prefix below logs/, default rlgym_go1_deploy
|
||||||
|
--cal-tag TAG calibration dir tag, default gym
|
||||||
--docker-image IMAGE D-Robotics CPU toolchain image
|
--docker-image IMAGE D-Robotics CPU toolchain image
|
||||||
--compare-limit N float ONNX equivalence sample count, default 64
|
--compare-limit N float ONNX equivalence sample count, default 64
|
||||||
|
--quant int16|int8 int16 keeps current conservative path; int8 omits set_all_nodes_int16
|
||||||
--skip-checker skip hb_mapper checker before makertbin
|
--skip-checker skip hb_mapper checker before makertbin
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -48,8 +52,10 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--samples) SAMPLES="$2"; shift 2 ;;
|
--samples) SAMPLES="$2"; shift 2 ;;
|
||||||
--min-samples) MIN_SAMPLES="$2"; shift 2 ;;
|
--min-samples) MIN_SAMPLES="$2"; shift 2 ;;
|
||||||
--log-prefix) LOG_PREFIX="$2"; shift 2 ;;
|
--log-prefix) LOG_PREFIX="$2"; shift 2 ;;
|
||||||
|
--cal-tag) CAL_TAG="$2"; shift 2 ;;
|
||||||
--docker-image) DOCKER_IMAGE="$2"; shift 2 ;;
|
--docker-image) DOCKER_IMAGE="$2"; shift 2 ;;
|
||||||
--compare-limit) COMPARE_LIMIT="$2"; shift 2 ;;
|
--compare-limit) COMPARE_LIMIT="$2"; shift 2 ;;
|
||||||
|
--quant) QUANT="$2"; shift 2 ;;
|
||||||
--skip-checker) RUN_CHECKER=0; shift ;;
|
--skip-checker) RUN_CHECKER=0; shift ;;
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
||||||
@@ -60,6 +66,11 @@ if [[ -z "${FLAT_DIM}" ]]; then
|
|||||||
FLAT_DIM=$((45 * HISTORY_LEN))
|
FLAT_DIM=$((45 * HISTORY_LEN))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
case "${QUANT}" in
|
||||||
|
int16|int8) ;;
|
||||||
|
*) echo "--quant must be int16 or int8, got: ${QUANT}" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
if [[ "${POLICY}" = /* ]]; then
|
if [[ "${POLICY}" = /* ]]; then
|
||||||
POLICY_ABS="${POLICY}"
|
POLICY_ABS="${POLICY}"
|
||||||
else
|
else
|
||||||
@@ -80,9 +91,14 @@ if [[ -z "${NAME}" ]]; then
|
|||||||
NAME="$(basename "${POLICY_ABS}" .onnx)"
|
NAME="$(basename "${POLICY_ABS}" .onnx)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CAL_DIR="calibration_data_${ROUND}_gym_fast${SAMPLES}"
|
CAL_DIR="calibration_data_${ROUND}_${CAL_TAG}_fast${SAMPLES}"
|
||||||
OUTPUT_DIR="mapper_output_${ROUND}_gemm"
|
if [[ "${QUANT}" = "int16" ]]; then
|
||||||
OUTPUT_PREFIX="${NAME}_int16_gemm"
|
OUTPUT_DIR="mapper_output_${ROUND}_gemm"
|
||||||
|
OUTPUT_PREFIX="${NAME}_int16_gemm"
|
||||||
|
else
|
||||||
|
OUTPUT_DIR="mapper_output_${ROUND}_int8_gemm"
|
||||||
|
OUTPUT_PREFIX="${NAME}_int8_gemm"
|
||||||
|
fi
|
||||||
YAML_FILE="${OUTPUT_PREFIX}.yaml"
|
YAML_FILE="${OUTPUT_PREFIX}.yaml"
|
||||||
|
|
||||||
echo "[INFO] repo : ${REPO_ROOT}"
|
echo "[INFO] repo : ${REPO_ROOT}"
|
||||||
@@ -91,6 +107,7 @@ echo "[INFO] name/round : ${NAME} / ${ROUND}"
|
|||||||
echo "[INFO] history/shape : ${HISTORY_LEN} / 1x1x1x${FLAT_DIM}"
|
echo "[INFO] history/shape : ${HISTORY_LEN} / 1x1x1x${FLAT_DIM}"
|
||||||
echo "[INFO] calibration : ${CAL_DIR} (${SAMPLES} samples, prefix ${LOG_PREFIX})"
|
echo "[INFO] calibration : ${CAL_DIR} (${SAMPLES} samples, prefix ${LOG_PREFIX})"
|
||||||
echo "[INFO] output : ${OUTPUT_DIR}/${OUTPUT_PREFIX}.bin"
|
echo "[INFO] output : ${OUTPUT_DIR}/${OUTPUT_PREFIX}.bin"
|
||||||
|
echo "[INFO] quant : ${QUANT}"
|
||||||
|
|
||||||
docker run --rm --platform linux/amd64 \
|
docker run --rm --platform linux/amd64 \
|
||||||
-e POLICY_REL="${POLICY_REL}" \
|
-e POLICY_REL="${POLICY_REL}" \
|
||||||
@@ -106,6 +123,7 @@ docker run --rm --platform linux/amd64 \
|
|||||||
-e YAML_FILE="${YAML_FILE}" \
|
-e YAML_FILE="${YAML_FILE}" \
|
||||||
-e COMPARE_LIMIT="${COMPARE_LIMIT}" \
|
-e COMPARE_LIMIT="${COMPARE_LIMIT}" \
|
||||||
-e RUN_CHECKER="${RUN_CHECKER}" \
|
-e RUN_CHECKER="${RUN_CHECKER}" \
|
||||||
|
-e QUANT="${QUANT}" \
|
||||||
-v "${REPO_ROOT}:/workspace/deploy_go1_pro" \
|
-v "${REPO_ROOT}:/workspace/deploy_go1_pro" \
|
||||||
"${DOCKER_IMAGE}" \
|
"${DOCKER_IMAGE}" \
|
||||||
bash -lc '
|
bash -lc '
|
||||||
@@ -151,6 +169,12 @@ docker run --rm --platform linux/amd64 \
|
|||||||
--flat-dim "${FLAT_DIM}" \
|
--flat-dim "${FLAT_DIM}" \
|
||||||
--limit "${COMPARE_LIMIT}"
|
--limit "${COMPARE_LIMIT}"
|
||||||
|
|
||||||
|
if [[ "${QUANT}" = "int16" ]]; then
|
||||||
|
OPTIMIZATION_LINE=" optimization: \"set_all_nodes_int16\""
|
||||||
|
else
|
||||||
|
OPTIMIZATION_LINE=""
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "${YAML_FILE}" <<YAML
|
cat > "${YAML_FILE}" <<YAML
|
||||||
model_parameters:
|
model_parameters:
|
||||||
onnx_model: "./${GEMM_ONNX}"
|
onnx_model: "./${GEMM_ONNX}"
|
||||||
@@ -172,7 +196,7 @@ calibration_parameters:
|
|||||||
cal_data_dir: "./${CAL_DIR}"
|
cal_data_dir: "./${CAL_DIR}"
|
||||||
cal_data_type: "float32"
|
cal_data_type: "float32"
|
||||||
calibration_type: "default"
|
calibration_type: "default"
|
||||||
optimization: "set_all_nodes_int16"
|
${OPTIMIZATION_LINE}
|
||||||
per_channel: true
|
per_channel: true
|
||||||
|
|
||||||
compiler_parameters:
|
compiler_parameters:
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from mujoco import viewer
|
|||||||
|
|
||||||
# ── path setup ──
|
# ── path setup ──
|
||||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
DEFAULT_ONNX = str(SCRIPT_DIR / "policy_robotlab_15000.onnx")
|
DEFAULT_ONNX = str(SCRIPT_DIR / "policy_robotlab_26000.onnx")
|
||||||
ROBOT_XML = str(SCRIPT_DIR / "go1.xml")
|
ROBOT_XML = str(SCRIPT_DIR / "go1.xml")
|
||||||
TERRAINS_DIR = SCRIPT_DIR / "terrains"
|
TERRAINS_DIR = SCRIPT_DIR / "terrains"
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -42,20 +43,39 @@ from pathlib import Path
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
DEPLOY_ROOT = HERE.parent
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_paths():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
fast_root = sdk_root / "fast_lowcmd_cpp"
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
if fast_root.exists():
|
||||||
|
sys.path.insert(0, str(fast_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_paths()
|
||||||
|
SDK_FAST_LOW_CMD = SDK_ROOT / "fast_lowcmd_cpp" if SDK_ROOT is not None else None
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MotorMode, PowerProtectViolation, JOINT_NAMES,
|
MotorMode, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
DEPLOY_ROOT = HERE.parent
|
|
||||||
WORKSPACE_ROOT = HERE.parents[1]
|
|
||||||
sys.path.insert(0, str(HERE / "bpu_deploy_x5"))
|
sys.path.insert(0, str(HERE / "bpu_deploy_x5"))
|
||||||
from bpu_policy import BpuInferLibPolicy
|
from bpu_policy import BpuInferLibPolicy
|
||||||
|
|
||||||
|
|
||||||
SDK_FAST_LOW_CMD = WORKSPACE_ROOT / "go1_pro_sdk" / "fast_lowcmd_cpp"
|
|
||||||
if SDK_FAST_LOW_CMD.exists():
|
|
||||||
sys.path.insert(0, str(SDK_FAST_LOW_CMD))
|
|
||||||
try:
|
try:
|
||||||
from fast_lowcmd import FastLowCmdBuilder
|
from fast_lowcmd import FastLowCmdBuilder
|
||||||
from fast_mcu import FastMCUClient
|
from fast_mcu import FastMCUClient
|
||||||
@@ -74,7 +94,7 @@ BPU_MODEL_REGISTRY = {
|
|||||||
}
|
}
|
||||||
DEFAULT_BPU_ROUND = "35k"
|
DEFAULT_BPU_ROUND = "35k"
|
||||||
DEFAULT_BPU_MODEL = BPU_MODEL_REGISTRY[DEFAULT_BPU_ROUND]
|
DEFAULT_BPU_MODEL = BPU_MODEL_REGISTRY[DEFAULT_BPU_ROUND]
|
||||||
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate"
|
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_cpp_udp"
|
||||||
SPORT_KILL_CMD = (
|
SPORT_KILL_CMD = (
|
||||||
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
||||||
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -42,13 +44,32 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import onnxruntime as ort
|
import onnxruntime as ort
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_root_to_path():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_root_to_path()
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MCUClient, LowCmd, MotorCmd, MotorMode,
|
MCUClient, LowCmd, MotorCmd, MotorMode,
|
||||||
apply_safety, PowerProtectViolation, JOINT_NAMES,
|
apply_safety, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
DEFAULT_ONNX = HERE / "policy_30k.onnx"
|
DEFAULT_ONNX = HERE / "policy_30k.onnx"
|
||||||
SPORT_KILL_CMD = (
|
SPORT_KILL_CMD = (
|
||||||
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -43,15 +44,35 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import onnxruntime as ort
|
import onnxruntime as ort
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_paths():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
fast_root = sdk_root / "fast_lowcmd_cpp"
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
if fast_root.exists():
|
||||||
|
sys.path.insert(0, str(fast_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_paths()
|
||||||
|
SDK_FAST_LOW_CMD = SDK_ROOT / "fast_lowcmd_cpp" if SDK_ROOT is not None else None
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
SDK_FAST_LOW_CMD = HERE.parents[1] / "go1_pro_sdk" / "fast_lowcmd_cpp"
|
|
||||||
if SDK_FAST_LOW_CMD.exists():
|
|
||||||
sys.path.insert(0, str(SDK_FAST_LOW_CMD))
|
|
||||||
try:
|
try:
|
||||||
from fast_lowcmd import FastLowCmdBuilder
|
from fast_lowcmd import FastLowCmdBuilder
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -43,13 +45,32 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import onnxruntime as ort
|
import onnxruntime as ort
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_root_to_path():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_root_to_path()
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MCUClient, LowCmd, MotorCmd, MotorMode,
|
MCUClient, LowCmd, MotorCmd, MotorMode,
|
||||||
apply_safety, PowerProtectViolation, JOINT_NAMES,
|
apply_safety, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
|
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
|
||||||
SPORT_KILL_CMD = (
|
SPORT_KILL_CMD = (
|
||||||
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ low-level checks, override the default with --power-factor 1.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -44,15 +45,35 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import onnxruntime as ort
|
import onnxruntime as ort
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_sdk_paths():
|
||||||
|
candidates = []
|
||||||
|
env_root = os.environ.get("GO1_PRO_SDK_ROOT")
|
||||||
|
if env_root:
|
||||||
|
candidates.append(Path(env_root).expanduser())
|
||||||
|
for parent in (HERE, *HERE.parents):
|
||||||
|
candidates.append(parent / "go1_pro_sdk")
|
||||||
|
|
||||||
|
for sdk_root in candidates:
|
||||||
|
fast_root = sdk_root / "fast_lowcmd_cpp"
|
||||||
|
if (sdk_root / "go1_pro_sdk").exists():
|
||||||
|
sys.path.insert(0, str(sdk_root))
|
||||||
|
if fast_root.exists():
|
||||||
|
sys.path.insert(0, str(fast_root))
|
||||||
|
return sdk_root
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SDK_ROOT = _add_sdk_paths()
|
||||||
|
SDK_FAST_LOW_CMD = SDK_ROOT / "fast_lowcmd_cpp" if SDK_ROOT is not None else None
|
||||||
|
|
||||||
from go1_pro_sdk import (
|
from go1_pro_sdk import (
|
||||||
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
|
||||||
SDK_FAST_LOW_CMD = HERE.parents[1] / "go1_pro_sdk" / "fast_lowcmd_cpp"
|
|
||||||
if SDK_FAST_LOW_CMD.exists():
|
|
||||||
sys.path.insert(0, str(SDK_FAST_LOW_CMD))
|
|
||||||
try:
|
try:
|
||||||
from fast_lowcmd import FastLowCmdBuilder
|
from fast_lowcmd import FastLowCmdBuilder
|
||||||
from fast_mcu import FastMCUClient
|
from fast_mcu import FastMCUClient
|
||||||
@@ -64,7 +85,7 @@ except ImportError as exc:
|
|||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
|
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
|
||||||
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate"
|
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_cpp_udp"
|
||||||
SPORT_KILL_CMD = (
|
SPORT_KILL_CMD = (
|
||||||
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
|
||||||
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'
|
||||||
|
|||||||
BIN
deploy_45dim_rl_gym/policy_robotlab_19500.onnx
Normal file
BIN
deploy_45dim_rl_gym/policy_robotlab_19500.onnx
Normal file
Binary file not shown.
BIN
deploy_45dim_rl_gym/policy_robotlab_26000.onnx
Normal file
BIN
deploy_45dim_rl_gym/policy_robotlab_26000.onnx
Normal file
Binary file not shown.
Reference in New Issue
Block a user