Compare commits

...

2 Commits

Author SHA1 Message Date
cyy_mac
8e2f5d6de5 26k and fix bpu round 2026-07-28 19:20:51 +08:00
cyy_mac
a729020c78 评估int16 2026-07-28 19:09:25 +08:00
13 changed files with 172 additions and 40 deletions

3
.gitignore vendored
View File

@@ -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_*_gym_fast*/
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_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_gemm.onnx
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_robotlab_15000_bpu4d.onnx
deploy_45dim_rl_gym/bpu_quantization/policy_robotlab_15000_bpu4d_gemm.onnx

View File

@@ -7,7 +7,7 @@
- `bpu_policy.py`BPU policy wrapper默认使用 `cpp_dnn_api_x5` C++ DNN API 后端。
- `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
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
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_deploy_x5/cpp/libbpu_dnn_policy.so
```
如果 `mapper_output_6500_gemm/` 没有同步到 git需要手动把 `.bin` 放到对应路径,
如果 `mapper_output_26000_gemm/` 没有同步到 git需要手动把 `.bin` 放到对应路径,
或用 `--bpu-model` 指定绝对路径。
## 纯离线 BPU 自检
@@ -82,38 +82,32 @@ ls deploy_45dim_rl_gym/bpu_deploy_x5/cpp/libbpu_dnn_policy.so
cd /root/go1_pro_deploy
PYTHONPATH=/root/go1_pro_deploy \
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
```
通过时应看到:
```text
reference_max_abs_diff 0.0
```
当前板端 `root@192.168.150.167` 已验证:
```text
reference_max_abs_diff 4.76837158203125e-07
reference_mean_abs_diff 3.071812386679085e-07
repeat=1000 avg_ms=1.019592 # Python ctypes -> C++ DNN API
duplicate_errors=0
repeat=1000 avg_ms=... # Python ctypes -> C++ DNN API
```
纯 C++ benchmark
```bash
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
reference_max_abs_diff 0
repeat=1000 cpp_avg_ms=0.711932
duplicate_errors=0
input_floats=450 output_floats=12
repeat=1000 cpp_avg_ms=...
```
## 离线推理检查
@@ -132,6 +126,18 @@ python3 deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py
这一步仍会连接 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 正常后,再悬空测试状态机。先不加

View File

@@ -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.
Policy:
- policy_robotlab_6500_int16_gemm.bin
- policy_robotlab_26000_int16_gemm.bin by default
- single-frame obs: 45 dims
- BPU input: 10-frame history, 1x1x1x450 featuremap, stacked by observation terms
- command scale: [1.0, 1.0, 1.0]
@@ -66,10 +66,16 @@ except ImportError as exc:
"PYTHONPATH=/root/go1_pro_sdk python3 setup.py build_ext --inplace"
) from exc
DEFAULT_BPU_MODEL = (
HERE.parent / "bpu_quantization" / "mapper_output_6500_gemm" /
"policy_robotlab_6500_int16_gemm.bin"
)
BPU_MODEL_REGISTRY = {
"6500": HERE.parent / "bpu_quantization" / "mapper_output_6500_gemm" /
"policy_robotlab_6500_int16_gemm.bin",
"15000": HERE.parent / "bpu_quantization" / "mapper_output_gemm" /
"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"
SPORT_KILL_CMD = (
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
@@ -217,6 +223,38 @@ def validate_joint_order():
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):
if deadzone <= 0.0:
return float(value)
@@ -346,7 +384,8 @@ class JsonlLogger:
"num_obs": NUM_OBS,
"history_len": HISTORY_LEN,
"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,
"bpu_input_shape": BPU_INPUT_SHAPE,
"bpu_output_shape": BPU_OUTPUT_SHAPE,
@@ -750,7 +789,7 @@ def run_infer_check(args):
logger = JsonlLogger(args.log_dir, args)
client = None
try:
policy = BpuInferLibPolicy(args.bpu_model, priority=args.bpu_priority, bpu_cores=args.bpu_cores)
policy = make_policy(args)
client, state = connect_client(args)
obs_builder = ObsHistoryBuilder()
cmd_filter = CommandFilter(args)
@@ -845,7 +884,7 @@ def run_deploy(args):
print(STARTUP_BANNER)
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)
client = None
state = None
@@ -1162,8 +1201,11 @@ def run_deploy(args):
def build_arg_parser():
parser = argparse.ArgumentParser(description="Deploy RoboGauge Go1 RobotLab BPU on Go1 PRO")
parser.add_argument("--bpu-model", default=str(DEFAULT_BPU_MODEL),
help="Path to policy_robotlab_6500_int16_gemm.bin")
parser.add_argument("--bpu-model", default="",
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-cores", type=int, nargs="+", default=[0],
help="Reserved BPU core ids for future runtime scheduling")

View File

@@ -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
```
速度优先 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 部署入口支持快速切换轮次:
```bash
@@ -105,25 +129,31 @@ cd /root/go1_pro_deploy/deploy_45dim_rl_gym/bpu_deploy_x5/cpp
1000
```
已经完成 `policy_robotlab_15000.onnx``policy_robotlab_6500.onnx` 的 int16
量化。RobotLab BPU 部署默认使用 6500 版本:
已经完成 `policy_robotlab_15000.onnx``policy_robotlab_6500.onnx`
`policy_robotlab_26000.onnx` 的 int16 量化。RobotLab BPU 部署当前默认使用
26000 版本:
- 原始模型:`../policy_robotlab_6500.onnx`
- 原始模型:`../policy_robotlab_26000.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_26000_gemm/policy_robotlab_26000_int16_gemm.bin`
`mapper_output*/``.hb_check/`、校准数据、中间 ONNX、`hb_mapper` 日志都被
`.gitignore` 忽略;需要时按下面步骤重新生成。仓库里只保留脚本和 YAML 配置。
6500 量化使用 `calibration_data_fast64/` 的 64 个真实样本。原因是
26000 量化使用 `calibration_data_26000_robotlab_fast64/` 的 64 个真实样本。原因是
`hb_mapper` 会先尝试 calibration batch 8但当前 4D featuremap 包装会被工具链
内部改成固定 batch 的 reshapebatch 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. 生成校准数据

