Add pose_test tool and harden solvePose; expose camera controls

- pose_test: verify the geometry chain (4 corners -> distance/yaw/pitch)
  before any detector exists; modes: camera + mouse clicks, --image,
  --corners (headless), --selftest (synthetic round-trip, no camera needed)
- solvePose: undistort corners first, then solvePnPGeneric(IPPE) and pick
  the best candidate; reject solutions behind the camera (z<=0) or with
  reprojection error > 8px
  - fixes IPPE + distortion + in-plane rotation returning garbage
  - 4 coplanar points: cyclic/mirrored orderings give the same quad and
    the same pose (harmless); crossing orderings blow up reprojection
    and are rejected
- camera_set: implement setAeState/setExposureTime/setAnalogGain(+-Times)/
  setWbMode/setFrameSpeed; configs/camera_runtime.yaml drives them
- selftest passes all 6 synthetic cases; verified on 6 real photos
  (chessboard outer rect 180x120mm): distance 0.44-0.69m, reproj <2.5px
This commit is contained in:
2026-09-16 21:08:03 +08:00
parent c50be9a48d
commit 19ac43da6f
8 changed files with 724 additions and 30 deletions
+48 -2
View File
@@ -5,7 +5,7 @@ RoboMaster 视觉工程(MindVision 相机 + OpenCV)。
## 构建
```bash
xmake # 产出三个程序(见下)
xmake # 产出四个程序(见下)
xmake run 27RM # 跑主程序
```
@@ -26,12 +26,14 @@ xmake project -k compile_commands .vscode
| `27RM` | 自瞄主程序 |
| `capture_calib` | **相机标定图采集工具** |
| `calibrate` | **标定计算工具**(照片 → 内参/畸变 → `configs/camera.yaml`) |
| `pose_test` | **几何链路验证工具**(4 角点 → 距离/yaw/pitch,含合成自检) |
## 配置
```
configs/
└── camera.yaml # 相机内参 + 畸变 + 标定元信息(由 calibrate 生成,需入库)
├── camera.yaml # 相机内参 + 畸变 + 标定元信息(由 calibrate 生成,需入库)
└── camera_runtime.yaml # 运行时参数:曝光/增益/白平衡/帧率(手工维护,需入库)
```
代码里这样用(`inc/camera_calib.hpp`):
@@ -54,8 +56,52 @@ if (auto pose = calib.solvePose(corners, ArmorSize::small())) {
}
```
`solvePose` 内部已处理:去畸变 → `solvePnPGeneric`(IPPE) 取回全部候选解 →
筛掉「相机后方 (z<=0)」「重投影 > 8px」→ 取最优。详情见源码注释。
相机参数设置(`inc/camera_set.hpp`,`initializeCamera` 之后调用):
```cpp
cam.setAeState(false); // 手动曝光(比赛必须,否则画面亮度漂移)
cam.setExposureTime(3000.0); // 曝光时间,微秒
cam.setAnalogGainTimes(2.0); // 模拟增益,倍率(内部按 fAnalogGainStep 换算)
cam.setWbMode(false); // 关自动白平衡(颜色阈值才稳定)
cam.setFrameSpeed(2); // 帧率档位 0/1/2
```
也可以直接由配置驱动:`applyRuntimeParams` 的示例见 `src/pose_test.cpp`。
> 程序必须在工程根目录运行(`configs/` 是相对路径),或自行传绝对路径。
## pose_test:几何链路验证
写检测器之前,先用它验证「标定 + 坐标约定 + PnP 解算」:
拿真的装甲板(或打印 135mm 矩形)摆在相机前,鼠标点 4 个角,
输出的距离/yaw/pitch 与卷尺、量角器核对。之后接检测器出问题必是检测的锅。
```bash
./build/linux/x86_64/debug/pose_test # 相机 + 鼠标点角
./build/linux/x86_64/debug/pose_test --image 照片.jpg # 图片模式
./build/linux/x86_64/debug/pose_test --selftest # 合成自检(无需相机/显示器)
# 脚本/无窗口:直接给 4 个角点(左下 左上 右上 右下),--size 自定义矩形(毫米)
./build/linux/x86_64/debug/pose_test --image 照片.jpg --size 180x120 \
--corners x1,y1 x2,y2 x3,y3 x4,y4
```
- 相机模式按键:`r` 清除重选 | `b` 切换大小装甲 | `u` 切换去畸变显示 | `s` 截图 | `q` 退出
- `--selftest` 自检已通过:正前方/右偏/上仰/复合/板内滚转均精确恢复,
交叉乱序被重投影门限正确拒绝
- 真实照片验证:6 张标定图(棋盘格外圈 180×120mm)距离 0.44–0.69m、
重投影 0.3–2.5px,与卷尺真值一致
### 两个踩过的坑(写检测器时别重蹈)
1. **IPPE + 畸变 + 板内旋转 = 垃圾解**(OpenCV 4.x/5.x 都中招)。
解法:角点先去畸变再喂 IPPE,重投影回原图算(`solvePose` 已内置)。
2. **4 个共面点的顺序错误查不出来**:循环/镜像顺序 = 同一个四边形 =
同一个解(无害);交叉乱序会被重投影门限拦下。真正需要 roll 门限的是
「检测器给出假四边形」场景,等写检测器时照搬 jlu 的 `roll_thres 15°`。
## 相机标定流程
> **本工程标定板规格**:10 × 7 内角点、**方格 20.0 mm**(卡尺实测值,不是打印标称值)