瀏覽代碼

docs: 总方案V5.0结构化重构(六篇+附录,整合协议手册/真机实测/搬迁清单)

- 新增文档导航与分篇结构(项目/技术/代码/协议/演进/待办+附录)
- 对焦算法按06-17代码校准:÷mean修正、粗精对焦实际参数
- 补全74000伪峰风险分析,结合真机Z焦准零点数据
- 标注遗留死参数,避免照着调错参数

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 1 周之前
父節點
當前提交
682afac475
共有 1 個文件被更改,包括 508 次插入393 次删除
  1. 508 393
      相机自动对焦项目-总方案.md

+ 508 - 393
相机自动对焦项目-总方案.md

@@ -1,17 +1,37 @@
 # 相机自动对焦项目 · 总方案(唯一权威文档)
 
-> **最后更新:2026-06-13**  
-> **用途:** 相机自动对焦/自动拍摄校正项目的需求、技术方案、代码结构、当前状态的**唯一权威记录**。  
-> **阅读建议:** 想快速了解进度 → 直接看**第十节"现状与下一步"**
+> **最后更新:2026-06-17(V5.0 结构化重构:整合协议手册/真机实测/调试界面搬迁清单)**
+> **用途:** 相机自动对焦/自动拍摄校正项目的需求、技术方案、代码结构、协议、当前状态的**唯一权威记录**。
+> **配套:** `自动对焦流程图.html`(最新全流程图解)
 
 ---
 
-## 一、项目背景
+## 文档导航
+
+| 篇 | 章节 | 看这里如果你要… |
+|----|------|----------------|
+| **第一篇·项目** | 1 背景 / 2 硬件架构 | 了解设备、相机、运动机构、为什么做这个项目 |
+| **第二篇·技术** | 3 对焦算法 / 4 踩坑教训 | 改算法、调参数、理解设计约束 |
+| **第三篇·代码** | 5 代码结构 / 6 编译运行 / 7 GUI功能 / 8 业务闭环 | 上手改代码、跑程序 |
+| **第四篇·协议** | 9 下位机串口协议手册 | 对接下位机、加新命令、写EEPROM |
+| **第五篇·演进** | 10 修复与演进史 / 11 真机实测记录 | 了解改了什么、真机实际数值 |
+| **第六篇·待办** | 12 调试界面搬迁清单 / 13 现状与下一步 | 搬迁老功能、接续工作 |
+| **附录** | A 被删工具 / B 老代码考古 / C 诊断存图源码 | 备查 |
+
+---
+---
+
+# 第一篇 · 项目
+
+## 1. 项目背景
 
 ### 1.1 设备与场景
-- **设备:** 10舱室时差培养箱(成都艾伟孚,川械注准20232180053)
+- **设备:** 10舱室时差培养箱(成都艾伟孚,川械注准20232180053)+ 1缓冲瓶(11号)
 - **用途:** 相机在显微镜下对胚胎进行延时拍摄
 - **核心问题:** 现有"自动选清晰层"算法效果差,7组测试全部选错
+  - **根因背景(见9.x/附录B):** 旧系统**客户端不做选层**,只上传图给服务器、向服务器索要最清晰层。
+    "选错"的根因大概率在**服务器算法**或**传给它的几何参数**(Z起点/层间距/抠图偏移),不在原C#仓库。
+    本项目对策:**把选层算法搬到本地、纯C#可解释**,绕开服务器。
 
 ### 1.2 项目目标
 开发**独立C#小软件**,实现"拍出一张可用图"的全自动校正流程:
@@ -19,451 +39,546 @@
 曝光校正 → XY皿孔居中 → 空皿检测 → Z对焦(选层/搜索)
 ```
 
----
-
-## 二、硬件架构(真机确认)
+## 2. 硬件架构(真机确认)
 
 ### 2.1 设备组成
-- **10舱室整机** + 1缓冲瓶(11号)
-- 每舱独立:CCD相机 + 光源LED + Z轴电机 + 水平旋转电机
+- 10舱室整机 + 1缓冲瓶(11号);每舱独立:CCD相机 + 光源LED + Z轴电机 + 水平旋转电机
+- **缓冲瓶(11号)无相机、不对焦**,且脉冲量级与培养舱不同(见11.2,千级 vs 万级)
 
 ### 2.2 相机
-- **型号:** Microview MVC2000
-- **接口:** USB2.0
-- **分辨率:** 2592×1944(必须用这个,曾误用1600×1200导致well被裁出画面)
+- **型号:** Microview MVC2000(型号串硬编码 `"MVC2000"`,USB2.0,约200万像素,Bayer传感器)
+- **分辨率:** 2592×1944(**必须用这个**,曾误用1600×1200导致well被裁出画面)
 - **枚举:** 全局0-9台,通过序列号(CCDSN)桥接到舱室
-- **曝光单位:** 100µs,增益RGB各0-255
+- **曝光单位:** 100µs(`CapInfoStruct.Exposure`);增益 RGB 三通道各 0-255(`Gain[3]`,非单一全局gain)
 - **数据:** 上下颠倒需FlipY
+- **初始化顺序:** `Init()`(MV_Usb2Init "MVC2000") → `SetOpMode(0)`(0=拍照单帧/1=实时)→ 循环 `GrabRgb()`
 
 ### 2.3 运动机构(关键)
 - **相机:** 只做Z轴垂直运动拍不同焦平面
 - **培养皿:** 水平电机旋转,让16个well圆形布局逐个对准相机
-- **单位:** 步进电机脉冲(步),非µm
-- **Z参数:** 层间距默认128脉冲,行程上限125000
-- **Well间距:** 约9000脉冲(如舱9: well1=70200, well2=79200...well16=205300)
-
-### 2.4 下位机通信
-- **串口:** 每舱独立COM口,9600/8N1
-- **协议:** `5E CMD 00 LTH 数据 累加和校验`,无帧尾,回复长度固定
+- **只有2个步进电机**(不是XYZ三轴):水平电机(皿孔定位) + 垂直电机(Z=对焦轴)。
+  方案里"XY两轴居中"在源码里只有**单个水平轴**——居中实际只能调Y偏移(见4.2)
+- **单位:** 步进电机脉冲(步),**非µm**(全代码无µm换算)
+- **★ Z脉冲量级(真机实测,见11.2):** 培养舱Z焦准零点 **7.4万~8.4万**、工作位最高 **11万**、软上限 **125000**。
+  每层步距各舱不同(实测96或128,默认128)。**新代码 `ZCoarseCenter=90000`、区间60000~120000 与真机吻合。**
+- **Well间距:** 约9000脉冲(实测各舱 well1≈7万 → well16≈20.5万)
+
+### 2.4 下位机通信(详见第9章协议手册)
+- **串口:** 每舱独立COM口,**9600/8N1**,超时基准3000ms(本项目按命令类型动态调)
+- **协议:** `5E CMD 00 LTH 数据 累加和校验`,**无独立帧尾**,回复长度按命令固定
 - **EEPROM:** 每个well的"水平位置"+"Z焦准零点"存下位机
-- **LED:** 只能开关+读亮度,无写亮度命令
+- **LED:** **只能开关+读亮度,无写亮度命令**(注:缓冲瓶程序里确有写亮度0x12到0x0534,见9.7)
 
 ---
+---
+
+# 第二篇 · 技术方案
 
-## 三、技术方案
+## 3. 对焦算法(★ 按当前代码 06-17 校准)
 
 ### 3.1 清晰度评价(核心算法)
-- **指标:** Tenengrad(梯度平方和),纯C#实现
-- **ROI:** 只在well圆内计算,排除背景干扰
-- **预处理:** 统一尺寸、去噪
-- **归一化:** 分数除以像素数,可跨帧比较
+- **指标:** Tenengrad(Sobel梯度幅值平方和),纯C#,无OpenCV(备用 Laplacian 方差)
+- **ROI:** 只在well圆内/中央区域计算,排除背景反光
+- **归一化:** `sumSq / 像素数 / mean`(**除以均值一次方**)
+  - ⚠️ **关键修正(06-16,提交 1216bbc):** 原为除以 `mean²`。实测随Z增大画面亮度从~53升到~185(光学效应),
+    `mean²` 把高Z清晰帧分数压死,导致选最暗最糊的低Z层。**改÷mean后峰正确落在92000。这是"选错层"的真根因级修复。**
+  - 代码:`Imaging/Sharpness.cs:105`
 
 ### 3.2 标定流程(四步)
 ```
