# Motion 运动框架使用说明

## 1. 文档范围

本文档基于当前代码实际实现整理，描述 [`MainShell/Motion`](MainShell/Motion) 目录下已经存在的运动控制能力、调用入口、约束行为与示例代码。

说明：
- 文档内容以当前源码实现为准。
- 若旧文档、注释或设计稿与代码不一致，以代码为准。
- 本文档仅描述已经实现的能力，不对未来规划功能做扩展解释。

适用对象：
- 业务流程开发人员
- 手动功能开发人员
- 运动控制维护人员
- 需要接入平台联动或视觉逼近对位功能的开发人员

## 2. 模块总览

当前 [`MainShell/Motion`](MainShell/Motion) 目录中的能力主要包括：

1. 单轴运动控制
2. 批量安全运动入口
3. 运动前安全检查与报警上报
4. 平台三轴联动封装
5. 速度参数下发管理
6. 逼近对位流程框架

主要文件如下：

- [`MotionController.cs`](MainShell/Motion/MotionController.cs)：单轴底层执行器
- [`MotionInfrastructure.cs`](MainShell/Motion/MotionInfrastructure.cs)：运动报警、预检查、日志、控制器注册表
- [`MotionMoveRequest.cs`](MainShell/Motion/MotionMoveRequest.cs)：运动请求模型
- [`MotionResult.cs`](MainShell/Motion/MotionResult.cs)：单轴结果与批量结果模型
- [`SafeAxisMotion.cs`](MainShell/Motion/SafeAxisMotion.cs)：业务层统一运动入口
- [`StagePlatformMotionService.cs`](MainShell/Motion/StagePlatformMotionService.cs)：平台三轴联动封装
- [`AxisSpeedManager.cs`](MainShell/Motion/AxisSpeedManager.cs)：轴速度曲线管理
- [`MotionSafetyStateProvider.cs`](MainShell/Motion/MotionSafetyStateProvider.cs)：运动安全状态提供器
- [`ApproachAlignment/ApproachAlignment.cs`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs)：逼近对位的数据结构和接口定义
- [`ApproachAlignment/ApproachAlignmentService.cs`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs)：逼近对位主流程
- [`ApproachAlignment/CenterRecognizer.cs`](MainShell/Motion/ApproachAlignment/CenterRecognizer.cs)：视觉中心识别实现
- [`ApproachAlignment/CoordinateTransformer.cs`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs)：坐标转换实现

## 3. 分层理解

### 3.1 单轴执行层

[`MotionController`](MainShell/Motion/MotionController.cs:10) 直接面向 `IAxis` / `IAxisFunc`，负责：

- 绝对运动 [`MoveAbsAsync()`](MainShell/Motion/MotionController.cs:35)
- 相对运动 [`MoveRelAsync()`](MainShell/Motion/MotionController.cs:60)
- Jog [`JogAsync()`](MainShell/Motion/MotionController.cs:86)
- 停止 [`StopAsync()`](MainShell/Motion/MotionController.cs:122)
- 回零 [`HomeAsync()`](MainShell/Motion/MotionController.cs:148)
- 运动状态校验、软限位检查、超时与取消
- 运动开始/结束事件通知

### 3.2 基础设施层

[`MotionInfrastructure.cs`](MainShell/Motion/MotionInfrastructure.cs) 中提供了几类基础设施：

- [`MotionAlarmReporter`](MainShell/Motion/MotionInfrastructure.cs:16)：统一报警上报
- [`MotionPrecheckService`](MainShell/Motion/MotionInfrastructure.cs:50)：运动前安全检查聚合入口
- [`MotionExecutionLogger`](MainShell/Motion/MotionInfrastructure.cs:112)：统一记录运动开始/结束日志
- [`MotionControllerRegistry`](MainShell/Motion/MotionInfrastructure.cs:149)：缓存并复用每根轴对应的 [`MotionController`](MainShell/Motion/MotionController.cs:10)

### 3.3 请求与结果模型层

[`MotionMoveRequest`](MainShell/Motion/MotionMoveRequest.cs:16) 与 [`MotionResult`](MainShell/Motion/MotionResult.cs:7) / [`MotionBatchResult`](MainShell/Motion/MotionResult.cs:80) 用于统一描述：

- 运动目标
- 超时时间
- 报警号
- 请求来源与标签
- 批次号与关联号
- 单轴结果
- 批量结果

### 3.4 业务统一入口层

