|
|
@@ -0,0 +1,442 @@
|
|
|
+# AutoFocus 扫描范围扩大与电机限位 — 实现计划
|
|
|
+
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
+
|
|
|
+**Goal:** 扩大 AutoFocus 标定的水平找圆与 Z 对焦扫描范围,并对所有电机移动加行程限位,解决"well 没转进画面导致整段检不出圆"的问题。
|
|
|
+
|
|
|
+**Architecture:** 全部改动集中在 `Calib/CalibrationEngine.cs` 单文件。新增可配置参数字段 + 两个钳位方法 + 一个全行程水平定位方法;修改 `CoarseFocus`(固定大窗口)、`CalibrateWell`(调用新定位、扩精对焦半幅)。不改协议层与电机接口。
|
|
|
+
|
|
|
+**Tech Stack:** .NET 8 / WPF / C#。无单元测试框架——验证靠 `dotnet build` 编译 + 一个纯逻辑钳位自检(写进 `SelfTest` 控制台工程)+ 真机标定验证。
|
|
|
+
|
|
|
+设计依据:`docs/superpowers/specs/2026-06-16-autofocus-scan-range-design.md`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 文件结构
|
|
|
+
|
|
|
+- 修改:`Calib/CalibrationEngine.cs`
|
|
|
+ - 新增字段(限位 + 水平全行程粗扫 + Z 大窗口粗对焦 + 精对焦半幅/步距)
|
|
|
+ - 新增 `ClampH(int)`、`ClampZ(int)` 私有方法
|
|
|
+ - 新增 `HCoarseLocate(int well)` 方法(全行程粗扫定位 + 局部密扫)
|
|
|
+ - 改 `CoarseFocus`:固定中心 90000、半幅 30000、步距 2000
|
|
|
+ - 改 `CalibrateWell`:用 `HCoarseLocate` 替换 ②首次 `ScanForCenter`;精对焦半幅改 `FineZHalf`、步距改 `FineZStep`
|
|
|
+ - 所有 `_motor.HorizontalMoveTo` 经 `ClampH`,`_motor.VerticalMoveTo` 经 `ClampZ`
|
|
|
+
|
|
|
+无新增文件。`ClampH`/`ClampZ` 是两行 `Math.Max/Min`,逻辑通过编译 + 真机日志验证(越界写 Log),不单独造测试文件(无测试框架,`SelfTest` Main 需硬件)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Task 1: 新增参数字段 + 钳位方法
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:27-43`(在现有参数字段区追加新字段)
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:51`(在 `CenterRoi40` 之前新增钳位方法)
|
|
|
+
|
|
|
+- [ ] **Step 1: 追加新参数字段**
|
|
|
+
|
|
|
+在 `CalibrationEngine.cs` 第 43 行 `public int CenterScanExposure = 60;` 之后插入:
|
|
|
+
|
|
|
+```csharp
|
|
|
+
|
|
|
+ // ── 行程限位(所有电机移动前钳到该区间)──
|
|
|
+ /// <summary>水平电机行程下/上限脉冲(旧工程自检值 70000)。</summary>
|
|
|
+ public int HMin = 0, HMax = 70000;
|
|
|
+ /// <summary>垂直电机行程下/上限脉冲(旧工程软上限 125000)。</summary>
|
|
|
+ public int ZMin = 0, ZMaxPulse = 125000;
|
|
|
+
|
|
|
+ // ── 水平全行程粗扫定位 ──
|
|
|
+ /// <summary>水平全行程粗扫起点/终点/步距。命中完整圆即停。</summary>
|
|
|
+ public int HCoarseStart = 0;
|
|
|
+ public int HCoarseEnd = 70000;
|
|
|
+ public int HCoarseStep = 2000;
|
|
|
+
|
|
|
+ // ── Z 全范围粗对焦(固定中心大窗口)──
|
|
|
+ /// <summary>Z 粗对焦固定中心(实测焦面集中区间 60000~120000 的中点)。</summary>
|
|
|
+ public int ZCoarseCenter = 90000;
|
|
|
+ /// <summary>Z 粗对焦半幅 → 区间 60000~120000。</summary>
|
|
|
+ public int ZCoarseHalf = 30000;
|
|
|
+ /// <summary>Z 粗对焦步距 → 约 31 层。</summary>
|
|
|
+ public int ZCoarseStep = 2000;
|
|
|
+
|
|
|
+ // ── Z 精对焦(围绕粗峰)──
|
|
|
+ /// <summary>精对焦半幅(覆盖粗扫 ±2000 峰定位误差并留余量)。</summary>
|
|
|
+ public int FineZHalf = 6000;
|
|
|
+ /// <summary>精对焦步距 → 约 24 层(精度优先)。</summary>
|
|
|
+ public int FineZStep = 500;
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 新增钳位方法**
|
|
|
+
|
|
|
+在 `CalibrationEngine.cs` 第 51 行 `/// <summary>中央40% ROI...` 注释之前插入:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ /// <summary>水平脉冲钳到 [HMin,HMax],越界写 Log。</summary>
|
|
|
+ int ClampH(int p)
|
|
|
+ {
|
|
|
+ int c = Math.Max(HMin, Math.Min(HMax, p));
|
|
|
+ if (c != p) Log?.Invoke($" ⚠ 水平脉冲 {p} 越界,钳到 {c} [{HMin},{HMax}]");
|
|
|
+ return c;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>垂直脉冲钳到 [ZMin,ZMaxPulse],越界写 Log。</summary>
|
|
|
+ int ClampZ(int p)
|
|
|
+ {
|
|
|
+ int c = Math.Max(ZMin, Math.Min(ZMaxPulse, p));
|
|
|
+ if (c != p) Log?.Invoke($" ⚠ 垂直脉冲 {p} 越界,钳到 {c} [{ZMin},{ZMaxPulse}]");
|
|
|
+ return c;
|
|
|
+ }
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: 编译验证**
|
|
|
+
|
|
|
+Run: `dotnet build AutoFocusTool.csproj -c Debug`
|
|
|
+Expected: 生成成功,0 错误(新字段/方法暂未被调用,会有 0 或仅"未使用"提示,可忽略)
|
|
|
+
|
|
|
+- [ ] **Step 4: 提交**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add Calib/CalibrationEngine.cs
|
|
|
+git commit -m "feat(calib): 新增扫描范围参数与电机限位钳位方法
|
|
|
+
|
|
|
+Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Task 2: 改 CoarseFocus 为固定大窗口 + 钳位
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:280-300`(整个 `CoarseFocus` 方法)
|
|
|
+
|
|
|
+当前 `CoarseFocus(int well, int centerZ, int half, int layers)` 以传入的 EEPROM 零点为中心、半幅 `ZHalf=1500`、层数 `CoarseFocusLayers=7`。改为固定中心 `ZCoarseCenter=90000`、半幅 `ZCoarseHalf=30000`、步距 `ZCoarseStep=2000`,并对每个 Z 目标钳位。
|
|
|
+
|
|
|
+- [ ] **Step 1: 替换 CoarseFocus 方法体**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:280-300` 的整个方法(从 `int CoarseFocus(...)` 到其结尾 `}`)替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int CoarseFocus(int well)
|
|
|
+ {
|
|
|
+ int lo = ZCoarseCenter - ZCoarseHalf;
|
|
|
+ int hi = ZCoarseCenter + ZCoarseHalf;
|
|
|
+ int bestZ = ZCoarseCenter; double bestS = -1;
|
|
|
+
|
|
|
+ // P0-6: 粗对焦使用中央40%区域ROI,避免背景干扰
|
|
|
+ var centerROI = CenterRoi40();
|
|
|
+
|
|
|
+ int layers = 0;
|
|
|
+ for (int z = lo; z <= hi; z += ZCoarseStep)
|
|
|
+ {
|
|
|
+ layers++;
|
|
|
+ _motor.VerticalMoveTo(ClampZ(z), ScanDelayMs);
|
|
|
+ // P0-5: 丢弃旧帧
|
|
|
+ Grab();
|
|
|
+ var b = Grab();
|
|
|
+ double sc = Sharpness.Compute(b, W, H, centerROI); // 中央ROI(避免背景带偏)
|
|
|
+ if (sc > bestS) { bestS = sc; bestZ = z; }
|
|
|
+ OnStep?.Invoke($"粗对焦 Z={z} (区间{lo}~{hi})", null, null);
|
|
|
+ }
|
|
|
+ Log?.Invoke($"[well{well}] 粗对焦扫{layers}层 区间[{lo},{hi}] 步距{ZCoarseStep}");
|
|
|
+ return bestZ;
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+注意:方法签名由 `CoarseFocus(int well, int centerZ, int half, int layers)` 改为 `CoarseFocus(int well)`——调用方在 Task 4 同步修改。同时更新方法上方的 XML 注释(`CalibrationEngine.cs:275-279`),把"围绕 centerZ±half"改为"固定中心 ZCoarseCenter±ZCoarseHalf":
|
|
|
+
|
|
|
+```csharp
|
|
|
+ /// <summary>
|
|
|
+ /// 粗对焦:固定中心 ZCoarseCenter±ZCoarseHalf 按 ZCoarseStep 扫描,中央40% ROI 找焦点。
|
|
|
+ /// 实测所有 well 焦面集中在 60000~120000,故用固定大窗口,不依赖 EEPROM 零点。
|
|
|
+ /// </summary>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 编译验证(预期此时报错)**
|
|
|
+
|
|
|
+Run: `dotnet build AutoFocusTool.csproj -c Debug`
|
|
|
+Expected: FAIL — `CalibrateWell` 仍按旧签名调用 `CoarseFocus(well, eepromZ, ZHalf, CoarseFocusLayers)`,报"无重载匹配"。这是预期的,Task 4 修复调用方。
|
|
|
+
|
|
|
+> 若希望每个 Task 都能独立编译通过,可将 Task 2、4 合并为一次提交执行。本计划按"先改被调方再改调用方"的顺序,最终在 Task 4 末尾统一编译通过。
|
|
|
+
|
|
|
+- [ ] **Step 3: 暂不提交,继续 Task 3、4**
|
|
|
+
|
|
|
+(CoarseFocus 与调用方需一起编译通过后再提交,见 Task 4 Step 末尾)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Task 3: 新增 HCoarseLocate(水平全行程粗扫定位 + 局部密扫)+ ScanForCenter 钳位
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:109-129`(`ScanForCenter` 内的移动加钳位)
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:129`(在 `ScanForCenter` 方法后新增 `HCoarseLocate`)
|
|
|
+
|
|
|
+- [ ] **Step 1: ScanForCenter 的移动加钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:111-113` 这三行:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int hp = center - range + step * i;
|
|
|
+ if (hp < 0) continue;
|
|
|
+ _motor.HorizontalMoveTo(hp, actualDelay);
|
|
|
+```
|
|
|
+
|
|
|
+替换为(去掉 `if (hp < 0) continue;`,改用 ClampH 统一钳位):
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int hp = ClampH(center - range + step * i);
|
|
|
+ _motor.HorizontalMoveTo(hp, actualDelay);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 新增 HCoarseLocate 方法**
|
|
|
+
|
|
|
+在 `CalibrationEngine.cs` 第 129 行(`ScanForCenter` 方法的结尾 `}` 之后、`CalibrateWell` 之前)插入:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ /// <summary>
|
|
|
+ /// 水平全行程粗扫定位 + 局部密扫居中。不依赖 EEPROM 水平位置准确:
|
|
|
+ /// ① 从 HCoarseStart 到 HCoarseEnd 按 HCoarseStep 扫,命中完整圆即停,记录该位置;
|
|
|
+ /// ② 以命中点为中心做局部密扫(ScanForCenter)优化 Y 居中;
|
|
|
+ /// ③ 全程未命中完整圆则取扫描中 |Y偏移| 最小且检出的位置;仍无返回 (-1,null)。
|
|
|
+ /// </summary>
|
|
|
+ (int bestHPos, WellCircle bestCircle) HCoarseLocate(int well)
|
|
|
+ {
|
|
|
+ _cam.SetExposure(CenterScanExposure);
|
|
|
+
|
|
|
+ int hitHPos = -1; WellCircle hitCircle = null;
|
|
|
+ int fallbackHPos = -1; WellCircle fallbackCircle = null;
|
|
|
+ double fallbackScore = double.MaxValue;
|
|
|
+
|
|
|
+ // ① 全行程粗扫,命中完整圆即停
|
|
|
+ for (int hp = HCoarseStart; hp <= HCoarseEnd; hp += HCoarseStep)
|
|
|
+ {
|
|
|
+ int p = ClampH(hp);
|
|
|
+ _motor.HorizontalMoveTo(p, ScanDelayMs);
|
|
|
+ var b = Grab();
|
|
|
+ var c = WellDetector.Detect(b, W, H);
|
|
|
+ DebugSave?.Invoke(b, $"hcoarse_w{well}_hp{p}");
|
|
|
+ Log?.Invoke(c.Found
|
|
|
+ ? $" 粗扫水平{p}: Y偏移={c.OffsetYPct:F1}% 完整={c.Complete}"
|
|
|
+ : $" 粗扫水平{p}: 未检出圆");
|
|
|
+ OnStep?.Invoke($"水平粗扫 hp={p}", c, null);
|
|
|
+ if (c.Found)
|
|
|
+ {
|
|
|
+ double score = Math.Abs(c.OffsetYPct) + (c.Complete ? 0 : 100);
|
|
|
+ if (score < fallbackScore) { fallbackScore = score; fallbackHPos = p; fallbackCircle = c; }
|
|
|
+ if (c.Complete) { hitHPos = p; hitCircle = c; break; }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 命中完整圆 → 局部密扫;否则用检出最优的降级点
|
|
|
+ int center = hitHPos >= 0 ? hitHPos : fallbackHPos;
|
|
|
+ if (center < 0)
|
|
|
+ {
|
|
|
+ Log?.Invoke($"[well{well}] ✗ 水平全行程未检出任何圆");
|
|
|
+ return (-1, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ② 以命中/降级点为中心局部密扫居中(范围取粗扫步距量级)
|
|
|
+ int fineRange = HCoarseStep;
|
|
|
+ var fine = ScanForCenter(well, center, fineRange, FineScanSteps, 800);
|
|
|
+ if (fine.bestCircle != null) return (fine.bestHPos, fine.bestCircle);
|
|
|
+
|
|
|
+ // ③ 局部密扫没检出 → 回退到粗扫命中/降级结果
|
|
|
+ return (center, hitCircle ?? fallbackCircle);
|
|
|
+ }
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: 暂不提交,继续 Task 4 后统一编译提交**
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Task 4: 改 CalibrateWell(调用新定位、新粗对焦、扩精对焦半幅、全程钳位)
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `Calib/CalibrationEngine.cs:135-273`(`CalibrateWell` 方法多处)
|
|
|
+
|
|
|
+- [ ] **Step 1: 初始水平移动加钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:140` 这行:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ if (!RetryMove(() => _motor.HorizontalMoveTo(eepromHPos, ScanDelayMs), $"well{well}初始水平"))
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ if (!RetryMove(() => _motor.HorizontalMoveTo(ClampH(eepromHPos), ScanDelayMs), $"well{well}初始水平"))
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 粗对焦调用改新签名 + 钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:150-151`:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int coarseZ = CoarseFocus(well, eepromZ, ZHalf, CoarseFocusLayers);
|
|
|
+ _motor.VerticalMoveTo(coarseZ, ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int coarseZ = CoarseFocus(well);
|
|
|
+ _motor.VerticalMoveTo(ClampZ(coarseZ), ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: ②居中改用 HCoarseLocate**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:156-162`:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ Log?.Invoke($"[well{well}] ②旋转居中(优化Y偏移)...");
|
|
|
+ var coarse = ScanForCenter(well, eepromHPos, HScanRange, HScanSteps);
|
|
|
+ int fineRange = Math.Max(300, 2 * HScanRange / Math.Max(1, HScanSteps - 1));
|
|
|
+ var fine = ScanForCenter(well, coarse.bestHPos, fineRange, FineScanSteps, 800); // 细扫用800ms长延时确保检测准确
|
|
|
+
|
|
|
+ int bestHPos = fine.bestCircle != null ? fine.bestHPos : coarse.bestHPos;
|
|
|
+ WellCircle bestCircle = fine.bestCircle ?? coarse.bestCircle;
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ Log?.Invoke($"[well{well}] ②水平全行程定位+居中...");
|
|
|
+ var located = HCoarseLocate(well);
|
|
|
+ if (located.bestHPos < 0)
|
|
|
+ {
|
|
|
+ Log?.Invoke($"[well{well}] ✗ 水平全行程未找到圆,跳过该well");
|
|
|
+ return new WellCalib { Well = well, Note = "水平全程未检出圆" };
|
|
|
+ }
|
|
|
+ int bestHPos = located.bestHPos;
|
|
|
+ WellCircle bestCircle = located.bestCircle;
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: 居中后水平移动加钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:166`:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ if (!RetryMove(() => _motor.HorizontalMoveTo(bestHPos, ScanDelayMs), $"well{well}居中水平"))
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ if (!RetryMove(() => _motor.HorizontalMoveTo(ClampH(bestHPos), ScanDelayMs), $"well{well}居中水平"))
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 5: 精对焦半幅/步距改用新字段 + 钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:211-217`:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int fineZHalf = Math.Max(200, ZHalf / 3); // 精对焦围绕粗焦点小范围
|
|
|
+ int zstep = ZLayers > 1 ? 2 * fineZHalf / (ZLayers - 1) : 1;
|
|
|
+ var curve = new List<(int z, double s)>();
|
|
|
+ for (int i = 0; i < ZLayers; i++)
|
|
|
+ {
|
|
|
+ int z = Math.Max(0, coarseZ - fineZHalf) + zstep * i;
|
|
|
+ _motor.VerticalMoveTo(z, ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+替换为(半幅用 `FineZHalf`,步距用 `FineZStep`,层数由半幅/步距算出,移动用 `ClampZ`):
|
|
|
+
|
|
|
+```csharp
|
|
|
+ int zstep = FineZStep > 0 ? FineZStep : 500;
|
|
|
+ int fineLayers = 2 * FineZHalf / zstep + 1; // 半幅6000步距500 → 25层
|
|
|
+ var curve = new List<(int z, double s)>();
|
|
|
+ for (int i = 0; i < fineLayers; i++)
|
|
|
+ {
|
|
|
+ int z = coarseZ - FineZHalf + zstep * i;
|
|
|
+ _motor.VerticalMoveTo(ClampZ(z), ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 6: 精对焦循环内的层数显示与 OnStep 同步**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:223`(精对焦 OnStep):
|
|
|
+
|
|
|
+```csharp
|
|
|
+ OnStep?.Invoke($"精对焦 {i + 1}/{ZLayers} Z={z} 分={sc:F4}", circle, null);
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ OnStep?.Invoke($"精对焦 {i + 1}/{fineLayers} Z={z} 分={sc:F4}", circle, null);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 7: 最终对焦移动加钳位**
|
|
|
+
|
|
|
+将 `CalibrationEngine.cs:267`:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ _motor.VerticalMoveTo(r.FocusZ, ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+替换为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+ _motor.VerticalMoveTo(ClampZ(r.FocusZ), ScanDelayMs);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 8: 编译验证(预期通过)**
|
|
|
+
|
|
|
+Run: `dotnet build AutoFocusTool.csproj -c Debug`
|
|
|
+Expected: 生成成功,0 错误。
|
|
|
+
|
|
|
+> 旧字段 `HScanRange`/`HScanSteps`/`ZHalf`/`ZLayers`/`CoarseFocusLayers` 现可能不再被引用,编译器不会报错(public 字段)。保留以兼容外部设置,无需删除。
|
|
|
+
|
|
|
+- [ ] **Step 9: 提交 Task 2~4**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add Calib/CalibrationEngine.cs
|
|
|
+git commit -m "feat(calib): 水平全行程粗扫定位 + Z固定大窗口粗对焦 + 扩精对焦半幅
|
|
|
+
|
|
|
+- CoarseFocus 改固定中心90000±30000步距2000
|
|
|
+- 新增HCoarseLocate全行程粗扫(0~70000)命中完整圆即停+局部密扫
|
|
|
+- 精对焦半幅6000步距500, 所有电机移动经ClampH/ClampZ限位
|
|
|
+
|
|
|
+Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Task 5: 验证
|
|
|
+
|
|
|
+无单元测试框架,按以下方式验证。
|
|
|
+
|
|
|
+- [ ] **Step 1: 编译全解决方案**
|
|
|
+
|
|
|
+Run: `dotnet build AutoFocusTool.csproj -c Debug`
|
|
|
+Expected: 生成成功,0 错误。
|
|
|
+
|
|
|
+- [ ] **Step 2: 启动程序确认 UI 不崩溃**
|
|
|
+
|
|
|
+Run: `./bin/Debug/net8.0-windows/AutoFocusTool.exe`(后台启动)
|
|
|
+Expected: 主窗口正常显示。不点"自动初始化"则不触发相机(已知相机抓帧段错误不在本次范围)。
|
|
|
+
|
|
|
+- [ ] **Step 3: 真机验证(需连接设备,由用户执行)**
|
|
|
+
|
|
|
+清单:
|
|
|
+1. 扫描设备 → 连接相机+串口 → 勾选若干 well → 一键自动初始化。
|
|
|
+2. 观察标定窗口实时画面:水平粗扫阶段应能看到 well 圆转入画面并命中。
|
|
|
+3. 看日志确认:
|
|
|
+ - 出现 `粗扫水平{p}: ... 完整=True` 且命中即停。
|
|
|
+ - 出现 `粗对焦扫{N}层 区间[60000,120000] 步距2000`。
|
|
|
+ - 精对焦层数约 25 层。
|
|
|
+ - 若有越界,出现 `⚠ 水平脉冲 ... 越界` / `⚠ 垂直脉冲 ... 越界` 日志。
|
|
|
+4. 确认原先"找不到圆"的 well 现在能检出并完成标定。
|
|
|
+
|
|
|
+- [ ] **Step 4: 清理后台进程**
|
|
|
+
|
|
|
+Run: `tasklist | grep -i AutoFocus`,确认后停止测试进程。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Self-Review(计划自检结果)
|
|
|
+
|
|
|
+- **Spec 覆盖**:4.1参数→Task1;4.2钳位→Task1 + 各调用点(Task3/4);4.3水平全行程→Task3 + Task4 Step3;4.4 Z粗对焦→Task2 + Task4 Step2;4.5精对焦扩半幅→Task4 Step5;第7节错误处理(退化/钳位Log)→Task3 ③分支 + ClampH/Z;第8节测试→Task5。无遗漏。
|
|
|
+- **占位符**:无 TBD/TODO,每个代码步骤含完整代码。
|
|
|
+- **类型/签名一致性**:`CoarseFocus(int well)` Task2定义、Task4 Step2调用一致;`HCoarseLocate(int well)` 返回 `(int bestHPos, WellCircle bestCircle)`,Task4 Step3按此解构;新字段命名 `FineZHalf`/`FineZStep`/`ZCoarseCenter`/`ZCoarseHalf`/`ZCoarseStep`/`HCoarseStart`/`HCoarseEnd`/`HCoarseStep`/`HMin`/`HMax`/`ZMin`/`ZMaxPulse` 全计划一致。
|
|
|
+- **编译顺序**:Task2 改被调方后单独编译会失败(已注明),Task2~4 合并提交,Task4 Step8 编译通过——有意的"先改被调方再改调用方"顺序。
|