将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!-- ==================== 运动控制配置 | Motion Control Configuration ==================== -->
|
||||
<!-- 直线轴配置(单位:mm)| Linear axis config (unit: mm) -->
|
||||
<add key="MotionControl:SourceZ:Min" value="0" />
|
||||
<add key="MotionControl:SourceZ:Max" value="500" />
|
||||
<add key="MotionControl:SourceZ:Origin" value="0" />
|
||||
<add key="MotionControl:DetectorZ:Min" value="0" />
|
||||
<add key="MotionControl:DetectorZ:Max" value="600" />
|
||||
<add key="MotionControl:DetectorZ:Origin" value="0" />
|
||||
<add key="MotionControl:StageX:Min" value="-150" />
|
||||
<add key="MotionControl:StageX:Max" value="150" />
|
||||
<add key="MotionControl:StageX:Origin" value="0" />
|
||||
<add key="MotionControl:StageY:Min" value="-150" />
|
||||
<add key="MotionControl:StageY:Max" value="150" />
|
||||
<add key="MotionControl:StageY:Origin" value="0" />
|
||||
<!-- 旋转轴配置(单位:度)| Rotary axis config (unit: degrees) -->
|
||||
<add key="MotionControl:DetectorSwing:Min" value="-45" />
|
||||
<add key="MotionControl:DetectorSwing:Max" value="45" />
|
||||
<add key="MotionControl:DetectorSwing:Origin" value="0" />
|
||||
<add key="MotionControl:DetectorSwing:Enabled" value="true" />
|
||||
<add key="MotionControl:StageRotation:Min" value="-360" />
|
||||
<add key="MotionControl:StageRotation:Max" value="360" />
|
||||
<add key="MotionControl:StageRotation:Origin" value="0" />
|
||||
<add key="MotionControl:StageRotation:Enabled" value="true" />
|
||||
<add key="MotionControl:FixtureRotation:Min" value="-90" />
|
||||
<add key="MotionControl:FixtureRotation:Max" value="90" />
|
||||
<add key="MotionControl:FixtureRotation:Origin" value="0" />
|
||||
<add key="MotionControl:FixtureRotation:Enabled" value="true" />
|
||||
<!-- 几何原点(mm)| Geometry origins (mm) -->
|
||||
<add key="MotionControl:Geometry:SourceZOrigin" value="0" />
|
||||
<add key="MotionControl:Geometry:DetectorZOrigin" value="600" />
|
||||
<add key="MotionControl:Geometry:StageRotationCenterZ" value="300" />
|
||||
<!-- 探测器摆动几何参数(mm)| Detector swing geometry parameters (mm) -->
|
||||
<!-- SwingPivotOffset: Pivot 相对于 DetectorZ 绝对坐标的 Z 方向偏移,正值表示 Pivot 在 DetectorZ_abs 上方 -->
|
||||
<!-- SwingPivotOffset: Pivot Z offset relative to DetectorZ absolute, positive = above -->
|
||||
<add key="MotionControl:Geometry:SwingPivotOffset" value="0" />
|
||||
<!-- SwingRadius: Pivot 到探测器感光面中心的距离,为 0 时退化为无摆动模型 -->
|
||||
<!-- SwingRadius: Distance from Pivot to detector active area center, 0 = no swing model -->
|
||||
<add key="MotionControl:Geometry:SwingRadius" value="0" />
|
||||
<!-- 运行参数 | Runtime parameters -->
|
||||
<add key="MotionControl:PollingInterval" value="100" />
|
||||
<add key="MotionControl:DefaultVelocity" value="500" />
|
||||
</appSettings>
|
||||
@@ -0,0 +1,322 @@
|
||||
# 运动控制模块集成指南 | Motion Control Module Integration Guide
|
||||
|
||||
## 1. 模块注册 | Module Registration
|
||||
|
||||
在 `App.xaml.cs` 中注册模块:
|
||||
|
||||
```csharp
|
||||
using XP.Hardware.MotionControl.Module;
|
||||
|
||||
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
|
||||
{
|
||||
moduleCatalog.AddModule<MotionControlModule>();
|
||||
}
|
||||
```
|
||||
|
||||
模块启动时自动完成:
|
||||
- 从 App.config 加载配置(轴范围、几何原点、轮询周期)
|
||||
- 注册 `IMotionSystem`、`IMotionControlService`、`GeometryCalculator` 为单例
|
||||
- 注册多语言资源到 Fallback Chain
|
||||
- 启动 PLC 状态轮询(100ms 周期)
|
||||
|
||||
---
|
||||
|
||||
## 2. 获取轴状态和位置 | Reading Axis Status and Position
|
||||
|
||||
通过 DI 注入 `IMotionSystem`:
|
||||
|
||||
```csharp
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
|
||||
public class YourService
|
||||
{
|
||||
private readonly IMotionSystem _motionSystem;
|
||||
|
||||
public YourService(IMotionSystem motionSystem)
|
||||
{
|
||||
_motionSystem = motionSystem;
|
||||
}
|
||||
|
||||
public void ReadStatus()
|
||||
{
|
||||
// 获取直线轴 | Get linear axis
|
||||
ILinearAxis sourceZ = _motionSystem.GetLinearAxis(AxisId.SourceZ);
|
||||
double position = sourceZ.ActualPosition; // 实际位置(mm)
|
||||
AxisStatus status = sourceZ.Status; // Idle/Moving/Homing/Error/Alarm
|
||||
bool posLimit = sourceZ.PositiveLimitHit; // 正限位
|
||||
bool negLimit = sourceZ.NegativeLimitHit; // 负限位
|
||||
|
||||
// 获取旋转轴 | Get rotary axis
|
||||
IRotaryAxis stageRot = _motionSystem.GetRotaryAxis(RotaryAxisId.StageRotation);
|
||||
double angle = stageRot.ActualAngle; // 实际角度(度)
|
||||
bool enabled = stageRot.Enabled; // 是否启用
|
||||
|
||||
// 获取安全门 | Get safety door
|
||||
ISafetyDoor door = _motionSystem.SafetyDoor;
|
||||
DoorStatus doorStatus = door.Status; // Unknown/Opening/Open/Closing/Closed/Locked/Error
|
||||
bool interlocked = door.IsInterlocked; // 联锁信号
|
||||
|
||||
// 遍历所有轴 | Iterate all axes
|
||||
foreach (var kvp in _motionSystem.LinearAxes)
|
||||
{
|
||||
AxisId id = kvp.Key;
|
||||
ILinearAxis axis = kvp.Value;
|
||||
// ...
|
||||
}
|
||||
foreach (var kvp in _motionSystem.RotaryAxes)
|
||||
{
|
||||
RotaryAxisId id = kvp.Key;
|
||||
IRotaryAxis axis = kvp.Value;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 下发运动控制命令 | Sending Motion Commands
|
||||
|
||||
通过 DI 注入 `IMotionControlService`:
|
||||
|
||||
```csharp
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.MotionControl.Services;
|
||||
|
||||
public class YourController
|
||||
{
|
||||
private readonly IMotionControlService _mc;
|
||||
|
||||
public YourController(IMotionControlService motionControlService)
|
||||
{
|
||||
_mc = motionControlService;
|
||||
}
|
||||
|
||||
// 单轴移动 | Single axis move
|
||||
public void MoveSingleAxis()
|
||||
{
|
||||
MotionResult result = _mc.MoveToTarget(AxisId.SourceZ, 100.0);
|
||||
if (!result.Success)
|
||||
Console.WriteLine($"移动失败: {result.ErrorMessage}");
|
||||
|
||||
// 带速度参数 | With speed parameter
|
||||
_mc.MoveToTarget(AxisId.SourceZ, 100.0, speed: 50.0);
|
||||
|
||||
// 旋转轴 | Rotary axis
|
||||
_mc.MoveRotaryToTarget(RotaryAxisId.DetectorSwing, 15.0);
|
||||
_mc.MoveRotaryToTarget(RotaryAxisId.DetectorSwing, 15.0, speed: 30.0);
|
||||
}
|
||||
|
||||
// 多轴联动 | Multi-axis coordinated move
|
||||
public void MoveMultipleAxes()
|
||||
{
|
||||
var targets = new Dictionary<AxisId, double>
|
||||
{
|
||||
{ AxisId.SourceZ, 100.0 },
|
||||
{ AxisId.DetectorZ, 400.0 },
|
||||
{ AxisId.StageX, 0.0 },
|
||||
{ AxisId.StageY, 0.0 }
|
||||
};
|
||||
// 原子性边界检查:任意轴越界则全部拒绝
|
||||
MotionResult result = _mc.MoveAllToTarget(targets);
|
||||
}
|
||||
|
||||
// 停止和回零 | Stop and Home
|
||||
public void StopAndHome()
|
||||
{
|
||||
_mc.StopAll(); // 停止所有已启用轴
|
||||
_mc.HomeAll(); // 所有已启用轴回零
|
||||
}
|
||||
|
||||
// Jog 点动 | Jog
|
||||
public void JogAxis()
|
||||
{
|
||||
_mc.JogStart(AxisId.SourceZ, positive: true); // 正向 Jog
|
||||
// ... 用户松开按钮时 ...
|
||||
_mc.JogStop(AxisId.SourceZ); // 停止 Jog
|
||||
|
||||
// 旋转轴 Jog
|
||||
_mc.JogRotaryStart(RotaryAxisId.DetectorSwing, positive: false);
|
||||
_mc.JogRotaryStop(RotaryAxisId.DetectorSwing);
|
||||
}
|
||||
|
||||
// 安全门控制 | Safety door control
|
||||
public void DoorControl()
|
||||
{
|
||||
MotionResult openResult = _mc.OpenDoor(); // 含联锁检查
|
||||
_mc.CloseDoor();
|
||||
_mc.StopDoor();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 几何计算 | Geometry Calculation
|
||||
|
||||
```csharp
|
||||
public class YourGeometryUser
|
||||
{
|
||||
private readonly IMotionControlService _mc;
|
||||
private readonly GeometryCalculator _calc;
|
||||
|
||||
public YourGeometryUser(
|
||||
IMotionControlService mc,
|
||||
GeometryCalculator calc)
|
||||
{
|
||||
_mc = mc;
|
||||
_calc = calc;
|
||||
}
|
||||
|
||||
// 正算:获取当前 FOD/FDD/放大倍率 | Forward: get current geometry
|
||||
public void GetCurrentGeometry()
|
||||
{
|
||||
var (fod, fdd, magnification) = _mc.GetCurrentGeometry();
|
||||
Console.WriteLine($"FOD={fod:F2}mm, FDD={fdd:F2}mm, M={magnification:F3}");
|
||||
}
|
||||
|
||||
// 反算:由目标 FOD+FDD 移动轴 | Inverse: move axes by target FOD+FDD
|
||||
public void ApplyGeometry()
|
||||
{
|
||||
MotionResult result = _mc.ApplyGeometry(targetFOD: 200.0, targetFDD: 500.0);
|
||||
}
|
||||
|
||||
// 反算:由目标 FOD+放大倍率 移动轴 | Inverse: move axes by target FOD+magnification
|
||||
public void ApplyByMagnification()
|
||||
{
|
||||
MotionResult result = _mc.ApplyGeometryByMagnification(targetFOD: 200.0, magnification: 2.5);
|
||||
}
|
||||
|
||||
// 直接使用 GeometryCalculator | Use GeometryCalculator directly
|
||||
public void DirectCalculation()
|
||||
{
|
||||
double fod = _calc.CalcFOD(sourceZAbsolute: 100.0, stageRotationCenterZ: 300.0);
|
||||
double fdd = _calc.CalcFDD(sourceZAbsolute: 100.0, detectorZAbsolute: 600.0);
|
||||
double mag = _calc.CalcMagnification(fdd, fod); // FOD < 0.001 时返回 NaN
|
||||
|
||||
// 反算轴目标位置
|
||||
var (szTarget, dzTarget) = _calc.CalcAxisTargets(
|
||||
targetFOD: 200.0, targetFDD: 500.0,
|
||||
stageRotationCenterZ: 300.0,
|
||||
sourceZOrigin: 0.0, detectorZOrigin: 600.0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 事件订阅 | Event Subscription
|
||||
|
||||
通过 Prism `IEventAggregator` 被动接收状态变化:
|
||||
|
||||
```csharp
|
||||
using Prism.Events;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.MotionControl.Abstractions.Events;
|
||||
|
||||
public class YourMonitor
|
||||
{
|
||||
public YourMonitor(IEventAggregator eventAggregator)
|
||||
{
|
||||
// 轴状态变化 | Axis status changed
|
||||
eventAggregator.GetEvent<AxisStatusChangedEvent>()
|
||||
.Subscribe(data =>
|
||||
{
|
||||
Console.WriteLine($"轴 {data.AxisId} 状态变为 {data.Status}");
|
||||
}, ThreadOption.UIThread);
|
||||
|
||||
// 门状态变化 | Door status changed
|
||||
eventAggregator.GetEvent<DoorStatusChangedEvent>()
|
||||
.Subscribe(status =>
|
||||
{
|
||||
Console.WriteLine($"门状态: {status}");
|
||||
}, ThreadOption.UIThread);
|
||||
|
||||
// 门联锁状态变化 | Door interlock status changed
|
||||
eventAggregator.GetEvent<DoorInterlockChangedEvent>()
|
||||
.Subscribe(isInterlocked =>
|
||||
{
|
||||
Console.WriteLine($"联锁状态: {(isInterlocked ? "已联锁" : "未联锁")}");
|
||||
}, ThreadOption.UIThread);
|
||||
|
||||
// 几何参数更新(每轮询周期触发)| Geometry updated (every polling cycle)
|
||||
eventAggregator.GetEvent<GeometryUpdatedEvent>()
|
||||
.Subscribe(data =>
|
||||
{
|
||||
Console.WriteLine($"FOD={data.FOD:F2}, FDD={data.FDD:F2}, M={data.Magnification:F3}");
|
||||
}, ThreadOption.UIThread);
|
||||
|
||||
// 运动错误(轴进入 Error/Alarm)| Motion error
|
||||
eventAggregator.GetEvent<MotionErrorEvent>()
|
||||
.Subscribe(data =>
|
||||
{
|
||||
Console.WriteLine($"运动错误: 轴 {data.AxisId} - {data.ErrorMessage}");
|
||||
}, ThreadOption.UIThread);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. PLC 信号定义 | PLC Signal Definitions
|
||||
|
||||
信号名称硬编码在 `MotionSignalNames.cs` 中,信号地址定义在 `PlcAddrDfn.xml`。
|
||||
|
||||
每个轴包含以下信号(以 SourceZ 为例):
|
||||
|
||||
| 信号名 | 方向 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| `MC_SourceZ_Pos` | Read | single | 实际位置 |
|
||||
| `MC_SourceZ_Target` | Write | single | 目标位置 |
|
||||
| `MC_SourceZ_Speed` | Write | single | 运动速度 |
|
||||
| `MC_SourceZ_JogPos` | Write | byte | 正向 Jog |
|
||||
| `MC_SourceZ_JogNeg` | Write | byte | 反向 Jog |
|
||||
| `MC_SourceZ_Home` | Write | byte | 回零 |
|
||||
| `MC_SourceZ_Stop` | Write | byte | 停止 |
|
||||
|
||||
安全门信号:
|
||||
|
||||
| 信号名 | 方向 | 说明 |
|
||||
|--------|------|------|
|
||||
| `MC_Door_Open` | Write | 开门 |
|
||||
| `MC_Door_Close` | Write | 关门 |
|
||||
| `MC_Door_Stop` | Write | 停门 |
|
||||
| `MC_Door_Status` | Read | 门状态(int: 0-6) |
|
||||
| `MC_Door_Interlock` | Read | 联锁信号 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 安全机制 | Safety Mechanisms
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| 边界检查 | 目标位置超出 Min/Max 范围时拒绝移动 |
|
||||
| 运动中防重入 | 轴处于 Moving 状态时拒绝新的移动命令 |
|
||||
| 联锁检查 | 联锁信号有效时禁止开门 |
|
||||
| 禁用轴检查 | Enabled=false 的旋转轴拒绝所有命令 |
|
||||
| Homing 中拒绝 Jog | 回零过程中不允许 Jog 操作 |
|
||||
| 多轴原子性检查 | MoveAllToTarget 任意轴越界则全部拒绝 |
|
||||
| 轮询异常不中断 | 轮询中 PLC 异常被捕获,不影响下一轮 |
|
||||
| UI 异常保护 | ViewModel 命令通过 SafeRun 包裹,PLC 异常弹窗而非崩溃 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 轮询机制 | Polling Mechanism
|
||||
|
||||
`MotionControlService` 使用 `System.Threading.Timer` 以配置的 `PollingInterval`(默认 100ms)周期执行:
|
||||
|
||||
1. 检查 PLC 连接状态(通过 `IPlcService.IsConnected`),未连接时跳过
|
||||
2. 缓存所有轴、门状态和联锁状态的旧值
|
||||
3. 调用 `IMotionSystem.UpdateAllStatus()` 从 PLC 读取最新状态
|
||||
4. 重新计算几何参数,发布 `GeometryUpdatedEvent`
|
||||
5. 检测状态变化,发布 `AxisStatusChangedEvent`、`DoorStatusChangedEvent`、`DoorInterlockChangedEvent`
|
||||
6. 轴进入 Error/Alarm 时额外发布 `MotionErrorEvent`
|
||||
7. 异常捕获并记录 Error 日志,不中断轮询
|
||||
|
||||
通过 `StartPolling()` / `StopPolling()` 控制轮询生命周期。
|
||||
|
||||
---
|
||||
|
||||
**最后更新 | Last Updated**: 2026-04-14
|
||||
@@ -0,0 +1,520 @@
|
||||
# 探测器摆动几何计算模型 | Geometry Calculation with Detector Swing
|
||||
|
||||
> 文档版本:v1.0
|
||||
> 创建日期:2026-04-13
|
||||
> 适用模块:XP.Hardware.MotionControl
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题背景 | Background
|
||||
|
||||
原始几何计算假设射线源(Source)、载物台旋转中心(Stage)、探测器(Detector)三者共线于 Z 轴,使用简单的绝对坐标差值计算 FOD 和 FDD。
|
||||
|
||||
但实际系统中,探测器具有摆动轴(DetectorSwing),可在 XZ 平面内绕固定旋转中心(Pivot)旋转。当摆动角度 θ ≠ 0 时,探测器感光面中心偏离 Z 轴,射线束从射线源到探测器的路径不再沿 Z 轴方向,FOD 和 FDD 的定义需要重新考虑。
|
||||
|
||||
---
|
||||
|
||||
## 2. 物理模型 | Physical Model
|
||||
|
||||
### 2.1 坐标系定义 | Coordinate System
|
||||
|
||||
- Z 轴:垂直方向(射线源和探测器的主运动方向)
|
||||
- X 轴:水平方向(探测器摆动平面内的横向)
|
||||
- 原点:系统参考零点
|
||||
|
||||
### 2.2 关键几何元素 | Key Geometry Elements
|
||||
|
||||
```
|
||||
探测器中心 D ● (Dx, Dz)
|
||||
\
|
||||
\ R ← 摆动半径(Pivot 到探测器感光面中心的距离)
|
||||
\
|
||||
\ θ ← 摆动角度
|
||||
/
|
||||
摆动旋转中心 P ● (Px=0, Pz=SwingPivotZ)
|
||||
|
|
||||
|
|
||||
载物台旋转中心 O ● (Ox=0, Oz=StageRotationCenterZ)
|
||||
|
|
||||
| ← 射线束中心线(从 S 到 D)
|
||||
|
|
||||
射线源 S (Sx=0, Sz=SourceZ_abs)
|
||||
●
|
||||
```
|
||||
|
||||
### 2.3 各点坐标 | Point Coordinates
|
||||
|
||||
| 点 | X 坐标 | Z 坐标 | 说明 |
|
||||
|----|--------|--------|------|
|
||||
| S(射线源)| 0 | `SourceZ_pos + SourceZOrigin` | 射线源始终在 Z 轴上 |
|
||||
| O(载物台旋转中心)| 0 | `StageRotationCenterZ`(配置固定值)| 载物台平面与 Z 轴的交点 |
|
||||
| P(摆动旋转中心)| 0 | `SwingPivotZ`(配置固定值)| 探测器摆动的旋转中心,在 Z 轴上 |
|
||||
| D(探测器感光面中心)| `Dx` | `Dz` | 由摆动角度和半径计算得出 |
|
||||
|
||||
### 2.4 探测器中心坐标计算 | Detector Center Coordinate Calculation
|
||||
|
||||
摆动角度 θ 定义:θ = 0 时探测器正对射线源(D 在 Z 轴上,P 正上方),正角度为顺时针(向 X 正方向偏转)。
|
||||
|
||||
```
|
||||
Dx = Px + R × sin(θ) = R × sin(θ) (因为 Px = 0)
|
||||
Dz = Pz - R × cos(θ) (探测器感光面在 Pivot 下方,朝向射线源)
|
||||
```
|
||||
|
||||
注意:这里假设 θ=0 时探测器在 Pivot 正下方(朝向射线源方向),Z 轴向上为正。射线源在底部(Z 值小),探测器在顶部(Z 值大),探测器感光面朝下。公式为:
|
||||
|
||||
```
|
||||
Dx = R × sin(θ)
|
||||
Dz = Pz - R × cos(θ)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. FOD 和 FDD 的正确定义 | Correct FOD and FDD Definitions
|
||||
|
||||
### 3.1 FOD(焦点到物体距离)| Focus-to-Object Distance
|
||||
|
||||
FOD 的定义是:射线源焦点到载物台旋转中心的距离,沿射线束中心线方向测量。
|
||||
|
||||
但在平面 CT 系统中,更常用的定义是:射线源到载物台旋转中心沿 Z 轴的投影距离(因为载物台是一个平面,射线束穿过载物台平面的位置才是关键)。
|
||||
|
||||
**方案 A:沿射线束中心线的 FOD(几何真实距离)**
|
||||
|
||||
射线束中心线从 S 到 D,载物台平面在 Z = StageRotationCenterZ 处。FOD 是射线源 S 到射线束与载物台平面交点的距离。
|
||||
|
||||
射线束参数方程(从 S 到 D):
|
||||
```
|
||||
x(t) = Sx + t × (Dx - Sx) = t × Dx
|
||||
z(t) = Sz + t × (Dz - Sz)
|
||||
```
|
||||
|
||||
载物台平面在 Z = Oz,求交点时 t:
|
||||
```
|
||||
t_stage = (Oz - Sz) / (Dz - Sz)
|
||||
```
|
||||
|
||||
交点坐标:
|
||||
```
|
||||
x_intersect = t_stage × Dx
|
||||
z_intersect = Oz
|
||||
```
|
||||
|
||||
FOD = S 到交点的欧几里得距离:
|
||||
```
|
||||
FOD = sqrt((x_intersect - Sx)² + (z_intersect - Sz)²)
|
||||
= sqrt((t_stage × Dx)² + (Oz - Sz)²)
|
||||
```
|
||||
|
||||
**方案 B:沿 Z 轴的 FOD(简化定义,与原始实现一致)**
|
||||
|
||||
```
|
||||
FOD_z = |Sz - Oz|
|
||||
```
|
||||
|
||||
这是原始实现的方式,不受探测器摆动影响。
|
||||
|
||||
### 3.2 FDD(焦点到探测器距离)| Focus-to-Detector Distance
|
||||
|
||||
FDD 是射线源焦点到探测器感光面中心的欧几里得距离:
|
||||
|
||||
```
|
||||
FDD = sqrt((Dx - Sx)² + (Dz - Sz)²)
|
||||
= sqrt((R × sin(θ))² + (Pz - R × cos(θ) - Sz)²)
|
||||
```
|
||||
|
||||
当 θ = 0 时:
|
||||
```
|
||||
FDD = |Pz - R - Sz|
|
||||
```
|
||||
此时 `Pz - R` 就是探测器感光面中心在 Z 轴上的绝对坐标(Pivot 下方 R 处),与原始公式 `|DetectorZ_abs - SourceZ_abs|` 一致。
|
||||
|
||||
### 3.3 放大倍率 | Magnification
|
||||
|
||||
```
|
||||
M = FDD / FOD
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐方案 | Recommended Approach
|
||||
|
||||
### 4.1 采用方案 A(射线束中心线投影)
|
||||
|
||||
对于平面 CT 系统,FOD 应该是沿射线束中心线测量的,因为放大倍率 M = FDD / FOD 直接决定了成像的几何放大关系。
|
||||
|
||||
### 4.2 正算公式汇总 | Forward Calculation Summary
|
||||
|
||||
已知输入:
|
||||
- `Sz`:射线源 Z 绝对坐标 = SourceZ_pos + SourceZOrigin
|
||||
- `Pz`:摆动旋转中心 Z 绝对坐标 = SwingPivotZ(配置)
|
||||
- `R`:摆动半径 = SwingRadius(配置)
|
||||
- `θ`:探测器摆动角度 = DetectorSwing 实际角度(度)
|
||||
- `Oz`:载物台旋转中心 Z 绝对坐标 = StageRotationCenterZ(配置)
|
||||
|
||||
计算步骤:
|
||||
```
|
||||
1. 探测器中心坐标:
|
||||
Dx = R × sin(θ)
|
||||
Dz = Pz - R × cos(θ)
|
||||
|
||||
2. FDD = sqrt(Dx² + (Dz - Sz)²)
|
||||
|
||||
3. 射线束与载物台平面交点参数:
|
||||
t_stage = (Oz - Sz) / (Dz - Sz)
|
||||
|
||||
4. 交点 X 坐标:
|
||||
x_intersect = t_stage × Dx
|
||||
|
||||
5. FOD = sqrt(x_intersect² + (Oz - Sz)²)
|
||||
|
||||
6. M = FDD / FOD
|
||||
```
|
||||
|
||||
### 4.3 反算公式汇总 | Inverse Calculation Summary
|
||||
|
||||
反算场景:给定目标 FOD 和 FDD(以及当前探测器摆动角度 θ),计算 SourceZ 和 DetectorZ 的目标位置。
|
||||
|
||||
**重要约束**:反算时,探测器摆动角度 θ 是已知的当前值(或目标值),不由 FOD/FDD 反算决定。
|
||||
|
||||
已知输入:
|
||||
- `targetFOD`:目标 FOD
|
||||
- `targetFDD`:目标 FDD
|
||||
- `θ`:探测器摆动角度(当前值或目标值)
|
||||
- `Pz`、`R`、`Oz`:配置参数
|
||||
|
||||
当 θ = 0 时(简化情况,退化为原始公式):
|
||||
```
|
||||
Dx = 0, Dz = Pz - R
|
||||
FOD = |Sz - Oz| → Sz = Oz - targetFOD(射线源在载物台下方)
|
||||
FDD = |Dz - Sz| → Dz = Sz + targetFDD
|
||||
DetectorZ_pos = Dz - DetectorZOrigin
|
||||
```
|
||||
|
||||
当 θ ≠ 0 时:
|
||||
|
||||
由 FOD 求 SourceZ:
|
||||
```
|
||||
FOD 的定义涉及射线束方向,而射线束方向取决于 S 和 D 的位置。
|
||||
D 的位置取决于 DetectorZ(通过 Pz 和 R 和 θ 计算)。
|
||||
|
||||
但在本系统中,探测器的 Z 位置由 DetectorZ 轴控制,
|
||||
而摆动角度 θ 只改变探测器在 XZ 平面内的朝向。
|
||||
|
||||
实际上,Pz(摆动旋转中心)的 Z 坐标 = DetectorZ_pos + DetectorZOrigin + PivotOffset
|
||||
其中 PivotOffset 是从 DetectorZ 轴位置到 Pivot 点的固定偏移。
|
||||
|
||||
简化模型:Pivot 的 Z 坐标随 DetectorZ 轴移动。
|
||||
```
|
||||
|
||||
**关键认识**:摆动旋转中心 P 是固定在探测器机械臂上的,它随 DetectorZ 轴一起上下移动。因此:
|
||||
|
||||
```
|
||||
Pz = DetectorZ_pos + DetectorZOrigin + PivotOffsetFromDetectorOrigin
|
||||
```
|
||||
|
||||
其中 `PivotOffsetFromDetectorOrigin` 是一个固定的机械偏移量(配置参数)。
|
||||
|
||||
但为了简化,我们可以将 Pivot 的 Z 坐标直接表示为:
|
||||
```
|
||||
Pz = DetectorZ_abs + SwingPivotOffset
|
||||
```
|
||||
其中 `SwingPivotOffset` 是 Pivot 相对于 DetectorZ 绝对坐标的固定偏移。
|
||||
|
||||
探测器感光面中心:
|
||||
```
|
||||
Dx = R × sin(θ)
|
||||
Dz = Pz - R × cos(θ) = DetectorZ_abs + SwingPivotOffset - R × cos(θ)
|
||||
```
|
||||
|
||||
反算步骤:
|
||||
|
||||
1. 由 targetFOD 确定 SourceZ:
|
||||
- 当 θ 较小时,FOD ≈ |Sz - Oz|,所以 Sz ≈ Oz - targetFOD
|
||||
- 精确求解需要迭代(因为 FOD 的定义涉及射线束方向,而射线束方向又取决于 D 的位置)
|
||||
|
||||
2. 由 targetFDD 和已知 Sz 确定 DetectorZ:
|
||||
```
|
||||
FDD² = Dx² + (Dz - Sz)²
|
||||
= (R × sin(θ))² + (DetectorZ_abs + SwingPivotOffset - R × cos(θ) - Sz)²
|
||||
```
|
||||
设 `A = SwingPivotOffset - R × cos(θ)`,`B = R × sin(θ)`,则:
|
||||
```
|
||||
FDD² = B² + (DetectorZ_abs + A - Sz)²
|
||||
DetectorZ_abs + A - Sz = ±sqrt(FDD² - B²)
|
||||
DetectorZ_abs = Sz - A + sqrt(FDD² - B²) (取正值,探测器在射线源上方)
|
||||
```
|
||||
|
||||
3. 转换为轴相对坐标:
|
||||
```
|
||||
SourceZ_pos = Sz - SourceZOrigin
|
||||
DetectorZ_pos = DetectorZ_abs - DetectorZOrigin
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 配置参数完整说明 | Complete Configuration Parameters
|
||||
|
||||
所有参数在 `App.config` 的 `<appSettings>` 中配置,键名前缀为 `MotionControl:`。
|
||||
|
||||
### 5.1 几何计算相关参数 | Geometry Calculation Parameters
|
||||
|
||||
以下参数直接参与 FOD/FDD/放大倍率的计算:
|
||||
|
||||
| 参数 | 键名 | 默认值 | 单位 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| 射线源Z原点偏移 | `Geometry:SourceZOrigin` | 0 | mm | 射线源Z轴的原点偏移。绝对坐标 = 轴位置 + Origin。用于将轴的相对坐标转换为系统绝对坐标 |
|
||||
| 探测器Z原点偏移 | `Geometry:DetectorZOrigin` | 600 | mm | 探测器Z轴的原点偏移。含义同上。通常设为探测器回零时感光面中心的绝对Z坐标 |
|
||||
| 载物台旋转中心Z | `Geometry:StageRotationCenterZ` | 300 | mm | 载物台旋转中心的绝对Z坐标(固定值)。载物台平面与Z轴的交点,FOD 的参考基准 |
|
||||
| 摆动旋转中心偏移 | `Geometry:SwingPivotOffset` | 0 | mm | 探测器摆动旋转中心(Pivot)相对于 DetectorZ 绝对坐标的Z方向偏移。Pivot 在探测器上方(Z 值更大),正值。Pivot 的绝对Z坐标 = DetectorZ_abs + SwingPivotOffset |
|
||||
| 摆动半径 | `Geometry:SwingRadius` | 0 | mm | Pivot 到探测器感光面中心的距离。当值为 0 时,系统退化为无摆动的简化模型(完全向后兼容) |
|
||||
|
||||
参数间的关系:
|
||||
|
||||
```
|
||||
射线源绝对Z坐标 Sz = SourceZ_pos + SourceZOrigin
|
||||
探测器绝对Z坐标 DetZ_abs = DetectorZ_pos + DetectorZOrigin
|
||||
摆动旋转中心Z坐标 Pz = DetZ_abs + SwingPivotOffset
|
||||
探测器感光面中心 Dx = SwingRadius × sin(θ)
|
||||
Dz = Pz - SwingRadius × cos(θ)
|
||||
载物台旋转中心 Oz = StageRotationCenterZ(固定值)
|
||||
```
|
||||
|
||||
### 5.2 直线轴配置 | Linear Axis Configuration
|
||||
|
||||
每个直线轴包含 3 个参数,键名格式为 `MotionControl:<AxisName>:<Param>`:
|
||||
|
||||
| 轴名 | Min | Max | Origin | 说明 |
|
||||
|------|-----|-----|--------|------|
|
||||
| SourceZ | 0 | 500 | 0 | 射线源Z轴,控制射线源上下运动(mm) |
|
||||
| DetectorZ | 0 | 600 | 0 | 探测器Z轴,控制探测器上下运动(mm) |
|
||||
| StageX | -150 | 150 | 0 | 载物台X轴,控制载物台前后运动(mm) |
|
||||
| StageY | -150 | 150 | 0 | 载物台Y轴,控制载物台左右运动(mm) |
|
||||
|
||||
- `Min` / `Max`:轴的软限位范围(相对坐标),超出范围的移动命令会被拒绝
|
||||
- `Origin`:轴的原点偏移,用于将相对坐标转换为绝对坐标
|
||||
|
||||
### 5.3 旋转轴配置 | Rotary Axis Configuration
|
||||
|
||||
每个旋转轴包含 4 个参数,键名格式为 `MotionControl:<AxisName>:<Param>`:
|
||||
|
||||
| 轴名 | Min | Max | Origin | Enabled | 说明 |
|
||||
|------|-----|-----|--------|---------|------|
|
||||
| DetectorSwing | -45 | 45 | 0 | true | 探测器摆动轴,在XZ平面内旋转(度) |
|
||||
| StageRotation | -360 | 360 | 0 | true | 载物台旋转轴,绕Z轴旋转(度) |
|
||||
| FixtureRotation | -90 | 90 | 0 | true | 夹具旋转轴,可选配置(度) |
|
||||
|
||||
- `Enabled`:是否启用该旋转轴,`false` 时 UI 隐藏且拒绝所有命令
|
||||
|
||||
### 5.4 运行参数 | Runtime Parameters
|
||||
|
||||
| 参数 | 键名 | 默认值 | 单位 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| 轮询周期 | `MotionControl:PollingInterval` | 100 | ms | PLC 状态轮询周期,每个周期读取所有轴位置并重新计算几何参数 |
|
||||
| 默认速度 | `MotionControl:DefaultVelocity` | 500 | - | 未指定速度时使用的默认运动速度 |
|
||||
|
||||
---
|
||||
|
||||
## 5.5 实际设备配置示例 | Real Device Configuration Example
|
||||
|
||||
以下是一个典型平面CT设备的配置示例和测量方法。
|
||||
|
||||
### 设备物理布局
|
||||
|
||||
假设设备的实际机械尺寸如下:
|
||||
|
||||
```
|
||||
Z=1000mm (顶部)
|
||||
┃
|
||||
┃ DetectorZ 轴行程 0~800mm
|
||||
┃ 探测器回零时 DetectorZ 绝对坐标在 Z=500mm 处
|
||||
┃ |
|
||||
┃ D | 探测器感光面中心(朝下,面向射线源)
|
||||
┃ ● |
|
||||
┃ \θ |
|
||||
┃ \ | R=200mm
|
||||
┃ \ |
|
||||
┃ \|
|
||||
┃ ● P 摆动旋转中心 (Z=580mm,随 DetectorZ 移动)
|
||||
┃ |
|
||||
┃ |
|
||||
┃ ● O 载物台旋转中心 (Z=350mm,固定)
|
||||
┃ |
|
||||
┃ |
|
||||
┃ |
|
||||
┃ ● S 射线源焦点 (Z=50mm,即 SourceZ_pos=0 时)
|
||||
┃
|
||||
┃ SourceZ 轴行程 0~500mm
|
||||
┃ 射线源回零时焦点在 Z=50mm 处
|
||||
┃
|
||||
Z=0mm (底部)
|
||||
```
|
||||
|
||||
### 测量方法
|
||||
|
||||
1. **SourceZOrigin(射线源原点偏移)**:
|
||||
- 将射线源移动到回零位置(SourceZ_pos = 0)
|
||||
- 测量此时射线源焦点的绝对Z坐标 → 即为 SourceZOrigin
|
||||
- 本例:焦点在 Z=50mm → `SourceZOrigin = 50`
|
||||
|
||||
2. **DetectorZOrigin(探测器原点偏移)**:
|
||||
- 将探测器移动到回零位置(DetectorZ_pos = 0)
|
||||
- 测量此时探测器Z轴参考点的绝对Z坐标 → 即为 DetectorZOrigin
|
||||
- 本例:参考点在 Z=500mm → `DetectorZOrigin = 500`
|
||||
|
||||
3. **StageRotationCenterZ(载物台旋转中心Z坐标)**:
|
||||
- 测量载物台旋转中心的绝对Z坐标(固定值,由机械结构决定)
|
||||
- 本例:Z=350mm → `StageRotationCenterZ = 350`
|
||||
|
||||
4. **SwingPivotOffset(摆动旋转中心偏移)**:
|
||||
- 将探测器移动到回零位置,此时 DetectorZ_abs = 0 + 500 = 500mm
|
||||
- 测量摆动旋转中心(Pivot)的绝对Z坐标 → 本例 Z=580mm
|
||||
- SwingPivotOffset = Pz - DetectorZ_abs = 580 - 500 = 80mm
|
||||
- `SwingPivotOffset = 80`
|
||||
- 注意:Pivot 随 DetectorZ 轴一起移动,此偏移是固定的机械常数
|
||||
|
||||
5. **SwingRadius(摆动半径)**:
|
||||
- 测量 Pivot 到探测器感光面中心的距离(固定值,由机械臂长度决定)
|
||||
- 本例:200mm → `SwingRadius = 200`
|
||||
|
||||
### 对应的 App.config 配置
|
||||
|
||||
```xml
|
||||
<!-- 直线轴配置 -->
|
||||
<add key="MotionControl:SourceZ:Min" value="0" />
|
||||
<add key="MotionControl:SourceZ:Max" value="500" />
|
||||
<add key="MotionControl:SourceZ:Origin" value="0" />
|
||||
<add key="MotionControl:DetectorZ:Min" value="0" />
|
||||
<add key="MotionControl:DetectorZ:Max" value="800" />
|
||||
<add key="MotionControl:DetectorZ:Origin" value="0" />
|
||||
<add key="MotionControl:StageX:Min" value="-150" />
|
||||
<add key="MotionControl:StageX:Max" value="150" />
|
||||
<add key="MotionControl:StageX:Origin" value="0" />
|
||||
<add key="MotionControl:StageY:Min" value="-150" />
|
||||
<add key="MotionControl:StageY:Max" value="150" />
|
||||
<add key="MotionControl:StageY:Origin" value="0" />
|
||||
|
||||
<!-- 旋转轴配置 -->
|
||||
<add key="MotionControl:DetectorSwing:Min" value="-45" />
|
||||
<add key="MotionControl:DetectorSwing:Max" value="45" />
|
||||
<add key="MotionControl:DetectorSwing:Origin" value="0" />
|
||||
<add key="MotionControl:DetectorSwing:Enabled" value="true" />
|
||||
<add key="MotionControl:StageRotation:Min" value="-360" />
|
||||
<add key="MotionControl:StageRotation:Max" value="360" />
|
||||
<add key="MotionControl:StageRotation:Origin" value="0" />
|
||||
<add key="MotionControl:StageRotation:Enabled" value="true" />
|
||||
<add key="MotionControl:FixtureRotation:Min" value="-90" />
|
||||
<add key="MotionControl:FixtureRotation:Max" value="90" />
|
||||
<add key="MotionControl:FixtureRotation:Origin" value="0" />
|
||||
<add key="MotionControl:FixtureRotation:Enabled" value="false" />
|
||||
|
||||
<!-- 几何参数 -->
|
||||
<add key="MotionControl:Geometry:SourceZOrigin" value="50" />
|
||||
<add key="MotionControl:Geometry:DetectorZOrigin" value="500" />
|
||||
<add key="MotionControl:Geometry:StageRotationCenterZ" value="350" />
|
||||
<add key="MotionControl:Geometry:SwingPivotOffset" value="80" />
|
||||
<add key="MotionControl:Geometry:SwingRadius" value="200" />
|
||||
|
||||
<!-- 运行参数 -->
|
||||
<add key="MotionControl:PollingInterval" value="100" />
|
||||
<add key="MotionControl:DefaultVelocity" value="500" />
|
||||
```
|
||||
|
||||
### 验证计算
|
||||
|
||||
以上述配置为例,当 SourceZ_pos=100, DetectorZ_pos=200, θ=15° 时:
|
||||
|
||||
```
|
||||
Sz = 100 + 50 = 150mm (射线源绝对Z,底部)
|
||||
DetZ_abs = 200 + 500 = 700mm (探测器绝对Z,顶部)
|
||||
Pz = 700 + 80 = 780mm (Pivot 绝对Z,探测器上方)
|
||||
Dx = 200 × sin(15°) = 200 × 0.2588 = 51.76mm (探测器中心X偏移)
|
||||
Dz = 780 - 200 × cos(15°) = 780 - 193.19 = 586.81mm (探测器感光面中心Z,Pivot 下方)
|
||||
|
||||
FDD = sqrt(51.76² + (586.81 - 150)²)
|
||||
= sqrt(2679.1 + 190,729.5)
|
||||
= sqrt(193,408.6)
|
||||
≈ 439.78mm
|
||||
|
||||
射线束与载物台平面交点参数:
|
||||
t_stage = (350 - 150) / (586.81 - 150) = 200 / 436.81 = 0.4579
|
||||
x_intersect = 0.4579 × 51.76 = 23.70mm
|
||||
|
||||
FOD = sqrt(23.70² + 200²)
|
||||
= sqrt(561.7 + 40000)
|
||||
= sqrt(40561.7)
|
||||
≈ 201.40mm
|
||||
|
||||
M = FDD / FOD = 439.78 / 201.40 ≈ 2.183
|
||||
|
||||
对比 θ=0 时(同样轴位置):
|
||||
Dx = 0, Dz = 780 - 200 = 580mm
|
||||
FDD_0 = |150 - 580| = 430mm
|
||||
FOD_0 = |150 - 350| = 200mm
|
||||
M_0 = 430 / 200 = 2.15
|
||||
|
||||
可以看到 θ=15° 时 FDD 略大(439.78 vs 430),FOD 略大(201.40 vs 200),
|
||||
放大倍率从 2.15 升为 2.183。这是因为摆动使探测器感光面中心在 X 方向偏移,
|
||||
增加了射线源到探测器的欧几里得距离。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 几何示意图 | Geometry Diagram
|
||||
|
||||
### 6.1 θ = 0(无摆动,退化为原始模型)
|
||||
|
||||
```
|
||||
D ●─────────────────── 探测器感光面中心 (Z = Pz - R)
|
||||
|
|
||||
| R(摆动半径)
|
||||
|
|
||||
P ●─────────────────── 摆动旋转中心 (Z = Pz = DetZ_abs + Offset)
|
||||
|
|
||||
|
|
||||
O ●─────────────────── 载物台旋转中心 (Z = Oz)
|
||||
|
|
||||
| FOD = |Sz - Oz|
|
||||
|
|
||||
S ●─────────────────── 射线源 (Z = Sz)
|
||||
|
||||
FDD = |Sz - (Pz - R)| = |Sz - Dz|
|
||||
```
|
||||
|
||||
### 6.2 θ ≠ 0(有摆动)
|
||||
|
||||
```
|
||||
D ●────/──────────── 探测器感光面中心 (Dx, Dz)
|
||||
/ /
|
||||
/θ /
|
||||
/ /
|
||||
P ●────/──────────────── 摆动旋转中心 (0, Pz)
|
||||
| /
|
||||
| / 射线束与载物台平面的交点 I (x_i, Oz)
|
||||
O ●──/────────────────── 载物台旋转中心 (0, Oz)
|
||||
| /
|
||||
| / ← 射线束中心线
|
||||
| /
|
||||
|/
|
||||
S ●─────────────────── 射线源 (0, Sz)
|
||||
|
||||
Dx = R × sin(θ)
|
||||
Dz = Pz - R × cos(θ)
|
||||
FDD = sqrt(Dx² + (Dz - Sz)²)
|
||||
FOD = sqrt(x_i² + (Oz - Sz)²)
|
||||
其中 x_i = Dx × (Oz - Sz) / (Dz - Sz)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 特殊情况处理 | Edge Cases
|
||||
|
||||
| 情况 | 处理方式 |
|
||||
|------|----------|
|
||||
| θ = 0 | 退化为原始公式,Dx = 0,FDD = \|Dz - Sz\| |
|
||||
| SwingRadius = 0 | 完全退化为原始模型(无摆动影响) |
|
||||
| FOD ≈ 0 | 放大倍率返回 NaN(与原始行为一致) |
|
||||
| Dz = Sz | FDD 方向水平,t_stage 无穷大,FOD 计算需要特殊处理 |
|
||||
| FDD < \|R × sin(θ)\| | 反算时 sqrt 内为负数,目标不可达,返回错误 |
|
||||
|
||||
---
|
||||
|
||||
**最后更新 | Last Updated**: 2026-04-14
|
||||
@@ -0,0 +1,494 @@
|
||||
# XP.Hardware.MotionControl 运动控制模块设计规划
|
||||
|
||||
> 文档版本:v1.0
|
||||
> 创建日期:2026-04-10
|
||||
> 适用项目:XplorePlane 平面CT工业检测系统
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与硬件描述
|
||||
|
||||
本模块用于控制平面CT系统中的所有运动轴、旋转轴及安全防护门。
|
||||
|
||||
### 1.1 直线轴(4轴)
|
||||
|
||||
| 轴标识 | 说明 | 运动方向 |
|
||||
|--------|------|----------|
|
||||
| 轴1 SourceZ | 射线源Z方向运动 | 上下 |
|
||||
| 轴2 DetectorZ | 探测器Z方向运动 | 上下 |
|
||||
| 轴3 StageX | 载物台X方向运动 | 前后 |
|
||||
| 轴4 StageY | 载物台Y方向运动 | 左右 |
|
||||
|
||||
### 1.2 旋转轴(3轴)
|
||||
|
||||
| 轴标识 | 说明 | 特点 |
|
||||
|--------|------|------|
|
||||
| 旋转轴1 DetectorSwing | 探测器在XZ平面的旋转摆动 | 有限角度,旋转中心在射线源Z方向延长线上 |
|
||||
| 旋转轴2 StageRotation | 载物台围绕XY中心的Z方向旋转 | 可选配置 |
|
||||
| 旋转轴3 FixtureRotation | 旋转夹具在载物台XY平面上围绕X方向的旋转 | 可选配置 |
|
||||
|
||||
### 1.3 安全门
|
||||
|
||||
防护门,具备状态机控制与互锁信号联动。
|
||||
|
||||
### 1.4 几何关系
|
||||
|
||||
```
|
||||
FOD(焦点到旋转中心距离)= |SourceZ绝对坐标 - 旋转中心绝对Z坐标|
|
||||
FDD(焦点到探测器距离) = |SourceZ绝对坐标 - DetectorZ绝对坐标|
|
||||
放大倍率 M = FDD / FOD
|
||||
```
|
||||
|
||||
只有确定了射线源、探测器、载物台旋转中心的绝对坐标,才能准确计算 FOD 和 FDD,进而控制放大倍率。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录结构
|
||||
|
||||
```
|
||||
XP.Hardware.MotionControl/
|
||||
├── Abstractions/
|
||||
│ ├── ILinearAxis.cs # 直线轴策略接口
|
||||
│ ├── IRotaryAxis.cs # 旋转轴策略接口
|
||||
│ ├── ISafetyDoor.cs # 安全门策略接口
|
||||
│ ├── IMotionSystem.cs # 顶层系统管理接口
|
||||
│ ├── LinearAxisBase.cs # 直线轴抽象基类
|
||||
│ ├── RotaryAxisBase.cs # 旋转轴抽象基类
|
||||
│ ├── SafetyDoorBase.cs # 安全门抽象基类
|
||||
│ ├── MotionResult.cs # 统一操作结果类型
|
||||
│ └── Enums/
|
||||
│ ├── AxisStatus.cs # 轴状态枚举
|
||||
│ ├── DoorStatus.cs # 门状态枚举
|
||||
│ └── AxisId.cs # 轴标识枚举
|
||||
├── Implementations/
|
||||
│ ├── PlcLinearAxis.cs # 基于PLC的直线轴实现
|
||||
│ ├── PlcRotaryAxis.cs # 基于PLC的旋转轴实现
|
||||
│ └── PlcSafetyDoor.cs # 基于PLC的安全门实现
|
||||
├── Services/
|
||||
│ ├── IMotionControlService.cs # 运动控制业务服务接口
|
||||
│ ├── MotionControlService.cs # 服务实现(含轮询线程、几何计算)
|
||||
│ └── GeometryCalculator.cs # FOD/FDD/放大倍率计算器
|
||||
├── Config/
|
||||
│ ├── MotionControlConfig.cs # 配置实体(轴范围、原点、PLC信号名)
|
||||
│ └── ConfigLoader.cs # 配置加载器
|
||||
├── ViewModels/
|
||||
│ └── MotionControlViewModel.cs # 操作面板 ViewModel
|
||||
├── Views/
|
||||
│ └── MotionControlView.xaml # 操作面板 UserControl(宽350)
|
||||
├── Module/
|
||||
│ └── MotionControlModule.cs # Prism 模块注册
|
||||
├── Documents/
|
||||
│ └── MotionControl_Design.md # 本文档
|
||||
└── Resources/
|
||||
├── Resources.resx
|
||||
├── Resources.zh-CN.resx
|
||||
└── Resources.en-US.resx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心抽象层设计
|
||||
|
||||
### 3.1 三类硬件抽象继承关系
|
||||
|
||||
```
|
||||
ILinearAxis IRotaryAxis ISafetyDoor
|
||||
│ │ │
|
||||
LinearAxisBase RotaryAxisBase SafetyDoorBase
|
||||
│ │ │
|
||||
PlcLinearAxis PlcRotaryAxis PlcSafetyDoor
|
||||
```
|
||||
|
||||
### 3.2 ILinearAxis 接口职责
|
||||
|
||||
- 位置读取(实际值)
|
||||
- 目标位置写入
|
||||
- Jog 正向 / Jog 反向(按住运动,松开停止)
|
||||
- 回零
|
||||
- 停止
|
||||
- 限位状态读取(正限位 / 负限位)
|
||||
- 轴状态(Idle / Moving / Homing / Error / Alarm)
|
||||
|
||||
### 3.3 IRotaryAxis 接口职责
|
||||
|
||||
- 角度读取(实际值)
|
||||
- 目标角度写入
|
||||
- Jog 正向 / Jog 反向
|
||||
- 回零
|
||||
- 停止
|
||||
- 角度软限位(MinAngle / MaxAngle,来自配置)
|
||||
- 轴状态
|
||||
|
||||
### 3.4 ISafetyDoor 接口职责
|
||||
|
||||
- 状态机读取(Unknown / Opening / Open / Closing / Closed / Locked / Error)
|
||||
- 开门指令
|
||||
- 关门指令
|
||||
- 互锁信号读取(是否允许开门)
|
||||
|
||||
### 3.5 IMotionSystem 顶层接口职责
|
||||
|
||||
- 按 `AxisId` 枚举索引各直线轴(SourceZ、DetectorZ、StageX、StageY)
|
||||
- 按 `RotaryAxisId` 枚举索引各旋转轴(DetectorSwing、StageRotation、FixtureRotation)
|
||||
- 持有安全门实例
|
||||
- 协调运动:开门前联锁检查
|
||||
- 几何计算委托给 `GeometryCalculator`
|
||||
|
||||
---
|
||||
|
||||
## 4. 服务层设计
|
||||
|
||||
### 4.1 IMotionControlService
|
||||
|
||||
封装业务规则,供 ViewModel 调用:
|
||||
|
||||
- `StartPolling()` / `StopPolling()` — 启停 PLC 数据轮询
|
||||
- `MoveToTarget(AxisId, double)` — 移动指定轴到目标位置
|
||||
- `MoveAllToTarget(targets)` — 多轴联动移动
|
||||
- `StopAll()` — 停止所有轴
|
||||
- `HomeAll()` — 全部回零
|
||||
- `JogStart(AxisId, direction)` / `JogStop(AxisId)` — Jog 控制
|
||||
- `OpenDoor()` / `CloseDoor()` / `StopDoor()` — 门控制(含联锁前置检查)
|
||||
- `ApplyGeometry(fod, fdd)` — 根据 FOD/FDD 反算并移动
|
||||
- `GetCurrentGeometry()` — 获取当前 FOD/FDD/M
|
||||
|
||||
### 4.2 GeometryCalculator
|
||||
|
||||
```csharp
|
||||
// 正算:由轴位置计算几何参数
|
||||
double CalcFOD(double sourceZAbs, double stageRotCenterZAbs)
|
||||
double CalcFDD(double sourceZAbs, double detectorZAbs)
|
||||
double CalcMagnification(double fdd, double fod)
|
||||
|
||||
// 反算:由目标几何参数计算轴目标位置
|
||||
(double sourceZTarget, double detectorZTarget) CalcAxisTargets(double targetFOD, double targetFDD)
|
||||
|
||||
// 边界检查:目标值是否在轴 Min/Max 范围内
|
||||
bool ValidateTargets(double sourceZTarget, double detectorZTarget)
|
||||
```
|
||||
|
||||
绝对坐标计算方式:
|
||||
|
||||
```
|
||||
SourceZ绝对坐标 = SourceZ当前位置 + SourceZOrigin(配置)
|
||||
DetectorZ绝对坐标 = DetectorZ当前位置 + DetectorZOrigin(配置)
|
||||
旋转中心绝对Z坐标 = StageRotationCenterZ(配置,固定值)
|
||||
```
|
||||
|
||||
### 4.3 轮询线程
|
||||
|
||||
在 `MotionControlService` 内部使用 `System.Threading.Timer`,周期约 100ms,仅在 PLC 已连接时激活。每次轮询通过 `ISignalDataService.GetValueByName()` 读取所有轴的实际位置和状态,更新到 ViewModel 的可绑定属性。
|
||||
|
||||
---
|
||||
|
||||
## 5. 与 PLC 模块的依赖关系
|
||||
|
||||
MotionControl 通过依赖注入获取 `ISignalDataService`(来自 XP.Hardware.PLC),所有读写操作通过信号逻辑名称进行,不直接操作 PLC 地址。信号名称在配置文件中定义。
|
||||
|
||||
```
|
||||
XP.Hardware.MotionControl
|
||||
└── 依赖注入 → ISignalDataService(XP.Hardware.PLC)
|
||||
└── 读:GetValueByName<float>("MC_SourceZ_Pos")
|
||||
└── 写:EnqueueWrite("MC_SourceZ_Target", value)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 配置文件设计(App.config)
|
||||
|
||||
```xml
|
||||
<MotionControl>
|
||||
<!-- 直线轴配置(单位:mm) -->
|
||||
<SourceZ Min="0" Max="500" Origin="0"
|
||||
PlcSignalRead="MC_SourceZ_Pos" PlcSignalWrite="MC_SourceZ_Target"
|
||||
PlcSignalJogPos="MC_SourceZ_JogPos" PlcSignalJogNeg="MC_SourceZ_JogNeg"
|
||||
PlcSignalHome="MC_SourceZ_Home" PlcSignalStop="MC_SourceZ_Stop" />
|
||||
<DetectorZ Min="0" Max="600" Origin="0"
|
||||
PlcSignalRead="MC_DetZ_Pos" PlcSignalWrite="MC_DetZ_Target"
|
||||
PlcSignalJogPos="MC_DetZ_JogPos" PlcSignalJogNeg="MC_DetZ_JogNeg"
|
||||
PlcSignalHome="MC_DetZ_Home" PlcSignalStop="MC_DetZ_Stop" />
|
||||
<StageX Min="-150" Max="150" Origin="0"
|
||||
PlcSignalRead="MC_StageX_Pos" PlcSignalWrite="MC_StageX_Target"
|
||||
PlcSignalJogPos="MC_StageX_JogPos" PlcSignalJogNeg="MC_StageX_JogNeg"
|
||||
PlcSignalHome="MC_StageX_Home" PlcSignalStop="MC_StageX_Stop" />
|
||||
<StageY Min="-150" Max="150" Origin="0"
|
||||
PlcSignalRead="MC_StageY_Pos" PlcSignalWrite="MC_StageY_Target"
|
||||
PlcSignalJogPos="MC_StageY_JogPos" PlcSignalJogNeg="MC_StageY_JogNeg"
|
||||
PlcSignalHome="MC_StageY_Home" PlcSignalStop="MC_StageY_Stop" />
|
||||
|
||||
<!-- 旋转轴配置(单位:度) -->
|
||||
<DetectorSwing Min="-45" Max="45" Origin="0" Enabled="true"
|
||||
PlcSignalRead="MC_DetSwing_Angle" PlcSignalWrite="MC_DetSwing_Target"
|
||||
PlcSignalJogPos="MC_DetSwing_JogPos" PlcSignalJogNeg="MC_DetSwing_JogNeg"
|
||||
PlcSignalHome="MC_DetSwing_Home" PlcSignalStop="MC_DetSwing_Stop" />
|
||||
<StageRotation Min="-360" Max="360" Origin="0" Enabled="true"
|
||||
PlcSignalRead="MC_StageRot_Angle" PlcSignalWrite="MC_StageRot_Target"
|
||||
PlcSignalJogPos="MC_StageRot_JogPos" PlcSignalJogNeg="MC_StageRot_JogNeg"
|
||||
PlcSignalHome="MC_StageRot_Home" PlcSignalStop="MC_StageRot_Stop" />
|
||||
<FixtureRotation Min="-90" Max="90" Origin="0" Enabled="false"
|
||||
PlcSignalRead="MC_FixRot_Angle" PlcSignalWrite="MC_FixRot_Target"
|
||||
PlcSignalJogPos="MC_FixRot_JogPos" PlcSignalJogNeg="MC_FixRot_JogNeg"
|
||||
PlcSignalHome="MC_FixRot_Home" PlcSignalStop="MC_FixRot_Stop" />
|
||||
|
||||
<!-- 安全门配置 -->
|
||||
<SafetyDoor PlcSignalOpen="MC_Door_Open"
|
||||
PlcSignalClose="MC_Door_Close"
|
||||
PlcSignalStop="MC_Door_Stop"
|
||||
PlcSignalStatus="MC_Door_Status"
|
||||
PlcSignalInterlock="MC_Door_Interlock" />
|
||||
|
||||
<!-- 几何原点(绝对坐标 mm,用于 FOD/FDD 计算) -->
|
||||
<Geometry SourceZOrigin="0"
|
||||
DetectorZOrigin="600"
|
||||
StageRotationCenterZ="300" />
|
||||
|
||||
<!-- 轮询周期(ms) -->
|
||||
<PollingInterval>100</PollingInterval>
|
||||
|
||||
<!-- 运动速度(%,0-100) -->
|
||||
<DefaultVelocity>30</DefaultVelocity>
|
||||
<JogVelocity>10</JogVelocity>
|
||||
</MotionControl>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ViewModel 设计
|
||||
|
||||
### 7.1 属性分组
|
||||
|
||||
```csharp
|
||||
// 直线轴(每轴一组实际值 + 目标值)
|
||||
double SourceZActual / SourceZTarget
|
||||
double DetectorZActual / DetectorZTarget
|
||||
double StageXActual / StageXTarget
|
||||
double StageYActual / StageYTarget
|
||||
|
||||
// 旋转轴(每轴一组,可选轴按配置控制 Visibility)
|
||||
double DetSwingActual / DetSwingTarget // 附带角度限制提示文本
|
||||
double StageRotActual / StageRotTarget
|
||||
double FixtureRotActual / FixtureRotTarget
|
||||
|
||||
// 几何信息(实时计算,只读显示)
|
||||
double FOD / FDD / Magnification
|
||||
|
||||
// 几何反算输入
|
||||
double TargetFOD / TargetFDD / TargetMagnification
|
||||
|
||||
// 安全门
|
||||
DoorStatus DoorStatus
|
||||
bool IsInterlocked
|
||||
|
||||
// 状态
|
||||
bool IsMoving
|
||||
bool IsPlcConnected
|
||||
|
||||
// 调试模式(默认 false,折叠隐藏调试区域)
|
||||
bool IsDebugMode
|
||||
```
|
||||
|
||||
### 7.2 命令列表
|
||||
|
||||
```csharp
|
||||
// 主操作命令
|
||||
ICommand MoveCommand // 执行运动到各轴目标位置
|
||||
ICommand StopCommand // 停止所有轴
|
||||
ICommand HomeCommand // 全部回零
|
||||
ICommand OpenDoorCommand
|
||||
ICommand CloseDoorCommand
|
||||
ICommand StopDoorCommand
|
||||
ICommand ApplyGeometryCommand // 根据 FOD/FDD/M 反算并移动
|
||||
|
||||
// 调试命令(Jog,按住触发,松开停止)
|
||||
ICommand JogPlusCommand // 参数:AxisId
|
||||
ICommand JogMinusCommand // 参数:AxisId
|
||||
ICommand JogStopCommand // 参数:AxisId
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. View 布局设计(宽 350px,UserControl)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐ 350px
|
||||
│ [安全门状态] ● 已关闭 [互锁: 正常] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 直线轴 │
|
||||
│ 源Z [实际: 123.4mm] [▲目标▼] │
|
||||
│ 探Z [实际: 456.7mm] [▲目标▼] │
|
||||
│ 台X [实际: 12.3mm] [▲目标▼] │
|
||||
│ 台Y [实际: -5.6mm] [▲目标▼] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 旋转轴 │
|
||||
│ 探摆 [实际: 15.0°] [▲目标▼] │
|
||||
│ 限制: -45° ~ +45° │
|
||||
│ 台转 [实际: 180.0°] [▲目标▼] │
|
||||
│ 夹具 [实际: 0.0°] [▲目标▼] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 几何信息 │
|
||||
│ FOD: 150.0mm FDD: 450.0mm M: 3.0│
|
||||
│ [目标FOD▲▼] [目标FDD▲▼] [应用] │
|
||||
├─────────────────────────────────────┤
|
||||
│ [开始运动] [停止运动] [回零] │
|
||||
│ [开门] [关门] │
|
||||
├─────────────────────────────────────┤
|
||||
│ ▼ 调试模式(默认折叠) │
|
||||
│ 源Z [+Jog][-Jog] 探Z [+][-] │
|
||||
│ 台X [+Jog][-Jog] 台Y [+][-] │
|
||||
│ 探摆 [+Jog][-Jog] 台转[+][-] │
|
||||
│ 夹具 [+Jog][-Jog] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. FOD/FDD 反算逻辑
|
||||
|
||||
用户输入目标 FOD 和 FDD(或放大倍率 M),系统自动计算各轴目标位置:
|
||||
|
||||
```
|
||||
// 已知:StageRotationCenterZ(旋转中心绝对Z坐标,来自配置,固定值)
|
||||
|
||||
// 输入 FOD + FDD:
|
||||
SourceZ_target = StageRotationCenterZ - FOD
|
||||
DetectorZ_target = SourceZ_target + FDD
|
||||
|
||||
// 输入 FOD + M(M = FDD/FOD):
|
||||
FDD = M * FOD
|
||||
DetectorZ_target = SourceZ_target + FDD
|
||||
|
||||
// 边界检查:
|
||||
if SourceZ_target < SourceZ.Min || SourceZ_target > SourceZ.Max → 拒绝并提示
|
||||
if DetectorZ_target < DetectorZ.Min || DetectorZ_target > DetectorZ.Max → 拒绝并提示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 关键安全机制
|
||||
|
||||
参考旧项目 `ControlDoor.openDoor()` 和 `_130kvMove` 的实现经验:
|
||||
|
||||
| 机制 | 说明 | 对标旧代码 |
|
||||
|------|------|-----------|
|
||||
| 运动中防重入 | `IsMoving` 标志位,运动中禁止再次触发运动命令 | `fdcRun` 标志位 |
|
||||
| 开门联锁检查 | 开门前通过 `IEventAggregator` 检查是否有扫描进行中 | `ControlDoor.openDoor()` 前置逻辑 |
|
||||
| 射线源联锁 | 开门前需确认射线源已关闭(暂不实现,预留扩展点,见下方说明) | `zeroXray2Correct()` 等待归零 |
|
||||
| 可选轴隐藏 | `StageRotation` 和 `FixtureRotation` 通过配置 `Enabled` 控制 UI 可见性 | 无对应,新增 |
|
||||
| Jog 安全 | Jog 操作通过 `MouseDown/Up` 事件控制,松开立即停止 | `radButton3_MouseDown/Up` |
|
||||
|
||||
---
|
||||
|
||||
## 11. Prism 模块注册
|
||||
|
||||
```csharp
|
||||
// MotionControlModule.RegisterTypes()
|
||||
containerRegistry.RegisterSingleton<MotionControlConfig>(); // 配置(单例)
|
||||
containerRegistry.RegisterSingleton<IMotionSystem, PlcMotionSystem>(); // 系统管理(单例)
|
||||
containerRegistry.RegisterSingleton<IMotionControlService, MotionControlService>(); // 服务(单例)
|
||||
|
||||
// MotionControlModule.OnInitialized()
|
||||
// 注册多语言资源到 Fallback Chain
|
||||
// 启动 PLC 轮询(若 PLC 已连接)
|
||||
```
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
## 12. 关于射线源联锁的扩展说明(暂不实现)
|
||||
|
||||
### 12.1 什么是事件聚合器(EventAggregator)
|
||||
|
||||
Prism 的 `IEventAggregator` 是一个**发布/订阅总线**,用于在模块之间传递消息,而不需要两个模块直接互相引用。
|
||||
|
||||
类比理解:就像一个广播电台,`RaySource` 模块是"电台"(发布者),`MotionControl` 模块是"收音机"(订阅者),两者只通过"频道"(事件类型)通信,彼此不知道对方的存在。
|
||||
|
||||
```
|
||||
XP.Hardware.RaySource XP.Hardware.MotionControl
|
||||
│ │
|
||||
│ 发布 RaySourceStatusChangedEvent │
|
||||
└──────────→ EventAggregator ──────→ 订阅并响应
|
||||
```
|
||||
|
||||
### 12.2 如何订阅(当需要实现时)
|
||||
|
||||
**第一步:RaySource 模块已有的事件定义**
|
||||
|
||||
```csharp
|
||||
// XP.Hardware.RaySource/Abstractions/Events/RaySourceStatusChangedEvent.cs
|
||||
// 该事件已存在,携带射线源当前状态
|
||||
public class RaySourceStatusChangedEvent : PubSubEvent<RaySourceStatus> { }
|
||||
```
|
||||
|
||||
**第二步:MotionControlService 中注入并订阅**
|
||||
|
||||
```csharp
|
||||
public class MotionControlService : IMotionControlService
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private bool _isRaySourceOn = false; // 缓存射线源状态
|
||||
|
||||
public MotionControlService(IEventAggregator eventAggregator, ...)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
|
||||
// 订阅射线源状态变化事件
|
||||
_eventAggregator
|
||||
.GetEvent<RaySourceStatusChangedEvent>()
|
||||
.Subscribe(OnRaySourceStatusChanged, ThreadOption.BackgroundThread);
|
||||
}
|
||||
|
||||
private void OnRaySourceStatusChanged(RaySourceStatus status)
|
||||
{
|
||||
// 根据状态判断射线是否开启
|
||||
_isRaySourceOn = (status == RaySourceStatus.XRayOn);
|
||||
}
|
||||
|
||||
public async Task OpenDoor()
|
||||
{
|
||||
// 开门前检查射线源
|
||||
if (_isRaySourceOn)
|
||||
{
|
||||
_logger.Warn("射线源未关闭,禁止开门 | Ray source is on, door open blocked");
|
||||
return;
|
||||
}
|
||||
// ... 继续开门逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**第三步:注意事项**
|
||||
|
||||
- 订阅时选择 `ThreadOption.BackgroundThread`,避免在 UI 线程处理硬件状态
|
||||
- 模块卸载时需要取消订阅,防止内存泄漏:`GetEvent<...>().Unsubscribe(OnRaySourceStatusChanged)`
|
||||
- 两个模块之间**不需要互相引用**,只需要共同引用定义事件的程序集(或在各自模块内定义相同结构的事件)
|
||||
|
||||
### 12.3 预留扩展点
|
||||
|
||||
当需要实现射线源联锁时,只需在 `MotionControlService.OpenDoor()` 方法开头加入上述订阅逻辑即可,不需要修改任何其他代码。
|
||||
|
||||
---
|
||||
|
||||
## 13. 事件定义(Prism EventAggregator)
|
||||
```
|
||||
Abstractions/Events/
|
||||
├── AxisStatusChangedEvent.cs # 轴状态变化事件(AxisId + AxisStatus)
|
||||
├── DoorStatusChangedEvent.cs # 门状态变化事件(DoorStatus)
|
||||
├── GeometryUpdatedEvent.cs # 几何参数更新事件(FOD + FDD + M)
|
||||
└── MotionErrorEvent.cs # 运动错误事件(AxisId + 错误信息)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 实现优先级建议
|
||||
|
||||
| 优先级 | 内容 |
|
||||
|--------|------|
|
||||
| P0 | 配置实体、枚举定义、MotionResult |
|
||||
| P0 | ILinearAxis / IRotaryAxis / ISafetyDoor 接口 + 抽象基类 |
|
||||
| P1 | PlcLinearAxis / PlcRotaryAxis / PlcSafetyDoor 实现 |
|
||||
| P1 | GeometryCalculator |
|
||||
| P1 | IMotionControlService + MotionControlService(含轮询) |
|
||||
| P2 | MotionControlViewModel |
|
||||
| P2 | MotionControlView.xaml(宽350 UserControl) |
|
||||
| P3 | MotionControlModule 注册 |
|
||||
| P3 | 多语言资源文件 |
|
||||
@@ -0,0 +1,139 @@
|
||||
# XP.Hardware.MotionControl
|
||||
|
||||
工业运动控制模块 | Industrial Motion Control Module
|
||||
|
||||
---
|
||||
|
||||
## 项目概述 | Project Overview
|
||||
|
||||
XP.Hardware.MotionControl 是 XplorePlane 平面CT工业检测系统的核心运动控制模块,负责管理4个直线轴(SourceZ、DetectorZ、StageX、StageY)、3个旋转轴(DetectorSwing、StageRotation、FixtureRotation)及1个安全防护门的全部运动控制逻辑,并提供 FOD/FDD/放大倍率的几何正算与反算能力。
|
||||
|
||||
### 主要特性 | Key Features
|
||||
|
||||
- 4个直线轴 + 3个旋转轴统一管理(策略模式)
|
||||
- 安全防护门控制(联锁检查、状态机)
|
||||
- FOD/FDD/放大倍率几何正算与反算
|
||||
- 多轴联动移动(原子性边界检查)
|
||||
- Jog 点动调试(MouseDown/MouseUp 安全控制)
|
||||
- 100ms 周期 PLC 状态轮询
|
||||
- 基于 Prism EventAggregator 的跨模块事件通讯
|
||||
- 可配置的轴启用/禁用(FixtureRotation 等可选轴)
|
||||
- Telerik Crystal 主题 UI 控件
|
||||
- 中/英/繁体中文多语言支持
|
||||
|
||||
---
|
||||
|
||||
## 框架架构 | Architecture
|
||||
|
||||
```
|
||||
XP.Hardware.MotionControl/
|
||||
├── Abstractions/ # 抽象层 | Abstraction Layer
|
||||
│ ├── ILinearAxis.cs # 直线轴策略接口
|
||||
│ ├── IRotaryAxis.cs # 旋转轴策略接口
|
||||
│ ├── ISafetyDoor.cs # 安全门策略接口
|
||||
│ ├── IMotionSystem.cs # 顶层系统管理接口
|
||||
│ ├── LinearAxisBase.cs # 直线轴抽象基类(含边界检查)
|
||||
│ ├── RotaryAxisBase.cs # 旋转轴抽象基类
|
||||
│ ├── SafetyDoorBase.cs # 安全门抽象基类
|
||||
│ ├── MotionResult.cs # 统一操作结果类型
|
||||
│ ├── Enums/ # 枚举定义
|
||||
│ │ ├── AxisId.cs # 直线轴标识
|
||||
│ │ ├── RotaryAxisId.cs # 旋转轴标识
|
||||
│ │ ├── AxisStatus.cs # 轴状态
|
||||
│ │ └── DoorStatus.cs # 门状态
|
||||
│ └── Events/ # Prism 事件定义
|
||||
│ ├── AxisStatusChangedEvent.cs
|
||||
│ ├── DoorStatusChangedEvent.cs
|
||||
│ ├── DoorInterlockChangedEvent.cs
|
||||
│ ├── GeometryUpdatedEvent.cs
|
||||
│ └── MotionErrorEvent.cs
|
||||
├── Implementations/ # PLC 实现层
|
||||
│ ├── PlcLinearAxis.cs # 基于 PLC 的直线轴实现
|
||||
│ ├── PlcRotaryAxis.cs # 基于 PLC 的旋转轴实现
|
||||
│ ├── PlcSafetyDoor.cs # 基于 PLC 的安全门实现
|
||||
│ └── PlcMotionSystem.cs # 运动系统管理器
|
||||
├── Services/ # 业务服务层
|
||||
│ ├── IMotionControlService.cs # 业务服务接口
|
||||
│ ├── MotionControlService.cs # 服务实现(轮询、事件、日志)
|
||||
│ └── GeometryCalculator.cs # 几何计算器
|
||||
├── Config/ # 配置层
|
||||
│ ├── MotionControlConfig.cs # 配置实体
|
||||
│ ├── ConfigLoader.cs # 配置加载器
|
||||
│ └── MotionSignalNames.cs # PLC 信号名称常量
|
||||
├── ViewModels/ # 视图模型
|
||||
│ ├── MotionControlViewModel.cs # 操作面板 ViewModel
|
||||
│ └── MotionDebugViewModel.cs # 调试窗口 ViewModel
|
||||
├── Views/ # WPF 视图
|
||||
│ ├── MotionControlView.xaml # 操作面板(350px UserControl)
|
||||
│ ├── MotionDebugWindow.xaml # Jog 调试窗口
|
||||
│ └── MotionControlView.xaml.cs
|
||||
├── Module/
|
||||
│ └── MotionControlModule.cs # Prism 模块注册
|
||||
├── Resources/ # 多语言资源
|
||||
│ ├── Resources.resx # 默认(英文)
|
||||
│ ├── Resources.zh-CN.resx # 简体中文
|
||||
│ ├── Resources.zh-TW.resx # 繁体中文
|
||||
│ └── Resources.en-US.resx # 英文
|
||||
└── Documents/ # 文档
|
||||
├── README.md # 本文档
|
||||
└── GUIDENCE.md # 外部集成指南
|
||||
```
|
||||
|
||||
|
||||
### 对外接口 | Public Interfaces
|
||||
|
||||
| 接口 | 用途 | 注册方式 |
|
||||
|------|------|----------|
|
||||
| `IMotionControlService` | 业务控制(移动、停止、回零、Jog、开关门、几何计算) | 单例 |
|
||||
| `IMotionSystem` | 底层状态读取(轴位置、状态、门状态) | 单例 |
|
||||
| `MotionControlConfig` | 配置参数(轴范围、几何原点、轮询周期) | 实例 |
|
||||
| `GeometryCalculator` | 几何正算/反算工具 | 单例 |
|
||||
|
||||
### 事件 | Events
|
||||
|
||||
| 事件 | 载荷 | 触发时机 |
|
||||
|------|------|----------|
|
||||
| `AxisStatusChangedEvent` | `AxisStatusChangedData(AxisId, AxisStatus)` | 轴状态变化 |
|
||||
| `DoorStatusChangedEvent` | `DoorStatus` | 门状态变化 |
|
||||
| `DoorInterlockChangedEvent` | `bool` | 门联锁状态变化(true=已联锁, false=未联锁) |
|
||||
| `GeometryUpdatedEvent` | `GeometryData(FOD, FDD, Magnification)` | 几何参数更新(每轮询周期) |
|
||||
| `MotionErrorEvent` | `MotionErrorData(AxisId, ErrorMessage)` | 轴进入 Error/Alarm 状态 |
|
||||
|
||||
---
|
||||
|
||||
## 技术要求 | Technical Requirements
|
||||
|
||||
| 依赖 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| .NET 8.0 | net8.0-windows7.0 | 运行时 |
|
||||
| Prism.Wpf | 9.0.537 | MVVM + DI + EventAggregator |
|
||||
| Telerik UI for WPF | 2024.1.408 | UI 控件(Crystal 主题) |
|
||||
| XP.Common | - | 日志、多语言 |
|
||||
| XP.Hardware.PLC | - | IPlcService(PLC 连接状态)、ISignalDataService(PLC 信号读写) |
|
||||
|
||||
---
|
||||
|
||||
## 配置参数 | Configuration
|
||||
|
||||
在 `App.config` 的 `<appSettings>` 中配置:
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `MotionControl:SourceZ:Min/Max/Origin` | 0/500/0 | 射线源Z轴范围和原点(mm) |
|
||||
| `MotionControl:DetectorZ:Min/Max/Origin` | 0/600/0 | 探测器Z轴范围和原点(mm) |
|
||||
| `MotionControl:StageX:Min/Max/Origin` | -150/150/0 | 载物台X轴范围和原点(mm) |
|
||||
| `MotionControl:StageY:Min/Max/Origin` | -150/150/0 | 载物台Y轴范围和原点(mm) |
|
||||
| `MotionControl:DetectorSwing:Min/Max/Enabled` | -45/45/true | 探测器摆动范围和启用 |
|
||||
| `MotionControl:StageRotation:Min/Max/Enabled` | -360/360/true | 载物台旋转范围和启用 |
|
||||
| `MotionControl:FixtureRotation:Min/Max/Enabled` | -90/90/false | 夹具旋转范围和启用 |
|
||||
| `MotionControl:Geometry:SourceZOrigin` | 0 | 射线源Z原点偏移(mm) |
|
||||
| `MotionControl:Geometry:DetectorZOrigin` | 600 | 探测器Z原点偏移(mm) |
|
||||
| `MotionControl:Geometry:StageRotationCenterZ` | 300 | 旋转中心Z坐标(mm) |
|
||||
| `MotionControl:PollingInterval` | 100 | 轮询周期(ms) |
|
||||
| `MotionControl:DefaultVelocity` | 100 | 默认速度 |
|
||||
|
||||
PLC 信号名称硬编码在 `MotionSignalNames.cs` 中,信号定义在 `PlcAddrDfn.xml` 的 ReadCommon/WriteCommon 组。
|
||||
|
||||
---
|
||||
|
||||
**最后更新 | Last Updated**: 2026-04-14
|
||||
Reference in New Issue
Block a user