Compare commits

...

5 Commits

Author SHA1 Message Date
cyy_mac
22a13d6659 s100 cpp sdk更新 2026-07-30 19:22:54 +08:00
cyy_mac
8c1b2aecae 更新对齐新sdk接口 2026-07-30 18:12:39 +08:00
cyy_mac
8a44857314 adapt s100 2026-07-29 21:00:26 +08:00
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
27 changed files with 2765 additions and 65 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_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

View 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
```

View 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

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

View 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;
}

View 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

View 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()

View File

@@ -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 正常后,再悬空测试状态机。先不加

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. 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")

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 ./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 的 reshapebatch 8 失败后会退回 batch 1用 64 样本可以把 内部改成固定 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. 生成校准数据 ## 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

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

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

View File

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

View File

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

View File

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

View File

@@ -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; '

View File

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

View File

@@ -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; '

View File

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

Binary file not shown.

Binary file not shown.