-①粗对焦(中央40% ROI) → ②旋转居中(粗扫+细扫) → ③曝光二分(well内ROI) → ④精对焦(0.95r ROI + 平滑插值)
+①粗对焦(中央40% ROI,固定大窗口 90000±30000)
+  → ②以EEPROM位置为中心小范围微调Y居中
+  → ③曝光二分(well内ROI)
+  → ④精对焦(围绕粗峰 ±6000,0.95r ROI + 3点平滑 + 抛物线插值)
 ```
+代码:`Calib/CalibrationEngine.cs · CalibrateWell()`
 
 **关键技术点:**
-1. **粗对焦:** 使用中央40% ROI避免被培养皿边缘带偏
-2. **居中:** 只优化Y偏移(旋转控制),X偏移~5%是硬件局限
-3. **曝光:** 每次设置后丢弃第1帧使用第2帧(解决帧延迟)
-4. **精对焦:** 0.95r ROI保留边缘 + 3点平滑 + 抛物线插值
-
-### 3.3 居中判据
-- **目标:** Y偏移 < ±12% + well完整
-- **检测:** Otsu阈值化 + 连通域 + 圆度过滤
-- **阈值:** aspect < 1.5, boxFill > 0.6, rConsist > 0.65, rFrac ∈ [0.10,0.28]
-- **延时:** 细扫用800ms确保稳定
+1. **粗对焦:** 中央40% ROI避免被培养皿边缘带偏;**固定大窗口**(不依赖EEPROM零点),
+   `ZCoarseCenter=90000 ± ZCoarseHalf=30000`,步距2000,约31层
+2. **开扫前停稳2秒:** 逐层扫描前先把Z移到起点并 `Thread.Sleep(CoarseSettleMs=2000)` 停稳再开扫(见3.4)
+3. **居中:** 以EEPROM水平位置为中心**小范围微调**(半幅 `HFineRange=2000`,9步),
+   **绝不大范围扫描**(well间距~9000,大扫会扫到相邻well);只优化Y偏移
+4. **曝光:** SetExposure后等 `max(200,e/5)`ms 并**丢第1帧用第2帧**(见4.6)
+5. **精对焦:** 围绕粗峰 `FineZHalf=6000`、步距 `FineZStep=500`(约25层),0.95r ROI保留边缘,
+   圆未检出降级中央40% ROI(**绝不用全图**);3点平滑 + 抛物线插值
+
+### 3.3 居中判据(圆检测)
+- **目标:** Y偏移 < ±12%(`CenterTolPct`)+ well完整
+- **检测:** 下采样 + Otsu阈值化 + 连通域 + 圆度过滤(`Imaging/WellDetector.cs`)
+- **阈值(与代码一致):** `aspect < 1.5` + `boxFill > 0.6` + `rConsist > 0.65` + `rFrac ∈ [0.10, 0.28]`
+- **延时:** 居中细扫800ms;其余扫描小步用 `ScanDelayMs=350`
+
+### 3.4 ⚠️ 已知未解决风险:粗对焦偶发 74000 伪峰(待现场复现)
+- **现象:** 偶发某well粗对焦选中 ~74000 伪峰(真实焦面 ~86000-92000),精对焦被锁错误小窗口、输出弱峰(≈1.0)。
+  同批只坏1个、坏值固定74000、坏的well编号在批次间变化。
+- **已排除:** 串口未到位、相机冷启动、清晰度算法(已修÷mean)、"第一个well"时序——多次实测无法复现。
+- **当前缓解:** 开扫前停稳2秒(516f02a) + 逐层分数落盘(007d9f5);最可疑"某层电机未真正到位/运动中采样形成伪峰"。
+- **★ 注意(结合11.1真机数据):** 真机实测**6个培养舱Z焦准零点全在74000~84000**!74000恰好落在这个真实焦准区。
+  这说明74000不一定是"伪峰",可能是**EEPROM零点位置附近的真实次峰**与90000主焦面竞争。下次复现时务必开DebugSave存图确认。
+- 详见 `CalibrationEngine.cs · CoarseFocus()` 长注释。
+
+### 3.5 遗留死参数(别照着调)
+`CalibrationEngine` 保留 `ZHalf=1500 / ZLayers=9 / CoarseFocusLayers=7 / HScanRange / HScanSteps / FineScanSteps`
+等字段,**在当前 `CalibrateWell` 流程中已不被使用**。改对焦范围认准**正在用的参数:**
+`ZCoarseCenter/ZCoarseHalf/ZCoarseStep`(粗)、`FineZHalf/FineZStep`(精)、`HFineRange/HFineSteps`(居中)、`CoarseSettleMs`(停稳)。
+
+## 4. 踩过的坑(长期有效)
+
+| # | 坑 | 教训/方案 |
+|---|-----|----------|
+| 4.1 | 硬编码1600×1200,相机原生2592×1944 → well被裁出画面 | 所有尺寸取自 `cam.Width/Height`,绝不硬编码 |
+| 4.2 | 曾优化X偏移,但只有1个水平旋转轴只能控Y | X的~5%固定偏移是硬件局限,只优化Y |
+| 4.3 | well间斜纹反光被误判成圆 | `aspect<1.5 + boxFill>0.6 + rConsist>0.65 + rFrac∈[0.10,0.28]` |
+| 4.4 | 曝光用全图均值被黑边拉低 → 过曝 | 只在well圆内ROI算亮度 |
+| 4.5 | 焦不对→画面糊→检不出圆→无法居中 | 先粗对焦让well清晰,再居中 |
+| 4.6 | 开环电机,回复≠停稳,移动后立刻抓图=运动拖影帧→伪峰 | 移动后等延时;大行程开扫前额外停稳2秒+丢残留帧 |
+| 4.7 | 高Z层更亮(53→185),÷mean²把清晰亮帧分数压死→选错 | 归一化只÷mean一次方 |
 
+---
 ---
 
-## 四、代码结构
+# 第三篇 · 代码与运行
 
-### 4.1 目录结构
+## 5. 代码结构(当前实际,仅主程序单工程)
 ```
 C:\claudeFile\TL\AutoFocusTool\    (WPF/.NET 8/x64)
