adapt gym 5history

This commit is contained in:
cyy_mac
2026-07-28 16:36:33 +08:00
parent 6c4dd6ed14
commit 071c576bfe
14 changed files with 1782 additions and 77 deletions

View File

@@ -59,8 +59,6 @@ class BpuInferLibPolicy:
"""Default fast backend: direct C++ DNN API via ctypes."""
backend_name = "cpp_dnn_api_x5"
input_shape = (1, 1, 1, 450)
output_shape = (1, 12, 1, 1)
def __init__(self, model_path, priority=0, bpu_cores=(0,), cpp_lib=DEFAULT_CPP_LIB):
self.model_path = Path(model_path).expanduser().resolve()
@@ -94,6 +92,10 @@ class BpuInferLibPolicy:
self._lib.rlgym_bpu_infer.restype = ctypes.c_int
self._lib.rlgym_bpu_destroy.argtypes = [ctypes.c_void_p]
self._lib.rlgym_bpu_destroy.restype = None
self._lib.rlgym_bpu_input_floats.argtypes = [ctypes.c_void_p]
self._lib.rlgym_bpu_input_floats.restype = ctypes.c_int
self._lib.rlgym_bpu_output_floats.argtypes = [ctypes.c_void_p]
self._lib.rlgym_bpu_output_floats.restype = ctypes.c_int
self._lib.rlgym_bpu_version.argtypes = []
self._lib.rlgym_bpu_version.restype = ctypes.c_char_p
@@ -107,7 +109,13 @@ class BpuInferLibPolicy:
)
if not self._handle:
raise RuntimeError(err.value.decode("utf-8", errors="replace"))
self._output = np.empty(12, dtype=np.float32)
self.input_size = int(self._lib.rlgym_bpu_input_floats(self._handle))
self.output_size = int(self._lib.rlgym_bpu_output_floats(self._handle))
if self.input_size <= 0 or self.output_size <= 0:
raise RuntimeError("invalid BPU tensor sizes reported by runtime")
self.input_shape = (1, 1, 1, self.input_size)
self.output_shape = (1, self.output_size, 1, 1)
self._output = np.empty(self.output_size, dtype=np.float32)
self._output_ptr = self._output.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
self._err = ctypes.create_string_buffer(1024)
@@ -131,8 +139,8 @@ class BpuInferLibPolicy:
def __call__(self, flat_input):
arr = np.asarray(flat_input, dtype=np.float32)
if arr.size != 450:
raise ValueError(f"BPU policy input has {arr.size} values, expected 450")
if arr.size != self.input_size:
raise ValueError(f"BPU policy input has {arr.size} values, expected {self.input_size}")
input_flat = np.ascontiguousarray(arr.reshape(-1), dtype=np.float32)
rc = self._lib.rlgym_bpu_infer(
self._handle,
@@ -172,6 +180,9 @@ class BpuInferLibPythonPolicy:
self.priority = int(priority)
self.bpu_cores = tuple(int(core) for core in bpu_cores)
self.suppress_runtime_output = bool(suppress_runtime_output)
self.input_size = 450
self.input_shape = (1, 1, 1, self.input_size)
self.output_shape = (1, 12, 1, 1)
with suppress_c_output(self.suppress_runtime_output):
self.infer = Infer(False)
loaded = self.infer.load_model(str(self.model_path))
@@ -185,8 +196,8 @@ class BpuInferLibPythonPolicy:
def __call__(self, flat_input):
arr = np.asarray(flat_input, dtype=np.float32)
if arr.size != 450:
raise ValueError(f"BPU policy input has {arr.size} values, expected 450")
if arr.size != self.input_size:
raise ValueError(f"BPU policy input has {arr.size} values, expected {self.input_size}")
input_4d = np.ascontiguousarray(arr.reshape(self.input_shape), dtype=np.float32)
with suppress_c_output(self.suppress_runtime_output):
copied = self.infer.read_numpy_arr_float32(input_4d, 0)

View File

@@ -12,8 +12,10 @@
extern "C" {
void *rlgym_bpu_create(const char *model_path, int bpu_core, int priority,
char *err, int err_len);
int rlgym_bpu_infer(void *handle, const float *input450, float *output12, char *err,
int rlgym_bpu_infer(void *handle, const float *input, float *output, char *err,
int err_len);
int rlgym_bpu_input_floats(void *handle);
int rlgym_bpu_output_floats(void *handle);
void rlgym_bpu_destroy(void *handle);
}
@@ -54,12 +56,6 @@ int main(int argc, char **argv) {
if (argc > 2) input = argv[2];
if (argc > 3) repeat = std::atoi(argv[3]);
std::vector<float> obs;
if (!read_f32_file(input, &obs) || obs.size() != 450) {
std::cerr << "failed to read 450 float32 input: " << input << "\n";
return 2;
}
char err[1024] = {};
void *handle = rlgym_bpu_create(model, 1, 0, err, sizeof(err));
if (handle == nullptr) {
@@ -67,20 +63,37 @@ int main(int argc, char **argv) {
return 3;
}
float out[12] = {};
if (rlgym_bpu_infer(handle, obs.data(), out, err, sizeof(err)) != 0) {
const int input_floats = rlgym_bpu_input_floats(handle);
const int output_floats = rlgym_bpu_output_floats(handle);
if (input_floats <= 0 || output_floats <= 0) {
std::cerr << "invalid tensor sizes from BPU runtime\n";
rlgym_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_bpu_destroy(handle);
return 2;
}
std::vector<float> out(static_cast<size_t>(output_floats), 0.0f);
if (rlgym_bpu_infer(handle, obs.data(), out.data(), err, sizeof(err)) != 0) {
std::cerr << err << "\n";
rlgym_bpu_destroy(handle);
return 4;
}
float max_diff = 0.0f;
for (int i = 0; i < 12; ++i) {
max_diff = std::max(max_diff, std::fabs(out[i] - kReference[i]));
if (input_floats == 450 && output_floats == 12) {
for (int i = 0; i < 12; ++i) {
max_diff = std::max(max_diff, std::fabs(out[static_cast<size_t>(i)] - kReference[i]));
}
}
const auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < repeat; ++i) {
if (rlgym_bpu_infer(handle, obs.data(), out, err, sizeof(err)) != 0) {
if (rlgym_bpu_infer(handle, obs.data(), out.data(), err, sizeof(err)) != 0) {
std::cerr << err << "\n";
rlgym_bpu_destroy(handle);
return 5;
@@ -90,10 +103,13 @@ int main(int argc, char **argv) {
const double elapsed_ms =
std::chrono::duration<double, std::milli>(t1 - t0).count();
std::cout << "reference_max_abs_diff " << max_diff << "\n";
std::cout << "input_floats=" << input_floats << " output_floats=" << output_floats << "\n";
if (input_floats == 450 && output_floats == 12) {
std::cout << "reference_max_abs_diff " << max_diff << "\n";
}
std::cout << "repeat=" << repeat << " cpp_avg_ms=" << (elapsed_ms / repeat) << "\n";
std::cout << "action0=" << out[0] << " action_max_abs="
<< *std::max_element(out, out + 12, [](float a, float b) {
<< *std::max_element(out.begin(), out.end(), [](float a, float b) {
return std::fabs(a) < std::fabs(b);
})
<< "\n";

View File

@@ -13,7 +13,6 @@
namespace {
constexpr int kInputFloats = 450;
constexpr int kOutputFloats = 12;
void set_error(char *err, int err_len, const std::string &msg) {
@@ -88,10 +87,10 @@ class BpuDnnPolicy {
check_dnn(hbDNNGetOutputTensorProperties(&output_tensors_[0].properties, dnn_handle_, 0),
"hbDNNGetOutputTensorProperties");
validate_tensor(input_tensors_[0].properties, kInputFloats, HB_DNN_TENSOR_TYPE_F32,
"input");
validate_tensor(output_tensors_[0].properties, kOutputFloats, HB_DNN_TENSOR_TYPE_F32,
"output");
input_floats_ = element_count(input_tensors_[0].properties.validShape);
output_floats_ = element_count(output_tensors_[0].properties.validShape);
validate_tensor(input_tensors_[0].properties, HB_DNN_TENSOR_TYPE_F32, "input");
validate_tensor(output_tensors_[0].properties, HB_DNN_TENSOR_TYPE_F32, "output");
alloc_tensor_mem(input_tensors_[0]);
alloc_tensor_mem(output_tensors_[0]);
@@ -112,7 +111,7 @@ class BpuDnnPolicy {
}
auto &input_mem = input_tensors_[0].sysMem[0];
std::memcpy(input_mem.virAddr, input, sizeof(float) * kInputFloats);
std::memcpy(input_mem.virAddr, input, sizeof(float) * input_floats_);
int code = hbSysFlushMem(&input_mem, HB_SYS_MEM_CACHE_CLEAN);
if (code != 0) {
throw std::runtime_error(dnn_error("hbSysFlushMem(input)", code));
@@ -137,18 +136,15 @@ class BpuDnnPolicy {
if (code != 0) {
throw std::runtime_error(dnn_error("hbSysFlushMem(output)", code));
}
std::memcpy(output, output_mem.virAddr, sizeof(float) * kOutputFloats);
std::memcpy(output, output_mem.virAddr, sizeof(float) * output_floats_);
}
int input_floats() const { return input_floats_; }
int output_floats() const { return output_floats_; }
private:
static void validate_tensor(const hbDNNTensorProperties &props, int expected_count,
int expected_type, const char *name) {
const int count = element_count(props.validShape);
if (count != expected_count) {
std::ostringstream oss;
oss << name << " valid element count " << count << " != " << expected_count;
throw std::runtime_error(oss.str());
}
static void validate_tensor(const hbDNNTensorProperties &props, int expected_type,
const char *name) {
if (props.tensorType != expected_type) {
std::ostringstream oss;
oss << name << " tensor type " << props.tensorType << " != " << expected_type;
@@ -182,6 +178,8 @@ class BpuDnnPolicy {
std::string model_name_;
std::vector<hbDNNTensor> input_tensors_;
std::vector<hbDNNTensor> output_tensors_;
int input_floats_{0};
int output_floats_{0};
int bpu_core_{HB_BPU_CORE_ANY};
int priority_{HB_DNN_PRIORITY_LOWEST};
};
@@ -201,14 +199,14 @@ void *rlgym_bpu_create(const char *model_path, int bpu_core, int priority,
}
}
int rlgym_bpu_infer(void *handle, const float *input450, float *output12, char *err,
int rlgym_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<BpuDnnPolicy *>(handle)->infer(input450, output12);
static_cast<BpuDnnPolicy *>(handle)->infer(input, output);
return 0;
} catch (const std::exception &e) {
set_error(err, err_len, e.what());
@@ -216,6 +214,20 @@ int rlgym_bpu_infer(void *handle, const float *input450, float *output12, char *
}
}
int rlgym_bpu_input_floats(void *handle) {
if (handle == nullptr) {
return 0;
}
return static_cast<BpuDnnPolicy *>(handle)->input_floats();
}
int rlgym_bpu_output_floats(void *handle) {
if (handle == nullptr) {
return 0;
}
return static_cast<BpuDnnPolicy *>(handle)->output_floats();
}
void rlgym_bpu_destroy(void *handle) {
delete static_cast<BpuDnnPolicy *>(handle);
}

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
"""Compatibility wrapper for the Gym 5-frame BPU deployment entrypoint."""
import runpy
from pathlib import Path
if __name__ == "__main__":
entry = Path(__file__).resolve().parents[1] / "deploy_go1_rlgym_bpu_x5_fastcpp.py"
runpy.run_path(str(entry), run_name="__main__")

View File

@@ -2,8 +2,8 @@
"""Offline BPU policy smoke test for RDK X5.
This does not connect to the robot. It loads a BPU .bin and one raw float32
input file, runs bpu_infer_lib repeatedly, and optionally checks the known
00000.bin reference output captured from hrt_model_exec.
input file, runs the C++ DNN API backend repeatedly, and optionally checks the
known RobotLab 00000.bin reference output captured from hrt_model_exec.
"""
import argparse
@@ -47,15 +47,19 @@ def main():
input_path = Path(args.input_bin).expanduser().resolve()
data = np.fromfile(input_path, dtype=np.float32)
if data.size != 450:
raise ValueError(f"{input_path} has {data.size} float32 values, expected 450")
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))))
if args.check_reference_00000:
if policy.input_size != 450:
raise ValueError("--check-reference-00000 is only valid for the 450-dim RobotLab reference input")
diff = np.abs(action - REFERENCE_00000)
print("reference_max_abs_diff", float(diff.max()))
print("reference_mean_abs_diff", float(diff.mean()))