一句话:把"具身机器人视觉"的成像链路、深度测量、标定与多传感器融合做成可验证的实验台, 每个结论都要同时给出独立参考与覆盖率——并把这两条做成类型层面的不变量,而不是文档里的提醒。
与前作的关系:本工程是第二版。第一版(vpercept,纯 Python)跑通了判据链,但框架被判定不适合,
其目录已于 2026-09-21 删除。本工程换掉框架与实现载体,保留它的判据与实验设计:
参考独立性、MAE 与覆盖率成对、无效即 NaN、消融一次只改一个开关、先预测再实测。
判据的实现与断言都在本工程里重建(见 tests/kernel/)。
轴1 SOURCE 轴2 KERNEL 轴3 PIPELINE 两个壳 SHELL
数据与真值从哪来 → 纯计算,无 IO/ROS → 显式 DAG(两路汇合) → A: pybind11 → Python 实验
TUM / KITTI geometry, metrics 时间门/注册/融合/指标 B: rclcpp → 在线节点/RViz
Gemini 305 / RAW depth, fuse, state Builder + Registry ← 两壳挂同一内核
内存(测试)
┌─ 不变量① REFERENCE:参考带"原理"标签,评估时强制两者原理不同 ─┐
└─ 不变量② EVIDENCE :MAE 永远与覆盖率成对出现;无效=NaN,不是 0 ─┘
旧框架(按抽象层次分:contracts/core/usecases/adapters)为什么不适合:本轮真正的变化轴是 "数据从哪来 / 算在哪跑 / 结果怎么验证"。按层次分,三个轴全挤进 adapters,那层会爆炸; 而"参考必须独立"埋在数据字段里等于没有防线。
| 不变量 | 机制 | 违反时的后果 |
|---|---|---|
| ① 参考与待评必须不同原理 | EstimateDepth / ReferenceDepth 两个类型无转换构造;assert_independent() 在每次评估入口检查(enum 比较,发布构建也生效);ReferenceDepth::make 直接拒绝 kStereo 当参考 |
kCircularEvaluation / kNotIndependent / kModalityMismatch |
| ② MAE 必带覆盖率 | ErrorWithCoverage 六字段无默认值、无部分构造;MetricSample 没有公开构造函数,只有 compute_metrics 是 friend |
想只报 MAE 就得改类型 —— 会被 review 逮住 |
为什么值得这么做:用 SGBM 的输出评 SGBM、用 Gemini 305 自带深度评我们自己的双目、
用出厂标定当"真值"——这三种错误都会给出漂亮而错误的数字,且不会有任何报错。
tests/kernel/test_reference_invariants.cpp 把这三条都写成了必须失败的测试。
第一版留下一个开放问题:MAE 说"直接 resize"比正确的 z-buffer 更好(全量程低 23.8%、 近距段低 27%)。根因不是 z-buffer 做错了,而是参考的定义方式在惩罚它:z-buffer 在一个 像素的覆盖足迹内取最近点,而参考是目标像素中心的一个点采样;在深度梯度面上,足迹内的 最近点系统性偏近。于是结论只能是"当前 setup 判不了注册方式"。
现在换判据(include/rvk/kernel/boundary.hpp):不再绕道参考的点值,直接测那个性质本身。
参考只负责说"这条边在哪、有多高",输出负责回答两件事 —— 值对不对(幽灵层:落在两层之间、
不对应任何真实表面的值)、位置对不对(丢失:边被整体挪走)。两者分开计,因为它们对应
两种不同的错误做法。
实测(rvk ablation,合成场景 12 帧):
| 变体 | MAE | MAE 相对变化 | 边界分 | 幽灵率 | 丢边率 |
|---|---|---|---|---|---|
| 正确(z-buffer + 冲突门控) | 44.63 mm | — | 0.714 | 11.9% | 18.9% |
| 同像素取均值 | 41.24 mm | −7.6% | 0.646 | 15.1% | 23.8% |
| 直接 resize(课件的反面教材) | 33.98 mm | −23.9% | 0.645 | 11.9% | 26.7% |
| 关掉冲突门控 | 43.61 mm | −2.3% | 0.699 | 12.5% | 20.1% |
两个判据给出相反的排名,而结构那一侧才是对的:MAE 把 resize 排第一,边界分把它排到末位、
并指出它的失效档是"边被整体挪走"(丢边率最高);"取均值"则各归其档(幽灵率最高)。
这条对照写成了会失败的测试:tests/pipeline/test_pipeline.cpp 的
SecondYardstickDisagreesWithMaeAboutRegistration。
这张表是哪个配置跑出来的(不写清就没法复现):rvk ablation --frames 12,合成源默认配置
—— 种子 20260921、双目 160×120、目标 80×60、相位差 20 ms、无俯仰、侧移 0.30 m/s。
同一组数字另有黄金值测试守着(AblationNumbersMatchTheOnesPrintedInTheReadme):
容差取到印刷精度(MAE ±0.005 mm、边界分 ±0.0005),所以算法一变就会红,逼人同步这张表。
两条配套约定,都是同一条不变量②的变体:
- 边太少时比例是 NaN 而不是 0(
meaningful=false):报 0 会被读成"边全丢了",而事实是 "这个场景里没有可供判定的边"。报告与消融表写「—」。 - 阈值判不出"边":只按"相邻差 > 阈值"判边,在倾斜地面上每条扫描线都成了边 —— 一个没有
结构的场景会产出一堆漂亮比例。所以一条边还要相对局部梯度显著(见
boundary.hpp)。
| 源 | 给什么 | 在验证链里的角色 |
|---|---|---|
Orbbec Gemini 305(本机 USB 2bc5:0840) |
深度 Z16、IR 灰度、彩色 YUYV、BA81 真 Bayer、出厂内外参 | 真实输入 + 真实缺陷 + 标定真值对照 + Bayer 真值对照 |
| TUM RGB-D | Kinect 结构光深度 + IMU + 动捕轨迹 | 独立参考(评双目)+ V3 状态融合(第15章预热) |
| KITTI | 真实双目对 + 稀疏 LiDAR 真值 + 位姿 | 真实双目输入 + 独立参考(激光飞行时间) |
| 真 RAW(libraw) | 线性 RAW 12/14 bit | 第 1 节成像链(相机的 BA81 只有 8 bit,动态范围不够) |
参考之所以能"独立",是因为原理互不相同:结构光 / 激光飞行时间 / 动捕 / 线性 RAW 四种原理, 互相都不是同一个东西。
设备:Orbbec Gemini 305(USB 2bc5:0840),10 个 video 节点(奇数节点是 UVC metadata)
video0 Z16 深度 ← 裸 V4L2 抓不到(实测两次失败),需走 Orbbec SDK
video2 GREY 红外 ← 只有一条!所以裸 V4L2 拿不到"双目红外对"
video4 BA81 Bayer ← 真原始马赛克,可做"自研去马赛克 vs 相机 ISP"对照
video6 YUYV 彩色 ← 已过 ISP
出厂标定(已从旧工程现场抓取并核实):
fx = fy = 373.983 px cx = 323.0 cy = 236.0 (640×480) hfov ≈ 81°
baseline = 0.018080 m = 18.08 mm
基线只有 18 mm —— 这决定了很多事:σZ = Z²/(fx·B)·σd,比 120 mm 基线劣化 6.6 倍。
| 距离 | 视差 fx·B/Z |
|---|---|
| 0.5 m | 13.5 px |
| 1.0 m | 6.8 px |
| 2.0 m | 3.4 px |
| 5.0 m | 1.35 px |
即这是一台近距传感器,2 m 外基本不可用。这不是缺陷,是它让"基线是深度精度的主导杠杆"变成能用真实数字演示的事情。
Orbbec SDK:v2(旧 ELF 的 ldd 显示依赖 libOrbbecSDK.so.2),MIT 许可,GitHub Release 的
linux_x86_64.tar.gz 约 10 MB。宿主上已无 SDK(只剩编译产物),故由 scripts/fetch_orbbec_sdk.sh vendor。
基底 isaacros_yx:jazzy(本地已有,零拉取):Ubuntu 24.04 + ROS 2 Jazzy + g++ 13.3 + cmake 3.28 +
Eigen 3.4 + pybind11 + gtest 现成;OpenCV C++ / libraw / numpy 等在 Dockerfile 里补装。
cd robot_vision_kit
docker compose -f docker/compose.yaml build # 构建期会跑 ctest
docker compose -f docker/compose.yaml run --rm rvk rvk-build # 就地重编 + 跑测试
docker compose -f docker/compose.yaml run --rm rvk bash # 进容器
bash scripts/fetch_data.sh tum freiburg1_xyz # 取数据集(挂载到 ./data)
bash scripts/fetch_orbbec_sdk.sh # 取 Orbbec SDK有意不装(默认关,省 2 GB 以上):libpcl-dev(会拉进 VTK9/Boost/OpenNI)、libceres-dev
(12 个参数的标定用 Eigen 手写 Gauss-Newton 就够)、ros-jazzy-pcl-conversions。
| 规矩 | 落实方式 |
|---|---|
| 实现用 C++17 | rclcpp(39 个包声明 cxx_std_17)、PCL(cxx_std_14)、Ceres 都要求 >C++11 |
| 公开头只能用 C++11 | rvk_abi_probe:一个只包含全部公开头、按 -std=c++11 -Werror 编译的探针目标。另有一个 rvk_abi_probe_pure 只挂公开头目录,用来抓"公开头里 include 了第三方" |
这条探针不是形式主义:写 metrics.hpp 时它立刻逮到一个真问题 —— 数组成员在构造体内赋值
要求元素可默认构造,而 ConfidenceBin 故意没有默认构造。同一段代码在 C++17 下是通过的,
正是"C++17 能过、C++11 不能过"的典型退化。
docs/architecture/rvk-architecture.svg(源头,可 diff、可缩放)与同名 .png(位图镜像,
由 SVG 光栅化而来,两者必然一致)。重新生成:python3 scripts/render_architecture.py。
图里状态即图例:实线+绿 = 已实现且有测试;琥珀 = 代码已写但从未编译/运行; 虚线+灰 = 未实现。所以这张图同时是架构图与进度图。
系统设计(为什么这样分层、用了哪些模式、每个决定的代价)在 docs/architecture/DESIGN.md:
架构的第一驱动力是「让错误写不出来」,不是"好扩展";模式分三档(被不变量逼出来的 /
顺手划算的 / 刻意不用的);重构范围与不动的部分、10 条 ADR 与它们的代价都在那里。
两者的分工:设计回答"为什么",本文件回答"现在到哪了",架构图回答"哪块还没做"。
include/rvk/kernel/ 冻结 ABI:status types frame reference metrics boundary
+ gates geometry register fusion rolling defects(13 个头,全部受探针守卫)
include/rvk/pipeline/ spec registry pipeline commands
include/rvk/algo|source|viz/ 适配层的公开声明(第三方只许挂在 src/ 的实现里)
src/kernel/ rvk_core —— 零第三方依赖,所以快速测试路径真的快
precheck.hpp 是内部头:两个指标共用的"评估前校验"(独立/同坐标系/同形状)
src/pipeline/ rvk_pipeline —— 显式拓扑 + Builder + Registry(认得 core,不认得任何库)
src/algo/vision/ 第三方只挂在这一层(当前只有自研块匹配;OpenCV/libraw/Ceres 属 M1/M3)
src/source/synth|orbbec/ 合成源(零依赖)· Orbbec 相机探针(RVK_WITH_ORBBEC 才编入)
src/source/files/ 公开数据集的读入:TUM 配对/位姿、KITTI 标定、雷达 .bin → 稀疏参考
(零依赖);另含图像解码(OpenCV 可选)与 KITTI 源适配
—— 单路评估形态:双目待评 + LiDAR 独立参考(见 spec.hpp)
src/viz/ 伪彩与落盘("无效=黑"的约定住在这里)
src/shell/cli/ 目前唯一的壳(pybind11 / rclcpp 两个壳在架构图里是"未实现")
tests/abi/ ABI 探针(编译期守卫,清单与 include/rvk 一一对应)
tests/kernel/ 内核单测:秒级、不碰数据集、不碰硬件
test_support.hpp:造帧/造内参/造待评/造参考 —— 只有这一份
tests/viz/ 报告渲染与伪彩:"没测到写 —、绝不写 0"这类规则由它守
tests/pipeline/ 管线端到端(含"两个判据排名相反"那条依赖场景的经验断言、读数与台账)
tests/source/ 数据集读入:用**手工构造的合成文件**验配对/标定/投影的约定
tests/support/ 跨测试文件共用的夹具(基准 spec、注册表)—— 报告测试必须在
**同一个**基准场景上跑,各写一份就会分叉
scripts/ci.sh 本机 CI:干净配置 + 警告即错误 + 全套 ctest(接 CI 服务时调它)
scripts/check_cli_contract.sh CLI 的退出码与提示契约(0 成功 · 1 结论不合格 · 2 运行出错)
scripts/check_architecture_claims.py 架构图守卫:图里的数字对着仓库核 + 图必须是生成的
docs/architecture/DESIGN.md 系统设计:驱动力 / 评估脊 / 模式三档映射 / 重构范围 / 10 条 ADR
cppcheck.supp 静态分析的已知**工具**误报(每条都带最小复现)
依赖只向下:rvk_abi ← rvk_core ← {rvk_pipeline, rvk_algo_*, rvk_source_*, rvk_viz} ← 壳。
这张图就是进度图:还没做的模块(TUM/KITTI loader、IMG/ISP 链、标定、pybind11/rclcpp 壳)
不占目录 —— git 跟踪不了空目录,占位目录一入库就消失,反而让"目录即进度"失真。
本图与 docs/architecture/rvk-architecture.svg(虚线+灰 = 未实现)才是进度的唯一表述。
"为什么这么写"在哪:在每个头文件顶部的注释里(那是判据与坑的现场),本文件讲全局取舍。 两者都不重复对方 —— 判据改了而文档没跟上,是这类工程最容易出的错。
已完成:
- ✅ 骨架与全部目标;
rvk_abi_probe/rvk_abi_probe_pure守卫生效 - ✅ 内核 13 个公开头 + 实现;全部 26 个公开头(include/rvk 下的每一个)都按
-std=c++11 -Werror -Wpedantic编过 —— 这条以前只覆盖 5 个,现在清单与include/rvk一一对应;另有tests/abi/test_abi_layout.cpp把尺寸/偏移/枚举数值钉死 ("冻结 ABI"不只是"能编过",还要"没变") - ✅ 版本控制(2026-09-22 起)、MIT LICENSE、
scripts/ci.sh(干净配置 + 警告即错误 + 全套测试) - ✅ 30 个 ctest 用例全绿(零依赖与 OpenCV 两个变体都跑过): 内核单测 13 · 管线端到端 12(含单路评估 5)· 界面层(伪彩 8 / 报告 5 / 落盘 5 / 台账 7)· 数据集与图像 IO 9 · CLI 端到端 5 · ABI 探针 + 布局守卫 · 三道源码守卫 (引号守卫 / cppcheck / 架构图不许说谎)
- ✅ 两条不变量 + 第二把尺子(结构保持性)都已落地并被测试钉住
- ✅ 命令:
run(含--source kitti --data DIR)/ablation/sweep/bench/ledger/data inspect/camera probe;报告有文本与 JSON 两种渲染 - ✅ 真实数据这条路的格式级端到端已通(OpenCV 变体):KITTI 目录 → 读图 → 读标定(基线从
P1解出)→ 读激光 → 投成稀疏独立参考 → 单路评估;实测估出来的深度与已知值一致 (自造数据:MAE 0.00 mm)。真实 KITTI 数据集仍未跑(约 12 GB 下载,本机网络勉强) - ✅ 实验台账:
--record 台账.jsonl追加、rvk ledger出对比表 —— 补上「跑 → 记 → 比」闭环, 于是"这次改动让 MAE 变好还是变坏"可回答(此前结论不可累积) - ✅ 消融台账现在同时给 MAE 与边界分 —— 那个"判不了注册方式"的开放问题已由第二把尺子解开
- ✅ Dockerfile / compose / entrypoint / 拉取脚本(尚未在容器内验证)
- 镜像还没在容器里跑过,所以 Docker 那条路是"写好了但未验证"。
- 边界分还没进验收判据(
rvk accept的硬门限):现在还不知道真实数据上"多少分算好", 先进台账、不进 PASS/FAIL(见第九节)。 - 真实 KITTI 数据集本身没跑过:格式级端到端(自造数据)已通,但那验的是格式与接口, 不验数据集的性质。边界分在真实场景下的分辨力、以及真实立体匹配的覆盖率,都仍然未知。 下一步是下载一小段 KITTI(或先用 TUM)跑一次,把结果记进台账。
- 结构保持性只测水平与竖直的相邻阶跃。斜向边缘、以及"边被挪走并且转了个角度"这两类 失效没有专门判据。
.clang-format与.clang-tidy写了但从未在本机跑过(没装这两个工具,装它们要 sudo)。 它们是按现有代码的写法写的;第一次用请先--dry-run看差异。src/source/orbbec/camera_probe.cpp(265 行)只有编译守卫与 CLI 冒烟,真机行为未验 (要 Orbbec SDK + 相机)。- 没有远端仓库,所以没有 CI 服务:
scripts/ci.sh是"能跑的那一半",接 GitHub Actions 时 workflow 里只需要调它 —— 判据只有一份。
M0 待完成:
rvk camera probe:打印 Gemini 305 可用流(并回答"Orbbec SDK 是否暴露 IR_LEFT/IR_RIGHT")- TUM loader + 一帧最小闭环(RGB-D → 深度 → 独立参考检查 → MAE + coverage)
- 第一条"预测 vs 实测":σZ ∝ Z² 的实测斜率 vs 解析预测
之后 M1 成像链(RAW→ISP)/M2 深度测量/M3 标定/M4 融合/M5 状态/M6 ROS 2 壳。
| 现象 | 根因 | 处置 |
|---|---|---|
docker compose build 看起来"卡死",十几分钟没动静 |
Dockerfile 顶部的 # syntax=docker/dockerfile:1.7 让 buildkit 去 docker.io 拉语法前端镜像;本机到 docker.io 实测约 200 KB/s(12 MB 的层要拉十几分钟) |
删掉 # syntax 指令 —— 本文件没用任何 1.7 特有语法,内置前端足够。构建从此不碰 Docker Hub |
同上,apt-get update 也可能挂住 |
packages.ros.org 从本机不可达(超时),apt 会在不可达的源上反复重试 |
把 ROS 源换成清华镜像(tuna / ustc / aliyun 实测均 200),并移除无关的 Jetson 源、给 apt 设 20 s 超时 |
| 裸 V4L2 抓不到深度(实测两次失败) | UVC 深度相机的流配置需要 SDK 参与,v4l2-ctl --set-fmt-video 不够 |
深度与双目都走 Orbbec SDK v2 |
| 裸 V4L2 只有一条 GREY 流 | Gemini 305 的 IR 左右未通过标准 UVC 暴露 | 用 SDK 取 OB_SENSOR_IR_LEFT / IR_RIGHT(旧工程已验证可行) |
| 公开头在 C++17 下编过、C++11 下编不过 | 数组成员在构造体内赋值要求元素可默认构造,而 ConfidenceBin 故意没有默认构造 |
rvk_abi_probe(-std=c++11 -Werror)当场逮住;改用 mem-init-list 花括号初始化 |
Result<T>::value() 的兜底要求 T 可默认构造 |
与本工程最要紧的两个类型(MetricSample/ConfidenceBins 故意不可默认构造)冲突 |
去掉兜底:在失败结果上取值会写到 stderr 并 SIGABRT —— 不把"出错"伪装成"一个看起来正常的值" |
宿主 mplot3d 不可用(旧 nspkg.pth 污染) |
在宿主的 /usr/local/lib/python3/dist-packages |
容器是干净 Python 环境,该文件不会进镜像 —— 很可能自然恢复(待容器内验证) |
| 一条没有结构的纯斜坡被判出成串的"参考边" | 判边只看"相邻差 > 阈值",而倾斜地面上每像素深度差本身就超过 5 cm(本场景正是如此) | 加第二条判据:相对局部梯度显著(` |
| 加了上面那条之后,纯斜坡的两端仍各长出一条假边 | 扫描线两端的相邻差越界取不到,我原本写成"任一侧取不到就退回只看幅度"——把"缺数据"当成了放行理由 | 改成"取得到的那一侧照样算数,两侧都取不到才退回"。SlopedGroundIsNotASequenceOfEdges 当场逮住 |
| 两个指标各写一遍"评估前校验"(独立/坐标系/形状) | 抄两遍就是两份会漂移的规则 —— 以后加一条检查时只改一处,另一条路径悄悄少一道门 | 抽出 src/kernel/precheck.hpp,metrics.cpp 与 boundary.cpp 共用;assert_independent 当年也是被这么抽出来的 |
| 24 个公开头都写着"必须能按 C++11 编",探针里却只列了 5 个 | 声明与守卫之间差了 19 个头 —— 写在注释里的约束就是没有守卫的约束,迟早被无声改掉 | 探针清单改成与 include/rvk 一一对应;实测这 24 个头当前确实都能编过(所以不是"有债",是"没锁门") |
"值来自 YAML 与命令行"(spec.hpp/spec.cpp),而仓库里从来没有 YAML 读取代码 |
注释声明了一件不存在的事 —— 与"用 SGBM 评 SGBM"同类:不报错,只误导 | 改成"当前只有命令行",并把这条教训写在注释里;空着的 configs/ 一并删掉 |
boundary_score_description() 注释写着"进报告",实际只有探针与测试引用它 |
声明与实现不一致(同上一条的另一种形态) | 真的接进报告:那一节的说明直接问内核要,不在报告里另写一份解释 |
ctest 的 WILL_FAIL + PASS_REGULAR_EXPRESSION 把正则判据一起反转 |
实测日志原文:Required regular expression found. Regex=[...] 被记成 Test Fail Reason —— 于是"输出里有正确提示"这件事反而导致失败 |
错误路径改由 scripts/check_cli_contract.sh 判(退出码与提示一起判),原因写在脚本注释里 |
cppcheck 抑制文件里出现"只有 # 的空注释行" |
解析器报 Failed to add suppression. No id. 并退出,输出是空的 —— 看起来与"零发现"一模一样。写错一个空行,静态检查就成了永远通过的空壳 |
抑制文件里不留空注释行;测试里加 --error-exitcode=1(否则 cppcheck 有发现时也返回 0,守卫永远绿) |
本机 gcc 输出是中文的(警告:),而我一直用 grep "warning:" 判"有没有警告" |
判据与工具的输出语言绑在一起 —— 换台机器换个 locale,同一句 grep 就失灵了。这次正是它漏掉了 -Wdangling-else(gtest 的 EXPECT_* 展开成 if/else,外面套一个不带括号的 if 就触发) |
判"有没有警告"只认 -Werror(scripts/ci.sh 默认开着),不认任何文本匹配 |
| 架构图停在三周前:图里还写着"五块未移植""没有 git 仓库",而代码早就长过去了 | 图不会报错,ctest 也不会 —— 一张会骗人的进度图比没有进度图更糟(README 却把"架构图即进度图"当作唯一表述) |
图里出现的数字收进 CLAIMS,由 scripts/check_architecture_claims.py 对着仓库核(内核头数/公开头总数/ctest 用例数),并检查磁盘上的 SVG 确实是脚本输出 —— 拦住"手改图"与"改了忘了重跑" |
同一张图里还有两处只有打开才看得见的错:矩阵的 ✔/✗ 在 PNG 里全是豆腐块;数据里的 Markdown ** 被字面印出星号 |
✔(U+2714)/✗(U+2717) 在中文字体(Noto Sans CJK)里没有字形 —— SVG 在浏览器里靠回退字体看不出来,PNG 直接光栅化就露了;Markdown 在图上根本不渲染 |
记号改用 √(U+221A) 与 ×(U+00D7);渲染器统一做一次去 Markdown(_plain)。两个文件都"生成成功",只有把 PNG 打开看才发现 —— 所以生成图之后要真的看一眼 |
公开头注释里写了 velodyne/*.bin 这种路径 |
注释里出现 /* 会触发 -Wcomment,而 ABI 探针按 -Werror 编 —— 一条只有守卫看得见的错 |
改成"velodyne 目录里的 .bin";这类写法在文档里很自然,在 C++ 注释里不行 |
rvk data inspect 把 TUM 目录报成"缺 calib.txt / velodyne" |
缺失清单没按数据集种类分开 —— 乱报缺失比不报更糟:会让人去补无关的东西,或者干脆不再看这份报告 | 按 kind 分别报缺什么,并加断言:TUM 目录的 missing 里不许出现 KITTI 的文件名 |
| 目录不存在与"目录在但文件不齐"混成一个结论 | 路径打错会看起来像"数据集缺东西",于是去补数据而不是改路径 | 库里分开报(目录不存在 → kIo → 退出码 2;不齐 → 结论 1),并写进 CLI 契约 |
| 写激光投影测试时把"同一个像素"算成了"同一个 x" | 透视:同像素要求 x/z 相同;同一个 x 在更近的深度上会投到更外的列 | 测试先算错、被自己的断言逮住((1,0,10) 落第 10 列、(1,0,5) 落第 20 列);改用 (0.5,0,5) 才对得上 |
| 校验脚本拿着旧数字在核(13 被读成 99),差点把守卫变成帮凶 | RVK_WITH_OPENCV 这个选项从来没人用过 |
声明了选项,却没有 find_package、没人定义那个宏 —— 于是 artifact_sinks.cpp 里的 PNG 分支是死代码(永远编不到)。"看起来配好了、其实没接上" |
| 单路模式下报错说"待评深度缺少坐标系标签",实际缺标签的是第二路 | 分岔点原本在"构造第二路"之后 —— 而单路模式没有第二路,先构造它必然失败,报错还指向错误的地方 | 分岔点前移到碰第二路之前;这条教训写进代码注释(报错指向错误的地方,比不报错更费时间) |
| 台账把"合成场景的 MAE"与"KITTI 的 MAE"相减,印出一个没有意义的差值 | 比较规则只比"场景名",而两条实验都叫 "run" | 台账加 source 字段;ΔMAE 改成"场景名与数据源都相同"才给 |
| 自造的 KITTI 测试数据"零结论",查了半天是纹理太弱 | 块匹配有纹理门限(texture_threshold),而斜坡纹理的二阶导几乎为 0;换成伪随机纹理后正常 |
这是旧工程"周期/平坦纹理害 SUT"那条老坑的翻版;写进 README,免得下次又查半天 |
Python 的字节码缓存判据是 (mtime, 源文件大小):一次"大小不变、又落在同一秒"的编辑被认为没改过,__pycache__ 里的旧 .pyc 被复用 |
守卫脚本绕过导入机制(自己 compile() 源码执行,见 check_architecture_claims.py 的注释);python3 foo.py 直接跑不受影响,只有 import 会命中缓存 |
| 不做 | 原因 |
|---|---|
| 用仿真器给真值 | 用户定案:用公开数据集 + 真实相机。缺陷注入改为在独立真值上叠加,比仿真更真实 |
| 用出厂标定当"真值"评深度 | 它是参数不是量测。已写成负向测试 |
| 主路径依赖 GPU | 容器基底无 CUDA。FFS(TensorRT)做成可选目标 RVK_WITH_FFS,需要 --gpus all 与 CUDA 基底 |
| 默认装 PCL / Ceres | 见第三节。需要时用 RVK_WITH_PCL / RVK_WITH_CERES 打开 |
在 core 里 include OpenCV/Eigen/PCL |
由 rvk_abi_probe_pure 强制 |
| 把边界分当验收的硬门限 | 现在还不知道真实数据上"多少分算好"。先进台账与报告、进测试,不进 PASS/FAIL —— 拿一个没标定过的阈值去卡人,只会得到"为了过门限而调参" |
MIT。课程与官方 snow_rover 参考实现归原作者。Orbbec SDK 为 MIT,由脚本单独拉取、不随本仓库分发。