-├── Camera/                         相机封装(MVCAPI P/Invoke)
-├── Serial/                         串口协议(Protocol/SerialMotor/HouseMotor)
-├── Imaging/                        图像处理(Sharpness/WellDetector/ExposureMeter)
-├── Calib/                          标定引擎(CalibrationEngine/CalibrationFile/CalibrationManager)
-├── Devices/                        设备扫描(DeviceScanner/HouseDevice)
-├── MainWindow.*.cs                 主窗口(分部类:连接/相机/电机/扫描/标定)
-├── CalibWindow.xaml                标定独立窗口(4x4=16格实时显示)
-├── Survey/Survey.cs                巡检工具(已集成CalibrationManager)
-├── Calibrate/Calibrate.cs          命令行标定工具
-├── SelfTest/Program.cs             硬件自检工具
-└── calibration.json                标定结果(16个well参数)
+├── Camera/        相机封装(Camera/MVCAPI P/Invoke/CapInfoStruct)
+├── Serial/        串口(Protocol协议 / SerialMotor底层 / HouseMotor语义层)
+├── Imaging/       图像(Sharpness / WellDetector / ExposureMeter / ImageConverter)
+├── Calib/         标定(CalibrationEngine / CalibrationFile / CalibrationManager)
+├── Devices/       设备扫描(DeviceScanner / HouseDevice)
+├── Logging/       FileLogger静态文件日志器(落盘 Logs/)
+├── MainWindow.*.cs  主窗口分部类(xaml/Camera/Motor/Scan/Calib)
+├── CalibWindow.*    标定独立窗口(4x4=16格实时显示)
+├── calibration.json 标定结果
+└── 自动对焦流程图.html  ★全流程图解(含每环节参数+异常分支)
 ```
-
-### 4.2 关键类
-- **CalibrationEngine:** 标定引擎核心,可复用,带回调
-- **CalibrationManager:** 优先读JSON标定结果,降级到EEPROM
-- **WellDetector:** 圆检测(连通域+圆度过滤)
-- **Sharpness:** Tenengrad清晰度计算(纯C#)
-- **ExposureMeter:** 曝光评价+二分搜索
-- **HouseMotor:** 电机语义层封装(支持delayMs重载)
-
----
-
-## 五、关键修复记录(2026-06-13)
-
-### 5.1 P0致命问题(全部已修复)
-
-| 问题 | 根因 | 修复方案 | 效果 |
-|------|------|---------|------|
-| **曝光不可重复** | SetExposure后返回旧帧 | 延时+丢弃第1帧用第2帧 | 7倍偏差→0 |
-| **居中成功率低** | aspect阈值过严+延时短 | 1.35→1.5 + 细扫800ms | 62.5%→100% |
-| **well2系统性偏差** | 真正居中位置被拒绝 | 同上 | 28.1%→1.1% |
-| **业务未闭环** | Survey不读JSON | 集成CalibrationManager | ✅ |
-| **异常处理薄弱** | 错误被忽略 | 检查返回值+质量判断 | ✅ |
-| **相机旧帧缓冲** | 未清空缓冲 | 每次移动后丢第1帧 | ✅ |
-| **粗对焦被背景带偏** | 使用全图清晰度 | 改用中央40% ROI | ✅ |
-
-### 5.2 P1算法优化(已完成)
-
-| 优化 | 方案 | 效果 |
-|------|------|------|
-| **对焦ROI不一致** | 精对焦0.7r→0.95r保留边缘 | 峰比提升 |
-| **峰值检测无插值** | 3点平滑+抛物线插值 | 提升精度 |
-
-### 5.3 真机验证结果(9号舱16个well)
-
-**测试日期:** 2026-06-13  
-**测试轮次:** 3轮
-
-| 指标 | 修复前 | 修复后 | 改善 |
-|------|--------|--------|------|
-| 曝光可重复性 | 3/16相差7倍 | 16/16完全一致(exp=58) | 100% |
-| 居中成功率 | 10/16 (62.5%) | 16/16 (100%) | +60% |
-| well2偏移 | 28.1% | 1.1% | -96% |
-| 最大Y偏移 | 28.1% | 5.9% | -79% |
-| 对焦峰比 | 全部<1.2 | 1.09~1.20 | 空well预期 |
-
-**可重复性验证(两轮对比):**
-- 曝光:0偏差
-- 居中:<0.1%偏差
-- 对焦Z:<20脉冲偏差
-
-### 5.4 第二轮修复(2026-06-13 · 针对同事审查报告 CODE_REVIEW_AND_ANALYSIS.md)
-
-对审查报告逐条核查后,修复了 **5 项真实缺陷**,其余条目要么已在第一轮修过、要么属非必要的架构重构(见 5.5)。
-
-| 编号 | 问题(核查属实) | 根因 | 修复 |
-|------|------|------|------|
-| **P0-1** | 标定结果"只存不用" | `CalibrationManager` 从未被调用;无 Survey 目录;转well直读EEPROM | GUI `BtnGoWell` 接入 `CalibrationManager`,优先用JSON(合格才用),存档后刷新缓存 |
-| **P0-2/3** | 手动路径运动模糊/旧帧 | 手动Z扫描、手动设曝光后立即抓帧=旧帧 | Z扫描每层丢第1帧用第2帧;设曝光后等待`max(200,e/5)`ms并丢帧 |
-| **P0-4** | 精对焦圆未检出用全图 | roi=null → 全图清晰度被背景/反光干扰 | 圆未检出降级到中央40% ROI(与粗对焦一致),绝不用全图 |
-| **P0-6** | 电机移动无重试 | 下位机偶发不回复即放弃整个well | 新增 `RetryMove`,关键定位移动重试3次 |
-| **P0-5** | 误导性死代码 | `GrabWithRollingExposure`/`FullMean` 写好但零调用 | 删除,避免后人误以为有自适应曝光(固定曝光60真机已验证16/16可靠) |
-| **P0-7** | **JSON反序列化静默失效**(真机测试发现,比审查报告更严重) | `CalibrationFile`等用public**字段**,`System.Text.Json`默认不反序列化字段→`Load`返回空对象(舱数0) | `Load`的`JsonSerializerOptions`加`IncludeFields=true`。**此bug导致即便P0-1接线正确,标定结果仍永远读不到、静默降级EEPROM——闭环形同虚设** |
-
-**附带统一:** 手动Z扫描ROI也改为"先检well圆→圆内0.95r,检不出→中央40%",与标定引擎一致。
-
-**离线验证:** 用合成图(中心清晰+四周噪声)实测 Tenengrad —— 全图焦/糊比值仅1.61(被边缘噪声淹没),中央40% ROI比值达∞(完美区分),证明 P0-4 降级策略有效。编译0错误0警告,GUI启动冒烟通过。
-
-**真机验证(2026-06-13 第二轮,SmokeTest工具,现场无1号舱→回退2号舱):** 4项全过——
-- 设备扫描:发现7舱(2/4/6/7/8/9有相机, 11缓冲瓶无相机),相机/COM映射正确,全分辨率2592×1944
-- **P0-2/3 实证**:设曝光30→亮度75.1,设曝光150→亮度228.8,丢帧后第2帧亮度正确跟随曝光,**确认无旧帧污染**
-- **P0-7 实证**:修复前`Load`读出舱数0(静默失效),修复后正确读出`house9`;当前9号皿为空皿,16个well峰比全≤1.2,按`PeakRatio>1.2`判据会全部降级EEPROM——符合空皿预期,待放真胚胎重标(峰比>1.5)后闭环才会启用JSON参数
-
-### 5.5 审查报告中**未采纳**的条目(评估为非必要)
-
-报告 P1-3/4/5、P2-1/2/3 主张:详细子步骤进度、续标/重标UI、参数热加载配置、**仿真模式、单元测试框架、状态机重构**。
-这些属"锦上添花"的架构升级,**真机已验证16/16通过**,贸然重构反而引入回归风险。经与用户确认后**本轮不做**(用户原话:"UX/仿真/单元测试/状态机,改动太大,就不弄,且非必要")。如未来需要再单独立项。
-
----
-
-## 六、踩过的坑(重要教训)
-
-### 6.1 画面分辨率被裁剪(最坑)
-- **问题:** 硬编码1600×1200,相机原生2592×1944 → well被裁出画面
-- **教训:** 所有图像处理尺寸必须取自`cam.Width/Height`,绝不硬编码
-
-### 6.2 居中优化错了方向
-- **问题:** 曾优化X偏移,但旋转只能控制Y
-- **真相:** X的~5%固定偏移是相机/转盘硬件偏移,旋转修不了
-- **正确:** 只优化Y偏移,X偏移接受
-
-### 6.3 圆检测被斜纹反光骗
-- **问题:** well间大块斜纹反光被误判成圆
-- **方案:** 增加判据aspect<1.5 + boxFill>0.6 + rConsist>0.65 + rFrac∈[0.10,0.28]
-
-### 6.4 曝光必须只看well圆内ROI
-- **问题:** 用全图均值被黑边拉低 → 一路过曝
-- **方案:** 只在检测到的well圆内ROI计算亮度
-
-### 6.5 对焦与居中耦合
-- **洞察:** 焦不对→画面糊→检不出圆→无法居中
-- **方案:** 先粗对焦让well清晰,再居中
-
----
-
-## 七、编译与运行
-
-### 7.1 编译
+> ⚠️ 旧文档列的 `Survey/`、`Calibrate/`、`SelfTest/`、`SmokeTest/`、`HScan` **已全删**(提交1e36b67)。
+> 价值评估见附录A。Survey目录从来不存在(备份亦无)。
+
+**关键类:**
+- **CalibrationEngine** 标定引擎(四步+RetryMove+CenterRoi40+ClampH/ClampZ限位)
+- **CalibrationManager** 优先JSON(`CircleFound && PeakRatio>1.2`)否则降级EEPROM
+- **CalibrationFile** 档案JSON读写。⚠️ public**字段**,`Load` 必须 `IncludeFields=true`(见10.2·P0-7)
+- **WellDetector** 圆检测;**Sharpness** 清晰度(÷mean);**ExposureMeter** 曝光二分
+- **HouseMotor** 电机+LED语义层(`VerticalMoveTo(pulse, delayMs)` 自定义延时重载)
+- **Protocol** 协议帧构造+校验(见第9章);**SerialMotor** 串口收发
+
+## 6. 编译与运行
 ```bash
 cd "C:\claudeFile\TL\AutoFocusTool"
 dotnet build -c Debug
