# 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 零点为中心 `±1500`(`ZHalf=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` 字段) ```csharp // ── 行程限位(所有电机移动前钳到该区间)── 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` 调用前先过钳位,避免越界指令发给下位机: ```csharp 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. **全行程粗扫**:从 `HCoarseStart` 到 `HCoarseEnd`,步距 `HCoarseStep`, 每个位置 `Grab` + `WellDetector.Detect`。 2. **命中即停**:一旦检出 `Found && Complete`,记录该粗扫位置 `hpHit`,立即停止粗扫。 3. **局部密扫**:以 `hpHit` 为中心,调用现有 `ScanForCenter(well, hpHit, fineRange, FineScanSteps)` 做 Y 居中精调(`fineRange` 取 `HCoarseStep` 量级,约 ±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 层 × (移动+`ScanDelayMs`350ms+抓帧) ≈ 单 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 参数配置界面(参数先以字段默认值形式存在,后续可接入设置页)。