Files

10 KiB
Raw Permalink Blame History

运动控制模块集成指南 | 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 加载配置(轴范围、几何原点、轮询周期)
  • 注册 IMotionSystemIMotionControlServiceGeometryCalculator 为单例
  • 注册多语言资源到 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)周期执行:

  1. 检查 PLC 连接状态(通过 IPlcService.IsConnected),未连接时跳过
  2. 缓存所有轴、门状态和联锁状态的旧值
  3. 调用 IMotionSystem.UpdateAllStatus() 从 PLC 读取最新状态
  4. 重新计算几何参数,发布 GeometryUpdatedEvent
  5. 检测状态变化,发布 AxisStatusChangedEventDoorStatusChangedEventDoorInterlockChangedEvent
  6. 轴进入 Error/Alarm 时额外发布 MotionErrorEvent
  7. 异常捕获并记录 Error 日志,不中断轮询

通过 StartPolling() / StopPolling() 控制轮询生命周期。


最后更新 | Last Updated: 2026-04-14