+./bin/Debug/net8.0-windows/AutoFocusTool.exe   # 唯一可执行程序
 ```
-
-### 7.2 运行GUI
-```bash
-./bin/Debug/net8.0-windows/AutoFocusTool.exe
-```
-
-### 7.3 命令行标定
-```bash
-./Calibrate/bin/Debug/net8.0-windows/Calibrate.exe [舱号] [起始well] [结束well]
-# 例如:./Calibrate.exe 9 1 16
-```
-
-### 7.4 查看结果
-- 标定参数:`calibration.json`
-- 标定终图:`calib/cal_h9_w*_FINAL.bmp`
-- 测试日志:`test_fix/*.log`
-
----
-
-## 八、GUI功能
-
-### 8.1 设备连接
-1. 点击"扫描设备" → 发现6个带相机舱(2/4/6/7/8/9)
-2. 选择舱室(如9号舱)
-3. 点击"连接"
-
-### 8.2 手动控制
-- 开/关光源LED
-- 抓帧实时预览(显示曝光/增益/Z/水平/well值)
-- 调整曝光/增益(自动抓帧看效果)
-- Z轴上下移动
-- 水平旋转(选well 1-16,读EEPROM位置并转过去)
-- Z扫描选层(显示清晰度曲线)
-
-### 8.3 自动标定
-1. 勾选要标定的well(1-16任选,支持全选/全不选)
-2. 点击"一键全自动初始化"
-3. 弹出全屏窗口,4x4=16格实时显示
-4. 正在标定的格:黄字进度+实时画面
-5. 完成的格:定格绿框
-6. 结果存`calibration.json` + 终图存`calib_result/`
-
----
-
-## 九、业务闭环
-
-### 9.1 标定结果使用(2026-06-13 第二轮修复后)
-
-- **GUI 转well(MainWindow.Calib.cs · BtnGoWell_Click):** 已接入 `CalibrationManager`
-- **逻辑:** 优先读 `calibration.json`,标定合格(`CircleFound && PeakRatio > 1.2`)才用JSON的水平/Z/曝光;否则降级到EEPROM(曝光不动)
-- **缓存刷新:** 每次"一键全自动初始化"存档后调用 `RefreshCache()`,转well立即用最新结果
-- **日志标注:** 转well时打印参数来源 `[标定结果]` 或 `[EEPROM(未标定/不合格)]`,便于现场确认
-
-> ⚠️ **更正历史记录:** 此前文档曾声称"Survey.cs 已集成 CalibrationManager、业务已闭环",
-> 经代码核查 **该说法不实**:项目中既无 Survey 目录,`CalibrationManager` 也从未被任何代码调用,
-> 标定结果实际"只存不用"。本轮(第二轮修复)已在 GUI 转well路径真正接通,业务闭环现已成立。
-
-### 9.2 标定文件格式
+- 标定参数:`calibration.json`;终图:`calib_result/`;日志:`Logs/`;诊断数据:`TestData/`
+- ⚠️ 旧文档的 `Calibrate.exe`/`SmokeTest.exe`/`HScan.exe` 已失效。现所有标定/测试通过GUI。
+
+## 7. GUI功能
+- **设备连接:** 扫描设备(实测带相机舱2/4/6/7/8/9,11缓冲瓶无相机)→ 选舱 → 连接
+- **手动控制:** 开关LED;抓帧预览(显示曝光/增益/Z/水平/well);调曝光/增益;Z上下移动;
+  水平转well(读EEPROM位置);Z扫描选层(清晰度曲线);按钮操作埋点落盘
+- **自动标定:** 勾选well(全选/全不选)→「一键全自动初始化」→ 弹窗4x4=16格实时显示 →
+  合格绿框(`偏移<12% && 检到圆 && 峰比>1.2`) → 存 `calibration.json` + 终图 `calib_result/`,存档后 `RefreshCache()`
+
+## 8. 业务闭环
+- **GUI转well(`MainWindow.Calib.cs · BtnGoWell_Click`):** 接入 `CalibrationManager`
+- **逻辑:** 优先读JSON,合格(`CircleFound && PeakRatio>1.2`)才用JSON的水平/Z/曝光;否则降级EEPROM
+- **缓存刷新:** 标定存档后 `RefreshCache()`,转well立即用最新结果
+- **日志:** 转well打印来源 `[标定结果]` 或 `[EEPROM(未标定/不合格)]`
+- **⚠️ 当前最大缺口:标定结果只写JSON,从未回写下位机EEPROM**(见第12章搬迁清单·P0)
+
+**标定文件格式(当前真实数据,house8):**
 ```json
 {
-  "tlSn": "house9",
-  "date": "2026-06-13 13:54",
+  "tlSn": "house8", "date": "2026-06-16 23:32",
   "houses": [{
-    "house": 9,
-    "wells": [{
-      "well": 1,
-      "horizontalPulse": 70198,
-      "exposure": 58,
-      "focusZ": 79555,
-      "centerOffsetPct": -0.0,
-      "circleFound": true,
-      "peakSharp": 0.0647,
-      "peakRatio": 1.11,
-      "note": "对焦峰弱;"
-    }, ...]
+    "house": 8, "port": "COM5", "ccdIndex": 0, "ccdSn": "23120646",
+    "wells": [{ "well": 1, "horizontalPulse": 71200, "exposure": 33, "focusZ": 88434,
+      "centerOffsetPct": -2.181, "circleFound": true, "peakSharp": 4.284, "peakRatio": 1.239, "note": "" }]
   }]
 }
 ```
 
+---
 ---
 
-## 十、现状与下一步(快速上手必读)
-
-### 10.1 ★ 当前状态(2026-06-13 第二轮修复后)
-
-**✅ 代码层缺陷已清零,等待真胚胎验证**
-
-- ✅ P0致命问题全部修复(第一轮7项 + 第二轮6项,含真机才暴露的P0-7)
-- ✅ 业务闭环**真正接通**:GUI转well已应用标定结果,且修复了底层JSON静默失效(P0-7)
-- ✅ 真机冒烟测试4/4通过(设备扫描/相机通路/曝光丢帧/JSON读取)
-- ✅ 曝光可重复性:0偏差;居中成功率:16/16
-- ⏳ **尚未做真胚胎验证**(计划过几天放胚胎后测,见10.2)
-
-**关键指标(空皿,9号舱):**
-- 曝光:16/16 well = 58(完全一致)
-- 居中:16/16成功,最大偏移5.9%
-- 对焦:可重复,Z偏差<20脉冲
-- 峰比:1.08~1.20(**空皿预期就这么低**,真胚胎应>1.5)
-
-> ⚠️ **当前闭环对空皿"看似不生效"是正常的**:calibration.json里16个well峰比全≤1.2,
-> 按合格判据`PeakRatio>1.2`会全部判不合格→转well时降级读EEPROM。这是空皿的正确行为。
-> 放真胚胎重新标定后峰比上去了,转well才会真正用JSON的标定参数。**别误以为闭环坏了。**
-
-### 10.2 ★★ 下一步:真胚胎验证(过几天放胚胎后做,必读)
-
-**现场情况:** 目前**没有 1 号舱室**(SmokeTest已确认,现场为2/4/6/7/8/9+11缓冲瓶)。
-9号舱有培养皿但**当前是空皿无胚胎**。
+# 第四篇 · 下位机串口协议手册(从老代码提取,对接下位机权威依据)
+
+> 来源:`ivf_tl_control_2.0` / `ivf_tl_operate_2.0` 的 Commander/ComBin/Analysiser/Channel。两版逻辑基本一致,operate 是超集。
+
+## 9. 协议手册
+
+### 9.1 帧格式总则
+**发送帧:** `[0]帧头0x5E [1]CMD命令码 [2]序号0x00 [3]整帧长LTH [4..N-2]辅助码+参数 [N-1]校验`
+- **校验** = 除末字节外所有字节累加和取低8位(`CreateORC`:`for i<len-1: sum+=b[i]`)。**无帧尾。**
+- **收帧** 按预设固定长度截取(不靠帧尾同步)——长度表必须与固件一致,否则错位。
+- **结果位** = 回复帧倒数第二字节(`[lenght-2]`),`0x00`=成功,非0=下位机失败。末字节是校验。
+
+### 9.2 ⚠️ 字节序(混用,最易踩坑)
+| 数据类型 | 字节序 |
+|---------|--------|
+| **电机脉冲**(发0x05 / 收0x18) | **大端**(高字节在前) |
+| **EEPROM数值**(发0x12 / 收0x11) | **小端**(低字节在前) |
+| 温度/压力(收0x06 / 0x20) | 大端 int16 |
+
+新代码 `Protocol.MotorAbsolute` 大端、`ParseEepromInt` 小端——已正确区分,务必沿用。
+
+### 9.3 命令码总表
+| CMD | 含义 | 回复长度 |
+|-----|------|---------|
+| 0x01 | 握手 | 6 |
+| 0x02 | 自检 | 6 |
+| 0x04 | 设目标温度 | 7 |
+| 0x05 | 电机控制 | 6 |
+| 0x06 | 读温度/压力 | 9 |
+| 0x08 | 读传感器AD | 9 |
+| 0x09 | 设IO(LED/气阀/补气/换气) | 6 |
+| 0x10 | 获取IO(含舱门状态) | 7 |
+| 0x11 | 读EEPROM | 10 |
+| 0x12 | 写EEPROM | **control=6 / operate=12** ⚠️需真机确认 |
+| 0x16 | 自动换气(仅operate) | 6 |
+| 0x18 | 读电机位置 | 10 |
+| 0x19 | 缓冲瓶补气 | 6 |
+| 0x20 | 读缓冲瓶数据(压力+双温度) | 12 |
+
+### 9.4 电机控制(CMD=0x05,11字节帧)
+布局:`5E 05 00 0B [辅助码] [脉冲4字节大端] 00 [校验]`
+辅助码 = 高半字节(轴:水平1/垂直2) | 低半字节(动作:正转0/反转1/脱机2/复位3/绝对4)
+
+| 命令 | 辅助码 | 帧 |
+|------|--------|-----|
+| 水平正转/反转 | 0x10 / 0x11 | `5E 05 00 0B 1x [4字节大端] 00 校验` |
+| 水平绝对运动 | 0x14 | 同上 |
+| 水平复位(硬编码,回退3000) | 0x13 | `5E 05 00 0B 13 00 00 00 0B B8 44` |
+| 垂直正转/反转 | 0x20 / 0x21 | `5E 05 00 0B 2x [4字节大端] 00 校验` |
+| 垂直绝对运动 | 0x24 | 同上 |
+| 垂直复位(硬编码,回退2000) | 0x23 | `5E 05 00 0B 23 00 00 00 07 D0 68` |
+
+> 脉冲是**4字节int32大端**(注释虽写"16位"但实现是32位,支持十几万,新旧一致,见11章)。
+
+### 9.5 读电机位置(CMD=0x18,回复10字节)
+- 读水平:`5E 18 00 06 01 00`+校验;读垂直:`5E 18 00 06 02 00`+校验
+- 解析:位置在回复 `[4..7]`,**4字节大端 int32**
+
+### 9.6 IO/LED/气阀(CMD=0x09,回复6字节)
+布局:`5E 09 00 07 [子设备] [开01/关00] [校验]`
+| 动作 | 子设备 | 帧 |
+|------|--------|-----|
+| 开/关 LED | 0x00 | 开`5E 09 00 07 00 01 6F` 关`5E 09 00 07 00 00 6E`(**硬编码不走校验函数**) |
+| 进气阀 开/关 | 0x01 | `5E 09 00 07 01 01/00 校验` |
+| 排气阀 开/关 | 0x02 | `5E 09 00 07 02 01/00 校验` |
+| 换气标志 前/后 | 0x03 | `5E 09 00 07 03 01/00 校验` |
+| 舱室补气 | 0x04 | `5E 09 00 07 04 01 校验` |
+| 舱室排气(operate) | 0x05 | `5E 09 00 07 05 01 校验` |
+> 阀类操作发完应 `Thread.Sleep(valueDelay)` 等气路稳定。
+
+### 9.7 读EEPROM(CMD=0x11,回复10字节)
+布局:`5E 11 00 09 [地址高] [地址中] [地址低] 04 00 [校验]`(倒数第二字节0x04=读取字节数)
+解析:值在回复 `[4..7]`,**4字节小端 int32**
+
+**EEPROM地址全表(高 中 低):**
+| 项 | 地址 |
+|----|------|
+| 仪器编号 TLNum | `00 00 08` |
+| CCDSN | `00 00 10` |
+| 下加热板目标温度(读出÷100) | `00 02 38` |
+| 舱室排气阀时间 | `00 03 08` |
+| 舱室进气阀时间 | `00 03 0C` |
+| 垂直焦准零点(**写**用,⚠️与读不一致) | `00 03 1C` |
+| 缓冲瓶进气阀时间 | `00 05 0C` |
+| 灯光亮度 | `00 05 34` |
+| Z扫描间隔脉冲 | `00 04 48` |
+| **16个well水平位置** | well1=`00 04 00`,well2~15=`00 04 50`起步进0x04,**well16=`00 04 04`(特例)** |
+| **16个well Z焦准零点(读用)** | well1=`00 04 08`,步进0x04,well16=`00 04 44`(连续) |
+
+⚠️ **读写地址错配:** Z焦准零点 **读** `00 04 08`(按well表),**写** `00 03 1C`(单地址)。
+新项目逐well写零点应用well表 `00 04 [08..44]`,但**必须真机确认下位机真实写地址**。
+
+### 9.8 写EEPROM(CMD=0x12,12字节帧)
+布局:`5E 12 00 0C [地址高] [地址中] [地址低] [数值4字节小端] [校验]`
+> 数值**小端**(与电机脉冲大端相反!)。`0x12` 回复长度两版本不同(6/12),**新项目须真机实测**。
+
+### 9.9 传感器/温压/舱门/缓冲瓶
+| 命令 | 帧 | 回复 | 解析 |
+|------|-----|------|------|
+| 读温度(0x06) | `5E 06 00 06 [通道] 00`,通道0下盖板/1上盖板/2玻璃片下 | 9字节 | `[4..5]`大端int16 **÷100** |
+| 读压力(0x06) | `5E 06 00 06 03 00` | 9字节 | `[4..5]`大端int16 **不除** |
+| 读舱门(0x10) | `5E 10 00 06 02 00` | 7字节 | `[4]==0x01`为开 |
+| 读缓冲瓶(0x20) | `5E 20 00 05 00` | 12字节 | 压力`[4..5]`/温度1`[6..7]÷100`/温度2`[8..9]÷100` |
+| 缓冲瓶补气(0x19) | `5E 19 00 05 00` | 6字节 | — |
+| 握手(0x01) | `5E 01 00 05 00` | 6字节 | houseSn=`[2]` |
+
+> 缓冲瓶是 houseSn=11 的独立串口。
 
-**测试舱室约定(用户指定):**
-- **有胚胎 → 用 9 号舱**(well 9 那个皿)
-- **空皿/无胚胎的纯功能测试 → 用 1 号舱**(但现场目前无1号舱,回退用其他空皿舱如2号)
+---
+---
 
-**放胚胎后要做的验证(按顺序):**
-1. **重新标定9号舱**:GUI「一键全自动初始化」勾选有胚胎的well,或命令行
-   `./Calibrate/bin/Debug/net8.0-windows/Calibrate.exe 9 1 16`
-2. **看峰比是否>1.5**:有胚胎的well峰比应明显高于空皿的1.1。若仍<1.2需查ROI/对焦范围
-3. **验证选层准确**:人工核对最清晰层Z是否选对(对比终图 calib_result/)
-4. **验证闭环真生效**:标定后在GUI转到该well,日志应显示`[标定结果]`而非`[EEPROM...]`,
-   且曝光自动设为标定值。这是P0-1+P0-7修复的最终验收点。
-5. **不同发育阶段**:PN/卵裂/囊胚 的对焦效果差异
+# 第五篇 · 演进史与真机记录
 
-**预期风险点:** 真胚胎透明、对比度低,Tenengrad峰可能仍不够尖。若验证不通过,
-优先怀疑:精对焦Z范围(`ZHalf`/`ZLayers`)、ROI半径(0.95r)、是否需要空皿检测先行。
+## 10. 修复与演进史
 
-### 10.3 可选优化(非阻塞)
+### 10.1 第一轮 P0 修复(2026-06-13)
+曝光丢帧(7倍偏差→0)、居中阈值放宽+细扫800ms(62.5%→100%)、业务闭环接CalibrationManager、
+异常处理、相机旧帧缓冲、粗对焦改中央40% ROI。
 
-**优先级低:**
-1. **空皿检测** - 需真实样本后设计(基于峰形或几何特征)
-2. **预测式居中** - 可提速70%(当前14秒可接受)
-3. **EEPROM降级** - zZero<0时改为跳过而非设0
-4. **曝光顺序** - 可移到精对焦之后(在清晰状态测定更准)
+### 10.2 第二轮 P0 修复(2026-06-13,针对同事审查报告)
+| 编号 | 问题 | 修复 |
+|------|------|------|
+| P0-1 | 标定"只存不用" | BtnGoWell接CalibrationManager,存档后RefreshCache |
+| P0-2/3 | 手动路径运动模糊/旧帧 | Z扫描每层丢第1帧;设曝光后等`max(200,e/5)`ms并丢帧 |
+| P0-4 | 精焦圆未检出用全图 | 降级中央40% ROI,绝不用全图 |
+| P0-6 | 电机移动无重试 | `RetryMove` 重试3次 |
+| P0-5 | 误导性死代码 | 删 `GrabWithRollingExposure`/`FullMean` |
+| **P0-7** | **JSON反序列化静默失效** | public字段→`System.Text.Json`默认不反序列化→`Load`空对象。加 `IncludeFields=true`。**否则标定永远读不到、闭环形同虚设** |
+
+### 10.3 ★ 对焦算法重写与扫描范围扩大(06-14~06-17)
+| 提交 | 改动 | 意义 |
+|------|------|------|
+| ac9867c | 扫描范围参数 + 电机限位钳位 | 防越界 |
+| 144f3db | **Z固定大窗口粗对焦** + 扩精焦半幅 | 不依赖EEPROM零点 |
+| 8e6508b | **水平定位改小范围微调,废弃全行程扫描** | 防扫到相邻well |
+| 1216bbc | **清晰度归一化 ÷mean²→÷mean** + 峰落边界报警 | 修高Z清晰帧被压低 |
+| adea570 | 串口按命令类型动态读超时 | 修大行程误判超时 |
+| f92a71d/6446adb | GetSourceBuffer加锁+空保护 | 修段错误/NPE |
+| 007d9f5 | 粗对焦逐层分数落盘 | 排查74000伪峰 |
+| 516f02a | **粗对焦开扫前停稳2秒** | 修运动帧伪峰 |
+| a354b79~18ef633 | FileLogger + 按钮埋点 | 操作可追溯 |
+| 1e36b67 | 清理测试子工程 | 见附录A |
+
+### 10.4 审查报告未采纳条目
+仿真模式/单元测试/状态机重构等架构升级,经用户确认本轮不做("改动太大,非必要")。
+
+## 11. 真机实测记录(2026-06-17 · ReadEepromProbe 只读探针)
+
+> 工具:`C:\claudeFile\TL\测试代码\ReadEepromProbe`(严格只读,只发握手/读EEPROM/读位置,不驱动任何电机)。
+
+### 11.1 ★ Z脉冲量级:一锤定音(澄清"10000 vs 100000")
+
+**结论:培养舱Z脉冲是"万级(7~11万)",新代码参数完全正确,不存在10倍单位错误。**
+
+| 舱(houseSn) | COM | Z焦准零点范围 | 当前Z位置 | 每层步距 | 水平位置范围 |
+|------|------|--------------|----------|---------|------------|
+| 8 | COM5 | 74000~74600 | 88434 | 128 | 70700~205650 |
+| 7 | COM18 | 79700~80500 | 60000 | 128 | 70700~205700 |
+| 9 | COM19 | 79300~80200 | 78456 | 128 | 70200~205300 |
+| 6 | COM11 | 75760~76400 | 95740 | 96 | 70800~205800 |
+| 4 | COM9 | 75372~76012 | 110000 | 96 | 71500~206650 |
+| 2 | COM4 | 83272~84128 | 0 | 96 | 71000~205850 |
+| **11(缓冲瓶)** | COM3 | **7025~7325** | — | 48 | **7464~22088** |
+
+**三点定论:**
+1. **培养舱(2/4/6/7/8/9):** Z焦准零点 7.4万~8.4万,工作位最高11万,软上限125000。
+   新代码 `ZCoarseCenter=90000`、区间60000~120000、`ZMaxPulse=125000` **与真机吻合,无需改**。
+2. **"1万"印象的来源 = 缓冲瓶舱(11号):** 其脉冲仅**千级**(Z焦准7000、水平7千~2.2万),比培养舱小~10倍。
+   很可能是看了缓冲瓶数据误以为整机Z是1万级。**缓冲瓶无相机不对焦,不影响对焦标定。**
+3. **每层步距真机各舱不同(96或128),不是固定128。** 文档/老代码的128只是默认值。
+
+### 11.2 与74000伪峰的关联(重要线索)
+真机6个培养舱Z焦准零点**全部落在74000~84000**,而74000伪峰恰在此区间下沿。
+→ 74000可能不是纯"运动拖影伪峰",而是**EEPROM零点附近的真实次清晰峰**与90000主焦面竞争。
+下次复现74000问题时,应对比该well的EEPROM零点值,并开DebugSave存粗对焦每层图(附录C)。
+
+### 11.3 旧选层"7组选错"的新佐证
+真机显示**同一舱16个well的Z焦准零点几乎相同**(如house8全是74000~74600),但**新代码精对焦实测最清晰层在88434**(差~14000脉冲≈110层)。
+→ 印证:**EEPROM零点严重偏离真实焦面**,老系统从零点出发只扫40层×128≈5120脉冲的小窗口,**焦面落在窗口外必然选错**。新代码大窗口(±30000)正是对症。
 
-### 10.4 快速上手流程
+---
+---
 
-**初次使用:**
-```bash
-1. cd "C:\claudeFile\TL\AutoFocusTool"
-2. 阅读本文档第十节(当前节)
-3. dotnet build -c Debug
-4. ./bin/Debug/net8.0-windows/AutoFocusTool.exe
-5. 扫描设备 → 选舱室 → 连接 → 手动测试
-6. 一键全自动初始化 → 勾选well → 开始标定
-7. 查看calibration.json和终图验证效果
+# 第六篇 · 待办与现状
+
+## 12. 调试界面搬迁清单(老程序有、新程序缺,要搬过来)
+
+> 老调试界面:`ivf_tl_operate_2.0 · HouseDebugPageViewModel / BufferDebugViewModel`。
+> 底层协议新程序已具备(第9章),多数缺失功能只需"加帧构造 + 加封装方法 + 上层循环"。
+
+### 12.1 缺失功能总览(按优先级)
+| 优先级 | 缺失功能 | 协议 | 备注 |
+|--------|---------|------|------|
+| **P0** | **写EEPROM框架**(新程序完全没有0x12写命令) | 0x12 | 是下面所有"写"的基础 |
+| **P0** | **标定结果回写下位机**(well水平位置+Z焦准零点) | 0x12 | 现只写JSON,换文件即丢 |
+| **P0** | 读温度/压力/舱门 | 0x06/0x10 | 调试台基本监控 |
+| P1 | 进/排气阀、舱室补气/排气、缓冲瓶补气与状态 | 0x09/0x19/0x20 | 固定帧,注意valueDelay |
+| P1 | 写灯光亮度/扫描间隔/阀门时间 | 0x12 | 复用写框架换地址 |
+| P2 | 逐well水平抓拍(ShuiPingZhuaPai) | 已有底层 | 上层循环1-16 |
+| P2 | 多轮重复对焦(AutoFocusPic的xun轮次) | 已有底层 | 对焦稳定性复检 |
+| P2 | 电机正转/反转/脱机、一键归位(MototReady) | 已有底层(0x05) | 组合命令 |
+
+### 12.2 写EEPROM框架(P0,所有"写"的基础)
+新程序 `Protocol.cs` 加(数值**小端**,与电机脉冲大端相反):
+```csharp
+public static byte[] WriteEeprom(byte addrHi, byte addrMid, byte addrLo, int value)
+{
+    byte[] p = BitConverter.GetBytes(value); // 小端: p[0]最低
+    return WithChecksum(new byte[] { ST, 0x12, 0x00, 0x0C, addrHi, addrMid, addrLo,
+                                     p[0], p[1], p[2], p[3], 0x00 });
+}
 ```
-
-**继续工作(新会话无缝衔接):**
-```bash
-# 新会话开场说这句即可接上:
-"继续相机自动对焦项目,读总方案文档第十节,代码在 C:\claudeFile\TL\AutoFocusTool"
-
-# 当前进度一句话:第二轮代码缺陷已清零(P0-1~P0-7),真机冒烟4/4过,
-#   就差"放胚胎做真胚胎验证"(见10.2)。放了胚胎用9号舱,空皿功能测试用1号舱(现场暂无则用其他空舱)。
-
-# 主要操作:
-- 真机测试:GUI 或 Calibrate.exe 9 1 16
-- 不动电机的快速回归:SmokeTest/bin/Debug/net8.0-windows/SmokeTest.exe
-- 查看结果:calibration.json + calib_result/*.bmp
-- 改算法:重点 Calib/CalibrationEngine.cs;改闭环:MainWindow.Calib.cs + Calib/CalibrationManager.cs
-- 编译:cd AutoFocusTool && dotnet build -c Debug
+**注意事项:** ① 数值小端(电机大端,别搞反);② 0x12回复长度老代码自相矛盾(6/12),**先真机实测确认**;
+③ 写后回读校验;④ well地址表照抄第9.7(well16=`0x04`、well1=`0x00`非线性)。
+
+### 12.3 标定结果回写(P0,解决"只存JSON不落硬件")
+在 `MainWindow.Calib.cs:205` 存JSON后,对每个合格well:
+```csharp
+motor.WriteWellHorizontalPos(well, wc.HorizontalPulse); // 地址 00 04 [well表]
+motor.WriteWellFocusZero(well, wc.FocusZ);              // 地址 00 04 [08..44],⚠️写地址需真机确认
+```
+**注意:** Z焦准零点读写地址错配(9.7),写之前务必真机验证"写哪个地址 → 读回一致"。
+
+### 12.4 读温度/压力/舱门(P0)
+| 功能 | 帧 | 解析 |
+|------|-----|------|
+| 下盖板温度 | `5E 06 00 06 00 00`+校验 | `[4..5]`大端int16 **÷100** |
+| 上盖板温度 | `5E 06 00 06 01 00`+校验 | 同上 |
+| 玻璃片下温度 | `5E 06 00 06 02 00`+校验 | 同上 |
+| 压力 | `5E 06 00 06 03 00`+校验 | `[4..5]`大端int16 **不除** |
+| 舱门 | `5E 10 00 06 02 00`+校验 | `[4]==0x01`为开 |
+| 下加热板目标温度 | `5E 11 00 09 00 02 38 04 00`+校验 | `[4..7]`小端int32 **÷100** |
+
+### 12.5 逐well水平抓拍 ShuiPingZhuaPai(P2)
+老代码 `HouseDebugPageViewModel.ShuiPingZhuaPai:615`:先水平复位 → for well=1..16:读该well水平位置→`HorizontalAbsolute`转过去→抓帧存盘。
+**注意:** 每步用 `motorDelay` 延时;循环内检查中止标志;**必须先打开实时图像**否则抓不到。底层新程序全有。
+
+### 12.6 多轮重复对焦 AutoFocusPic(P2)
+老代码 `AutoFocusPic(focalCount, xun):672`:for k=0..xun(轮):每轮先垂直复位 → for i=0..focalCount:目标=起点+i×间隔脉冲→`VerticalAbsolute`→抓帧。
+**注意:** 间隔脉冲默认128(真机实测96/128各异,应读EEPROM);超 `verticalMotorPulseMax` 立即终止;用于对焦稳定性复检。
+
+### 12.7 老代码可借鉴的健壮性(搬功能时一并吸收,详见附录B)
+- 相机native调用挂 `[HandleProcessCorruptedStateExceptions][SecurityCritical]` + static锁(防dll崩溃)
+- 抓帧失败=重建相机而非重抓(最多3次)
+- 串口命令队列+单发送线程+双信号量,异步包成同步 `XxxWait()`
+- finally强制关LED+电机复位+释放忙标志;循环内查急停/门开
+
+## 13. 现状与下一步
+
+### 13.1 当前状态(2026-06-17)
+- **代码:** 两轮P0清零 + 对焦算法重写(÷mean、固定大窗口、小范围居中、停稳2秒)。分支 `feature/autofocus-scan-range`。
+- **真机:** ReadEepromProbe已确认Z脉冲量级(万级,参数正确);6个培养舱EEPROM参数已读取。
+- **开放问题:**
+  1. ⚠️ 粗对焦偶发74000伪峰未根除(见3.4;真机线索见11.2)
+  2. ⚠️ 标定结果未回写下位机EEPROM(见12.3)
+  3. ⏳ 真胚胎验证待做(峰比预期>1.5)
+  4. 调试界面功能搬迁(第12章清单)
+
+### 13.2 真胚胎验证步骤
+**舱室约定:** 有胚胎→9号舱;空皿/功能测试→1号舱(现场无1号则用2号等空舱)。
+1. GUI「一键全自动初始化」勾选有胚胎well重标
+2. 看峰比是否>1.5(空皿~1.1)
+3. 人工核对最清晰层Z(对比 `calib_result/` 终图)
+4. 验证闭环:转该well日志应显示 `[标定结果]` 且曝光自动设标定值
+5. 若现74000伪峰:开DebugSave存粗对焦每层图(附录C)+ 对比该well EEPROM零点
+
+### 13.3 新会话衔接
+```
+开场:"继续相机自动对焦项目,读总方案文档第13章现状,代码在 C:\claudeFile\TL\AutoFocusTool"
+进度:两轮P0清零+算法重写,真机已确认Z万级参数正确(第11章)。
+  开放:74000伪峰、标定回写EEPROM、真胚胎验证、调试界面搬迁(第12章)。
+改算法:Calib/CalibrationEngine.cs(认准3.2参数,别动3.5死参数)
+改协议:Serial/Protocol.cs(第9章手册)
+测试代码:C:\claudeFile\TL\测试代码\(只读探针等,不混入主工程)
 ```
 
-### 10.5 关键文件速查
-
-**代码:**
-- 标定核心:`Calib/CalibrationEngine.cs`(四步:粗焦→居中→曝光→精焦;含RetryMove/CenterRoi40)
-- 业务闭环:`Calib/CalibrationManager.cs`(优先JSON降级EEPROM)+ `MainWindow.Calib.cs`(BtnGoWell接线)
-- 标定档案读写:`Calib/CalibrationFile.cs`(⚠️ Load必须`IncludeFields=true`,见P0-7)
-- 圆检测:`Imaging/WellDetector.cs` 清晰度:`Imaging/Sharpness.cs` 曝光二分:`Imaging/ExposureMeter.cs`
-- 命令行标定:`Calibrate/Calibrate.cs`
-- **真机回归冒烟(本轮新增):** `SmokeTest/SmokeTest.cs`(不大幅驱动电机,验证扫描/相机/曝光/JSON)
-
-> 注:文档旧版提到的 `Survey/Survey.cs` **实际不存在**,已废弃该说法(见9.1更正)。
-
-**数据:**
-- 标定结果:`calibration.json`(当前为9号空皿数据,峰比全≤1.2)
-- 标定终图:`calib_result/house9_well*_标定后.bmp`、`calib/cal_h9_w*_FINAL.bmp`
-
-**文档(唯一权威):** `相机自动对焦项目-总方案.md`(本文)
-**审查报告(同事,已逐条评估):** `AutoFocusTool/CODE_REVIEW_AND_ANALYSIS.md`
-
-### 10.6 常见问题
-
-**Q: 程序崩溃怎么办?**
-A: 检查Grab()方法的安全性,已加重试机制,正常不会崩溃
-
-**Q: 居中失败怎么办?**
-A: 检查aspect阈值(1.5)和细扫延时(800ms),必要时用HScan工具诊断
-
-**Q: 曝光不一致怎么办?**
-A: 确认每次SetExposure后都丢弃第1帧使用第2帧
-
-**Q: 对焦峰比<1.2?**
-A: 空well正常,真胚胎会>1.5;若真胚胎也<1.2则检查ROI设置
-
-**Q: 标定明明做了,转well却不用标定参数(日志显示[EEPROM...])?**
-A: 两种可能:①该well峰比≤1.2被判不合格(空皿就是这样,正常);②calibration.json没读进来。
-   先跑 SmokeTest.exe 看【2】是否"舱数=1"。若舱数=0说明JSON反序列化失效——
-   检查 CalibrationFile.Load 是否带 `IncludeFields=true`(P0-7的坑,字段非属性)。
-
-**Q: 测试该用哪个舱室?**
-A: 有胚胎用9号舱;空皿纯功能测试用1号舱。现场目前无1号舱,空皿测试回退用2号等其他空舱。
-
-**Q: 不想驱动电机只想快速回归核心逻辑?**
-A: 跑 `SmokeTest/bin/Debug/net8.0-windows/SmokeTest.exe`,验证设备扫描/相机/曝光丢帧/JSON读取,不跑标定。
-
-**Q: 如何调试特定well?**
-A: 命令行单well标定 `./Calibrate/bin/Debug/net8.0-windows/Calibrate.exe 9 2 2`(舱9 well2),
-   中间帧会存到 hscan/ 目录供排查。(注:文档旧版提到的独立 HScan.exe 工具现已不存在)
-
-### 10.7 联系上下文
-
-**本项目解决的核心问题:**
-1. ✅ 曝光帧延迟 → 丢帧策略
-2. ✅ 居中检测严格 → 放宽阈值+延时
-3. ✅ 背景干扰对焦 → 中央ROI
-4. ✅ 业务未闭环 → CalibrationManager
-5. ✅ 异常处理薄弱 → 完善检查
-
-**审查报告对应:**
-- 审查报告:`评审报告/AutoFocusTool 自动校准工具代码审查报告.md`
-- 10/13问题已修复,高优先级8/8全部完成
-
-**测试报告对应:**
-- 测试报告:`report/自动标定测试报告_2026-06-13.html`
-- 3个P0根因全部修复并验证通过
-
----
-
-## 十一、Memory提示
-
-**如果需要更新memory:**
-```markdown
 ---
-name: autofocus-system-ready
-description: 自动对焦系统两轮P0修复完成(P0-1~P0-7),真机冒烟4/4过,业务闭环真正接通。待真胚胎验证。空皿用1号舱(现场暂无则2号),胚胎用9号舱。
-metadata:
-  type: project
 ---
 
-2026-06-13 两轮修复。第一轮:曝光丢帧/居中阈值/中央ROI。第二轮(针对同事审查报告):
-P0-1转well接入CalibrationManager、P0-2/3手动路径补丢帧、P0-4精焦ROI降级、
-P0-6电机重试、P0-5删死代码,外加真机才暴露的P0-7(JSON反序列化字段缺IncludeFields,
-导致标定结果静默读不到、闭环形同虚设)。
+# 附录
 
-**真机冒烟4/4过:** 设备扫描7舱、相机2592x1944、曝光丢帧无旧帧污染、JSON正确读取。
-
-**关键坑(务必记住):**
-- CalibrationFile用public字段非属性 → Load必须 IncludeFields=true,否则静默失效
-- 空皿峰比全≤1.2 → 闭环按>1.2判据会全降级EEPROM,这是正常的,别误判闭环坏了
-- 现场无1号舱;项目里无Survey目录(旧文档说法不实)
-
-**系统状态:** 代码缺陷清零,等过几天放胚胎做真胚胎验证(峰比预期>1.5)
-
-**唯一文档:** [[相机自动对焦项目-总方案]] 第十节
+## 附录A:被删测试工具的价值评估
+提交1e36b67删除了所有子工程。备份在 `C:\Users\AIVFO\Desktop\AutoFocusTool-master\autofocustool`。
+| 工具 | 价值 | 建议 |
+|------|------|------|
+| **`SelfTest/Diag.cs`** | **高**:纯C#裸写BMP+亮度统计,抓74000伪峰要靠它存图 | **建议恢复**(源码见附录C) |
+| `SmokeTest/SmokeTest.cs` | 中:无电机验证扫描/相机/曝光/JSON | 需快速回归时恢复 |
+| `Calibrate/Calibrate.cs` | 中:命令行单well标定 | 需命令行调试时恢复,注意对齐当前参数 |
+| `ToPng/`、`CalibTest/`、`topng.csx` | 低 | 不必恢复 |
+
+## 附录B:老代码考古(可借鉴逻辑 + 必坑点)
+
+### B.1 脉冲单位核实(与第11章真机互证)
+新旧脉冲单位**完全一致,无10倍差异**。铁证:垂直复位命令帧两边逐字节相同(`5E 05 00 0B 23 00 00 00 07 D0 68`)。
+老代码 `verticalMotorPulseMax=125000`、绝对运动用32位脉冲,与新代码、与真机(万级)全部吻合。
+"1万"印象源于缓冲瓶舱(11号,千级,见11.1)。
+
+### B.2 可借鉴的成熟逻辑
+1. **相机native崩溃防护(最该借鉴):** 所有进 `mvcapi.dll` 的方法加 `[HandleProcessCorruptedStateExceptions][SecurityCritical]` + static锁。dll抛AccessViolation时默认catch不住、进程挂。新代码近期 `f92a71d/6446adb` 修段错误属同域。
+2. **抓帧失败=重建相机**(`GetRgbDataFun` 关相机+重开,最多3次),不重试单帧。
+3. **串口模型:** 命令队列+单发送线程+双AutoResetEvent;超时30s/重发3次/失败`ReopenPort`(丢缓冲+Close+Dispose+重建+重订阅);扫描类快速失败。
+4. **流程finally强制收尾:** 关LED+电机复位+释放忙标志;循环内查急停/门开。
+5. **开机一次性读全EEPROM序列**(`SerialBin.GetHouseInfo`):是"读设备全部标定值"现成模板。
+
+### B.3 老代码自己的坑(要避开)
+1. `SourceBuffer` 每次new数组 → 高频抓图GC压力;应复用缓冲。
+2. 校验和失败只记日志不重发 → 标定应丢帧重发。
+3. 电机位置回读不一致只告警不纠偏 → 标定精度敏感,应重等/重发/废弃该层。
+4. 环形缓冲`Clear()`全清、一问一答容不得粘包 → 若下位机主动上报需改按长度清。
+5. LED开后无稳定延时 → 标定应显式补LED亮起延时。
+6. 异常路径未在finally关LED → 务必补上。
+7. App.config明文密码、`autoFocus`键名词不符义、返回码语义不统一 → 别学。
+
+## 附录C:诊断存图工具源码(Diag.cs,备查·不单独建文件)
+
+> 来自备份 `SelfTest/Diag.cs`。纯C#不依赖WPF,把相机24bpp BGR裸写BMP(含FlipY翻正)+统计亮度。
+> **用途:** 排查3.4的74000伪峰时挂到 `CalibrationEngine.DebugSave` 逐层存图。需要时贴进项目临时用、用完即删。
+>
+> **挂载:**
+> ```csharp
+> engine.DebugSave = (buf, name) =>
+>     Diag.SaveBmp(buf, cam.Width, cam.Height, $@"C:\claudeFile\TL\AutoFocusTool\TestData\{name}.bmp");
+> ```
+> 并在 `CoarseFocus` 逐层处加 `DebugSave?.Invoke(b, $"coarse_w{well}_z{z}")`。
+
+```csharp
+using System;
+using System.IO;
+
+namespace AutoFocusTool
+{
+    /// <summary>自检用轻量图像诊断(不依赖 WPF)。统计像素亮度;裸写 24bpp BMP 存盘。</summary>
+    internal static class Diag
+    {
+        /// <summary>返回 (最小, 最大, 平均) 灰度。</summary>
+        public static (int min, int max, double mean) Stats(byte[] bgr24)
+        {
+            int min = 255, max = 0; long sum = 0; int n = 0;
+            for (int i = 0; i + 2 < bgr24.Length; i += 27)
+            {
+                int g = (bgr24[i] * 29 + bgr24[i + 1] * 150 + bgr24[i + 2] * 77) >> 8;
+                if (g < min) min = g;
+                if (g > max) max = g;
+                sum += g; n++;
+            }
+            return (min, max, n > 0 ? (double)sum / n : 0);
+        }
+
+        /// <summary>裸写 24bpp BMP(含 FlipY 翻正)。</summary>
+        public static void SaveBmp(byte[] bgr24, int w, int h, string path)
+        {
+            int rowSize = ((w * 3 + 3) / 4) * 4;
+            int imgSize = rowSize * h;
+            int fileSize = 54 + imgSize;
+            using var fs = new FileStream(path, FileMode.Create);
+            using var bw = new BinaryWriter(fs);
+            bw.Write((byte)'B'); bw.Write((byte)'M');
+            bw.Write(fileSize); bw.Write(0); bw.Write(54);
+            bw.Write(40); bw.Write(w); bw.Write(h); // h>0 自底向上=翻正相机倒像
+            bw.Write((short)1); bw.Write((short)24);
+            bw.Write(0); bw.Write(imgSize);
+            bw.Write(2835); bw.Write(2835); bw.Write(0); bw.Write(0);
+            byte[] pad = new byte[rowSize - w * 3];
+            for (int y = 0; y < h; y++)
+            {
+                int row = y * w * 3;
+                bw.Write(bgr24, row, w * 3);
+                if (pad.Length > 0) bw.Write(pad);
+            }
+        }
+    }
+}
 ```
 
 ---
 
-**文档版本:** V3.1(2026-06-13 第二轮修复+真机冒烟验证)
-**状态:** 唯一权威,其他文档仅供参考
-**维护:** 有重大变更时更新本文档第十节
-**下次衔接:** 读10.1(现状)+10.2(真胚胎验证步骤)即可无缝接上
+**文档版本:** V5.0(2026-06-17 结构化重构:六篇+附录;整合协议手册/真机实测/调试界面搬迁清单)
+**状态:** 唯一权威。配套 `自动对焦流程图.html`。
+**维护:** 重大变更更新第10章演进史 + 第13章现状。