228 lines
14 KiB
Markdown
Raw Normal View History

2026-06-26 17:55:15 +08:00
# libstereo_bolt 交付说明(.so + 头文件)
2026-07-01 18:16:18 +08:00
你拿到的是**预编译的 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` 由接入方取舍。
2026-06-26 17:55:15 +08:00
## 包内容
| 路径 | 说明 |
|---|---|
2026-07-01 18:16:18 +08:00
| `lib/libstereo_bolt.so` | 预编译共享库aarch64`sb_create_ex` 内存接口) |
2026-06-26 17:55:15 +08:00
| `include/stereo_bolt/c_api.h` | C ABI 头文件(唯一对外接口) |
2026-07-03 14:19:06 +08:00
| `weights/best_obb_1024_0703_20260702.rknn` | NPU 模型 —— OBB 检测(精测+测距共用imgsz 1024FP16 |
2026-07-01 18:16:18 +08:00
| `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 |
2026-06-26 17:55:15 +08:00
---
2026-07-01 18:16:18 +08:00
## 编译期 vs 运行期
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**你不需要重编这个库**(预编译成品,无源码)。你只编译*你自己*调用它的程序。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
| 东西 | 编译你的调用程序 | 板上运行 |
2026-06-26 17:55:15 +08:00
|---|:---:|:---:|
| `c_api.h` | ✅ | — |
| `libstereo_bolt.so` | ✅(链接,带 `--allow-shlib-undefined` | ✅ |
2026-07-01 18:16:18 +08:00
| OpenCV | 你的程序:❌ 不需要C ABI 不暴露 cv 类型,相机直接给 Mono8 buffer<br>本包 example❌ 运行不需要系统 OpenCV | ✅ 已随包 `lib/`,无需 apt |
| `librknnrt.so` | ❌ | ✅ 已随包 `lib/``LD_LIBRARY_PATH=./lib` |
2026-06-26 17:55:15 +08:00
| NPU 驱动 + `/dev/rknpu` | ❌ | ✅ 必须(内核态) |
2026-07-01 18:16:18 +08:00
| 模型 / 标定 | ❌ | ✅(已在本包;`sb_create_ex` 由你读进来传,`sb_create` 由库读) |
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
> example 用 OpenCV 只是为了独立跑(读本包的标定 XML + 测试图)。**你的产品里相机直接给 Mono8 buffer、
> 标定来自你自己的来源,调用端无需 OpenCV库也始终零文件 IO。**
2026-06-26 17:55:15 +08:00
---
2026-07-01 18:16:18 +08:00
## ⚠️ 必须匹配的 ABI 指纹
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
`.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`**。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
自检(应无任何 `not found``ldd lib/libstereo_bolt.so | grep "not found"`
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
---
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
## 公共错误码
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
`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` 颗(仅精测返回);**仍返回已测出的部分螺栓**(见下)。
2026-06-26 17:55:15 +08:00
---
2026-07-01 18:16:18 +08:00
## 接口 1 —— 精测PRECISE
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**用途**12MP 逐颗高精度测量。触发式"测得准"路径,板上 ~2.5Hz@1024
**输入**:内存 Mono8 buffer生产首选或文件路径离线。buffer 尺寸**必须等于标定尺寸**,否则 `SB_ERR_IO`
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
```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); /* 用完必须释放 */
```
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**输出 `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`)。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**坐标系/单位**高度、间距、3D 坐标 = **mm****校正后左相机坐标系**x 右 / y 下 / z=深度,典型 z≈16002200mm
2026-06-26 17:55:15 +08:00
2026-07-03 14:19:06 +08:00
**★ 部分输出契约(但不导出假高度)**:即使 `success==0`(数量不符 `count_mismatch`,无论检到的多还是少于 `expected`
`data.bolts[]` 只包含**完整右目配对且测量干净的螺栓**(各带 height/top/foot/confidence`idx_R < 0` 的 left-only fallback 不作为产品高度输出,避免误导。`failure`reason + 完整 ROI + 配对状态)始终填充,能看出缺哪颗、为何缺。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**最简用法(`sb_create_ex`**
2026-06-26 17:55:15 +08:00
```c
2026-07-01 18:16:18 +08:00
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);
2026-06-26 17:55:15 +08:00
```
2026-07-01 18:16:18 +08:00
完整示例见 `example/main.c`。**imgsz 首选 1024**OBB 模型,自动检测旋转框);交付权重 FP16INT8 检出全废,不可用)。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
---
## 高度测量模式(`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 # 开启:高度 = 螺杆顶端到支撑平面的法向距离
2026-06-26 17:55:15 +08:00
```
2026-07-01 18:16:18 +08:00
`sb_create_ex` 用户:当前 `StereoBoltParamsC` 不含此字段,默认为 false顶到底。如需平面模式使用 `sb_create(config.yaml)` 并在 config 中设置。
2026-06-26 17:55:15 +08:00
---
2026-07-01 18:16:18 +08:00
## 接口 2 —— 测距RANGING`sb_range_bolt_binned`
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
**用途**:实时回答"左目光心→螺栓多远"。容忍部分可见:**绝不要求螺栓数**≥1 颗即给距离。当前板上约 153ms/帧(~6.5Hz@1024
**输入**2×2 binned 内存 buffer12MP→3MP=2048×1500Mono8/BGR。binned 内参**内部推导**,无需单独 binned 标定。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
```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.32.4m)。
**可选 `ranging.*` 参数**`StereoBoltParamsC``rng_*`,都有默认):`z_min/max_mm` 视差带、`lrc_max_mm`/`lrc_trusted_mm` 物理同根门 等。完整示例见 `example/range_example.cpp`
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
> 注:`sb_create_ex` 即使**只做测距**也要求提供精测视差带(`wd_z_*` 或 `sgbm_*`),因为同一 `ctx` 两接口共用。
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
---
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
## 该用哪个 + 板上实测吞吐
| | 精测 `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** |
| 定位 | 触发式"测得准" | 无人机实时定位反馈 |
2026-06-26 17:55:15 +08:00
2026-07-03 14:19:06 +08:00
> 测距历史基准于 RK3588v4 旧模型 best_obb_1024.rknndistance_153304_40321 帧):均值 153.146ms,距离均值 1652.360mm,跨度 0.491mm。
> v50703 模型)板上测距复核 0702-12mp8/8 螺杆 status=ok逐杆距离 1.52.2m 连贯;整帧 ~450475ms。
2026-07-01 18:16:18 +08:00
> 本交付为单模型 OBB@1024精测+测距共用)。
2026-06-26 17:55:15 +08:00
---
2026-07-01 18:16:18 +08:00
## 编译你的调用程序 + 运行
2026-06-26 17:55:15 +08:00
2026-07-01 18:16:18 +08:00
示例用 C/C++,运行时库从本包 `lib/` 加载:
2026-06-26 17:55:15 +08:00
```bash
2026-07-01 18:16:18 +08:00
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 \
2026-07-03 14:19:06 +08:00
../weights/best_obb_1024_0703_20260702.rknn L.bmp R.bmp 6
2026-07-01 18:16:18 +08:00
# 测距:传标定 + 模型 + 左右图demo 内部 2×2 binning生产直接喂相机 binned buffer
LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH \
./build/range_demo ../calib/stereo_calib_20260621_202857_ascii.xml \
2026-07-03 14:19:06 +08:00
../weights/best_obb_1024_0703_20260702.rknn L.bmp R.bmp
2026-06-26 17:55:15 +08:00
```