diff --git a/deploy_45dim_rl_gym/bpu_deploy_s100/README.md b/deploy_45dim_rl_gym/bpu_deploy_s100/README.md index 7aec824..6fbc3cd 100644 --- a/deploy_45dim_rl_gym/bpu_deploy_s100/README.md +++ b/deploy_45dim_rl_gym/bpu_deploy_s100/README.md @@ -130,6 +130,38 @@ 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 读取状态,但不会发送电机指令: diff --git a/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/build_board.sh b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/build_board.sh new file mode 100755 index 0000000..8eaf8b6 --- /dev/null +++ b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/build_board.sh @@ -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" diff --git a/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_bench.cpp b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_bench.cpp new file mode 100644 index 0000000..e9c7eef --- /dev/null +++ b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_bench.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 *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(sizeof(float)) != 0) { + return false; + } + data->resize(static_cast(size) / sizeof(float)); + ifs.read(reinterpret_cast(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 obs; + if (!read_f32_file(input, &obs) || static_cast(obs.size()) != input_floats) { + std::cerr << "failed to read " << input_floats << " float32 input: " << input + << "\n"; + rlgym_s100_bpu_destroy(handle); + return 2; + } + + std::vector out(static_cast(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(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(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; +} diff --git a/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_policy.cpp b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_policy.cpp new file mode 100644 index 0000000..82e55d6 --- /dev/null +++ b/deploy_45dim_rl_gym/bpu_deploy_s100/cpp/s100_bpu_policy.cpp @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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(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(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(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(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(input_props.alignedByteSize)); + copy_compact_to_strided(reinterpret_cast(input_tensor.sysMem.virAddr), + reinterpret_cast(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(output), + reinterpret_cast(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(std::numeric_limits::max())) { + throw std::runtime_error(std::string(name) + " tensor is too large"); + } + return static_cast(floats); + } + + static void alloc_tensor_mem(hbDNNTensor &tensor) { + std::memset(&tensor.sysMem, 0, sizeof(tensor.sysMem)); + check_api(hbUCPMallocCached(&tensor.sysMem, + static_cast(tensor.properties.alignedByteSize), 0), + "hbUCPMallocCached"); + } + + static void release_tensor_mem(std::vector &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 input_tensors_; + std::vector 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(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(handle)->input_floats(); +} + +int rlgym_s100_bpu_output_floats(void *handle) { + if (handle == nullptr) { + return 0; + } + return static_cast(handle)->output_floats(); +} + +void rlgym_s100_bpu_destroy(void *handle) { + delete static_cast(handle); +} + +const char *rlgym_s100_bpu_version() { + return "cpp_dnn_api_s100"; +} + +} diff --git a/deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py b/deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py index 5a26d50..bb13964 100644 --- a/deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py +++ b/deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py @@ -92,7 +92,7 @@ BPU_MODEL_REGISTRY = { } DEFAULT_BPU_ROUND = "26000" DEFAULT_BPU_MODEL = BPU_MODEL_REGISTRY[DEFAULT_BPU_ROUND] -LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_s100" +LOWCMD_BACKEND = "cpp_lowcmd_cpp_lowstate_cpp_udp_s100" 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"' diff --git a/deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py b/deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py index 399cafb..45fa393 100644 --- a/deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py +++ b/deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py @@ -96,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"' diff --git a/deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py b/deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py index 8dc0557..100ee2e 100644 --- a/deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py +++ b/deploy_45dim_rl_gym/deploy_go1_rlgym_bpu_x5_fastcpp.py @@ -94,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"' diff --git a/deploy_45dim_rl_gym/deploy_go1_rlgym_pro_sdk_lab_fastcpp.py b/deploy_45dim_rl_gym/deploy_go1_rlgym_pro_sdk_lab_fastcpp.py index 01974e3..417af46 100644 --- a/deploy_45dim_rl_gym/deploy_go1_rlgym_pro_sdk_lab_fastcpp.py +++ b/deploy_45dim_rl_gym/deploy_go1_rlgym_pro_sdk_lab_fastcpp.py @@ -85,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"'