8.0 KiB
控制与状态说明
实现依据为需求审查稿及用户批准的 A~E 建议。模块保留附件控制器,历史预测、时间初始化、恢复预算和异常输入处理是补充实现。
历史与预测
每张接收的新帧保存采集时间、可选目标灰度、全局灰度及其有效性,并绑定真实曝光、工况和配置版本。历史不保存图像。帧诊断记录与有效目标记录分别保留,避免大量无目标帧挤掉仍在窗口内的最近 3/5 条目标记录。两种队列都受时间窗及 history_capacity 限制。
valid_history_count 是当前缓存中位于有效窗口内的可用目标记录数,不是全天总计;prediction.points_used 是本次实际用于拟合的数量。窗口边界包含恰好等于窗口长度的记录,超过才剔除。3/5 点不足只降级预测,不初始化、不补拍。
仅当调用方确认增益、响应曲线稳定,灰度处于近似线性区且黑电平 B 已知时采用:
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 表示已选入控制链;即使最终被死区或连续确认保持,也会保留采用标志和具体保持原因。
控制器
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、本帧无目标、本次过程尚未使用恢复机会、调用方明确设置 allow_no_target_recovery=true、配置了独立全局参考灰度、全局测光有效且超出恢复死区。启动时尚未进入 TRACK,可在 CAPTURE 满足这些条件后尝试一次;由 LOST 返回的 CAPTURE 则须先完成冻结。
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 描述最终调整或保持原因;测光、预测分别有独立原因。预测结果包含拟合数量、时刻、斜率、残差、预测灰度和采用标志。控制跟踪包含实际误差、选用误差、积分、确认计数、原始/最终修正量及限幅标志,便于判断预测是否真正影响建议。
采集或设置曝光失败、真实场景识别率和实际成像质量由外部验证。模块不会主动发起重拍,也不把无目标等同于曝光故障。