10 KiB
10 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. 获取轴状态和位置 | 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();
}
}
4. 几何计算 | 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);
}
}
5. 事件订阅 | 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);
}
}
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)周期执行:
- 检查 PLC 连接状态(通过
IPlcService.IsConnected),未连接时跳过 - 缓存所有轴、门状态和联锁状态的旧值
- 调用
IMotionSystem.UpdateAllStatus()从 PLC 读取最新状态 - 重新计算几何参数,发布
GeometryUpdatedEvent - 检测状态变化,发布
AxisStatusChangedEvent、DoorStatusChangedEvent、DoorInterlockChangedEvent - 轴进入 Error/Alarm 时额外发布
MotionErrorEvent - 异常捕获并记录 Error 日志,不中断轮询
通过 StartPolling() / StopPolling() 控制轮询生命周期。
最后更新 | Last Updated: 2026-04-14