# libstereo_bolt 交付说明(.so + 头文件) 你拿到的是**预编译的 aarch64 动态库 + C 头文件 + 模型 + 标定 + 示例**,不含算法源码。 运行时依赖中 **`libstereo_bolt.so`、OpenCV 4.5.0 `.so`、`librknnrt.so` 均已随包 `lib/`**;无需 apt 安装 OpenCV。NPU 驱动由板子系统提供。 > **本库导出两个独立的产品接口**(同一个 `.so`、同一个 `StereoBoltCtx`): > 1. **精测(PRECISE)** `sb_process_bolt_module_buffers` / `_files` —— 12MP 高精度测量, > 每颗螺栓高度 / 相邻间距 / 地平面 / 3D 中心轴 / 顶底 3D 坐标。**触发式"测得准"路径(板上约 400ms/帧,依输入和 QC 耗时波动)。** > 2. **测距(RANGING)** `sb_range_bolt_binned` —— 2×2 binned(3MP) 实时测距, > 输出相机→螺栓代表直线距离。**无人机实时路径(统一用 OBB@1024,当前 distance_153304_403 复测约 153ms/帧)。** > > **创建上下文推荐用内存接口 `sb_create_ex`**(见下节):标定、模型、参数全部经接口/buffer 传入, > 库**零文件 IO、无路径假设、无安装目录读权限问题**。旧的 `sb_create(config.yaml)` 仍保留作向后兼容。 --- ## ★ 创建上下文:`sb_create_ex`(推荐)vs `sb_create`(兼容) 两个产品接口共用同一个 `StereoBoltCtx`。创建它有两种方式: ### 推荐:`sb_create_ex` —— 全参数经接口传入,零文件 IO ```c StereoBoltCtx* sb_create_ex(const StereoBoltCalibC* calib, const StereoBoltModelC* model, const StereoBoltParamsC* params); StereoBoltParamsC sb_default_params(void); /* 预填默认值,再覆盖你关心的 */ ``` - `StereoBoltCalibC`:标定 —— 左右 `K[9]`/`D[5]` + `R[9]`/`T[3]` + `baseline_mm` + 图像尺寸。**替代标定 XML。** - `StereoBoltModelC`:模型 —— `.rknn` 字节流 `data`+`size` + `backend`(rk3588 用 `SB_YOLO_BACKEND_RKNN`) + `imgsz`/`conf`/`iou`。**替代 weights 文件路径。** - `StereoBoltParamsC`:算法数值参数 —— 视差带(`wd_z_min/max_mm` 物理带,**或** `sgbm_min/num_disparities` 固定带,二选一必填)、`rectification`、`measurement.foot_rim`、`ranging.*`。**替代 config.yaml 数值键。** 带 `struct_size` 做前向兼容。 **这条路径库不碰磁盘**:标定/模型从哪来由你决定(你的标定流程、资源文件、相机 SDK);本包提供的 `calib/*.xml` 与 `weights/*.rknn` 只是**给你读进来填结构体**的现成数据。 ### 兼容:`sb_create(const char* config_yaml_path)` 读 `config.rk3588.12mp.yaml`(其内部再按相对路径找标定 XML + 模型)。**有两文件依赖 + 工作目录/读权限要求**——正是 `sb_create_ex` 要规避的。仅在离线/快速测试时用。 > `sb_create` / `sb_create_ex` **只调一次**(载配置+标定+模型进 NPU,慢),之后每帧复用同一个 `ctx`。 > `sb_destroy` / `sb_last_error` 两接口共用。线程安全:同一 `ctx` 别多线程并发。 --- ## 交付边界(职责划分) **本交付物是一个"算法模块",不是完整软件系统。** 软件总体集成由你(接入方)负责。 | 项 | 由谁负责 | |---|---| | 双目螺栓**测量/测距算法**(`.so` + `.h` + 模型 + 标定 + 示例 + 文档) | **算法方(本包提供)** | | 板子**运行时环境**:aarch64 + glibc、NPU 驱动;算法依赖的 OpenCV 与 `librknnrt.so` 已随包 `lib/` | **接入方** | | **取图**(相机驱动 / Mono8 buffer 喂入)、上层业务、UI、调度、与上位机/无人机通信 | **接入方** | | **标定数据来源**(填入 `StereoBoltCalibC`)、按 `c_api.h` 调用、对 height/distance/confidence 做业务决策 | **接入方** | > 算法方只交付"喂进左右图 → 返回螺栓高度/距离"的纯算法能力(C ABI)。本库不碰相机、不碰网络、 > **不碰磁盘(`sb_create_ex` + buffer 接口)**、不做悬停/对中等业务决策。逐颗 `confidence`(trusted/suspect) + `status` 由接入方取舍。 ## 包内容 | 路径 | 说明 | |---|---| | `lib/libstereo_bolt.so` | 预编译共享库(aarch64,含 `sb_create_ex` 内存接口) | | `include/stereo_bolt/c_api.h` | C ABI 头文件(唯一对外接口) | | `weights/best_obb_1024_0703_20260702.rknn` | NPU 模型 —— OBB 检测(精测+测距共用,imgsz 1024,FP16) | | `calib/stereo_calib_20260621_202857_ascii.xml` | 12MP 双目标定(**0621 装配 baseline 482mm**,Halcon 位姿已修正) | | `config/config.rk3588.12mp.yaml` | 仅 `sb_create`(兼容)用 + 作参数参考;**`sb_create_ex` 不需要它** | | `lib/*.so` | 算法库、OpenCV 4.5.0 运行库、RKNN 运行时(aarch64 2.3.2),运行时通过 `LD_LIBRARY_PATH=./lib` 加载 | | `INSTALL_3588.md` / `ABI_FINGERPRINT.txt` | 板上装环境说明 / .so 二进制指纹(**你的环境必须匹配**) | | `example/` | C 调用示例(`main.c` 精测,**sb_create + buffer 接口**)+ CMakeLists | --- ## 编译期 vs 运行期 **你不需要重编这个库**(预编译成品,无源码)。你只编译*你自己*调用它的程序。 | 东西 | 编译你的调用程序 | 板上运行 | |---|:---:|:---:| | `c_api.h` | ✅ | — | | `libstereo_bolt.so` | ✅(链接,带 `--allow-shlib-undefined`) | ✅ | | OpenCV | 你的程序:❌ 不需要(C ABI 不暴露 cv 类型,相机直接给 Mono8 buffer)
本包 example:❌ 运行不需要系统 OpenCV | ✅ 已随包 `lib/`,无需 apt | | `librknnrt.so` | ❌ | ✅ 已随包 `lib/`,`LD_LIBRARY_PATH=./lib` | | NPU 驱动 + `/dev/rknpu` | ❌ | ✅ 必须(内核态) | | 模型 / 标定 | ❌ | ✅(已在本包;`sb_create_ex` 由你读进来传,`sb_create` 由库读) | > example 用 OpenCV 只是为了独立跑(读本包的标定 XML + 测试图)。**你的产品里相机直接给 Mono8 buffer、 > 标定来自你自己的来源,调用端无需 OpenCV,库也始终零文件 IO。** --- ## ⚠️ 必须匹配的 ABI 指纹 `.so` 二进制里写死了依赖的 so 名字和 glibc 版本。打开 `ABI_FINGERPRINT.txt` 确保板子满足: - CPU = **aarch64**;**glibc ≥ 指纹版本**(GLIBC_2.32); - **运行时优先使用本包 `lib/`**(内含标准 SONAME `libopencv_*.so.4.5`,无需系统 OpenCV); - **librknnrt 版本** = 转 `.rknn` 的 toolkit 版本(**2.3.x**)。 - 指纹应列出 9 个导出符号,含 **`sb_create_ex` / `sb_default_params`**。 自检(应无任何 `not found`):`ldd lib/libstereo_bolt.so | grep "not found"` --- ## 公共错误码 `SB_OK=0` / `SB_ERR_INVALID_CONFIG=-1` / `SB_ERR_IO=-2` / `SB_ERR_ALGORITHM_REJECTED=-3` / `SB_ERR_INTERNAL=-99`。 > `SB_ERR_ALGORITHM_REJECTED` 不是崩溃,是算法判定该帧不满足 `expected` 颗(仅精测返回);**仍返回已测出的部分螺栓**(见下)。 --- ## 接口 1 —— 精测(PRECISE) **用途**:12MP 逐颗高精度测量。触发式"测得准"路径,板上 ~2.5Hz@1024。 **输入**:内存 Mono8 buffer(生产首选)或文件路径(离线)。buffer 尺寸**必须等于标定尺寸**,否则 `SB_ERR_IO`。 ```c sb_status_t sb_process_bolt_module_buffers( StereoBoltCtx* ctx, const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes, const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes, int channels, int expected_bolt_count, StereoBoltModuleResultC* out); sb_status_t sb_process_bolt_module_files(StereoBoltCtx*, const char* left, const char* right, int expected_bolt_count, StereoBoltModuleResultC* out); void sb_free_bolt_module_result(StereoBoltModuleResultC* out); /* 用完必须释放 */ ``` **输出 `StereoBoltModuleResultC`** —— 每颗 `data.bolts[i]`: - **`height_mm`**(高度) - **`top_xyz` / `foot_xyz`**(顶 / 底 3D 坐标) - **`centerline`**(3D 中心轴:`point` + `direction`) - `left_roi` / `right_roi`(左右图 2D 框 x,y,w,h,score) - **`confidence`**(0=trusted / 1=suspect)+ `status` 另含 `adjacent_distances[]`(相邻 `distance_mm`)、`ground_plane`(`model[4]`/`rms_mm`/`inlier_count`)。 **坐标系/单位**:高度、间距、3D 坐标 = **mm**,**校正后左相机坐标系**(x 右 / y 下 / z=深度,典型 z≈1600–2200mm)。 **★ 部分输出契约(但不导出假高度)**:即使 `success==0`(数量不符 `count_mismatch`,无论检到的多还是少于 `expected`), `data.bolts[]` 只包含**完整右目配对且测量干净的螺栓**(各带 height/top/foot/confidence)。`idx_R < 0` 的 left-only fallback 不作为产品高度输出,避免误导。`failure`(reason + 完整 ROI + 配对状态)始终填充,能看出缺哪颗、为何缺。 **最简用法(`sb_create_ex`)**: ```c StereoBoltCalibC calib = {...}; /* 填 K/D/R/T/size/baseline */ StereoBoltModelC model = {.data=rknn_bytes, .size=n, .backend=SB_YOLO_BACKEND_RKNN, .imgsz=1024, .conf=0.5f}; StereoBoltParamsC p = sb_default_params(); p.wd_z_min_mm=1200; p.wd_z_max_mm=2800; StereoBoltCtx* ctx = sb_create_ex(&calib, &model, &p); /* 一次 */ StereoBoltModuleResultC out; sb_process_bolt_module_buffers(ctx, L,w,h,0, R,w,h,0, 1, /*expected=*/6, &out); /* 遍历 out.data.bolts[i].height_mm / top_xyz / foot_xyz ... */ sb_free_bolt_module_result(&out); sb_destroy(ctx); ``` 完整示例见 `example/main.c`。**imgsz 首选 1024**(OBB 模型,自动检测旋转框);交付权重 FP16(INT8 检出全废,不可用)。 --- ## 高度测量模式(`measurement.height_from_plane`) 精测接口支持两种高度计算方式,通过 config 中 `measurement.height_from_plane` 切换: | 模式 | config 值 | 高度定义 | 是否需要平面拟合 | 板上耗时(8颗场景) | |---|---|---|---|---| | **顶到底**(默认) | `height_from_plane: false` | 螺杆顶端到可见底端沿拟合轴的长度 | **不需要**(自动跳过) | ~400ms | | **顶到平面** | `height_from_plane: true` | 螺杆顶端到支撑平面的法向距离 | 需要(StereoBM 拟合) | ~800ms | **默认关闭平面测量**(`height_from_plane: false`),此时平面计算完全跳过,节省 300-550ms。 **开启平面测量**:在 `config.rk3588.12mp.yaml` 中修改: ```yaml measurement: height_from_plane: true # 开启:高度 = 螺杆顶端到支撑平面的法向距离 ``` `sb_create_ex` 用户:当前 `StereoBoltParamsC` 不含此字段,默认为 false(顶到底)。如需平面模式,使用 `sb_create(config.yaml)` 并在 config 中设置。 --- ## 接口 2 —— 测距(RANGING)`sb_range_bolt_binned` **用途**:实时回答"左目光心→螺栓多远"。容忍部分可见:**绝不要求螺栓数**,≥1 颗即给距离。当前板上约 153ms/帧(~6.5Hz)@1024。 **输入**:2×2 binned 内存 buffer(12MP→3MP=2048×1500),Mono8/BGR。binned 内参**内部推导**,无需单独 binned 标定。 ```c typedef struct { double distance_mm; int left_x,left_y,right_x,right_y; double ncc_score,lrc_px; int confidence; } StereoBoltRangeBoltC; typedef struct { int found; double distance_mm; int bolt_count; char status[32]; } StereoBoltRangeSummaryC; sb_status_t sb_range_bolt_binned( StereoBoltCtx* ctx, const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes, const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes, int channels, StereoBoltRangeSummaryC* out, StereoBoltRangeBoltC* bolts, int max_bolts); ``` **输出**:主用 `out.distance_mm`(可见杆代表距离);`out.found` 每帧轮询;`bolts[]` 给出每颗候选的左右匹配点。OBB 轴路线下 `ncc_score` 为检测/匹配分数、`lrc_px` 为轴视差自洽度(字段名保留用于 ABI 兼容);NCC 回退路线下为 NCC/LRC 指标。精度目标 **±50mm**(稳定区 1.3–2.4m)。 **可选 `ranging.*` 参数**(`StereoBoltParamsC` 里 `rng_*`,都有默认):`z_min/max_mm` 视差带、`lrc_max_mm`/`lrc_trusted_mm` 物理同根门 等。完整示例见 `example/range_example.cpp`。 > 注:`sb_create_ex` 即使**只做测距**也要求提供精测视差带(`wd_z_*` 或 `sgbm_*`),因为同一 `ctx` 两接口共用。 --- ## 该用哪个 + 板上实测吞吐 | | 精测 `sb_process_bolt_module_*` | 测距 `sb_range_bolt_binned` | |---|---|---| | 问题 | 每颗螺栓高度/间距/地平面/3D 坐标 | 相机离螺栓多远(代表距离) | | 输入 | 12MP 原图(Mono8 buffer / 文件) | 2×2 binned 3MP buffer | | 螺栓数 | 要 `expected`(不符=部分输出+`count_mismatch`) | **不要**,≥1 颗即 `found` | | 板上吞吐@1024 | **约 400ms/帧** | **约 153ms/帧 ≈ 6.5Hz** | | 定位 | 触发式"测得准" | 无人机实时定位反馈 | > 测距历史基准于 RK3588(v4 旧模型 best_obb_1024.rknn,distance_153304_403,21 帧):均值 153.146ms,距离均值 1652.360mm,跨度 0.491mm。 > v5(0703 模型)板上测距复核 0702-12mp:8/8 螺杆 status=ok,逐杆距离 1.5–2.2m 连贯;整帧 ~450–475ms。 > 本交付为单模型 OBB@1024(精测+测距共用)。 --- ## 编译你的调用程序 + 运行 示例用 C/C++,运行时库从本包 `lib/` 加载: ```bash cd example cmake -S . -B build -DSB_ROOT="$(cd .. && pwd)" cmake --build build # -> bolt_demo(精测)+ range_demo(测距) # ../lib 含 libstereo_bolt.so + OpenCV 4.5.0 + librknnrt.so # 精测:传标定 XML + 模型 + 左右图 + 期望螺栓数 LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH \ ./build/bolt_demo ../calib/stereo_calib_20260621_202857_ascii.xml \ ../weights/best_obb_1024_0703_20260702.rknn L.bmp R.bmp 6 # 测距:传标定 + 模型 + 左右图(demo 内部 2×2 binning;生产直接喂相机 binned buffer) LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH \ ./build/range_demo ../calib/stereo_calib_20260621_202857_ascii.xml \ ../weights/best_obb_1024_0703_20260702.rknn L.bmp R.bmp ```