相机自动对焦项目-总方案.md 21 KB

相机自动对焦项目 · 总方案(唯一权威文档)

最后更新:2026-06-13
用途: 相机自动对焦/自动拍摄校正项目的需求、技术方案、代码结构、当前状态的唯一权威记录
阅读建议: 想快速了解进度 → 直接看第十节"现状与下一步"


一、项目背景

1.1 设备与场景

  • 设备: 10舱室时差培养箱(成都艾伟孚,川械注准20232180053)
  • 用途: 相机在显微镜下对胚胎进行延时拍摄
  • 核心问题: 现有"自动选清晰层"算法效果差,7组测试全部选错

1.2 项目目标

开发独立C#小软件,实现"拍出一张可用图"的全自动校正流程:

曝光校正 → XY皿孔居中 → 空皿检测 → Z对焦(选层/搜索)

二、硬件架构(真机确认)

2.1 设备组成

  • 10舱室整机 + 1缓冲瓶(11号)
  • 每舱独立:CCD相机 + 光源LED + Z轴电机 + 水平旋转电机

2.2 相机

  • 型号: Microview MVC2000
  • 接口: USB2.0
  • 分辨率: 2592×1944(必须用这个,曾误用1600×1200导致well被裁出画面)
  • 枚举: 全局0-9台,通过序列号(CCDSN)桥接到舱室
  • 曝光单位: 100µs,增益RGB各0-255
  • 数据: 上下颠倒需FlipY

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 数据 累加和校验,无帧尾,回复长度固定
  • EEPROM: 每个well的"水平位置"+"Z焦准零点"存下位机
  • LED: 只能开关+读亮度,无写亮度命令

三、技术方案

3.1 清晰度评价(核心算法)

  • 指标: Tenengrad(梯度平方和),纯C#实现
  • ROI: 只在well圆内计算,排除背景干扰
  • 预处理: 统一尺寸、去噪
  • 归一化: 分数除以像素数,可跨帧比较

3.2 标定流程(四步)

①粗对焦(中央40% ROI) → ②旋转居中(粗扫+细扫) → ③曝光二分(well内ROI) → ④精对焦(0.95r ROI + 平滑插值)

关键技术点:

  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确保稳定

四、代码结构

4.1 目录结构

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参数)

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) LoadJsonSerializerOptionsIncludeFields=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 编译

cd "C:\claudeFile\TL\AutoFocusTool"
dotnet build -c Debug

7.2 运行GUI

./bin/Debug/net8.0-windows/AutoFocusTool.exe

7.3 命令行标定

./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 标定文件格式

{
  "tlSn": "house9",
  "date": "2026-06-13 13:54",
  "houses": [{
    "house": 9,
    "wells": [{
      "well": 1,
      "horizontalPulse": 70198,
      "exposure": 58,
      "focusZ": 79555,
      "centerOffsetPct": -0.0,
      "circleFound": true,
      "peakSharp": 0.0647,
      "peakRatio": 1.11,
      "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号舱有培养皿但当前是空皿无胚胎

测试舱室约定(用户指定):

  • 有胚胎 → 用 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.3 可选优化(非阻塞)

优先级低:

  1. 空皿检测 - 需真实样本后设计(基于峰形或几何特征)
  2. 预测式居中 - 可提速70%(当前14秒可接受)
  3. EEPROM降级 - zZero<0时改为跳过而非设0
  4. 曝光顺序 - 可移到精对焦之后(在清晰状态测定更准)

10.4 快速上手流程

初次使用:

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和终图验证效果

继续工作(新会话无缝衔接):

# 新会话开场说这句即可接上:
"继续相机自动对焦项目,读总方案文档第十节,代码在 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

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*_标定后.bmpcalib/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:

---
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)

**唯一文档:** [[相机自动对焦项目-总方案]] 第十节

文档版本: V3.1(2026-06-13 第二轮修复+真机冒烟验证) 状态: 唯一权威,其他文档仅供参考 维护: 有重大变更时更新本文档第十节 下次衔接: 读10.1(现状)+10.2(真胚胎验证步骤)即可无缝接上