Compare commits

...

3 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
15 changed files with 2593 additions and 25 deletions

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

@@ -32,6 +32,7 @@ low-level checks, override the default with --power-factor 1.
import argparse
import json
import os
import signal
import subprocess
import sys
@@ -43,6 +44,31 @@ from pathlib import Path
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 (
MCUClient, MotorMode, PowerProtectViolation, JOINT_NAMES,
)
@@ -50,12 +76,6 @@ from go1_pro_sdk import (
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:
from fast_lowcmd import FastLowCmdBuilder
from fast_mcu import FastMCUClient
@@ -76,7 +96,7 @@ BPU_MODEL_REGISTRY = {
}
DEFAULT_BPU_ROUND = "26000"
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 = (
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'

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

@@ -31,6 +31,7 @@ low-level checks, override the default with --power-factor 1.
import argparse
import json
import os
import signal
import subprocess
import sys
@@ -42,20 +43,39 @@ from pathlib import Path
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 (
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"))
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:
from fast_lowcmd import FastLowCmdBuilder
from fast_mcu import FastMCUClient
@@ -74,7 +94,7 @@ BPU_MODEL_REGISTRY = {
}
DEFAULT_BPU_ROUND = "35k"
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 = (
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
'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 json
import os
import signal
import subprocess
import sys
import time
from collections import deque
from datetime import datetime
@@ -42,13 +44,32 @@ from pathlib import Path
import numpy as np
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 (
MCUClient, LowCmd, MotorCmd, MotorMode,
apply_safety, PowerProtectViolation, JOINT_NAMES,
)
HERE = Path(__file__).parent.resolve()
DEFAULT_ONNX = HERE / "policy_30k.onnx"
SPORT_KILL_CMD = (
'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 json
import os
import signal
import subprocess
import sys
@@ -43,15 +44,35 @@ from pathlib import Path
import numpy as np
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 (
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:
from fast_lowcmd import FastLowCmdBuilder
except ImportError as exc:

View File

@@ -32,8 +32,10 @@ low-level checks, override the default with --power-factor 1.
import argparse
import json
import os
import signal
import subprocess
import sys
import time
from collections import deque
from datetime import datetime
@@ -43,13 +45,32 @@ from pathlib import Path
import numpy as np
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 (
MCUClient, LowCmd, MotorCmd, MotorMode,
apply_safety, PowerProtectViolation, JOINT_NAMES,
)
HERE = Path(__file__).parent.resolve()
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
SPORT_KILL_CMD = (
'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 json
import os
import signal
import subprocess
import sys
@@ -44,15 +45,35 @@ from pathlib import Path
import numpy as np
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 (
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:
from fast_lowcmd import FastLowCmdBuilder
from fast_mcu import FastMCUClient
@@ -64,7 +85,7 @@ except ImportError as exc:
) from exc
DEFAULT_ONNX = HERE / "policy_robotlab_6500.onnx"
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate"
LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_cpp_udp"
SPORT_KILL_CMD = (
'ssh pi@192.168.123.161 "sudo pkill -9 -f keep_sport_alive; '
'sudo pkill -9 -f Legged_sport; sudo pkill -9 -f appTransit"'