2026-06-16-autofocus-scan-range-design.md 7.6 KB

AutoFocus 扫描范围扩大与电机限位 — 设计文档

日期:2026-06-16 涉及文件:Calib/CalibrationEngine.cs(主要)、Serial/HouseMotor.cs(参考,不改)

1. 背景与问题

AutoFocusTool 自动标定单 well 时,偶发"找不到圆孔"。经排查确认现象为: 水平扫描的 7 个位置从头到尾都检不出圆,且标定窗口实时画面里始终看不到完整圆孔—— 即 well 没有充分转进相机画面。

根因:当前水平找圆以 well 的 EEPROM 位置为中心做 ±4000 脉冲、7 步扫描 (CalibrationEngine.cs:30-31)。当某 well 的 EEPROM 水平位置偏差大于该窗口时, well 圆始终落在画面外,整段扫描都检不出。

同理 Z 对焦:粗对焦以 EEPROM Z 零点为中心 ±1500ZHalf=1500)扫描, 若真实焦面离零点超过 1500 脉冲也会扫不到峰。

2. 目标

  1. 水平找圆改为全行程粗扫定位 + 局部密扫,不再依赖 EEPROM 位置准确。
  2. Z 粗对焦改为大范围固定窗口扫描,覆盖实测焦面分布区间。
  3. 所有电机移动加行程限位钳位,水平 [0,70000]、垂直 [0,125000]。
  4. 关键扫描参数做成可配置字段,便于现场调整。

3. 关键决策(已与用户确认)

