Files
VSAG_AUTO_AIM_PRACTICE_27RM/README.md
T
mzxwww 19ac43da6f 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
2026-09-16 21:08:03 +08:00

214 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 27RM
RoboMaster 视觉工程(MindVision 相机 + OpenCV)。
## 构建
```bash
xmake # 产出四个程序(见下)
xmake run 27RM # 跑主程序
```
### VSCode + clangd
`compile_commands.json` 含本机绝对路径,**不入库**,克隆后自己生成一次:
```bash
xmake project -k compile_commands .vscode
```
(`.vscode/settings.json` 里已配置 `clangd.arguments: ["--compile-commands-dir=.vscode"]`)
## 程序清单
| 程序 | 用途 |
|---|---|
| `27RM` | 自瞄主程序 |
| `capture_calib` | **相机标定图采集工具** |
| `calibrate` | **标定计算工具**(照片 → 内参/畸变 → `configs/camera.yaml`) |
| `pose_test` | **几何链路验证工具**(4 角点 → 距离/yaw/pitch,含合成自检) |
## 配置
```
configs/
├── camera.yaml # 相机内参 + 畸变 + 标定元信息(由 calibrate 生成,需入库)
└── camera_runtime.yaml # 运行时参数:曝光/增益/白平衡/帧率(手工维护,需入库)
```
代码里这样用(`inc/camera_calib.hpp`):
```cpp
#include "camera_calib.hpp"
CameraCalib calib;
if (!calib.load("configs/camera.yaml")) return -1; // 启动时加载一次
cv::Mat undist;
calib.undistort(frame, undist); // 每帧去畸变
// 装甲板四角点(左下、左上、右上、右下)→ 位姿
std::array<cv::Point2f, 4> corners{lb, lt, rt, rb};
if (auto pose = calib.solvePose(corners, ArmorSize::small())) {
double yaw, pitch;
CameraCalib::poseToYawPitch(pose->tvec, yaw, pitch);
// pose->distance_mm / pose->reproj_error_px 可用于过滤坏帧
}
```
`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**(卡尺实测值,不是打印标称值)
> **相机**:MV-SUA131GC(1/3" CMOS,3.75μm 像元)+ MV-LD-M12-2.8MM-C 镜头,输出 1280×1024
> ⚠️ M12 镜头**调焦会改变有效焦距**,标定和使用时必须保持对焦环位置一致
### 1. 采集标定图
```bash
# 用 MindVision 相机(机器人上那台)
./build/linux/x86_64/debug/capture_calib --square 20.0
# 或改用 USB 摄像头
./build/linux/x86_64/debug/capture_calib --usb 0 --square 20.0
```
按键:
| 键 | 功能 |
|---|---|
| 空格 | 保存当前帧(默认要求检测到棋盘格) |
| `f` | 强制保存(未检测到也存) |
| `d` | 删除上一张 |
| `c` | 开关实时角点检测显示 |
| `g` | 灰度预览(排查过曝/反光) |
| `q` / ESC | 退出 |
拍摄要求:
- 标定板出现在画面**中心、四角、边缘**(2.8mm 广角镜头边缘畸变最大)
- 姿态多样:正对、倾斜、旋转、俯仰
- 清晰不糊、光照均匀、无反光、板子贴平硬板
- **20~30 张**,保存于 `calib_images/`(已加入 .gitignore,不会入库)
### 2. 计算内参
```bash
./build/linux/x86_64/debug/calibrate --dir calib_images --square 20.0 # 只算顶层
./build/linux/x86_64/debug/calibrate --dir calib_all --square 20.0 # 全部 31 张(推荐)
./build/linux/x86_64/debug/calibrate --square 20.0 --show # 逐张查看角点与重投影
```
工具会输出重投影误差 RMS、内参 K、畸变系数、每张图误差排行,并写入 `camera_calib.yml`。
| 重投影误差 | 评价 |
|---|---|
| < 0.3 px | 很好 |
| 0.3 ~ 0.5 px | 可接受 |
| > 0.5 px | 建议重拍 |
> **当前标定结果**(`calib_all/` 31 张,20mm 方格):
> fx=657.47, fy=658.25, cx=625.64, cy=509.00,RMS **0.483 px**
> **距离验证**:卷尺 700mm vs 程序 687~697mm,误差 **< 2%** ✓
### ⚠️ 标定图必须同时具备「三种多样性」
| 维度 | 作用 | 不满足的后果 |
|---|---|---|
| **画面位置**(九宫格覆盖) | 约束**畸变系数** | 边缘畸变解不出来,RMS 偏高 |
| **倾角**(正对/上下倾/左右倾/大角度) | 约束**焦距与主点** | 参数耦合,精度下降 |
| **距离**(近/中/远,板子占画面 40%/20%/8%) | 约束**尺度** ⭐ | **fx 无法确定**:所有图同距离时,"焦距×距离"可整体缩放而图像不变,优化器会落到错误值,**且 RMS 依然很低**(最阴险的坑) |
> 实测教训:一套 19 张、覆盖 9/9 但**距离相近**的图,标出的 fx=883(实际 659,偏差 34%),距离误差直接 +32%;
> 与旧的 12 张(距离 24~65cm)合并后 fx 回到 657,误差 <2%。**距离多样性是 fx 的锚点,不可省。**
### 3. 使用内参
推荐直接用封装好的 `CameraCalib`(见上方「配置」一节),它已经处理了配置路径、去畸变缓存和位姿门限。
若要手动读:
```cpp
cv::Mat K, D;
cv::FileStorage fs("configs/camera.yaml", cv::FileStorage::READ);
fs["camera_matrix"] >> K;
fs["dist_coeffs"] >> D;
```
之后用 `cv::undistort` / `cv::solvePnP` 做去畸变与位姿解算。
### 4. 距离验证(改完参数一定要做)
```bash
~/yolo-env/bin/python ~/rm_vision/verify_distance.py 图片.jpg --square 20.0 --distance 647
```
用卷尺量出真实距离,与程序算出的距离对比:误差 < 2% 说明标定可用。
## 提交规则(.gitignore)
| 内容 | 是否入库 | 原因 |
|---|---|---|
| `src/` `inc/` `xmake.lua` `README.md` | ✅ 入库 | 源码与构建脚本 |
| `configs/camera.yaml` | ✅ **必须入库** | 相机内参,全队共用 |
| `calib_images/` `calib_all/` | ❌ 不入库 | 标定照片,20MB+ 且随时重拍 |
| `*.jpg` `*.png` `*.bmp` `*.avi` `*.mp4` | ❌ 不入库 | 图片视频一律不入库 |
| `build/` `.xmake/` `Camera/` `*.log` | ❌ 不入库 | 编译产物、SDK 运行时日志 |
| `.vscode/compile_commands.json` | ❌ 不入库 | xmake 生成,含本机绝对路径 |
确有少量配图(例如 README 插图)要提交时,用强制添加:
```bash
git add -f docs/xxx.png
```
### ⚠️ 标定图请自己留备份
照片不入库 = 只有本机一份。重装系统前记得把 `calib_images/` 拷到网盘或 U 盘,
否则将来想重新标定(换镜头、调焦后)就没有原始数据了。
标定**结果**在 `configs/camera.yaml` 里,但结果无法替代原始照片。