From 41e056f85c9ec0b49fa082ffe0dd5f5adb914731 Mon Sep 17 00:00:00 2001 From: QI Mingxuan Date: Tue, 28 Jul 2026 18:58:22 +0800 Subject: [PATCH] =?UTF-8?q?[XP.Hardware.MotionControl]=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20CoordinateCompensation.md=EF=BC=9A=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E6=8F=8F=E8=BF=B0=E8=BD=BD=E7=89=A9=E5=8F=B0=E5=9D=90?= =?UTF-8?q?=E6=A0=87=E7=B3=BB=E8=A1=A5=E5=81=BF=E5=8A=9F=E8=83=BD=EF=BC=88?= =?UTF-8?q?=E5=9D=90=E6=A0=87=E6=A8=A1=E5=9E=8B=E3=80=81=E5=83=8F=E7=B4=A0?= =?UTF-8?q?=E5=88=B0=20mm=20=E6=8D=A2=E7=AE=97=E3=80=81Replace/Accumulate?= =?UTF-8?q?=20=E8=AE=B0=E5=BD=95=E6=A8=A1=E5=BC=8F=E4=B8=8E=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E3=80=81=E6=8E=A5=E5=8F=A3=E8=AF=B4=E6=98=8E=E3=80=81?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=B5=81=E7=A8=8B=E3=80=81=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E6=B3=A8=E6=84=8F=E4=BA=8B=E9=A1=B9=E3=80=81=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=EF=BC=89=20[XP.Hardware.MotionControl]=20REA?= =?UTF-8?q?DME=20=E6=96=87=E6=A1=A3=E7=9B=AE=E5=BD=95=E6=A0=91=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=E5=85=A8=E9=83=A8=E6=96=87=E6=A1=A3=E6=9D=A1=E7=9B=AE?= =?UTF-8?q?=E5=B9=B6=E5=8A=A0=E5=85=A5=20CoordinateCompensation.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Documents/App.config.example | 15 +- .../Documents/Calibration_README.md | 22 ++ .../Documents/CoordinateCompensation.md | 280 ++++++++++++++++++ .../Documents/GUIDENCE.md | 92 +++++- .../Documents/GeometryWithDetectorSwing.md | 2 +- .../Documents/MotionControl_Design.md | 70 +++++ XP.Hardware.MotionControl/Documents/README.md | 22 +- 7 files changed, 494 insertions(+), 9 deletions(-) create mode 100644 XP.Hardware.MotionControl/Documents/CoordinateCompensation.md diff --git a/XP.Hardware.MotionControl/Documents/App.config.example b/XP.Hardware.MotionControl/Documents/App.config.example index 672cba66..11506274 100644 --- a/XP.Hardware.MotionControl/Documents/App.config.example +++ b/XP.Hardware.MotionControl/Documents/App.config.example @@ -46,7 +46,20 @@ + + + + + + + + - + + + + + + diff --git a/XP.Hardware.MotionControl/Documents/Calibration_README.md b/XP.Hardware.MotionControl/Documents/Calibration_README.md index 5caca544..d18543fe 100644 --- a/XP.Hardware.MotionControl/Documents/Calibration_README.md +++ b/XP.Hardware.MotionControl/Documents/Calibration_README.md @@ -258,10 +258,32 @@ public class AnotherViewModel } ``` +### 6.1 两种用途辨析:点击居中 vs 坐标系补偿 | Click-to-Center vs Coordinate Compensation + +`MoveStageByPixelOffset` 与坐标系补偿接口都基于相同的像素→mm 换算(内部统一由私有方法 `TryConvertPixelToStageMm` 计算),但用途不同: + +| 用途 | 接口 | 是否记录补偿 | 说明 | +|------|------|--------------|------| +| 点击居中 | `MoveStageByPixelOffset(dx, dy)` | 否 | 用户点击图像某点,一次性把该点移到中心,不影响后续运动 | +| 坐标系补偿 | `ApplyPixelOffsetCompensation(dx, dy)` | 是 | 主框架坐标匹配后记录补偿并修正到位,后续定点运动可叠加补偿 | + +坐标系补偿的完整说明见 `MotionControl_Design.md` 第 14 节与 `GUIDENCE.md` 第 11 节。 + --- ## 7. 版本变更说明 | Change Log +### v2.1(2026-07-28) + +**新增坐标系补偿功能** + +- **新增** `IMotionControlService` 坐标系补偿接口:`ApplyPixelOffsetCompensation`、`RecordPixelOffsetCompensation`、`GetStageCompensation`、`SetStageCompensation`、`ResetStageCompensation` +- **新增** `CompensationMode` 枚举(`Replace` 默认 / `Accumulate`) +- **新增** `StageCompensationChangedEvent` 事件 +- **变更** `MoveToTarget` / `MoveAllToTarget` 增加 `applyCompensation` 参数(默认 `false`),仅对 StageX/StageY 叠加补偿 +- **重构** 将 `MoveStageByPixelOffset` 的像素→mm 换算抽为私有方法 `TryConvertPixelToStageMm`,供补偿接口复用(对外行为不变) +- 补偿功能复用现有几何配置,无需新增 App.config 参数 + ### v2.0(2026-07-01) **重大变更:从手动标定改为基于几何配置的直接计算** diff --git a/XP.Hardware.MotionControl/Documents/CoordinateCompensation.md b/XP.Hardware.MotionControl/Documents/CoordinateCompensation.md new file mode 100644 index 00000000..7aab3a4e --- /dev/null +++ b/XP.Hardware.MotionControl/Documents/CoordinateCompensation.md @@ -0,0 +1,280 @@ +# 载物台坐标系补偿功能 | Stage Coordinate Compensation + +> 文档版本:v1.0 +> 创建日期:2026-07-28 +> 适用模块:XP.Hardware.MotionControl + +--- + +## 1. 功能概述 | Overview + +在平面 CT 检测中,工件在载物台上的实际摆放位置常与程序假定的"名义位置"存在偏差。主框架通过图像坐标匹配可计算出工件当前的**像素偏移** `(dX, dY)`(可为负)。 + +坐标系补偿功能负责: + +1. 将主框架传入的像素偏移换算为载物台的**物理位移(mm)**,记录为**补偿值**; +2. 执行修正移动,使工件对齐到位; +3. 后续任意"移动到名义位置"的指令,可选择**自动叠加**该补偿值,使工件在整个检测流程中持续对齐。 + +补偿值在软件启动时初始化为 `(0, 0)`。 + +--- + +## 2. 坐标模型 | Coordinate Model + +### 2.1 补偿值的本质 + +补偿值是载物台 `StageX / StageY` 坐标系上的一个**物理偏移向量(mm)**,表示"工件实际位置"与"程序名义位置"之间的差。 + +作用链条: + +``` +名义 home 位置 → 工件特征在图像里偏了 (dX, dY) 像素 + ↓ 按当前放大比换算 +需要载物台移动 (stageDx, stageDy) mm 才能把特征移正 + ↓ 记录 +补偿值 comp = (stageDx, stageDy) + ↓ 后续 +"移动到名义位置 P" → 实际目标 = P + comp,工件特征始终对齐 +``` + +### 2.2 为什么存 mm 而非像素 + +像素偏移与放大比绑定,一旦放大比变化就失效;而**物理 mm 偏移代表工件的真实错位,与放大比无关**,在后续任何放大比下都成立。因此补偿值以 mm(Stage 坐标系)存储。 + +### 2.3 作用范围 + +补偿仅作用于 `StageX / StageY`。`SourceZ / DetectorZ / 旋转轴` 不参与补偿。 + +--- + +## 3. 像素 → mm 换算 | Pixel-to-mm Conversion + +补偿的换算逻辑与 `MoveStageByPixelOffset` 一致,内部统一由私有方法 `TryConvertPixelToStageMm` 实现: + +``` +mmPerPixelX = DetectorPixelSizeX / M +mmPerPixelY = DetectorPixelSizeY / M +dxMm = dxPixels × mmPerPixelX × PixelDirectionX +dyMm = dyPixels × mmPerPixelY × PixelDirectionY +``` + +| 参数 | 来源 | 说明 | +|------|------|------| +| `DetectorPixelSizeX/Y` | App.config(`MotionControl:Geometry:*`) | 探测器单像素物理尺寸(mm),由探测器规格书获取 | +| `PixelDirectionX/Y` | App.config | 对齐图像像素方向与载物台运动方向(+1 或 -1) | +| `M` | `GetCurrentGeometry()` 实时计算 | 当前放大倍率(FDD / FOD) | + +换算失败的情形(返回失败结果): +- 探测器像素尺寸未配置(`DetectorPixelSizeX/Y <= 0`) +- 当前放大倍率无效(`M` 为 NaN 或 `<= 0`) + +> 补偿功能**复用现有几何配置**,不引入新的 App.config 参数。 + +--- + +## 4. 记录模式 | Compensation Mode + +补偿的记录方式由 `CompensationMode` 枚举控制: + +| 模式 | 语义 | 适用场景 | +|------|------|----------| +| `Replace`(默认) | `comp = 本次换算值` | 每次都从同一名义参考位置测量、每位置只记录一次的**一次性对位** | +| `Accumulate` | `comp += 本次换算值` | 在同一位置**迭代修正残差**的闭环逼近 | + +### 4.1 如何选择 + +判断标准是**主框架每次传入的 `dX, dY` 代表什么**: + +- 传入的是"**这次还差多少**"(残差/增量)→ 用 `Accumulate` +- 传入的是"**离名义原点一共差多少**"(绝对总量)→ 用 `Replace` + +### 4.2 覆盖模式的重要前提 + +`ApplyPixelOffsetCompensation` 的移动为**相对移动**。覆盖模式假设"每次测量都从同一名义参考位置开始、每位置只调用一次"。 + +若在同一位置**连续多次**修正而不回到参考位置,覆盖模式只保证补偿值不累积,但相对移动会重复叠加,导致物理位置与补偿值不一致。此时应改用 `Accumulate`。 + +### 4.3 累加 vs 覆盖示例 + +前提:`1 像素 = 0.01mm`(当前放大比下)。 + +**场景 A:闭环迭代残差(用 Accumulate)** —— 每轮都在当前位置重新测残差: + +| 轮次 | 测得残差 | 换算 mm | 相对移动 | Accumulate comp | Replace comp | +|------|------|------|------|------|------| +| 第 1 轮 | +200px | +2.0mm | +2.0 | 2.0 ✓ | 2.0 | +| 第 2 轮 | +30px | +0.3mm | +0.3 | **2.3 ✓** | 0.3 ✗ | +| 第 3 轮 | +2px | +0.02mm | +0.02 | **2.32 ✓** | 0.02 ✗ | + +台子实际总共走了 2.32mm,只有累加能记成真实总偏移。 + +**场景 B:绝对总量(用 Replace)** —— 每次都相对固定名义原点给出总偏移: + +| 时刻 | 测得总偏移 | 换算 mm | Accumulate comp | Replace comp | +|------|------|------|------|------| +| 第 1 次 | +200px | +2.0mm | 2.0 | 2.0 ✓ | +| 第 2 次重测 | +205px | +2.05mm | 4.05 ✗ | **2.05 ✓** | + +第 2 次的 205px 已包含第一次的 200px,累加会重复计入。 + +--- + +## 5. 接口说明 | API Reference + +全部定义在 `IMotionControlService`(单例)。 + +| 方法 | 说明 | +|------|------| +| `ApplyPixelOffsetCompensation(dxPixels, dyPixels, mode = Replace)` | 换算像素→mm,按模式记录补偿,并以相对移动方式驱动 StageX/StageY 修正到位(记录 + 移动,一步完成) | +| `RecordPixelOffsetCompensation(dxPixels, dyPixels, mode = Replace)` | 仅换算并记录补偿,不移动(预览/仅计算) | +| `GetStageCompensation()` | 返回当前补偿值 `(Xmm, Ymm)` | +| `SetStageCompensation(xMm, yMm)` | 直接以物理量(mm)设置补偿值 | +| `ResetStageCompensation()` | 补偿值清零 | +| `MoveToTarget(axisId, target, speed = null, applyCompensation = false)` | 单轴定点移动;`applyCompensation = true` 时仅对 StageX/StageY 叠加补偿 | +| `MoveAllToTarget(targets, applyCompensation = false)` | 多轴联动移动;`applyCompensation = true` 时仅对字典中的 StageX/StageY 目标叠加补偿 | + +### 5.1 补偿值生命周期 + +- 补偿值随软件运行保持,存储在 `MotionControlService` 单例中。 +- **`HomeAll()` / `HomeAllAsync()` 回零不会清零补偿**,补偿的清零仅通过 `ResetStageCompensation()`。 +- 换工件、重新装夹时应由主框架显式调用 `ResetStageCompensation()`(覆盖模式下也可依赖新值直接冲掉旧值,但显式清零语义更明确)。 + +### 5.2 边界校验 + +`MoveToTarget` / `MoveAllToTarget` 叠加补偿后,**仍执行原有的 `Min/Max` 边界校验**。若补偿把目标推出行程范围,命令会被拒绝。 + +### 5.3 线程安全 + +补偿值以 `_compXmm / _compYmm` 字段存储,读写通过 `_compLock` 加锁保护,以应对轮询线程与 UI/命令线程的并发访问。 + +### 5.4 事件通知 + +补偿值被记录、设置或清零时,发布 `StageCompensationChangedEvent`: + +```csharp +public record StageCompensationData(double Xmm, double Ymm); +public class StageCompensationChangedEvent : PubSubEvent { } +``` + +UI 可订阅该事件实时显示当前补偿量。 + +--- + +## 6. 典型调用流程 | Typical Usage + +### 6.1 主框架:坐标匹配后记录并修正 + +```csharp +public class CoordinateMatcher +{ + private readonly IMotionControlService _mc; + + public CoordinateMatcher(IMotionControlService mc) => _mc = mc; + + // 坐标匹配得到像素偏移后,一步完成"记录补偿 + 修正移动"(默认覆盖) + public void OnMatchCompleted(double dxPixels, double dyPixels) + { + var result = _mc.ApplyPixelOffsetCompensation(dxPixels, dyPixels); + if (!result.Success) + _log.Warn($"补偿失败:{result.ErrorMessage}"); + } + + // 同一位置迭代修正残差时,使用累加模式 + public void OnResidualMatch(double dxPixels, double dyPixels) + { + _mc.ApplyPixelOffsetCompensation(dxPixels, dyPixels, CompensationMode.Accumulate); + } +} +``` + +### 6.2 其他类库:定点移动叠加补偿 + +```csharp +// 单轴:仅 StageX/StageY 生效 +_mc.MoveToTarget(AxisId.StageX, 100.0, applyCompensation: true); +_mc.MoveToTarget(AxisId.StageY, 50.0, applyCompensation: true); + +// 多轴联动一次性带补偿 +_mc.MoveAllToTarget(new Dictionary +{ + { AxisId.StageX, 100.0 }, + { AxisId.StageY, 50.0 } +}, applyCompensation: true); +``` + +### 6.3 查询、设置与清零 + +```csharp +// 查询当前补偿值 +var (compX, compY) = _mc.GetStageCompensation(); + +// 直接设置(如从历史记录恢复) +_mc.SetStageCompensation(1.25, -0.80); + +// 换工件时清零 +_mc.ResetStageCompensation(); +``` + +### 6.4 订阅补偿变化事件 + +```csharp +eventAggregator.GetEvent() + .Subscribe(data => + { + // 更新 UI 显示 + CompensationText = $"补偿: X={data.Xmm:F4}mm, Y={data.Ymm:F4}mm"; + }, ThreadOption.UIThread); +``` + +--- + +## 7. 点击居中 vs 坐标系补偿 | Click-to-Center vs Compensation + +两者基于相同的像素→mm 换算,但用途不同: + +| 用途 | 接口 | 是否记录补偿 | 说明 | +|------|------|--------------|------| +| 点击居中 | `MoveStageByPixelOffset(dx, dy)` | 否 | 用户点击图像某点,一次性把该点移到中心,不影响后续运动 | +| 坐标系补偿 | `ApplyPixelOffsetCompensation(dx, dy)` | 是 | 坐标匹配后记录补偿并修正到位,后续定点运动可叠加补偿 | + +`MoveStageByPixelOffset` 保持原有行为不变,继续服务点击居中场景。 + +--- + +## 8. 边界与注意事项 | Edge Cases & Notes + +| 情况 | 处理方式 | +|------|----------| +| 探测器像素尺寸未配置 | 换算失败,返回 `MotionResult.Fail`,不记录、不移动 | +| 当前放大倍率无效(NaN 或 ≤ 0) | 换算失败,返回 `MotionResult.Fail` | +| 补偿后目标越界 | 移动被 `Min/Max` 边界校验拒绝 | +| `SetStageCompensation` 传入 NaN/Infinity | 返回 `MotionResult.Fail`,补偿值不变 | +| 回零(HomeAll) | 补偿值**不变**,需显式 `ResetStageCompensation()` 清零 | +| 覆盖模式同位置连续修正 | 物理移动会重复叠加,应改用 `Accumulate` | + +--- + +## 9. 相关代码位置 | Source Locations + +| 内容 | 文件 | +|------|------| +| 补偿模式枚举 | `Abstractions/Enums/CompensationMode.cs` | +| 补偿变化事件 | `Abstractions/Events/StageCompensationChangedEvent.cs` | +| 接口定义 | `Services/IMotionControlService.cs`("坐标系补偿"区域) | +| 实现(含 `TryConvertPixelToStageMm`、`_compXmm/_compYmm`、`_compLock`) | `Services/MotionControlService.cs`("坐标系补偿"区域) | + +--- + +## 10. 关联文档 | Related Documents + +| 文档 | 说明 | +|------|------| +| `MotionControl_Design.md` | 运动控制模块设计文档(第 14 节补偿设计) | +| `GUIDENCE.md` | 外部集成指南(第 11 节坐标系补偿) | +| `Calibration_README.md` | 像素-载物台映射子模块 | +| `GeometryWithDetectorSwing.md` | FOD/FDD/放大倍率几何计算模型 | + +--- + +**最后更新 | Last Updated**: 2026-07-28 diff --git a/XP.Hardware.MotionControl/Documents/GUIDENCE.md b/XP.Hardware.MotionControl/Documents/GUIDENCE.md index 90078a53..d718bb7b 100644 --- a/XP.Hardware.MotionControl/Documents/GUIDENCE.md +++ b/XP.Hardware.MotionControl/Documents/GUIDENCE.md @@ -215,6 +215,38 @@ public class YourController MotionResult result = _mc.MoveToSafePosition(); // 所有轴移动到配置的安全点 } + // 坐标系补偿 | Coordinate compensation + // 主框架坐标匹配得到像素偏移 (dX, dY) 后,一步完成"记录补偿 + 修正移动"(默认覆盖模式) + // After coordinate matching, record compensation and perform corrective move in one step (Replace by default) + public void ApplyCompensation(double dxPixels, double dyPixels) + { + // 默认覆盖模式;同一位置迭代修正残差时用 CompensationMode.Accumulate + MotionResult result = _mc.ApplyPixelOffsetCompensation(dxPixels, dyPixels); + + // 仅记录补偿、不移动(预览/仅计算) + _mc.RecordPixelOffsetCompensation(dxPixels, dyPixels); + + // 查询当前补偿值(mm) + var (compX, compY) = _mc.GetStageCompensation(); + + // 换工件/维护时清零补偿(HomeAll 回零不会清补偿) + _mc.ResetStageCompensation(); + } + + // 定点移动并叠加补偿(仅 StageX/StageY 生效,补偿后仍过边界校验) + // Move to target with compensation (only StageX/StageY, boundary check still applies) + public void MoveWithCompensation() + { + _mc.MoveToTarget(AxisId.StageX, 100.0, applyCompensation: true); + + var targets = new Dictionary + { + { AxisId.StageX, 100.0 }, + { AxisId.StageY, 50.0 } + }; + _mc.MoveAllToTarget(targets, applyCompensation: true); + } + // Jog 点动 | Jog public void JogAxis() { @@ -371,6 +403,13 @@ public class YourMonitor { Console.WriteLine($"几何反算请求: SourceZ={data.SourceZTarget}, DetectorZ={data.DetectorZTarget}"); }, ThreadOption.UIThread); + + // 载物台坐标系补偿变化(记录/设置/清零时触发)| Stage compensation changed + eventAggregator.GetEvent() + .Subscribe(data => + { + Console.WriteLine($"补偿值: X={data.Xmm:F4}mm, Y={data.Ymm:F4}mm"); + }, ThreadOption.UIThread); } } ``` @@ -536,4 +575,55 @@ MotionResult result = _motionControlService.MoveToSafePosition(); --- -**最后更新 | Last Updated**: 2026-07-06 +## 11. 坐标系补偿 | Coordinate Compensation + +用于将主框架坐标匹配得到的图像像素偏移换算为载物台物理补偿,并让后续定点运动自动叠加补偿,保持工件对齐。 + +### 11.1 补偿模型 + +- 补偿值以物理量 mm 存储(Stage 坐标系,放大比无关),初始 `(0,0)` +- 仅作用于 `StageX / StageY`,其余轴不补偿 +- `HomeAll()` 回零**不会清零补偿**;仅 `ResetStageCompensation()` 显式清零 + +### 11.2 记录模式(CompensationMode) + +| 模式 | 语义 | 适用场景 | +|------|------|----------| +| `Replace`(默认)| `comp = 本次换算值` | 每次从同一名义参考位置测量、每位置只记录一次 | +| `Accumulate` | `comp += 本次换算值` | 在同一位置迭代修正残差 | + +> `ApplyPixelOffsetCompensation` 的移动为相对移动。覆盖模式假设"同参考位置、每位置一次";同位置迭代修正请用 `Accumulate`,否则物理移动会重复叠加。 + +### 11.3 接口一览 + +| 方法 | 说明 | +|------|------| +| `ApplyPixelOffsetCompensation(dx, dy, mode = Replace)` | 记录补偿 + 执行相对修正移动 | +| `RecordPixelOffsetCompensation(dx, dy, mode = Replace)` | 仅记录补偿,不移动 | +| `GetStageCompensation()` | 获取当前补偿值 `(Xmm, Ymm)` | +| `SetStageCompensation(xMm, yMm)` | 直接以 mm 设置补偿值 | +| `ResetStageCompensation()` | 清零补偿(换工件/维护时调用) | +| `MoveToTarget(..., applyCompensation: true)` | 单轴定点移动叠加补偿(仅 StageX/StageY) | +| `MoveAllToTarget(targets, applyCompensation: true)` | 多轴定点移动叠加补偿(仅 StageX/StageY) | + +### 11.4 调用示例 + +```csharp +// 主框架坐标匹配后:记录补偿并修正到位(默认覆盖) +_mc.ApplyPixelOffsetCompensation(dxPixels, dyPixels); + +// 后续定点运动叠加补偿 +_mc.MoveAllToTarget(new Dictionary +{ + { AxisId.StageX, 100 }, { AxisId.StageY, 50 } +}, applyCompensation: true); + +// 换工件清零 +_mc.ResetStageCompensation(); +``` + +补偿值变化时发布 `StageCompensationChangedEvent`(见第 6 节)。补偿功能复用现有几何配置(`DetectorPixelSizeX/Y`、`PixelDirectionX/Y`),无需新增 App.config 参数。 + +--- + +**最后更新 | Last Updated**: 2026-07-28 diff --git a/XP.Hardware.MotionControl/Documents/GeometryWithDetectorSwing.md b/XP.Hardware.MotionControl/Documents/GeometryWithDetectorSwing.md index 58376d4c..cb2ecc62 100644 --- a/XP.Hardware.MotionControl/Documents/GeometryWithDetectorSwing.md +++ b/XP.Hardware.MotionControl/Documents/GeometryWithDetectorSwing.md @@ -309,7 +309,7 @@ Dz = Pz - R × cos(θ) = DetectorZ_abs + SwingPivotOffset - R × cos(θ) | 参数 | 键名 | 默认值 | 单位 | 说明 | |------|------|--------|------|------| | 轮询周期 | `MotionControl:PollingInterval` | 100 | ms | PLC 状态轮询周期,每个周期读取所有轴位置并重新计算几何参数 | -| 默认速度 | `MotionControl:DefaultVelocity` | 500 | - | 未指定速度时使用的默认运动速度 | +| 默认速度 | `MotionControl:DefaultVelocity` | 100 | - | 未指定速度时使用的默认运动速度(代码默认 100) | --- diff --git a/XP.Hardware.MotionControl/Documents/MotionControl_Design.md b/XP.Hardware.MotionControl/Documents/MotionControl_Design.md index 9b4d008d..b78e016b 100644 --- a/XP.Hardware.MotionControl/Documents/MotionControl_Design.md +++ b/XP.Hardware.MotionControl/Documents/MotionControl_Design.md @@ -152,6 +152,13 @@ PlcLinearAxis PlcRotaryAxis PlcSafetyDoor - `ApplyGeometry(fod, fdd)` — 根据 FOD/FDD 反算并移动 - `GetCurrentGeometry()` — 获取当前 FOD/FDD/M - `MoveStageByPixelOffset(dxPixels, dyPixels, executeMove)` — 根据图像像素偏移驱动载物台移动(自动计算放大比和物理距离) +- `MoveToSafePosition()` — 所有轴移动到配置的安全点位置 +- `MoveToTarget(axisId, target, speed, applyCompensation)` / `MoveAllToTarget(targets, applyCompensation)` — 定点移动,`applyCompensation=true` 时对 StageX/StageY 叠加坐标系补偿 +- `ApplyPixelOffsetCompensation(dx, dy, mode)` — 记录像素偏移补偿并执行修正移动(默认覆盖) +- `RecordPixelOffsetCompensation(dx, dy, mode)` — 仅记录补偿不移动(默认覆盖) +- `GetStageCompensation()` / `SetStageCompensation(xMm, yMm)` / `ResetStageCompensation()` — 补偿值查询/设置/清零 + +> 坐标系补偿的完整设计见第 14 节。 ### 4.2 GeometryCalculator @@ -497,3 +504,66 @@ Abstractions/Events/ | P2 | MotionControlView.xaml(宽350 UserControl) | | P3 | MotionControlModule 注册 | | P3 | 多语言资源文件 | + +--- + +## 14. 图像坐标系补偿设计 | Coordinate Compensation Design + +### 14.1 功能目标 + +主框架在坐标匹配过程中会计算出工件位置的图像像素偏移 `(dX, dY)`(可为负)。本模块负责: + +1. 将像素偏移换算为载物台物理位移(mm),记录为**补偿值**(初始 `(0,0)`); +2. 执行修正移动,使工件对齐; +3. 后续任意"移动到名义位置"的指令,可选择自动叠加该补偿值,保证工件持续对齐。 + +### 14.2 补偿值模型 + +- **单位**:物理量 mm(Stage 坐标系),**放大比无关**。像素偏移随放大比变化,但对应的物理错位是固定的,因此存 mm 而非像素。 +- **作用范围**:仅 `StageX / StageY`,其余轴不补偿。 +- **存储位置**:`MotionControlService`(单例)私有字段 `_compXmm / _compYmm`,用 `_compLock` 保护跨线程(轮询线程 / UI 线程)读写。 +- **生命周期**:随软件运行保持;`HomeAll` 回零**不清补偿**;仅 `ResetStageCompensation()` 显式清零(换工件/维护时由主框架调用)。 + +### 14.3 记录模式(CompensationMode) + +| 模式 | 语义 | 适用场景 | +|------|------|----------| +| `Replace`(默认)| `comp = 本次换算值` | 每次从同一名义参考位置测量、每位置只记录一次的一次性对位 | +| `Accumulate` | `comp += 本次换算值` | 在同一位置迭代修正残差的闭环逼近 | + +> 注意:`ApplyPixelOffsetCompensation` 的移动为**相对移动**。覆盖模式假设"同参考位置、每位置一次";若在同一位置连续修正而不回参考点,请改用 `Accumulate`,否则物理移动会重复叠加。 + +### 14.4 像素→mm 换算 + +复用像素偏移换算逻辑(私有方法 `TryConvertPixelToStageMm`),与 `MoveStageByPixelOffset` 一致: + +``` +mmPerPixelX = DetectorPixelSizeX / M +mmPerPixelY = DetectorPixelSizeY / M +dxMm = dxPixels × mmPerPixelX × PixelDirectionX +dyMm = dyPixels × mmPerPixelY × PixelDirectionY +``` + +其中 `M` 为当前放大倍率(由 `GetCurrentGeometry()` 实时计算)。 + +### 14.5 典型调用流程 + +```csharp +// 主框架坐标匹配完成后:一步完成"记录补偿 + 修正移动"(默认覆盖) +_mc.ApplyPixelOffsetCompensation(dxPixels, dyPixels); + +// 其他类库定点运动并叠加补偿(仅 StageX/StageY 生效,补偿后仍过边界校验) +_mc.MoveAllToTarget(new Dictionary +{ + { AxisId.StageX, 100 }, { AxisId.StageY, 50 } +}, applyCompensation: true); + +// 换工件时清零补偿 +_mc.ResetStageCompensation(); +``` + +补偿值变化时发布 `StageCompensationChangedEvent(StageCompensationData(Xmm, Ymm))`,供 UI 显示当前补偿量。 + +### 14.6 无需新增配置 + +补偿功能复用现有几何配置(`DetectorPixelSizeX/Y`、`PixelDirectionX/Y`)与实时放大比,不引入新的 App.config 参数。 diff --git a/XP.Hardware.MotionControl/Documents/README.md b/XP.Hardware.MotionControl/Documents/README.md index c4bd5859..26297b06 100644 --- a/XP.Hardware.MotionControl/Documents/README.md +++ b/XP.Hardware.MotionControl/Documents/README.md @@ -13,6 +13,7 @@ XP.Hardware.MotionControl 是 XplorePlane 平面CT工业检测系统的核心运 - 4个直线轴 + 3个旋转轴统一管理(策略模式) - 安全防护门控制(联锁检查、状态机) - FOD/FDD/放大倍率几何正算与反算 +- 图像坐标系补偿(像素偏移→载物台物理补偿,支持覆盖/累加两种模式) - 多轴联动移动(原子性边界检查) - Jog 点动调试(虚拟摇杆控制) - 100ms 周期 PLC 状态轮询 @@ -92,7 +93,8 @@ XP.Hardware.MotionControl/ │ │ ├── AxisId.cs # 直线轴标识 │ │ ├── RotaryAxisId.cs # 旋转轴标识 │ │ ├── AxisStatus.cs # 轴状态 -│ │ └── DoorStatus.cs # 门状态 +│ │ ├── DoorStatus.cs # 门状态 +│ │ └── CompensationMode.cs # 坐标系补偿记录模式(Replace/Accumulate) │ └── Events/ # Prism 事件定义 │ ├── AxisStatusChangedEvent.cs │ ├── DoorStatusChangedEvent.cs @@ -100,7 +102,8 @@ XP.Hardware.MotionControl/ │ ├── GeometryUpdatedEvent.cs │ ├── GeometryApplyRequestEvent.cs │ ├── JoystickActiveEvent.cs -│ └── MotionErrorEvent.cs +│ ├── MotionErrorEvent.cs +│ └── StageCompensationChangedEvent.cs ├── Implementations/ # PLC 实现层 │ ├── PlcLinearAxis.cs # 基于 PLC 的直线轴实现 │ ├── PlcRotaryAxis.cs # 基于 PLC 的旋转轴实现 @@ -133,8 +136,14 @@ XP.Hardware.MotionControl/ │ ├── Resources.zh-TW.resx # 繁体中文 │ └── Resources.en-US.resx # 英文 └── Documents/ # 文档 - ├── README.md # 本文档 - └── GUIDENCE.md # 外部集成指南 + ├── README.md # 本文档 + ├── GUIDENCE.md # 外部集成指南 + ├── MotionControl_Design.md # 模块设计文档 + ├── GeometryWithDetectorSwing.md # 探测器摆动几何计算模型 + ├── Calibration_README.md # 像素-载物台映射子模块 + ├── CoordinateCompensation.md # 载物台坐标系补偿功能 + ├── AxisControlView_PLC_Communication.md # AxisControlView PLC 通信机制 + └── App.config.example # 配置示例 ``` @@ -142,7 +151,7 @@ XP.Hardware.MotionControl/ | 接口 | 用途 | 注册方式 | |------|------|----------| -| `IMotionControlService` | 业务控制(移动、停止、回零、Jog、开关门、几何计算) | 单例 | +| `IMotionControlService` | 业务控制(移动、停止、回零、Jog、开关门、几何计算、像素偏移驱动、坐标系补偿) | 单例 | | `IMotionSystem` | 底层状态读取(轴位置、状态、门状态、摇杆状态、轴复位) | 单例 | | `MotionControlConfig` | 配置参数(轴范围、几何原点、轮询周期) | 实例 | | `GeometryCalculator` | 几何正算/反算工具 | 单例 | @@ -158,6 +167,7 @@ XP.Hardware.MotionControl/ | `GeometryApplyRequestEvent` | `GeometryApplyRequestData(SourceZTarget, DetectorZTarget, ...)` | 几何反算结果填入请求(DebugWindow → MotionControlViewModel) | | `JoystickActiveEvent` | `bool` | 实体摇杆激活状态变化(true=激活, false=未激活) | | `MotionErrorEvent` | `MotionErrorData(AxisId, ErrorMessage)` | 轴进入 Error/Alarm 状态 | +| `StageCompensationChangedEvent` | `StageCompensationData(Xmm, Ymm)` | 载物台坐标系补偿值被记录、设置或清零 | --- @@ -201,4 +211,4 @@ PLC 信号名称硬编码在 `MotionSignalNames.cs` 中,信号定义在 `PlcAd --- -**最后更新 | Last Updated**: 2026-05-11 +**最后更新 | Last Updated**: 2026-07-28