决策值
水平方案 全行程粗扫定位 + 局部密扫
水平行程上限 可配置,默认 70000(来源:旧工程自检 HouseBin.cs:468
水平粗扫步距 默认 2000,命中完整圆即停,不必扫完全程
Z 粗对焦中心 固定 90000(所有 well 统一;用户实测焦面均落在 60000~120000)
Z 粗对焦区间 60000~120000(中心 90000,±30000)
Z 粗对焦步距 2000 → 约 31 层
Z 精对焦半幅 6000(围绕粗峰 ±6000)
Z 精对焦步距 500 → 约 24 层(B 方案:精度优先)
限位 水平 [0,HMax]、垂直 [0,ZMax],所有移动前钳位

4. 详细设计

4.1 新增/修改参数(CalibrationEngine 字段)

// ── 行程限位(所有电机移动前钳到该区间)──
public int HMin = 0, HMax = 70000;          // 水平行程上下限
public int ZMin = 0, ZMaxPulse = 125000;    // 垂直行程上下限(旧工程软上限)

// ── 水平全行程粗扫定位 ──
public int HCoarseStart = 0;                // 粗扫起点
public int HCoarseEnd   = 70000;            // 粗扫终点(= HMax)
public int HCoarseStep  = 2000;             // 粗扫步距(够小不跨过 well)
// 命中完整圆即停:扫到 Found && Complete 立即转入局部密扫

// ── Z 全范围粗对焦(固定中心窗口)──
public int ZCoarseCenter = 90000;           // 粗对焦中心(固定,所有 well 统一)
public int ZCoarseHalf   = 30000;           // 粗对焦半幅 → 区间 60000~120000
public int ZCoarseStep   = 2000;            // 粗对焦步距 → 约 31 层

// ── Z 精对焦(围绕粗峰)──
// 复用现有精对焦逻辑,半幅与步距改为:
public int FineZHalf = 6000;                // 精对焦半幅(原 fineZHalf=500)
public int FineZStep = 500;                 // 精对焦步距 → 约 24 层

保留现有 HScanRange/HScanSteps/FineScanSteps 用于水平局部密扫阶段 (命中后在最佳点附近精调 Y 居中),无需删除。

4.2 限位钳位(统一入口)

新增两个私有方法,所有 _motor.HorizontalMoveTo / _motor.VerticalMoveTo 调用前先过钳位,避免越界指令发给下位机:

int ClampH(int p) => Math.Max(HMin, Math.Min(HMax, p));
int ClampZ(int p) => Math.Max(ZMin, Math.Min(ZMaxPulse, p));

钳位发生时写一条 Log(便于发现 EEPROM 异常值或参数配置过界)。

4.3 水平找圆:全行程粗扫定位 + 局部密扫

新增方法 HCoarseLocate,替换 CalibrateWell 中"②旋转居中" 对 ScanForCenter(eepromHPos, HScanRange, ...) 的首次调用:

  1. 全行程粗扫:从 HCoarseStartHCoarseEnd,步距 HCoarseStep, 每个位置 Grab + WellDetector.Detect
  2. 命中即停:一旦检出 Found && Complete,记录该粗扫位置 hpHit,立即停止粗扫。
  3. 局部密扫:以 hpHit 为中心,调用现有 ScanForCenter(well, hpHit, fineRange, FineScanSteps) 做 Y 居中精调(fineRangeHCoarseStep 量级,约 ±2000)。
  4. 全程未命中:若粗扫扫完全程仍无 Found && Complete, 退化为"取扫描中 |Y偏移| 最小且 Found 的位置";仍无则记 Note="水平全程未检出圆", 该 well 标定失败(沿用现有失败返回风格)。

粗扫阶段沿用 CenterScanExposure 固定中低曝光(well 是暗背景中灰盘)。

4.4 Z 粗对焦:固定大窗口扫描

修改 CoarseFocus:扫描中心由"传入的 EEPROM 零点"改为固定 ZCoarseCenter=90000, 半幅 ZCoarseHalf=30000,步距 ZCoarseStep=2000

for z in [ZCoarseCenter-ZCoarseHalf .. ZCoarseCenter+ZCoarseHalf] step ZCoarseStep:
    move ClampZ(z); discard frame; grab; score = Sharpness(中央40% ROI)
取最高分 z 作为 coarseZ

约 31 层。CalibrateWell 中调用处随之调整(不再传 eepromZ 作中心)。

4.5 Z 精对焦:扩大半幅

现有精对焦围绕 coarseZ ± fineZHalf 密扫。将半幅改用 FineZHalf=6000、 步距 FineZStep=500(约 24 层)。半幅 6000 足以覆盖粗扫 ±2000 的峰值定位误差并留余量。 保留现有 3 点平滑 + 抛物线插值峰顶逻辑不变。

5. 数据流(单 well 标定)

读 EEPROM(水平位置仅作参考/不再作扫描中心) → 开灯
 ① Z 粗对焦:固定 90000±30000 步距2000 (31层) → coarseZ
 ② 水平全行程粗扫 0~70000 步距2000 命中完整圆即停 → hpHit
    → 局部密扫 ScanForCenter(hpHit, ±~2000) → 最居中水平位置
 ③ 曝光二分(well 内 ROI,逻辑不变)
 ④ Z 精对焦:coarseZ±6000 步距500 (24层) + 平滑插值 → focusZ
 存图 + 写 calibration.json

注:相比原流程,①②顺序保持"先对焦后居中" (对焦与居中耦合,焦准画面才检得出圆),但①改为固定大窗口、②改为全行程粗扫。

6. 性能影响(需用户知晓)

  • Z 粗对焦:约 31 层 × (移动+ScanDelayMs350ms+抓帧) ≈ 单 well 15~20 秒。
  • Z 精对焦:约 24 层 ≈ 单 well 10~15 秒。
  • 水平全行程粗扫:最坏 35 步(命中即停通常更少)。
  • 16 个 well 全标定预计较原方案明显变慢(分钟级增加)。精度优先,已确认接受。

7. 错误处理

  • 所有移动经 ClampH/ClampZ 钳位,越界写 Log 但不崩溃。
  • 水平全程未检出圆 → 退化策略(4.3 第 4 点),最终失败则记 Note 并跳过该 well。
  • 抓帧失败沿用现有 Grab() 重试 3 次机制。
  • Z 精对焦峰过弱(PeakRatio<1.2)沿用现有"弱峰/可能空well"告警。

8. 测试

  • 现有 SelfTest/SmokeTest 子工程验证编译与基本调用不被破坏。
  • 真机验证:对一批已知 EEPROM 偏差较大的 well 运行标定,确认 (a) 水平全行程能找到圆;(b) Z 粗对焦命中 60000~120000 内焦面; (c) 越界脉冲被钳位并有 Log。
  • 无真机时:纯参数/钳位逻辑可加轻量单元验证(ClampH/ClampZ 边界)。

9. 不在本次范围

  • 不改协议层 Protocol.cs、不改 HouseMotor 的移动接口(限位放在 CalibrationEngine)。
  • 不改相机分辨率/崩溃问题(GetSourceBuffer 段错误)——另行处理。
  • 不做 UI 参数配置界面(参数先以字段默认值形式存在,后续可接入设置页)。