214 lines
20 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.

# 目标测光曝光控制模块需求审查
审查状态:用户已于 2026-09-22 同意按 A~E 建议实施,并确认模块应可复用。
本稿保留审查时的设计依据及建议表述;A~E 已获确认。当前实施记录见 `实施记录.md`,验证门禁见 `.codex/delivery-integrity.json`。
主体依据是用户提供的《视觉引导抓取的目标测光自动曝光控制方案》(文件名为《视觉引导抓取自动曝光控制方案.docx》);用户本次补充约束优先。下文以“附件”“补充”“建议,待确认”区分来源,不把附件中的实施建议当作本轮执行授权。
## 1. 当前目录与实施边界
- 新模块目录:`E:\02_Modules\20_Libraries\TargetExposureControl`。
- 父目录不是 Git 仓库;其中是独立的 `CalibrationDeviceAdapters`、`VisionCalibrationCore`、`VizumCameraKit` 目录。
- 已检查的两个相邻库使用 C++17 和 CMake。`CalibrationDeviceAdapters` 的构建文件引用 OpenCV 4.7;这不等于已经确定本模块的接入工程、依赖版本或实际构建环境。
- 本模块负责图像测光、历史、预测、控制状态和曝光建议。调用方负责识别、提供最终 ROI、采图和设置相机。
- 不修改已有业务库,不接相机 SDK、机器人或三维流程,不增加自动增益、检测前亮度增强、曝光扫描、HDR 或额外采图流程。
- 附件中的增益兜底、检测前归一化、曝光包围等不属于本次已授权模块边界。
## 2. 附件要求—用户补充—实现位置
下列文件是拟定落点,当前尚未创建源码骨架。
| 附件要求 | 用户补充或限定 | 拟实现位置 |
|---|---|---|
| 第 2 章:第 N 帧反馈用于第 N+1 帧 | 模块只建议曝光,实际曝光由调用方逐帧传入 | 公共接口 `include/target_exposure_control/`;调度 `src/exposure_control.cpp` |
| 第 3 章:目标区域测光,多目标汇总 | 矩形、旋转矩形、mask 按像素取并集,重叠只计一次,不取总包围框 | `src/metering.cpp` |
| 第 3 章:框可内缩 10%~20% | 最终 ROI 原样使用;内缩只作为调用方显式使用的辅助函数 | ROI 辅助接口及 `src/metering.cpp` |
| 第 3.1 节:上下各去掉 5% 后取均值 | 截尾可配置;无目标、空区域、裁剪后无像素、样本不足必须给出有效性和原因 | `src/metering.cpp` |
| 第 2、4 章:正常闭环只使用目标亮度 | 同时记录全局或固定中央区域的平均亮度,但不混入 TRACK 目标闭环 | `src/metering.cpp`、`src/history.cpp` |
| 第 4.2 节:可选灰度趋势外推,未给完整公式 | 最近 3 或 5 条有效目标记录,时间窗口默认示例为 5 分钟;绑定真实曝光、使用真实时间间隔 | `src/history.cpp`、`src/predictor.cpp`,属于补充实现 |
| 第 4、7 章:大误差比例、小误差 PI、死区保持 | 明确归一化误差;积分限幅与复位;预测只提供一个控制输入 | `src/controller.cpp` |
| 第 4.2 节:滞回、连续同方向确认、步长限制 | 参数可配置;正文和伪代码差异须先确认,见第 7 节 | `src/controller.cpp` |
| 第 2.1、7 章:CAPTURE、TRACK、LOST | 增加拍照前超时检查、一次初始化和跨状态的单次恢复预算 | `src/exposure_control.cpp` |
| CAPTURE 可档位轮询或全图粗调 | 选择经验曝光启动、全图或中央区域粗调恢复;不扫描曝光档 | 恢复逻辑及配置 |
| 第 4.1、6 章:示例目标灰度和曝光范围 | 目标灰度、阈值、曝光上下限均可配置;静止采图上限由调用方给定 | 公共 `Config`,`examples/` 中的配置示例 |
| 第 8 章:离线和现场验证 | 本批必须实际构建、运行无相机自动测试;不把模拟或回放称为实机成像验证 | `tests/`、`examples/minimal.cpp`、中文说明与验证记录 |
## 3. 输入、测光和输出契约
**每帧必需输入**:原始图像、最终目标 ROI、`has_usable_target`、采集时间戳、当帧实际曝光。ROI 存在与否不替代调用方的目标标志。曝光统一以微秒 `us` 表达;转换为毫秒只用于展示。
**测光规则**:
1. 多种区域可以混合取并集,先裁剪到图像边界,再按同一个二值集合统计。非法 mask 尺寸或数据格式直接报告错误,不隐式缩放。
2. 默认不内缩。显式内缩辅助函数的结果由调用方作为最终 ROI 传入,模块不再次内缩。
3. 全局测光采用整图平均,或配置固定中央区域平均;同一配置期间统计区域和灰度转换方法固定,不随目标框变化。
4. 目标测光在并集内截尾后取均值。建议每侧删除 `floor(像素数 × 截尾比例)` 个样本;比例小于 0.5,原始和保留像素数量均接受最小数量检查。
5. 无目标时目标亮度无效,即使 ROI 非空也不进入目标闭环。真实测得的全黑灰度 0 可以是有效测量,不能和“无测量”混为一谈。
6. 有目标但 ROI 无有效像素或像素不足时,报告测光无效并保持曝光;不向预测器或目标控制器传入伪造数值。
7. 每个新帧形成记录:时间、可选目标亮度、全局亮度及各自有效性、实际曝光、配置版本、原因。预测只筛选其中有效的目标记录。
8. 图像格式与灰度转换范围需按第 7 节确认。附件以 8 bit 灰度为例,不擅自为 12/16 bit 图像猜测有效位宽、黑电平或缩放范围。
**至少提供三个接口**:
| 接口 | 职责 | 关键输出 |
|---|---|---|
| `preCapture(当前时间, 配置及版本)` | 在拍照前检查首次启动、长期停机、历史过期、配置切换 | 是否触发本次初始化、经验曝光建议及单位、原因、当前状态 |
| `processFrame(帧输入, 可选的下一次计划采集时间)` | 测光、记录、预测、三态更新、计算下一帧建议 | 本帧实际曝光、状态、亮度及有效性、是否调整、建议曝光、原因 |
| `reset(原因)` | 显式清理预测、积分和状态计数,安排下一次拍照前初始化 | 复位原因、初始化待执行状态 |
附加可观测字段:窗口内有效目标数量、实际使用的 3/5 点数量、预测时刻、预测亮度、预测可用性、是否被选为控制输入、降级原因、实际误差与控制误差、原始修正量与限幅后建议、初始化原因、冻结余量、恢复机会是否已消耗。
建议对重复时间戳及乱序帧拒绝再次更新历史、积分、连续计数及建议,输出明确原因。拍照前时间和帧采集时间必须使用调用方同一时钟域;预测内部将时间差换算为秒。
## 4. 历史预测方案(补充实现,待确认)
附件仅提出“若干拍目标灰度趋势线性外推”。以下具体数学定义不属于附件原文。
### 4.1 预测什么
建议预测“单位曝光对应的目标灰度在下一次拍照时的值”,再折算为当前实际曝光不变时的下一次目标灰度。需要同一配置下相机增益、响应曲线保持稳定,且选用灰度区间近似线性。黑电平偏置必须已知或可视为零,不能仅因偏置固定就直接忽略;调用方不能保证这些条件时,禁用此预测,使用当前帧反馈。
对最近 `n = 3 或 5` 条有效目标记录(包含当前有效帧)定义:
```text
t_i:真实采集时间,计算时间差时使用秒
E_i:当帧实际曝光,单位 us,必须大于 0
G_i:目标截尾平均灰度,使用统一的灰度标度
B:同一灰度标度下已知的固定黑电平偏置;确认已消除偏置时取 0
q_i = (G_i - B) / E_i
b = Σ[(t_i - mean(t)) × (q_i - mean(q))] / Σ[(t_i - mean(t))²]
h = t_next - t_now
q_pred = q_now + b × h
G_pred = B + E_now × q_pred
```
这里用历史拟合变化斜率,但以当前有效测量为外推起点。恒定斜率为零时,预测量退化为当前测量;不同曝光下不会直接对原始灰度混合拟合。`B` 由已知相机处理契约或配置给出,不从目标/背景临时猜测;扣除仅用于预测器数值计算,不生成增强图像或改变原始测光输出。`q` 仅是目标的光度响应代理量,不宣称能将光照与工件反射率变化物理解耦。
`t_next` 优先由调用方给出计划采集时间;否则使用配置中的预计下一拍间隔。两者都缺失时不猜测预测时距,回退当前帧控制。拟合始终使用历史真实采集时刻,不用帧序号代替时间。
### 4.2 如何进入控制器
```text
G_control = G_pred (TRACK 且预测通过有效性检查)
G_control = G_now (预测不成立,但当前目标测光有效)
r = (G_target - G_control) / G_target
```
预测只替换这一处控制输入。后续仍经过同一个分段控制器、滞回、连续确认及限幅;不另外叠加一个预测曝光修正。真实当前灰度仍单独输出和记录,不被预测值覆盖。
连续确认也使用选定的控制误差并逐个新帧计数。预测被选为输入,但因死区、确认计数或限幅没有改变曝光时,应同时报告“预测已参与”和最终保持原因。
### 4.3 何时降级
- 配置选择 5 点但不足 5 点,或选择 3 点但不足 3 点:直接使用当前帧反馈,不补拍,也不自动把 5 点改成 3 点。
- 时间窗外的记录先剔除;曝光无效、目标无效、配置不同的记录不用于预测。
- 时间间隔退化、当前帧无有效目标、非有限计算值、预测为负或超出适用灰度范围:不采用预测。
- 有效灰度不高于黑电平、近黑或饱和像素过多、拟合残差过大、外推跨度超过配置上限:不采用预测。判断阈值作为补充配置,不能冒称附件已给出现场适用值。
- 当前目标测光也无效:保持曝光,冻结或清理相应控制状态;仅无目标恢复分支可以在独立条件满足时使用新的有效全局反馈。
- 历史少于预测所需点数,不等于需要初始化。只有首次启动、规定的停机/过期事件或配置切换才触发初始化。
## 5. 保留的控制器及待冻结细节
附件伪代码给出的实际公式可以按相同含义改写为:
```text
灰度差:dG = G_target - G_control,单位为灰度级
相对误差:r = dG / G_target,无量纲;0.10 表示 10%
大误差:dE = alpha × r × E_actual
小误差:dE = (Kp × r + Ki × I) × E_actual
死区内:dE = 0
E_suggested = clamp(E_actual + 限幅后的 dE, E_min, E_max)
```
保留附件的比例修正公式,不改成按亮度比直接求解曝光。目标灰度 118、死区 10%、大误差阈值 40%、滞回 2 个百分点、alpha=0.7、Kp=0.4、Ki=0.1 都只是示例配置。
建议积分仍采用附件的离散逐帧语义:`I` 为相对误差的累计量,不擅自改为按秒积分。真实时间间隔用于历史有效性和预测。
建议在获得新的有效目标反馈、连续确认满足且执行小误差分支时才更新积分,并执行积分限幅及条件抗饱和:曝光或步长限幅已阻止同方向修正时,不继续积累该方向误差。等待确认、死区保持、无效测光时不累计。目标丢失、显式复位、初始化和进入大误差分支时清除积分,避免旧小误差项带入后续阶段。这些积分状态细节属于附件未展开的补充规则,随本审查确认。
滞回示例:进入 10% 死区后,须到达 12% 的重新启调阈值;已处于调节状态时继续到重新进入 10% 死区。连续同方向确认和方向反转重计数独立于历史预测。
所有曝光计算都以本帧传入的 `E_actual` 为基准。调用方未采用上次建议时,模块不假设相机已经改变曝光;重复调用也不能以上一次建议为基准叠加下一次修正。
## 6. 状态转移表与时间规则
计数参数示例:连续检出 `N_detect=3`,连续丢失 `N_lost=3`,LOST 冻结 `N_freeze=1 或 2`;全部可配置。状态转移依据调用方的目标标志,测光有效性单独决定是否允许控制,不能把 ROI 错误伪装成“检测未检出”。
| 当前状态 | 事件或条件 | 下一状态 | 本次曝光处理及状态动作 |
|---|---|---|---|
| 任意/未启动 | 拍照前首次启动、长期停机、有效历史过期、配置版本变化 | CAPTURE | 一次性清理旧预测、积分及相关计数,给出经验曝光建议;连续丢失恢复预算按下文独立管理 |
| CAPTURE | 连续检出不足 N_detect | CAPTURE | 保持;有效测光可积累历史,不提前启动目标控制 |
| CAPTURE | 连续检出达到 N_detect | TRACK | 按附件伪代码,本次保持;下一张新的有效目标帧开始闭环 |
| CAPTURE | 无目标、满足粗调条件、恢复机会未用 | CAPTURE | 最多一次保守全局/中央区域恢复建议;消耗本次丢失过程的恢复机会 |
| CAPTURE | 无目标且恢复条件不成立、机会已用或尚无有效全局反馈 | CAPTURE | 保持;机会用后仍未稳定检出则报告“尚未恢复” |
| TRACK | 有目标且目标测光有效 | TRACK | 使用预测或当前目标灰度,经原控制器给出下一帧建议 |
| TRACK | 有目标但目标测光无效 | TRACK | 报告测光无效并保持;不积分、不预测、不误判为曝光故障 |
| TRACK | 连续未检出不足 N_lost | TRACK | 保持实际曝光,清理积分及误差确认计数;保留仍在时间窗内的历史 |
| TRACK | 连续未检出达到 N_lost | LOST | 保持,开始冻结计数,不重置成经验曝光 |
| LOST | 尚在冻结拍数内,或重新检出尚未达到 N_detect | LOST | 保持;检出时可记录有效历史,不进行无目标粗调 |
| LOST | 连续重新检出达到 N_detect | TRACK | 短时重捕获,不执行默认曝光初始化;建议本次保持、下一有效帧闭环 |
| LOST | 规定冻结拍数已完成且仍无目标 | CAPTURE | 进入有预算约束的全局粗调恢复流程;不因此恢复经验曝光或重置恢复次数 |
补充边界建议:
- 冻结计数按进入 LOST 后的新采集帧计算;重复帧和拍照前查询不增加计数。冻结的最后一帧仍保持,之后的新帧才可给恢复建议。
- “一次连续丢失过程”从首次未检出开始,到连续稳定检出 N_detect 次结束。一次或两次偶发检出不足以重新发放恢复机会。
- 恢复机会是独立于三态的记录。LOST→CAPTURE、超时初始化及配置切换均不因状态清理而给同一未结束丢失过程增加恢复次数。人工开始全新业务周期的复位是否允许重发预算,须显式区分原因,见第 7 节。
- 恢复建议一旦发出就消耗机会,不以调用方是否采用建议作为重新发放依据。已到曝光限值而无法实施恢复时也应报告限幅结果,不能每帧重试。
- 历史过期指曾取得有效目标记录且最后一条已超过有效时间窗;初始历史为空、初始化后持续无目标,都不能每周期再次触发“历史过期”。
- 长时间停机阈值单独可配置,停机时间按真实采集活动判断。窗口过滤、停机和配置事件均在 `preCapture` 检查。
- 同一次检查同时遇到多个初始化条件,只初始化一次。初始化后解除旧过期事件,直到新的有效记录/采集活动或新的配置变化建立新的事件;重复查询不重复复位。
- `processFrame` 若发现应初始化但拍照前检查被遗漏,只能报告调用顺序错误并拒绝使用旧控制状态,不能宣称已经保障了当前帧曝光正确。
- TRACK 不混入全局误差。无目标时全局亮度只能提供恢复线索,不能证明曝光故障,也不能单靠它区分光照变化、隔板和空场景。
## 7. 需要本次审查确认的事项
| 编号 | 未定义或矛盾点 | 建议确认的处理 |
|---|---|---|
| A | “沿用当前工程”未指明接入哪个工程,附件只定义 8 bit 灰度 | V1 采用 C++17 + CMake,在 Windows/MSVC x64 构建;图像与 ROI 复用现有 OpenCV 的 core/imgproc;先支持 Gray8、BGR8,固定灰度转换。若有指定业务工程或 12/16 bit 输入,以你指定的契约为准 |
| B | 附件没有 3/5 点公式,也未给下一次采集时间来源 | 采用第 4 节的曝光归一化、真实时间斜率和当前帧锚定外推;调用方提供下一次计划时刻或配置预计间隔,缺失就退回反馈;确认相机光度响应稳定这一使用前提 |
| C | 第 4.2、6 章说大步可不受 ±25% 限制;第 7 章却统一 clamp。正文连续确认描述与伪代码大误差豁免也不同 | 推荐以第 7 章为准:大小误差均受配置步长限幅,大误差可立即动作,小误差须连续同方向确认。若要求所有误差都连续确认,需要明确选择,不能在实施时暗自决定 |
| D | “必要时全图粗调”没有定义参考亮度、触发阈值,也没有办法只凭均值识别隔板/空场景 | 建议用明确配置的全局参考灰度/允许区间,不把目标灰度 118 自动用作全局目标;新增调用方“本帧允许无目标恢复”许可,缺省不恢复。获得许可、冻结完成且全局超差后,用受更小步长约束的比例粗调一次。若不增加此许可,需确认接受可能由场景变化触发的一次启发式建议 |
| E | 自动初始化要清理计数,但连续丢失恢复机会不能因状态切换重复;人工 reset 的业务语义也未定义 | 接受第 6 节的一次初始化及独立恢复预算规则:自动初始化不重发未结束丢失过程的预算;仅稳定检出,或调用方明确指定“开始新业务周期”的 reset,重新开放机会。其余积分、冻结与确认细节按第 5、6 节建议冻结 |
建议的全局粗调只在 D 获批后实施,具体形式仍是一次受限比例修正:
```text
r_global = (G_global_reference - G_global_now) / G_global_reference
dE_recovery = clamp(k_recovery × r_global × E_actual,
-recovery_step × E_actual, +recovery_step × E_actual)
E_recovery = clamp(E_actual + dE_recovery, E_min, E_max)
```
其中全局参考灰度必须为正;`recovery_step` 不超过正常配置的步长上限。缺少参考或许可、全局测光无效时保持。输出原因是“根据全局亮度给出一次恢复尝试”,不是“已确认曝光故障”。现场适用参数由对应工况配置,不硬编码附件的运动曝光上限。
## 8. 实施后的自动测试与交付验收
测试使用内存图像、模拟亮度、显式模拟时间和实际曝光输入,不连接相机,不使用真实等待凑时间。
| 测试组 | 必须证明的结果 |
|---|---|
| ROI 与测光 | 单矩形、多矩形、旋转矩形和 mask 混合并集;重叠像素去重;非总包围框;裁剪、空 ROI、过少像素、无目标及真实零灰度有效性 |
| 历史 | 5 分钟配置窗口边界、过期过滤、3/5 点不足、非等间隔时间、实际曝光不同但 q 恒定时不虚构环境趋势 |
| 预测进入控制 | 保持当前帧灰度和曝光相同,仅改变此前历史趋势,应产生不同的控制输入及可观察的曝光建议;不可只比较预测输出字段 |
| 控制器 | 大误差、小误差 PI、死区、滞回、连续同方向确认及反向重计数;积分限幅、目标丢失清理、步长及曝光上下限 |
| 三态 | CAPTURE 连续检出进入 TRACK;连续丢失进入 LOST;冻结完成转 CAPTURE;短时重捕获不恢复经验曝光 |
| 单次恢复 | 一个连续丢失过程最多一次建议;短暂检出、LOST/CAPTURE 切换、历史过期不能重发预算;全局变化不被报告为已确认曝光故障 |
| 拍照前初始化 | 隔夜、历史过期、配置切换在拍照前触发;同一事件一次;初始无历史与持续无目标不反复复位;遗漏预检查能报告错误 |
| 新反馈与幂等 | 重复帧、乱序帧、无新有效反馈不叠加修正,不推进积分/计数;上次建议未应用时仍以新的真实曝光为基准 |
交付物:独立模块源码、配置示例、最小调用示例、中文接口/控制说明、实际构建日志与自动测试结果。构建产物放在隔离的构建目录,不改相邻业务工程。
验收结论限定为构建与逻辑自动测试通过。模拟亮度和离线图片回放不能证明真实调整曝光后的成像效果、相机参数生效或现场检测成功率。
## 9. 当前交接
- 已完成:读取附件全部正文和三张表格,核对局部工程约定,新建目录,形成此需求审查稿。
- 审查结论:用户已同意 A~E,进入独立可复用模块实施。
- 本稿不作为实时进度记录;实际进度与验证边界见 `实施记录.md`。