CommonLibraries/docs/控制与状态说明.md
Ben 11236718ea chore: preserve existing library source changes for MatchVerify migration
Isolated source snapshot; original checkout and index unchanged. This migration does not claim new build or hardware validation.
2026-10-20 23:59:35 +08:00

127 lines
13 KiB
Markdown
Raw Permalink 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.

# 控制与状态说明
实现依据为需求审查稿及用户批准的 A~E 建议。模块保留附件控制器,历史预测、时间初始化、恢复预算和异常输入处理是补充实现。
## 历史与预测
每张接收的新帧保存采集时间、可选目标灰度、全局灰度及其有效性,并绑定真实曝光、工况和配置版本。历史不保存图像。帧诊断记录与有效目标记录分别保留,避免大量无目标帧挤掉仍在窗口内的最近 3/5 条目标记录。两种队列都受时间窗及 `history_capacity` 限制。
`valid_history_count` 是当前缓存中位于有效窗口内的可用目标记录数,不是全天总计;`prediction.points_used` 是本次实际用于拟合的数量。窗口边界包含恰好等于窗口长度的记录,超过才剔除。3/5 点不足只降级预测,不初始化、不补拍。
仅当调用方确认增益、响应曲线稳定,灰度处于近似线性区且黑电平 B 已知时采用:
```text
q_i = (G_i - B) / E_i
b = Σ[(t_i-平均t)(q_i-平均q)] / Σ[(t_i-平均t)²]
q_next = q_current + b × (t_next-t_current)
G_predicted = B + E_actual_current × q_next
```
E 单位 us,时间差在拟合内部换算为秒。用实际采集时间拟合,不使用帧序号;先计算整数时间差,再转浮点,避免大纪元时间损失精度。外推锚定当前测量,历史只提供斜率。这里的 q 是目标光度响应代理量,不能将反射率变化与环境光变化解耦。
预测时刻优先取 `FrameInput.next_capture_timestamp_us`,缺失时用配置预计间隔。两者都缺失、跨度超限、目标无效、近黑/饱和比例超限、拟合残差过大或预测越界时退回当前目标反馈。灰度有效但不适用于线性预测,仍可用于原反馈控制。
预测可用且处于 TRACK 目标闭环时,选用 `G_control=G_predicted`;否则使用本帧 `G_current`。只生成这一个控制输入,不另外计算并叠加前馈曝光量。`prediction.adopted=true` 表示已选入控制链;即使最终被死区或连续确认保持,也会保留采用标志和具体保持原因。
## 控制器
```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
```
误差绝对值小于死区阈值时保持,并锁存死区;随后达到“死区+滞回”的阈值才重新启动。小误差必须在新的有效帧中连续同方向超差达到指定次数;方向反转、无效测光或死区会清掉确认计数。确认后若一直同方向超差,可逐个新帧继续修正,不需要额外等待。
大误差按附件第 7 章伪代码立即修正。大、小误差均经过单拍步长限制,再经过曝光上下限。当前真实曝光不在配置范围时拒绝该帧控制,避免在满足绝对范围时又无法同时满足相对步长的矛盾,不擅自把真实曝光裁剪成一个假输入。
原接口及未启用时间模式时,I 是逐次有效 PI 调节累计的相对误差,不按秒积分。仅在新目标反馈有效且小误差连续确认满足时尝试累计;积分自身限幅。若候选累计导致步长或绝对曝光限幅阻挡同方向修正,则拒绝这次积分累计,使用此前积分计算建议。死区、迟滞保持和误差方向反转清零积分,防止先前暗场积分在当前过亮时继续增加曝光。等待确认不累计,无效目标测光保持积分但清除确认计数。目标丢失、进入大误差分支、复位和初始化也清除积分。
所有建议以输入的 `actual_exposure_us` 为基准。控制器没有“上次建议已经生效”的隐含状态,不会在重复帧上继续叠加修正。
## 状态转移
| 状态 | 条件 | 动作 |
|---|---|---|
| CAPTURE | 连续检出不足要求 | 保持并积累有效历史 |
| CAPTURE | 连续检出达到要求 | 进入 TRACK,本帧保持,下一有效帧闭环 |
| CAPTURE | 无目标 | 按下面的单次恢复规则处理 |
| TRACK | 目标标志为真且测光有效 | 目标控制;全局灰度不混入误差 |
| TRACK | 目标标志为真但测光无效 | 保持、报告 InvalidMetering,不伪装成检出失败 |
| TRACK | 未检出但连续数量不足 | 保持实际曝光、清积分,保留未过期历史 |
| TRACK | 连续丢失达到要求 | 进入 LOST,设置冻结拍数 |
| LOST | 新帧到来 | 减少冻结余量;期间保持 |
| LOST | 连续检出达到要求 | 进入 TRACK,本帧保持,不恢复经验曝光 |
| LOST | 冻结结束且本帧仍无目标 | 进入 CAPTURE,本帧仍保持,后续新帧才允许恢复建议 |
检出/丢失计数以调用方明确的 `has_usable_target` 为准。LOST 冻结计数包含间歇检出的新采集帧,重复帧和拍照前查询不计数。
## 连续反馈与原目标状态机的选择
本文件其余 CAPTURE/TRACK/LOST、预测与单次恢复说明适用于原 `ExposureControl`。版本 1.2.0 新增的 `ContinuousExposureControl` 是另一种明确调用入口,持续按成像亮度闭环,不把无目标当作曝光错误,也不需要外部“工件存在”信号。
连续控制只接收已计算的全局/区域测光结果。指定全局时使用全局普通均值;指定手动 ROI 或 YOLO 区域时,区域有效优先,失效后明确回退全局。手动 ROI 不参与任何真实目标存在计数。源切换后清积分和同向计数,避免 ROI 与全局不同亮度基准相互污染。
每次 `update` 先校验配置、严格递增时间、设备实际曝光及最终选用测光;有效后才调用同一个比例/PI 控制实现。黑图是有效的零灰度,会向增曝光方向调整;饱和图会向减曝光方向调整;正常死区保持。持续无目标不消耗任何恢复预算,每张新图都可继续反馈。饱和/近黑比例保留在诊断中,本批没有另创比例阈值保护策略。
当前参考灰度由实际来源选择 `global_controller` 或 `region_controller`;配置、来源或闲置超时变化清反馈状态,拒绝帧不消费时间水位。曝光建议仍是库输出,调用方必须落实“成功写入 → 读回 → 下一帧用实际值反馈”,才能构成设备闭环;库内验证不代表相机已生效。
### 稀疏采集与外部执行器保护
`ContinuousConfig::preserve_confirmation_on_idle` 默认 `false`,保留上述历史行为。定时采集调用方可设为 `true`:自动检测到 `IdleTimeout` 时保留同向误差确认和迟滞状态,清零旧 PI 积分,并禁止该帧累积积分。这样即使每帧间隔超过 300 秒,中等偏差仍能在第二次同向有效反馈后调整,而不会永久等待第一帧确认。若下一帧间隔正常,允许重新积累积分。配置切换、来源切换和任何显式 `reset()` 仍完整清空反馈状态,包括显式 `reset(IdleTimeout)`。时间倒退仍拒绝;切换时钟必须显式 `reset(ClockChanged)`,不隐式接纳乱序帧。
外部饱和保护或执行器策略预先确定要覆盖 PI 输出时,调用方应在 **本帧 `update()` 前** 设置 `ContinuousFrameInput::suppress_integral=true`。通过校验后,该帧清零已有积分并禁止新增积分,比例项、同向确认、迟滞和时间水位继续正常推进。下一帧未设置此标志即可恢复正常积分;这不是完整状态重置。拒绝帧不会因该标志而改变任何控制状态。
这两种抑制都在 `ControlTrace::integral_suppressed` 中明确记录;自动闲置仍另外报告 `reset_reason=IdleTimeout`。标志只表示接受的反馈执行了积分抑制,不等于实际相机参数已写入。原 `ExposureControl::processFrame()` 未启用这些行为,默认源码调用保持兼容;公共结构增加成员后消费工程需要与静态库一起重新编译。
### 按实际经过时间的连续反馈
`ContinuousConfig::time_aware=true` 启用可选时序控制,默认关闭以保留旧接口节拍。时间取调用方提供的单调采集时间戳,拒绝帧不会更新时钟、滤波或确认历史。
- 默认 `nominal_interval_us=5000000`:积分乘 `min(Δt / 5秒, 1)`;间隔再长也不把空白期间当成持续误差累积。闲置抑制帧的积分系数为0。
- 默认 `smoothing_tau_us=2000000`:对 `灰度/本帧实际曝光` 做 `α=1-exp(-Δt/τ)` 的指数平滑,再映射到本帧曝光。曝光本身改变不会被当成环境变化。该局部近似不假设增益单位或增益响应;**调用方检测实际增益变化后必须 reset**。大误差、外部积分抑制、闲置或其他重置使用当前原始灰度。滤波误差与原始误差反向时也改用当前原始值,原始值已经进入死区时立即保持。
- 默认 `confirmation_us=1000000`:小误差同时满足至少2次同方向观测及持续1秒才允许调整。大误差跳过确认,仍遵守限速。
- 默认 `max_step_ratio_per_second=0.05`:曝光单次变化上限是原每帧上限与 `每秒速率×Δt秒` 中较小者。首帧尚无时间差时使用5秒基准;长间隔不超过原每帧上限。该值限制库的普通曝光建议,不声称限速外部紧急保护或相机增益变化。
`raw_control_gray` 保留原始选中灰度,`control_gray` 是本次实际控制采用值;`ControlTrace` 记录 `elapsed_us`、`filtered_gray`、`filter_alpha`、`integral_time_scale`、`effective_max_step_ratio`、`same_direction_duration_us`、`confirmation_time_satisfied` 和 `time_rate_limited`。仅时间模式填入过滤灰度;该机制做平滑和节拍约束,不进行趋势外推,也不改变原 CAPTURE/TRACK/LOST 状态机。
## 单次无目标恢复
从首次未检出开始,到连续检出达到配置数量结束,视为同一丢失过程。恢复机会独立于三态及初始化记录。
恢复需要同时满足:处于 CAPTURE、本帧无目标、本次过程尚未使用恢复机会、调用方明确设置 `allow_no_target_recovery=true`、配置了独立全局参考灰度、全局测光有效且超出恢复死区。启动时尚未进入 TRACK,可在 CAPTURE 满足这些条件后尝试一次;由 LOST 返回的 CAPTURE 则须先完成冻结。
```text
r_global = (G_reference - G_global) / G_reference
dE = clamp(gain × r_global × E_actual, ±recovery_step × E_actual)
E_next = clamp(E_actual + dE, E_min, E_max)
```
发出一次恢复建议即消耗机会;限幅后无法改变曝光也消耗这次机会并报告 ExposureLimit。之后持续未恢复只保持并输出 NotRecovered。状态切换、超时、配置切换和普通复位均不重发机会。达到稳定检出条件或显式 `NewBusinessCycle` 复位才重新开放。
全局变化只触发一个已获许可的启发式尝试,不输出“已确认曝光故障”。模块无法只凭这些输入区分隔板、空场景与光照变化,因此许可由业务提供,默认 false;业务不确定时可以保持。
## 拍照前初始化与复位
`preCapture` 检查首次启动、显式复位、配置内容/版本变化、长期停机和有效历史过期。一次检查多个条件同时成立仍只初始化一次,优先级为显式待执行复位、配置变化、停机、历史过期。初始化清历史、积分和状态计数,返回 CAPTURE 和经验曝光建议。
历史不足不等于历史过期。只有曾取得有效目标记录且它已经过期,才会触发该事件。初始化会解除旧的历史/停机事件;新的有效目标或采集活动才建立新的时间基准,因此初始空历史和持续无目标不会逐周期重新初始化。超过停机阈值后真正再次停机,是新的停机事件。
每次 `processFrame` 需要一张尚未被成功帧消耗的拍照前检查凭据,并再次检查采集时刻是否已经越过超时条件。检查遗漏或过早时返回 PreCaptureRequired,拒绝使用旧状态,不宣称已保障这张照片曝光正确。配置只有通过 `preCapture` 才会生效。
| 复位原因 | 行为差别 |
|---|---|
| Manual | 清瞬态状态,下一拍初始化;保留未结束丢失过程及恢复预算 |
| NewBusinessCycle | 同时明确开始新业务周期,重置恢复预算 |
| ClockChanged | 同时解除旧帧时间水位,允许新时钟从较小值开始;恢复预算仍保留 |
| 其他原因 | 标注调用方指定的复位来源,保留恢复预算 |
## 输出诊断与边界
`accepted` 表示该帧通过元数据和调用顺序检查并已进入状态管理,**不等于**目标测光有效;还须检查 `metering.target.valid()`。无效亮度使用 `std::optional`,不是 0。
`reason` 描述最终调整或保持原因;测光、预测分别有独立原因。预测结果包含拟合数量、时刻、斜率、残差、预测灰度和采用标志。控制跟踪包含实际误差、选用误差、积分、确认计数、原始/最终修正量及限幅标志,便于判断预测是否真正影响建议。
采集或设置曝光失败、真实场景识别率和实际成像质量由外部验证。模块不会主动发起重拍,也不把无目标等同于曝光故障。