View File

@@ -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"

View File

@@ -12,9 +12,11 @@ FLAT_DIM=""
SAMPLES=64
MIN_SAMPLES=32
LOG_PREFIX="rlgym_go1_deploy"
CAL_TAG="gym"
DOCKER_IMAGE="openexplorer/ai_toolchain_ubuntu_20_x5_cpu:v1.2.8"
COMPARE_LIMIT=64
RUN_CHECKER=1
QUANT="int16"
usage() {
cat <<'EOF'
@@ -32,8 +34,10 @@ Options:
--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 CPU toolchain image
--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
EOF
}
@@ -48,8 +52,10 @@ while [[ $# -gt 0 ]]; do
--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 ;;
--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 ;;
@@ -60,6 +66,11 @@ 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
@@ -80,9 +91,14 @@ if [[ -z "${NAME}" ]]; then
NAME="$(basename "${POLICY_ABS}" .onnx)"
fi
CAL_DIR="calibration_data_${ROUND}_gym_fast${SAMPLES}"
OUTPUT_DIR="mapper_output_${ROUND}_gemm"
OUTPUT_PREFIX="${NAME}_int16_gemm"
CAL_DIR="calibration_data_${ROUND}_${CAL_TAG}_fast${SAMPLES}"
if [[ "${QUANT}" = "int16" ]]; then
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"
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] calibration : ${CAL_DIR} (${SAMPLES} samples, prefix ${LOG_PREFIX})"
echo "[INFO] output : ${OUTPUT_DIR}/${OUTPUT_PREFIX}.bin"
echo "[INFO] quant : ${QUANT}"
docker run --rm --platform linux/amd64 \
-e POLICY_REL="${POLICY_REL}" \
@@ -106,6 +123,7 @@ docker run --rm --platform linux/amd64 \
-e YAML_FILE="${YAML_FILE}" \
-e COMPARE_LIMIT="${COMPARE_LIMIT}" \
-e RUN_CHECKER="${RUN_CHECKER}" \
-e QUANT="${QUANT}" \
-v "${REPO_ROOT}:/workspace/deploy_go1_pro" \
"${DOCKER_IMAGE}" \
bash -lc '
@@ -151,6 +169,12 @@ docker run --rm --platform linux/amd64 \
--flat-dim "${FLAT_DIM}" \
--limit "${COMPARE_LIMIT}"
if [[ "${QUANT}" = "int16" ]]; then
OPTIMIZATION_LINE=" optimization: \"set_all_nodes_int16\""
else
OPTIMIZATION_LINE=""
fi
cat > "${YAML_FILE}" <<YAML
model_parameters:
onnx_model: "./${GEMM_ONNX}"
@@ -172,7 +196,7 @@ calibration_parameters:
cal_data_dir: "./${CAL_DIR}"
cal_data_type: "float32"
calibration_type: "default"
optimization: "set_all_nodes_int16"
${OPTIMIZATION_LINE}
per_channel: true
compiler_parameters:

View File

@@ -43,7 +43,7 @@ from mujoco import viewer
# ── path setup ──
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")
TERRAINS_DIR = SCRIPT_DIR / "terrains"

Binary file not shown.

Binary file not shown.