17 KiB
17 KiB
运动控制模块集成指南 | Motion Control Module Integration Guide
1. 模块注册 | Module Registration
在 App.xaml.cs 中注册模块:
using XP.Hardware.MotionControl.Module;
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<MotionControlModule>();
}
模块启动时自动完成:
- 从 App.config 加载配置(轴范围、几何原点、轮询周期)
- 注册
IMotionSystem、IMotionControlService、GeometryCalculator为单例 - 注册多语言资源到 Fallback Chain
- 启动 PLC 状态轮询(100ms 周期)
2. 界面集成 | UI Integration
AxisControlView 控件使用 | AxisControlView Usage
AxisControlView 是运动控制模块的核心用户控件,提供完整的轴控制和调试功能。
XAML 引用 | XAML Reference
<UserControl x:Class="YourView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:motion="clr-namespace:XP.Hardware.MotionControl.Views;assembly=XP.Hardware.MotionControl">
<Grid>
<!-- 基本使用 | Basic usage -->
<motion:AxisControlView />
<!-- 设置最小宽度 | Set minimum width -->
<motion:AxisControlView MinWidth="400" />
</Grid>
</UserControl>
自动 ViewModel 绑定 | Auto ViewModel Binding
控件使用 Prism 的 ViewModelLocator.AutoWireViewModel="True" 自动绑定 AxisControlViewModel,无需手动设置 DataContext。
ViewModel 属性说明 | ViewModel Properties
| 属性 | 类型 | 说明 |
|---|---|---|
StageXPosition |
double | 载物台 X 轴位置(mm),范围由配置决定 |
StageYPosition |
double | 载物台 Y 轴位置(mm),范围由配置决定 |
SourceZPosition |
double | 射线源 Z 轴位置(mm),范围由配置决定 |
DetectorZPosition |
double | 探测器 Z 轴位置(mm),范围由配置决定 |
DetectorSwingAngle |
double | 探测器摆动角度(度),范围 -45~45 |
StageRotationAngle |
double | 载物台旋转角度(度),范围 -360~360 |
FixtureRotationAngle |
double | 夹具旋转角度(度),范围 -90~90 |
IsJoystickEnabled |
bool | 摇杆使能开关(默认 false) |
SwapMouseButtons |
bool | 摇杆左右键功能切换(默认 false) |
SZDZLock |
bool | 射线源/探测器Z轴锁定移动(默认 false) |
HasSavedPositions |
bool | 是否有已保存的位置快照 |
交互方式 | Interaction Modes
-
手动输入位置
- 直接编辑 RadNumericUpDown 控件
- Enter 键确认并移动轴
- Escape 键取消修改,恢复原值
-
单轴摇杆(SourceZ/DetectorZ)
- 左键拖拽:正向 Jog
- 右键拖拽:反向 Jog
- 松开鼠标:停止 Jog
-
双轴摇杆(StageX/Y + Rotation)
- 默认模式(SwapMouseButtons=false):
- 左键拖拽:控制 StageX/Y(X轴=左右,Y轴=上下)
- 右键拖拽:控制 StageRotation(X轴=旋转)
- 切换模式(SwapMouseButtons=true):
- 左键拖拽:控制 StageRotation(X轴=旋转)
- 右键拖拽:控制 DetectorSwing(X轴=摆动,Y轴=摆动)
- 默认模式(SwapMouseButtons=false):
-
快捷按钮
- 使能开关:启用/禁用摇杆控制(图标切换)
- 摇杆模式:切换左右键功能(图标切换)
- SZDZ锁定:锁定 SourceZ/DetectorZ 同步移动(图标切换)
- 保存位置:保存当前所有轴位置到快照
- 恢复位置:恢复到上次保存的位置快照
3. 获取轴状态和位置 | Reading Axis Status and Position
通过 DI 注入 IMotionSystem:
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:
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();
}
}
5. 几何计算 | Geometry Calculation
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);
}
}
6. 事件订阅 | Event Subscription
通过 Prism IEventAggregator 被动接收状态变化:
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);
}
}
7. PLC 信号定义 | PLC Signal Definitions
信号名称硬编码在 MotionSignalNames.cs 中,信号地址定义在 PlcAddrDfn.xml(位于 XplorePlane/bin/Debug/net8.0-windows/win-x64/)。
7.1 写入信号(WriteCommon,DB31)
| 信号名 | 类型 | 地址 | 说明 |
|---|---|---|---|
MC_SourceZ_Target |
single | 10 | 射线源Z目标位置(mm) |
MC_SourceZ_Speed |
single | 14 | 射线源Z运动速度 |
MC_SourceZ_JogPos |
byte | 18 | 射线源Z正向Jog |
MC_SourceZ_JogNeg |
byte | 19 | 射线源Z反向Jog |
MC_SourceZ_Home |
byte | 20 | 射线源Z回零 |
MC_SourceZ_Stop |
byte | 21 | 射线源Z停止 |
MC_DetZ_Target |
single | 22 | 探测器Z目标位置(mm) |
MC_DetZ_Speed |
single | 26 | 探测器Z运动速度 |
MC_DetZ_JogPos |
byte | 30 | 探测器Z正向Jog |
MC_DetZ_JogNeg |
byte | 31 | 探测器Z反向Jog |
MC_DetZ_Home |
byte | 32 | 探测器Z回零 |
MC_DetZ_Stop |
byte | 33 | 探测器Z停止 |
MC_StageX_Target |
single | 34 | 载物台X目标位置(mm) |
MC_StageX_Speed |
single | 38 | 载物台X运动速度 |
MC_StageX_JogPos |
byte | 42 | 载物台X正向Jog |
MC_StageX_JogNeg |
byte | 43 | 载物台X反向Jog |
MC_StageX_Home |
byte | 44 | 载物台X回零 |
MC_StageX_Stop |
byte | 45 | 载物台X停止 |
MC_StageY_Target |
single | 46 | 载物台Y目标位置(mm) |
MC_StageY_Speed |
single | 50 | 载物台Y运动速度 |
MC_StageY_JogPos |
byte | 54 | 载物台Y正向Jog |
MC_StageY_JogNeg |
byte | 55 | 载物台Y反向Jog |
MC_StageY_Home |
byte | 56 | 载物台Y回零 |
MC_StageY_Stop |
byte | 57 | 载物台Y停止 |
MC_DetSwing_Target |
single | 58 | 探测器摆动目标角度(度) |
MC_DetSwing_Speed |
single | 62 | 探测器摆动运动速度 |
MC_DetSwing_JogPos |
byte | 66 | 探测器摆动正向Jog |
MC_DetSwing_JogNeg |
byte | 67 | 探测器摆动反向Jog |
MC_DetSwing_Home |
byte | 68 | 探测器摆动回零 |
MC_DetSwing_Stop |
byte | 69 | 探测器摆动停止 |
MC_StageRot_Target |
single | 70 | 载物台旋转目标角度(度) |
MC_StageRot_Speed |
single | 74 | 载物台旋转运动速度 |
MC_StageRot_JogPos |
byte | 78 | 载物台旋转正向Jog |
MC_StageRot_JogNeg |
byte | 79 | 载物台旋转反向Jog |
MC_StageRot_Home |
byte | 80 | 载物台旋转回零 |
MC_StageRot_Stop |
byte | 81 | 载物台旋转停止 |
MC_FixRot_Target |
single | 82 | 夹具旋转目标角度(度) |
MC_FixRot_Speed |
single | 86 | 夹具旋转运动速度 |
MC_FixRot_JogPos |
byte | 90 | 夹具旋转正向Jog |
MC_FixRot_JogNeg |
byte | 91 | 夹具旋转反向Jog |
MC_FixRot_Home |
byte | 92 | 夹具旋转回零 |
MC_FixRot_Stop |
byte | 93 | 夹具旋转停止 |
MC_Door_Open |
byte | 94 | 安全门开门 |
MC_Door_Close |
byte | 95 | 安全门关门 |
MC_Door_Stop |
byte | 96 | 安全门停止 |
MC_SourceDetZ_Linkage_Enable |
bool | 101 | 射线源与探测器Z轴联动使能 |
MC_VirtualJoystick_Enable |
bool | 111 | 虚拟摇杆使能 |
7.2 读取信号(ReadCommon,DB31)
| 信号名 | 类型 | 地址 | 说明 |
|---|---|---|---|
MC_SourceZ_Pos |
single | 100 | 射线源Z实际位置(mm) |
MC_DetZ_Pos |
single | 104 | 探测器Z实际位置(mm) |
MC_StageX_Pos |
single | 108 | 载物台X实际位置(mm) |
MC_StageY_Pos |
single | 112 | 载物台Y实际位置(mm) |
MC_DetSwing_Angle |
single | 116 | 探测器摆动实际角度(度) |
MC_StageRot_Angle |
single | 120 | 载物台旋转实际角度(度) |
MC_FixRot_Angle |
single | 124 | 夹具旋转实际角度(度) |
MC_Door_Status |
byte | 128 | 安全门状态(0:未知,1:开门中,2:已开,3:关门中,4:已关,5:已锁定,6:故障) |
MC_Door_Interlock |
byte | 130 | 安全门联锁信号(0:无联锁,10:联锁有效) |
MC_Joystick_Active |
bool | 110 | 实体摇杆输入激活 |
7.3 信号类型说明 | Signal Type Notes
- single:32位浮点数(4字节)
- byte:8位无符号整数(1字节)
- bool:布尔值(1字节,0=false, 非0=true)
8. 安全机制 | Safety Mechanisms
| 机制 | 说明 |
|---|---|
| 边界检查 | 目标位置超出 Min/Max 范围时拒绝移动 |
| 运动中防重入 | 轴处于 Moving 状态时拒绝新的移动命令 |
| 联锁检查 | 联锁信号有效时禁止开门 |
| 禁用轴检查 | Enabled=false 的旋转轴拒绝所有命令 |
| Homing 中拒绝 Jog | 回零过程中不允许 Jog 操作 |
| 多轴原子性检查 | MoveAllToTarget 任意轴越界则全部拒绝 |
| 轮询异常不中断 | 轮询中 PLC 异常被捕获,不影响下一轮 |
| UI 异常保护 | ViewModel 命令通过 SafeRun 包裹,PLC 异常弹窗而非崩溃 |
9. 轮询机制 | Polling Mechanism
MotionControlService 使用 System.Threading.Timer 以配置的 PollingInterval(默认 100ms)周期执行:
- 检查 PLC 连接状态(通过
IPlcService.IsConnected),未连接时跳过 - 缓存所有轴、门状态和联锁状态的旧值
- 调用
IMotionSystem.UpdateAllStatus()从 PLC 读取最新状态 - 重新计算几何参数,发布
GeometryUpdatedEvent - 检测状态变化,发布
AxisStatusChangedEvent、DoorStatusChangedEvent、DoorInterlockChangedEvent - 轴进入 Error/Alarm 时额外发布
MotionErrorEvent - 异常捕获并记录 Error 日志,不中断轮询
通过 StartPolling() / StopPolling() 控制轮询生命周期。
最后更新 | Last Updated: 2026-05-08