[`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 是当前推荐的业务调用入口，负责：

- 通过轴名解析实际轴对象
- 复用 [`MotionController`](MainShell/Motion/MotionController.cs:10)
- 单轴绝对/相对运动
- 多轴绝对联动
- 单轴/多轴回零
- 调用 [`MotionPrecheckService.ValidateAsync()`](MainShell/Motion/MotionInfrastructure.cs:82) 做运动前安全校验
- 失败时联动取消并停止相关轴
- 失败后按请求配置上报报警
- 为结果补充 `CorrelationId` / `BatchId` / `FailureStage`

### 3.5 业务场景服务层

[`StagePlatformMotionService`](MainShell/Motion/StagePlatformMotionService.cs:11) 封装平台三轴动作：

- 等高移动 [`MoveFlatAsync()`](MainShell/Motion/StagePlatformMotionService.cs:29)
- 平面移动 [`MovePlaneAsync()`](MainShell/Motion/StagePlatformMotionService.cs:39)

### 3.6 视觉逼近对位层

[`ApproachAlignmentService`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:17) 封装迭代式逼近对位流程：

1. 识别中心
2. 将视觉中心转换为各轴目标位置
3. 驱动多轴移动
4. 再次识别中心
5. 重新计算目标位置与当前误差
6. 判断是否收敛

## 4. 当前代码中的真实能力

结合当前代码，以下能力已经明确存在：

- 单轴绝对移动
- 单轴相对移动
- 单轴 Jog
- 单轴停止
- 单轴回零
- 多轴绝对联动移动
- 多轴批量回零
- 运动前安全检查
- 失败报警上报
- 平台三轴等高/平面联动
- 基于视觉中心识别的迭代式逼近对位框架
- 轴速度配置与速度档位下发

以下内容需要特别注意：

- 批量移动仅支持绝对运动，不支持批量相对运动
- 逼近对位中的 [`CoordinateTransformer`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs:14) 仍是占位实现
- 文档中若提到通过 [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 直接调用逼近对位，不符合当前代码真实入口

## 5. MotionController 说明

[`MotionController`](MainShell/Motion/MotionController.cs:10) 是单轴动作的最终执行器。

### 5.1 关键属性

- [`AxisName`](MainShell/Motion/MotionController.cs:26)
- [`CurrentPos`](MainShell/Motion/MotionController.cs:27)
- [`IsBusy`](MainShell/Motion/MotionController.cs:28)
- [`IsAlarm`](MainShell/Motion/MotionController.cs:29)
- [`InPos`](MainShell/Motion/MotionController.cs:30)

### 5.2 关键事件

- [`MotionStarted`](MainShell/Motion/MotionController.cs:32)
- [`MotionFinished`](MainShell/Motion/MotionController.cs:33)

### 5.3 关键方法

- [`MoveAbsAsync()`](MainShell/Motion/MotionController.cs:35)
- [`MoveRelAsync()`](MainShell/Motion/MotionController.cs:60)
- [`JogAsync()`](MainShell/Motion/MotionController.cs:86)
- [`StopAsync()`](MainShell/Motion/MotionController.cs:122)
- [`HomeAsync()`](MainShell/Motion/MotionController.cs:148)

### 5.4 内部保障机制

[`MotionController`](MainShell/Motion/MotionController.cs:10) 内部具备以下保障：

- 使用 `SemaphoreSlim` 保证同一根轴同一时刻只执行一个动作，见 [`_motionLock`](MainShell/Motion/MotionController.cs:15)
- 每次运动前调用 [`EnsureMotionStateReady()`](MainShell/Motion/MotionController.cs:280) 检查轴状态
- 运动执行过程中调用 [`EnsureMotionStateHealthyDuringExecution()`](MainShell/Motion/MotionController.cs:304) 检查状态是否恶化
- 绝对与相对移动执行前都会调用 [`EnsureWithinSoftLimit()`](MainShell/Motion/MotionController.cs:328) 做软限位检查
- 取消时通过 [`TryStop()`](MainShell/Motion/MotionController.cs:368) 自动尝试停轴
- 超时时通过 [`ThrowIfTimeoutExceeded()`](MainShell/Motion/MotionController.cs:351) 自动尝试停轴并抛出超时异常
- 完成后统一生成 [`MotionResult`](MainShell/Motion/MotionResult.cs:7)

### 5.5 失败返回行为

[`ExecuteProtectedMotionAsync()`](MainShell/Motion/MotionController.cs:167) 会捕获执行异常，并在最终返回的 [`MotionResult`](MainShell/Motion/MotionResult.cs:7) 中体现成功、取消和异常信息，而不是始终直接向外抛出异常。

因此调用后建议统一执行：

```csharp
var result = await controller.MoveAbsAsync(100.0, 10000, cancellationToken);
result.EnsureSuccess();
```

## 6. MotionInfrastructure 说明

[`MotionInfrastructure.cs`](MainShell/Motion/MotionInfrastructure.cs) 提供了运动框架的关键支撑能力。

### 6.1 MotionAlarmReporter

[`MotionAlarmReporter`](MainShell/Motion/MotionInfrastructure.cs:16) 对 [`AlarmOperate`](MainShell/Alarm/AlarmOperate.cs) 做了一层封装：

- 传入 `null` 报警号时直接返回 `false`
- 传入有效报警号时调用 [`ReportAlarmAsync()`](MainShell/Motion/MotionInfrastructure.cs:25) 间接触发报警

### 6.2 MotionPrecheckService

[`MotionPrecheckService`](MainShell/Motion/MotionInfrastructure.cs:50) 在构造函数中聚合了以下安全检查：

- `MotionRequestDuplicateAxisCheck`
- `MotionBatchSameSourceCheck`
- `EmergencyStopReleasedCheck`
- `SafetyDoorClosedCheck`
- `AutoFlowModeCheck`
- `StageVacuumReadyCheck`
- `BondHeadSafePositionCheck`
- `StagePlatformPlaneMoveSafetyCheck`

调用 [`ValidateAsync()`](MainShell/Motion/MotionInfrastructure.cs:82) 时，会：

1. 逐条执行安全规则
2. 对失败规则记录系统错误日志
3. 使用规则上的 `AlarmId` 或传入的 `fallbackAlarmId` 上报报警
4. 最终抛出 `InvalidOperationException`

这意味着：
- 当前代码中已经存在统一运动前预检查机制
- [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 的各类入口会先走预检查，再执行实际运动

### 6.3 MotionExecutionLogger

[`MotionExecutionLogger`](MainShell/Motion/MotionInfrastructure.cs:112) 负责处理：

- [`HandleMotionStarted()`](MainShell/Motion/MotionInfrastructure.cs:114)
- [`HandleMotionFinished()`](MainShell/Motion/MotionInfrastructure.cs:126)

它会输出开始位置、目标位置、结束位置、耗时、是否取消、失败阶段等日志信息。

### 6.4 MotionControllerRegistry

[`MotionControllerRegistry`](MainShell/Motion/MotionInfrastructure.cs:149) 负责：

- 按轴名缓存控制器实例
- 对同名且同引用的轴复用已有控制器
- 在创建控制器时自动挂接 [`MotionExecutionLogger`](MainShell/Motion/MotionInfrastructure.cs:112) 事件

业务侧一般不需要自己维护 [`MotionController`](MainShell/Motion/MotionController.cs:10) 生命周期，直接通过 [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 使用即可。

## 7. MotionMoveRequest 说明

[`MotionMoveRequest`](MainShell/Motion/MotionMoveRequest.cs:16) 当前并不轻量，除基础字段外还已经支持业务元数据。

### 7.1 主要属性

- [`AxisName`](MainShell/Motion/MotionMoveRequest.cs:34)
- [`Axis`](MainShell/Motion/MotionMoveRequest.cs:35)
- [`TargetPosition`](MainShell/Motion/MotionMoveRequest.cs:36)
- [`TimeoutMilliseconds`](MainShell/Motion/MotionMoveRequest.cs:37)
- [`AlarmId`](MainShell/Motion/MotionMoveRequest.cs:38)
- [`RequestKind`](MainShell/Motion/MotionMoveRequest.cs:39)
- [`Source`](MainShell/Motion/MotionMoveRequest.cs:40)
- [`Tags`](MainShell/Motion/MotionMoveRequest.cs:41)
- [`CorrelationId`](MainShell/Motion/MotionMoveRequest.cs:42)
- [`BatchId`](MainShell/Motion/MotionMoveRequest.cs:43)
- [`PositionTolerance`](MainShell/Motion/MotionMoveRequest.cs:44)
- [`StopOnFailure`](MainShell/Motion/MotionMoveRequest.cs:45)

### 7.2 创建方式

绝对运动：
- [`ForAxisName()`](MainShell/Motion/MotionMoveRequest.cs:64)
- [`ForAxis()`](MainShell/Motion/MotionMoveRequest.cs:74)

相对运动：
- [`ForRelativeAxisName()`](MainShell/Motion/MotionMoveRequest.cs:84)
- [`ForRelativeAxis()`](MainShell/Motion/MotionMoveRequest.cs:94)

补充元数据：
- [`WithMetadata()`](MainShell/Motion/MotionMoveRequest.cs:47)

### 7.3 行为说明

- `TargetPosition` 对绝对运动表示目标位置，对相对运动表示位移量
- `Tags` 会通过 [`NormalizeTags()`](MainShell/Motion/MotionMoveRequest.cs:104) 去空、去重
- `WithMetadata()` 会保留原请求主体，仅覆盖指定元数据
- 批量绝对移动时会由 [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 自动补充 `CorrelationId` 与 `BatchId`

## 8. MotionResult 与 MotionBatchResult 说明

[`MotionResult`](MainShell/Motion/MotionResult.cs:7) 包含：

- [`AxisName`](MainShell/Motion/MotionResult.cs:30)
- [`Operation`](MainShell/Motion/MotionResult.cs:31)
- [`TargetPosition`](MainShell/Motion/MotionResult.cs:32)
- [`StartPosition`](MainShell/Motion/MotionResult.cs:33)
- [`EndPosition`](MainShell/Motion/MotionResult.cs:34)
- [`Succeeded`](MainShell/Motion/MotionResult.cs:35)
- [`Cancelled`](MainShell/Motion/MotionResult.cs:36)
- [`Exception`](MainShell/Motion/MotionResult.cs:37)
- [`Message`](MainShell/Motion/MotionResult.cs:38)
- [`AlarmReported`](MainShell/Motion/MotionResult.cs:39)
- [`StartedAtUtc`](MainShell/Motion/MotionResult.cs:40)
- [`FinishedAtUtc`](MainShell/Motion/MotionResult.cs:41)
- [`CorrelationId`](MainShell/Motion/MotionResult.cs:42)
- [`BatchId`](MainShell/Motion/MotionResult.cs:43)
- [`FailureStage`](MainShell/Motion/MotionResult.cs:44)
- [`TimedOut`](MainShell/Motion/MotionResult.cs:45)
- [`StoppedByCoordinator`](MainShell/Motion/MotionResult.cs:46)
- [`DurationMilliseconds`](MainShell/Motion/MotionResult.cs:47)

常用方法：
- [`WithAlarmReported()`](MainShell/Motion/MotionResult.cs:49)
- [`WithFailureDetails()`](MainShell/Motion/MotionResult.cs:59)
- [`EnsureSuccess()`](MainShell/Motion/MotionResult.cs:64)

[`MotionBatchResult`](MainShell/Motion/MotionResult.cs:80) 包含：

- [`Results`](MainShell/Motion/MotionResult.cs:87)
- [`Succeeded`](MainShell/Motion/MotionResult.cs:88)
- [`Cancelled`](MainShell/Motion/MotionResult.cs:89)
- [`EnsureSuccess()`](MainShell/Motion/MotionResult.cs:91)

如果批量结果中只有一根轴失败，[`EnsureSuccess()`](MainShell/Motion/MotionResult.cs:91) 会直接抛该轴异常；如果多根轴失败，会抛 `AggregateException`。

## 9. SafeAxisMotion 说明

[`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 是当前业务最推荐使用的统一入口。

### 9.1 主要能力

- 单轴绝对移动
- 单轴相对移动
- 多轴绝对联动移动
- 单轴回零
- 多轴批量回零
- 轴名解析
- 控制器缓存与日志挂接
- 运动前安全预检查
- 失败联动停轴
- 失败报警
- 批次与关联信息补充

### 9.2 主要异步方法

单轴移动：
- [`MoveAbsAsync()`](MainShell/Motion/SafeAxisMotion.cs:156)
- [`MoveAbsAsync()`](MainShell/Motion/SafeAxisMotion.cs:163)
- [`MoveRelAsync()`](MainShell/Motion/SafeAxisMotion.cs:170)
- [`MoveRelAsync()`](MainShell/Motion/SafeAxisMotion.cs:177)

批量绝对移动：
- [`SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57)

回零：
- [`HomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:226)
- [`HomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:234)
- [`SafeHomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:194)
- [`SafeHomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:204)

同步封装：
- [`SafeMove()`](MainShell/Motion/SafeAxisMotion.cs:42)
- [`MoveAbs()`](MainShell/Motion/SafeAxisMotion.cs:76)
- [`MoveRel()`](MainShell/Motion/SafeAxisMotion.cs:96)
- [`Home()`](MainShell/Motion/SafeAxisMotion.cs:136)
- [`SafeHome()`](MainShell/Motion/SafeAxisMotion.cs:116)

### 9.3 真实执行流程

以 [`MoveAbsAsync()`](MainShell/Motion/SafeAxisMotion.cs:156) 为例，执行顺序为：

1. 构造 [`MotionMoveRequest`](MainShell/Motion/MotionMoveRequest.cs:16)
2. 调用 [`EnsureRequestBatchMetadata()`](MainShell/Motion/SafeAxisMotion.cs:385) 补齐关联元数据
3. 解析轴对象
4. 调用 [`MotionPrecheckService.ValidateAsync()`](MainShell/Motion/MotionInfrastructure.cs:82) 执行预检查
5. 调用内部 [`MoveAbsInternalAsync()`](MainShell/Motion/SafeAxisMotion.cs:244)
6. 通过 [`MotionControllerRegistry.GetController()`](MainShell/Motion/MotionInfrastructure.cs:172) 获取控制器并执行运动
7. 按请求补充结果元数据与失败阶段
8. 必要时上报报警

### 9.4 批量运动行为说明

[`SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57) 当前执行的是多轴绝对联动：

- 请求数组为空时，返回空的 [`MotionBatchResult`](MainShell/Motion/MotionResult.cs:80)
- 会先对整批请求生成 `batchId`
- 会统一做安全检查
- 会并发启动各轴绝对运动任务
- 任一轴失败时，若请求 `StopOnFailure = true`，会调用 [`CancelAndStopRelatedAxes()`](MainShell/Motion/SafeAxisMotion.cs:438)
- [`CancelAndStopRelatedAxes()`](MainShell/Motion/SafeAxisMotion.cs:438) 会取消协调令牌，并逐轴调用 [`StopAsync()`](MainShell/Motion/MotionController.cs:122)

### 9.5 超时与失败细节

- [`NormalizeTimeout()`](MainShell/Motion/SafeAxisMotion.cs:423) 会把小于等于 0 的超时值恢复为默认值
- 失败结果会通过 [`WithFailureDetails()`](MainShell/Motion/MotionResult.cs:59) 写入 `FailureStage`、`TimedOut`、`StoppedByCoordinator`、`CorrelationId`、`BatchId`
- 当 `alarmId` 为空时，失败不会触发报警
- 当 `stopOnFailure = false` 时，失败不会触发联动停轴

## 10. MotionSafetyStateProvider 说明

[`MotionSafetyStateProvider`](MainShell/Motion/MotionSafetyStateProvider.cs:13) 为预检查规则提供外部状态。

### 10.1 主要能力

- 读取当前机器模式 [`CurrentMachineMode`](MainShell/Motion/MotionSafetyStateProvider.cs:26)
- 判断急停是否释放 [`IsEmergencyStopReleased()`](MainShell/Motion/MotionSafetyStateProvider.cs:28)
- 判断安全门是否关闭 [`AreSafetyDoorsClosed()`](MainShell/Motion/MotionSafetyStateProvider.cs:39)
- 判断流程是否在运行 [`IsFlowRunning()`](MainShell/Motion/MotionSafetyStateProvider.cs:57)
- 获取当前流程名 [`CurrentFlowName`](MainShell/Motion/MotionSafetyStateProvider.cs:63)
- 判断平台真空是否就绪 [`AreStageVacuumsReady()`](MainShell/Motion/MotionSafetyStateProvider.cs:65)
- 获取吸嘴/打头相关不安全信号 [`GetUnsafeBondHeadSignals()`](MainShell/Motion/MotionSafetyStateProvider.cs:78)
- 判断来源是否属于自动流程 [`IsAutoWorkflowSource()`](MainShell/Motion/MotionSafetyStateProvider.cs:93)

### 10.2 行为说明

- 当 `DeviceIo` 离线时，多数安全状态会直接返回不可用或不安全结果
- 安全门支持 `SecurityDoorShield` 旁路逻辑
- 自动流程判断不是只看精确名称，也支持来源字符串包含 `Auto`

## 11. StagePlatformMotionService 说明

[`StagePlatformMotionService`](MainShell/Motion/StagePlatformMotionService.cs:11) 封装平台三轴 [`Axis_Stage_Z7`](MainShell/Motion/StagePlatformMotionService.cs:45)、[`Axis_Stage_Z8`](MainShell/Motion/StagePlatformMotionService.cs:46)、[`Axis_Stage_Z9`](MainShell/Motion/StagePlatformMotionService.cs:47) 的联动动作。

### 11.1 主要方法

- [`MoveFlatAsync()`](MainShell/Motion/StagePlatformMotionService.cs:29)
- [`MovePlaneAsync()`](MainShell/Motion/StagePlatformMotionService.cs:39)
- [`MoveFlat()`](MainShell/Motion/StagePlatformMotionService.cs:24)
- [`MovePlane()`](MainShell/Motion/StagePlatformMotionService.cs:34)

### 11.2 内部实现方式

- [`MoveFlatAsync()`](MainShell/Motion/StagePlatformMotionService.cs:29) 实际调用 [`MovePlaneAsync()`](MainShell/Motion/StagePlatformMotionService.cs:39)
- [`MovePlaneAsync()`](MainShell/Motion/StagePlatformMotionService.cs:39) 会先调用 [`EnsurePlaneTargetsOrdered()`](MainShell/Motion/StagePlatformMotionService.cs:53)
- 允许的三轴目标最大高差为 `5.0`
- 然后将三根轴封装为带 `Source` 和 `Tags` 的 [`MotionMoveRequest`](MainShell/Motion/MotionMoveRequest.cs:16)
- 再统一调用 [`SafeAxisMotion.SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57)
- 方法内部已经执行 [`EnsureSuccess()`](MainShell/Motion/MotionResult.cs:91)

### 11.3 使用建议

只要是平台三轴动作，建议统一走 [`StagePlatformMotionService`](MainShell/Motion/StagePlatformMotionService.cs:11)，不要在业务代码里手工拼三轴请求。

## 12. AxisSpeedManager 说明

[`AxisSpeedManager`](MainShell/Motion/AxisSpeedManager.cs:38) 用于从系统参数生成速度曲线并下发到轴参数。

### 12.1 相关类型

- [`AxisSpeedProfile`](MainShell/Motion/AxisSpeedManager.cs:13)：`Low` / `Medium` / `High` / `Custom`
- [`AxisMotionProfile`](MainShell/Motion/AxisSpeedManager.cs:21)：封装 `Velocity` / `Acceleration` / `Deceleration` / `Jerk`

### 12.2 主要方法

- [`SetAxisSpeed(string axisName, double speed)`](MainShell/Motion/AxisSpeedManager.cs:49)
- [`SetAxisSpeed(string axisName, AxisSpeedProfile profile)`](MainShell/Motion/AxisSpeedManager.cs:56)
- [`ApplySpeedSettings()`](MainShell/Motion/AxisSpeedManager.cs:63)

### 12.3 真实行为说明

[`SetAxisSpeed(string axisName, double speed)`](MainShell/Motion/AxisSpeedManager.cs:49) 的行为不是固定倍数计算，而是：

1. 从速度参数表中找到该轴对应的 `SpeedTypeItem`
2. 以配置基准速度 `speedItem.Speed` 为基数
3. 用 `speed / speedItem.Speed` 计算缩放比例
4. 按比例缩放 `Acc` / `Dec` / `Jerk`
5. 最终通过 [`ApplyAxisMotionProfile()`](MainShell/Motion/AxisSpeedManager.cs:85) 下发到轴参数

[`SetAxisSpeed(string axisName, AxisSpeedProfile profile)`](MainShell/Motion/AxisSpeedManager.cs:56) 的行为是：

- 读取当前轴配置中的 `LowPercent` / `MedianPercent` / `HighPercent`
- 调用 [`CreateProfile()`](MainShell/Motion/AxisSpeedManager.cs:151) 生成缩放后的运动参数
- 调用 [`ApplyAxisMotionProfile()`](MainShell/Motion/AxisSpeedManager.cs:85) 下发

[`ApplySpeedSettings()`](MainShell/Motion/AxisSpeedManager.cs:63) 的行为是：

- 从 `GlobalParam.SpeedParaSysSetting.SpeedTypeItemCollection.SpeedTypeItemList` 读取所有速度配置
- 根据当前 `CurrentSpeedType` 通过 [`MapCurrentSpeedTypeText()`](MainShell/Motion/AxisSpeedManager.cs:187) 映射到 `Low` / `Medium` / `High`
- 对每根轴生成对应档位的速度参数并下发

### 12.4 校验与异常

[`AxisSpeedManager`](MainShell/Motion/AxisSpeedManager.cs:38) 会校验：

- 轴名不能为空
- 轴必须存在
- 轴必须有对应速度配置
- 速度、加速度、减速度、Jerk 必须为正且有限值，见 [`ValidatePositiveFinite()`](MainShell/Motion/AxisSpeedManager.cs:216)
- 百分比必须在 `(0, 100]` 范围内，见 [`ValidatePercent()`](MainShell/Motion/AxisSpeedManager.cs:224)

## 13. 逼近对位模块说明

### 13.1 组成文件

- [`ApproachAlignment/ApproachAlignment.cs`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs)
- [`ApproachAlignment/ApproachAlignmentService.cs`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs)
- [`ApproachAlignment/CenterRecognizer.cs`](MainShell/Motion/ApproachAlignment/CenterRecognizer.cs)
- [`ApproachAlignment/CoordinateTransformer.cs`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs)

### 13.2 核心类型

[`ApproachAlignment.cs`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:15) 中定义了：

- [`ApproachAlignmentAxis`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:15)
- [`ApproachAlignmentRequest`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:52)
- [`ApproachAlignmentResult`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:109)
- [`CoordinateTransformResult`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:151)
- [`ICenterRecognizer`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:186)
- [`ICoordinateTransformer`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:204)

### 13.3 ApproachAlignmentRequest 说明

[`ApproachAlignmentRequest`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:52) 构造函数要求至少传入一根轴，并提供以下默认值：

- `Camera = CameraType.UpCamera`
- `MaxIterations = 5`
- `MoveTimeoutMilliseconds = 30000`
- `RecognitionTimeoutMilliseconds = 10000`

### 13.4 ApproachAlignmentService 流程

[`ApproachAlignmentService.ApproachAlignmentAsync()`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:42) 当前流程如下：

1. 最多执行 `MaxIterations` 次迭代
2. 首次迭代调用 [`RecognizeCenterAsync()`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:182) 识别中心
3. 调用 [`ICoordinateTransformer.TransformAsync()`](MainShell/Motion/ApproachAlignment/ApproachAlignment.cs:213) 计算各轴目标位置
4. 将目标位置转成 [`MotionMoveRequest.ForAxisName()`](MainShell/Motion/MotionMoveRequest.cs:64)
5. 调用 [`SafeAxisMotion.SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57) 执行多轴移动
6. 再次识别中心
7. 再次转换，得到理论目标位置
8. 读取各轴当前位置并计算误差
9. 若全部轴误差都小于等于容差，则成功结束
10. 否则进入下一轮迭代

### 13.5 CenterRecognizer 现状

[`CenterRecognizer`](MainShell/Motion/ApproachAlignment/CenterRecognizer.cs:14) 已接入视觉算法服务：

- 依赖 `IVisionAlgorithmService`
- 使用 `VisionProcessRequest`
- `AlgorithmType = VisionAlgorithmType.FindCenter`
- `CaptureOptions = CameraCaptureOptions.CreateStream(timeoutMilliseconds)`
- 从结果中读取 `OffsetX` / `OffsetY`
- 识别失败或超时返回 `null`

### 13.6 CoordinateTransformer 现状

[`CoordinateTransformer`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs:14) 当前仍是占位实现。

实际行为：
- 若轴列表为空，则返回 `Succeeded = false`
- 否则延时约 10ms 后，为每根轴写入 `0.0` 作为目标位置
- 返回 `Succeeded = true`

这表示：
- 逼近对位的流程框架已经存在
- 但真实标定坐标变换尚未完成
- 若直接用于生产逻辑，会把各轴目标位置算成固定 `0.0`

### 13.7 当前真实入口

当前应直接注入并调用 [`ApproachAlignmentService.ApproachAlignmentAsync()`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:42)，而不是通过 [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 调用逼近对位。

## 14. 推荐调用方式

### 14.1 普通业务运动

优先调用 [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14)：

- 单轴绝对移动用 [`MoveAbsAsync()`](MainShell/Motion/SafeAxisMotion.cs:156)
- 单轴相对移动用 [`MoveRelAsync()`](MainShell/Motion/SafeAxisMotion.cs:170)
- 多轴绝对联动用 [`SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57)
- 单轴回零用 [`HomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:226)
- 多轴回零用 [`SafeHomeAsync()`](MainShell/Motion/SafeAxisMotion.cs:194)

### 14.2 平台三轴业务

统一调用 [`StagePlatformMotionService`](MainShell/Motion/StagePlatformMotionService.cs:11)：

- 等高移动用 [`MoveFlatAsync()`](MainShell/Motion/StagePlatformMotionService.cs:29)
- 平面移动用 [`MovePlaneAsync()`](MainShell/Motion/StagePlatformMotionService.cs:39)

### 14.3 单轴调试或事件监听

可以直接使用 [`MotionController`](MainShell/Motion/MotionController.cs:10)：

- 适合底层调试
- 适合观察 [`MotionStarted`](MainShell/Motion/MotionController.cs:32) / [`MotionFinished`](MainShell/Motion/MotionController.cs:33)
- 不建议作为一般业务入口

### 14.4 逼近对位业务

当前应直接调用 [`ApproachAlignmentService`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:17)，前提是先替换 [`CoordinateTransformer`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs:14) 为真实可用的标定变换实现。

## 15. 示例代码

### 15.1 单轴绝对移动

```csharp
public async Task MoveAxisXAsync(CancellationToken cancellationToken)
{
    var result = await _safeAxisMotion.MoveAbsAsync(
        "Axis_X",
        100.0,
        timeoutMilliseconds: 10000,
        cancellationToken: cancellationToken,
        alarmId: 5001);

    result.EnsureSuccess();
}
```

### 15.2 单轴相对移动

```csharp
public async Task NudgeAxisYAsync(CancellationToken cancellationToken)
{
    var result = await _safeAxisMotion.MoveRelAsync(
        "Axis_Y",
        -2.0,
        timeoutMilliseconds: 8000,
        cancellationToken: cancellationToken,
        alarmId: 5002);

    result.EnsureSuccess();
}
```

### 15.3 带元数据的多轴绝对联动

```csharp
public async Task MoveXYAsync(double x, double y, CancellationToken cancellationToken)
{
    var batch = await _safeAxisMotion.SafeMoveAsync(
        cancellationToken,
        MotionMoveRequest.ForAxisName(
            "Axis_X",
            x,
            timeoutMilliseconds: 12000,
            alarmId: 6001,
            source: "Manual.Align",
            tags: new[] { "Manual", "Align" },
            correlationId: "ALIGN-001",
            batchId: "BATCH-ALIGN-001",
            positionTolerance: 0.005,
            stopOnFailure: true),
        MotionMoveRequest.ForAxisName(
            "Axis_Y",
            y,
            timeoutMilliseconds: 12000,
            alarmId: 6002,
            source: "Manual.Align",
            tags: new[] { "Manual", "Align" },
            correlationId: "ALIGN-002",
            batchId: "BATCH-ALIGN-001",
            positionTolerance: 0.005,
            stopOnFailure: true));

    batch.EnsureSuccess();
}
```

### 15.4 单轴回零

```csharp
public async Task HomeAxisZAsync(CancellationToken cancellationToken)
{
    var result = await _safeAxisMotion.HomeAsync(
        "Axis_Z",
        timeoutMilliseconds: 30000,
        cancellationToken: cancellationToken,
        alarmId: 7001);

    result.EnsureSuccess();
}
```

### 15.5 多轴批量回零

```csharp
public async Task HomeAllAxesAsync(CancellationToken cancellationToken)
{
    var batch = await _safeAxisMotion.SafeHomeAsync(
        cancellationToken,
        "Axis_X",
        "Axis_Y",
        "Axis_Z");

    batch.EnsureSuccess();
}
```

### 15.6 平台三轴等高移动

```csharp
public async Task MoveStageFlatAsync(CancellationToken cancellationToken)
{
    var batch = await _stagePlatformMotionService.MoveFlatAsync(
        20.0,
        timeoutMilliseconds: 20000,
        cancellationToken: cancellationToken);

    batch.EnsureSuccess();
}
```

### 15.7 平台三轴平面移动

```csharp
public async Task MoveStagePlaneAsync(CancellationToken cancellationToken)
{
    var batch = await _stagePlatformMotionService.MovePlaneAsync(
        z7Target: 20.000,
        z8Target: 20.150,
        z9Target: 19.950,
        timeoutMilliseconds: 25000,
        cancellationToken: cancellationToken);

    batch.EnsureSuccess();
}
```

### 15.8 直接使用 MotionController 监听事件

```csharp
var controller = new MotionController(axis);

controller.MotionStarted += (sender, e) =>
{
    Console.WriteLine(
        $"Start Axis={e.AxisName}, Op={e.Operation}, Start={e.StartPosition}, Target={e.TargetPosition}");
};

controller.MotionFinished += (sender, e) =>
{
    Console.WriteLine(
        $"Finish Axis={e.AxisName}, Success={e.Succeeded}, Cancelled={e.Cancelled}, End={e.EndPosition}, Error={e.Exception}");
};

var result = await controller.MoveAbsAsync(50.0, 10000, cancellationToken);
result.EnsureSuccess();
```

### 15.9 使用 AxisSpeedManager 切换速度档位

```csharp
public void ApplyMediumSpeedToStageX()
{
    _axisSpeedManager.SetAxisSpeed("Axis_Stage_X", AxisSpeedProfile.Medium);
}
```

### 15.10 使用 AxisSpeedManager 设置自定义速度

```csharp
public void ApplyCustomSpeedToStageX()
{
    _axisSpeedManager.SetAxisSpeed("Axis_Stage_X", 150.0);
}
```

### 15.11 逼近对位调用示例

```csharp
public async Task<ApproachAlignmentResult> RunApproachAlignmentAsync(CancellationToken cancellationToken)
{
    var request = new ApproachAlignmentRequest(
        new[]
        {
            new ApproachAlignmentAxis("Axis_X", 0.01) { Description = "X axis" },
            new ApproachAlignmentAxis("Axis_Y", 0.01) { Description = "Y axis" }
        },
        camera: CameraType.UpCamera)
    {
        MaxIterations = 5,
        MoveTimeoutMilliseconds = 30000,
        RecognitionTimeoutMilliseconds = 10000
    };

    var result = await _approachAlignmentService.ApproachAlignmentAsync(
        request,
        cancellationToken);

    if (!result.Succeeded)
    {
        throw result.Exception ?? new InvalidOperationException(result.Message);
    }

    return result;
}
```

### 15.12 自定义真实坐标转换器示例

```csharp
public class CalibratedCoordinateTransformer : ICoordinateTransformer
{
    public Task<CoordinateTransformResult> TransformAsync(
        double centerX,
        double centerY,
        IEnumerable<ApproachAlignmentAxis> axes)
    {
        var result = new CoordinateTransformResult();

        foreach (var axis in axes)
        {
            var target = CalculateTarget(axis.AxisName, centerX, centerY);
            result.AxisPositions[axis.AxisName] = target;
        }

        result.Succeeded = true;
        return Task.FromResult(result);
    }

    private double CalculateTarget(string axisName, double centerX, double centerY)
    {
        throw new NotImplementedException();
    }
}
```

## 16. 注意事项

- [`SafeAxisMotion.SafeMoveAsync()`](MainShell/Motion/SafeAxisMotion.cs:57) 当前仅用于批量绝对移动
- 单轴相对运动支持 [`MoveRelAsync()`](MainShell/Motion/SafeAxisMotion.cs:170)，但没有批量相对移动入口
- 运动前安全检查已经存在，不应再按旧文档理解为“未实现”
- [`MotionMoveRequest`](MainShell/Motion/MotionMoveRequest.cs:16) 已支持 `Source`、`Tags`、`CorrelationId`、`BatchId`、`PositionTolerance`、`StopOnFailure`
- [`AxisSpeedManager`](MainShell/Motion/AxisSpeedManager.cs:38) 的自定义速度是基于配置比例缩放，不是固定倍数算法
- [`CoordinateTransformer`](MainShell/Motion/ApproachAlignment/CoordinateTransformer.cs:14) 当前仍是占位实现，接入前必须替换
- [`ApproachAlignmentService`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:17) 是逼近对位真实入口

## 17. 总结

当前 [`MainShell/Motion`](MainShell/Motion) 模块已经具备一套较完整的运动控制框架：

- [`MotionController`](MainShell/Motion/MotionController.cs:10) 负责可靠单轴执行
- [`MotionPrecheckService`](MainShell/Motion/MotionInfrastructure.cs:50) 负责运动前安全校验
- [`SafeAxisMotion`](MainShell/Motion/SafeAxisMotion.cs:14) 负责业务层统一调用、批量联动与失败联停
- [`StagePlatformMotionService`](MainShell/Motion/StagePlatformMotionService.cs:11) 负责平台三轴动作封装
- [`AxisSpeedManager`](MainShell/Motion/AxisSpeedManager.cs:38) 负责轴速度参数下发与档位管理
- [`ApproachAlignmentService`](MainShell/Motion/ApproachAlignment/ApproachAlignmentService.cs:17) 提供逼近对位流程框架

其中基础运动控制、预检查、安全联停和平台联动已经可以作为业务入口直接使用；逼近对位部分仍需补齐真实坐标变换，才能形成可用于生产的完整闭环。
