Browse Source

docs: AutoFocus 扫描范围扩大与电机限位设计

水平改全行程粗扫定位+局部密扫(0~70000可配置),Z粗对焦固定90000±30000
步距2000,Z精对焦半幅6000步距500,所有电机移动加[0,70000]/[0,125000]限位。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 1 tuần trước cách đây
mục cha
commit
099b6f5766

+ 157 - 0
docs/superpowers/specs/2026-06-16-autofocus-scan-range-design.md

@@ -0,0 +1,157 @@
+# 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 参数配置界面(参数先以字段默认值形式存在,后续可接入设置页)。