167 lines
8.5 KiB
Markdown
Raw Normal View History

2026-06-26 17:55:15 +08:00
# libstereo_bolt 交付说明(.so + 头文件)
你拿到的是**预编译的 aarch64 动态库 + C 头文件**,不含算法源码。
OpenCV 4.5.4、RKNN runtime 及全部传递依赖(约 131 个 `.so`**已随包内置于 `lib/`**
无需在目标板上安装任何软件包NPU 内核驱动由固件提供,不在用户态包内)。
> **本包标定 = 12MP 生产相机4096×3000。** 喂进来的左右图必须是该相机的 **4096×3000** 帧
> `config` 里 `image.width=4096 / height=3000` 已配好,指向 `calib/stereo_calib_20260607_211135.xml`)。
> **包内不含测试图** —— 请用你自己这套 12MP 相机拍的左右图。
> ✅ **已在板上实测**:本 12MP 标定**加载 + rectify 通过**4096×3000stereo_rms=0.373、baseline=200.19、f_rect=3330
> ⚠️ 但**检测 + 测量在该相机上还没用真实螺杆图验过**(包内无螺杆图)。
> ⚠️ 本标定单相机 RMS=**0.373**5MP 那套是 0.19),已把 `calibration.max_single_rms` **放宽到 0.40** 才放行;
> 0.373 偏松会折损测量精度,建议后续**重标到 <0.20** 再收回此闸。首次上机务必用真实螺杆图核对结果。
## 包内容
| 路径 | 说明 |
|---|---|
| `lib/libstereo_bolt.so` | 预编译共享库aarch64 |
| `lib/librknnrt.so` 等(共 ~131 个) | OpenCV 4.5.4 + 全部传递依赖(**已随包,无需 apt** |
| `include/stereo_bolt/c_api.h` | C ABI 头文件(唯一对外接口) |
| `weights/best_1280_20260621.rknn` | NPU 模型(精测 + 测距共用imgsz 1280 |
| `calib/stereo_calib_20260621_202857_ascii.xml` | 出厂标定0621 装配baseline 482 mm |
| `config/config.rk3588.12mp.yaml` | 配置文件(兼容 `sb_create` 接口用) |
| `example/` | 最小 C 示例(精测 + 测距) + CMakeLists |
| `INSTALL_3588.md` | 安装与运行说明(**从这里开始** |
| `DEPENDENCIES.md` | 依赖库详细清单及说明 |
| `ABI_FINGERPRINT.txt` | 二进制 ABI 指纹 |
---
## 你什么时候需要什么(编译期 vs 运行期)
**重点:你不需要重编这个库**(它是预编译好的成品,你也没有源码)。你只编译*你自己*
调用它的小程序那一步几乎零依赖。OpenCV / librknnrt 是**运行那一刻**才需要的。
| 东西 | 编译你自己的调用程序 | 在板上**运行** |
|---|:---:|:---:|
| `c_api.h` | ✅ | — |
| `libstereo_bolt.so` | ✅(链接,带 `--allow-shlib-undefined` | ✅ |
| OpenCV 4.5.4 | ❌ 不需要C ABI 不暴露 cv 类型) | ✅ **已随包 `lib/`,无需安装** |
| `librknnrt.so`RKNN 运行时) | ❌ | ✅ **已随包 `lib/`** |
| NPU 驱动 + `/dev/rknpu` | ❌ | ✅ 必须(内核态) |
| `.rknn` 模型 + 标定 XML + `config` | ❌ | ✅ 必须(**已在本包** weights/ config/ calib/ |
> **“YOLO 依赖”在板上很轻**:就是 `librknnrt.so` + NPU 驱动 + `.rknn` 文件。
> **不需要** Python / PyTorch / ultralytics / ONNXRuntime / rknn-toolkit2 —— 那些是 PC 上把
> `.onnx` 转成 `.rknn` 用的,转完板上就用不到。
---
## ⚠️ 必须匹配的 ABI 指纹(动态链接的硬约束)
这个 `.so` 在某个具体环境里编出来,二进制里**写死了**它依赖的 so 名字和 glibc 版本。
**“自己准备 OpenCV” 不等于随便哪个 OpenCV 都行——必须是指纹里那个 major.minor。**
打开 `ABI_FINGERPRINT.txt`,确保你的板子满足:
- CPU 架构 = **aarch64**
- **glibc ≥ 指纹里的版本**(低于它,加载即 `GLIBC_x.xx not found`
- **OpenCV soname 与指纹一致**(本包指纹 = `libopencv_core.so.4.5d` ⇒ 装 **OpenCV 4.5.x**;其它版本 soname 不通用)
- **librknnrt 版本** = 转换 `.rknn` 所用 toolkit 版本(**2.3.x**
一条命令自检(应当**没有任何** `not found`
```bash
ldd lib/libstereo_bolt.so | grep "not found"
```
---
## 板子上需要准备的
| 依赖 | 状态 |
|---|---|
| OpenCV 4.5.4 + 传递依赖131 个 .so | ✅ **已随包 `lib/`**,无需 apt |
| `librknnrt.so`RKNN 运行时 2.3.2 | ✅ **已随包 `lib/`** |
| `.rknn` 模型 + 标定 XML + config | ✅ **已随包** `weights/` `calib/` `config/` |
| NPU 内核驱动 + `/dev/rknpu` | ⚠️ **板子固件提供**,出厂 RK3588 一般已有 |
| glibc ≥ 2.32Ubuntu 22.04 自带 2.35 | ✅ 系统自带,无需操作 |
> 完整依赖说明见 `DEPENDENCIES.md`。
---
## 怎么用C ABI
完整示例见 `example/main.c`。有**两个**喂帧入口,生产用内存版:
**① 生产路径 —— 内存版(不落盘,直接喂相机帧):**
```c
StereoBoltCtx* ctx = sb_create("config/config.rk3588.yaml"); // 一次:载配置/标定/RKNN 模型
while (抓帧) {
const uint8_t* L = camera_left_frame(); // 相机 SDK 给的 Mono8 缓冲(W*H 字节,在内存)
const uint8_t* R = camera_right_frame();
StereoBoltModuleResultC out;
sb_process_bolt_module_buffers(ctx,
L, W, H, 0, // stride=0 表示行紧凑排列(每行 W 字节)
R, W, H, 0,
1, // channels: 1=Mono8(灰度), 3=BGR 也接受(内部转灰度)
8, &out); // 8 = 期望螺栓数
/* out.success==1: out.data.bolts[i].height_mm / foot_xyz / top_xyz + adjacent_distances + ground_plane
out.success==0: out.failure.reason + left_rois/right_rois/pairs(诊断用) */
sb_free_bolt_module_result(&out);
}
sb_destroy(ctx); // 一次
```
> `W`/`H` **必须等于标定分辨率**(`config` 的 `image.width`/`image.height`),否则返回 `SB_ERR_IO`。
> 内存版**全程不碰磁盘**(已实测:删掉源文件后内存版照常出结果)。
**② 离线/测试路径 —— 文件版(读磁盘图,用你自己的 12MP 左右图):**
```c
sb_process_bolt_module_files(ctx, "your_left.bmp", "your_right.bmp", 8, &out);
```
> 图必须是本套 12MP 相机的 4096×3000(灰度或彩色均可),格式 .bmp/.tiff/.png。本包不含测试图。
错误码:`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` 不是崩溃,是算法判定该帧不可测,`out.failure` 里有原因 + ROI + 配对状态。
---
## 输入 / 输出(契约)
**输入**
- 一次性(`sb_create``config/config.rk3588.yaml`(内部指向 `calib/*.xml``weights/best.rknn`)。
- 每帧(生产用 `sb_process_bolt_module_buffers`):左/右 Mono8 内存缓冲 + 宽/高/stride、通道数、期望螺栓数(int)、`&out`
- 每帧(离线用 `sb_process_bolt_module_files`):左图路径、右图路径、期望螺栓数(int)、`&out`
**输出**`StereoBoltModuleResultC out` + 返回码)
- 成功 `out.success==1``out.data`
- `bolts[]` 每颗:`bolt_id` / `left_roi`,`right_roi`(x,y,width,height,score) / **`height_mm`** /
**`foot_xyz`,`top_xyz`**(底/顶 3D 点) / `centerline`(3D 轴 = point+direction)
- `adjacent_distances[]``bolt_id_i`,`bolt_id_j`,**`distance_mm`**
- `ground_plane``model[4]`(ax+by+cz+d=0)、`rms_mm``inlier_count`
- 失败 `out.success==0``out.failure``reason` + `left_rois[]`/`right_rois[]` + `pairs[]`(配对诊断)
**单位 / 坐标系**:高度、间距 = **mm**3D 坐标(foot/top/centerline) = **mm校正后左相机坐标系**
x 右 / y 下 / z=深度,典型 z≈16002200mm`score`=YOLO 置信度。
输出格式示例(**仅示意字段结构**,数值来自早先 5MP 验证机,非本包 12MP 相机):
```
success=1 bolts=8 distances=8
bolt 0 height=151.2mm top=(-191.9,-122.9,1995.3) foot=(-204.3,2.9,2078.3)
...
dist 0-1=268.3mm dist 1-2=254.4mm ... dist 7-0=335.8mm
```
> 一句话:**入 = 一对左右图 + 期望螺栓数(配置/标定/模型初始化时给一次);
> 出 = 每颗螺栓 高度 + 顶/底 3D + 3D 中心轴 + 相邻间距 + 地平面,或失败原因 + 诊断。**
---
## 编译你自己的调用程序 + 运行(**都在包根目录执行**
```bash
# 在包根目录(与 lib/ config/ calib/ 同级)编译
cmake -S example -B example/build -DSB_ROOT="$(pwd)"
cmake --build example/build
# 在包根目录运行(config 里 calib/、weights/ 是相对路径,按当前目录解析)
# example 走【内存接口】: 读 raw Mono8 裸数据(W*H 字节)喂 sb_process_bolt_module_buffers,
# 库全程不见文件路径。your_left/right.raw = 你 12MP 相机帧 dump 的 Mono8(4096*3000 字节)
LD_LIBRARY_PATH=lib:/usr/lib:$LD_LIBRARY_PATH \
./example/build/bolt_demo config/config.rk3588.yaml your_left.raw your_right.raw 4096 3000 8
```