Files
FlyShotHost/docs/superpowers/specs/2026-06-22-rust-icsp-native-design.md
yunxiao.zhu bcbf8017ad 📝 docs(planning): 统一 Rust 规划库命名
将 Rust 原生规划库设计口径统一为 flyshot_planning。

同步更新 Windows 和 Linux 产物名以及复制命令示例。
2026-06-27 15:10:06 +08:00

272 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rust ICSP 原生规划核心设计
## 1. 目标
在不改变现有上层业务接口的前提下,使用 Rust 重新实现当前 C# 版本的 ICSP 和 clamped-zero 三次样条算法,并通过稳定的 C ABI 供 .NET 8 调用。
第一阶段必须实现:
- Rust `cdylib` 中的 clamped-zero 三次样条系数求解。
- 样条位置求值和每段一至三阶导数绝对值峰值解析计算。
- ICSP 初始分段时长、迭代缩放、最优结果保存和最终收敛判定。
- C# P/Invoke 适配层。
- Rust 与现有 C# 算法的自动对拍测试。
- Windows x64 和 Linux x64 原生库加载边界。
第一阶段不实现:
- `SelfAdaptIcspPlanner` 的补中点逻辑。
- 轨迹稠密采样、J519 发送采样和触发绑定。
- 正运动学、MoveJoint 和 MovePose。
- FANUC 协议、Socket 和运行时状态机。
- 删除或替换现有 C# ICSP 实现。
## 2. 总体架构
新增原生算法模块和 C# 适配模块:
```text
ControllerClientTrajectoryOrchestrator
|
ICSP 规划接口
/ \
C# ICspPlanner RustIcspPlanner
|
Flyshot.Native.Interop
|
C ABI
|
flyshot_planning
Rust cdylib
```
现有 `ICspPlanner` 保持不变,继续作为行为基准和回退实现。Rust 规划器返回的数据仍由 C# 组装成现有 `PlannedTrajectory`,因此领域层、兼容层和运行时不感知原生对象。
## 3. Rust 模块边界
原生模块建议位于:
```text
native/
flyshot-native-planning/
Cargo.toml
src/
lib.rs
error.rs
ffi.rs
icsp.rs
spline.rs
```
各文件职责:
- `spline.rs`:三对角方程求解、样条系数、位置求值、解析导数峰值。
- `icsp.rs`:纯 Rust ICSP 输入、迭代规划和结果模型。
- `ffi.rs`:C ABI 数据结构、指针校验、结果分配与释放。
- `error.rs`:原生错误分类和 UTF-8 错误信息。
- `lib.rs`:只导出稳定 ABI,不导出内部算法符号。
Rust 内部算法不依赖 .NET 类型、JSON、日志框架、网络或文件系统。
## 4. C ABI 设计
ABI 使用固定宽度整数、C 布局结构和连续 `double` 数组。路点输入采用行优先扁平布局:
```text
[waypoint0_joint0, waypoint0_joint1, ...,
waypoint1_joint0, waypoint1_joint1, ...]
```
主要入口:
```c
int32_t flyshot_icsp_plan(
const double* waypoints,
int32_t waypoint_count,
int32_t dof,
const double* velocity_limits,
const double* acceleration_limits,
const double* jerk_limits,
const FlyshotIcspOptions* options,
FlyshotIcspResult** result);
void flyshot_icsp_result_free(FlyshotIcspResult* result);
const char* flyshot_last_error_message(void);
```
`FlyshotIcspOptions` 包含:
- `threshold`
- `max_iterations`
- `enforce_final_scale`
- `final_scale_tolerance`
`FlyshotIcspResult` 包含:
- `waypoint_times` 及长度
- `segment_durations` 及长度
- `segment_scales` 及长度
- `iterations`
- `threshold`
调用成功返回 `0`。输入非法、数值计算失败、未收敛和内部异常使用不同错误码。错误文本由线程局部存储保存,只保证在当前线程下一次原生调用前有效。
所有结果内存都由 Rust 分配,并且只能通过 `flyshot_icsp_result_free` 释放。C# 不直接释放 Rust 分配的数组。
所有可预期失败必须在 FFI 内转换为错误码,FFI 路径不得依赖 panic 表达业务错误。Debug 和测试构建使用 `catch_unwind` 检测遗漏的 panic;Release 使用 `panic=abort`,此时 `catch_unwind` 不能恢复 panic,因此实现必须通过边界校验避免索引越界、空指针解引用和显式 `unwrap`。任何构建模式都不得让 Rust unwind 穿过 C ABI。
## 5. C# 适配层
新增一个只负责原生互操作的 C# 项目,建议命名为:
```text
src/Flyshot.Native.Planning/
```
包含:
- `NativeIcspMethods`:使用 `LibraryImport` 声明 C ABI。
- `NativeIcspSafeHandle`:封装 Rust 结果指针和释放函数。
- `RustIcspPlanner`:把 `TrajectoryRequest` 转换为连续数组,再把结果转换为 `PlannedTrajectory`。
- `NativePlanningLibraryResolver`:按当前操作系统和体系结构解析原生库。
- `NativePlanningException`:携带原生错误码和错误文本。
原生库逻辑名称固定为 `flyshot_planning`:
- Windows:`flyshot_planning.dll`
- Linux:`libflyshot_planning.so`
C# 适配层负责:
- 检查输入自由度和数组长度。
- 固定数组,避免调用期间被 GC 移动。
- 将 Rust 返回数据复制到托管数组。
- 无论成功或失败都正确释放结果句柄。
- 将原生错误转换为包含诊断信息的 C# 异常。
`RustIcspPlanner` 的构造参数和 `Plan` 行为应与 `ICspPlanner` 对齐,但第一阶段不自动替换现有依赖注入注册。
## 6. 数值一致性
Rust 实现必须逐项复刻当前 C# 行为:
- 初始段时长使用相邻路点的关节空间欧氏距离。
- 使用 clamped-zero 边界条件求三次样条。
- 每轮解析计算各段速度、加速度和 jerk 峰值。
- 分段倍率分别使用一次方、平方根和立方根。
- 收敛指标为 `sum(abs(scale - 1))`。
- 始终保存历史最小收敛指标对应的结果。
- 迭代范围与当前 C# 保持一致,即包含第 `max_iterations` 轮。
- 最终按 `global_scale > 1 + tolerance` 判定是否失败。
第一阶段采用以下对拍容差:
- 路点时间:绝对误差不超过 `1e-9`。
- 段时长:绝对误差不超过 `1e-9`。
- 段缩放:绝对误差不超过 `1e-9`。
- 最终 threshold:绝对误差不超过 `1e-9`。
- 迭代次数:必须完全一致。
如果真实样本因浮点指令或编译器优化无法满足 `1e-9`,必须先定位首个分歧计算步骤,再基于证据调整容差,不直接放宽整个测试。
## 7. 测试策略
遵循 TDD,先增加失败测试,再实现 Rust 和 C# 适配。
测试分四层:
1. Rust 单元测试
- 三次样条边界值。
- 一至三阶导数。
- 解析峰值。
- ICSP 收敛与不收敛。
- 非法指针之外的纯算法输入校验。
2. Rust FFI 测试
- 正常调用和结果释放。
- 空指针、错误维度、非有限数。
- 错误码与错误文本。
- 重复释放由 C# `SafeHandle` 防止,不把未定义行为包装成可接受用法。
3. C# 互操作测试
- 原生库加载。
- 托管数组到 C ABI 的转换。
- 结果复制和释放。
- 原生错误到 C# 异常的转换。
4. C#/Rust 对拍测试
- 仓库中的稳定规划样本。
- 当前 `PlanningCompatibilityTests` 使用的基础输入。
- 多自由度、多路点和极限参数。
- `maxIterations=0` 的未收敛路径。
- 至少一个父目录真实样本固化后的 golden case。
对拍测试必须同时调用现有 `ICspPlanner` 和 `RustIcspPlanner`,不能把预期值只写成 Rust 自身输出。
## 8. 构建与发布
本地构建脚本负责:
- 执行 `cargo build --release`。
- 根据目标平台复制原生库到 C# 测试和发布输出目录。
- Windows x64 构建 Rust `x86_64-pc-windows-msvc`。
- Linux x64 构建 Rust `x86_64-unknown-linux-gnu`。
第一阶段不要求在 Windows 主机上完成 Linux 交叉编译;Linux 原生库可以在 Linux 构建环境生成。仓库必须提供对应命令和 CI/离线构建说明。
发布时原生库与托管程序集放在同一部署目录。现有 Obfuscar 继续处理 `Flyshot.*.dll` 托管程序集,不处理 Rust 原生库。
Rust Release 配置启用:
```toml
[profile.release]
lto = true
codegen-units = 1
strip = "symbols"
panic = "abort"
```
## 9. 回退与上线边界
第一阶段只提供显式的 Rust 规划器,不改变生产默认算法。完成全部对拍和跨平台验证后,再单独设计配置切换:
```text
Planning:Backend = Managed | Rust
```
原生库缺失、架构不匹配或加载失败时,显式选择 Rust 后应返回清晰错误,不静默回退。静默回退会掩盖部署缺陷,也无法确认现场实际使用的是哪套算法。
保留 C# 实现至少经过一个完整现场验证周期。后续是否删除托管算法,需要独立评审,不属于本设计范围。
## 10. 验收标准
- Rust 单元测试全部通过。
- C# 原生互操作测试全部通过。
- 选定样本的 C#/Rust 对拍全部通过。
- `dotnet test` 和解决方案构建通过。
- Windows x64 实际加载 `.dll` 并完成规划。
- Linux x64 实际加载 `.so` 并完成规划。
- 缺少原生库时能得到明确且可诊断的错误。
- 现有 C# `ICspPlanner` 和上层运行链路行为不变。
## 11. 第一阶段落地状态(2026-06-23)
当前已落地:
- `native/flyshot-native-planning` Rust `cdylib`,包含 clamped-zero 样条、
一至三阶导数解析峰值、ICSP 迭代和稳定 C ABI。
- `src/Flyshot.Native.Planning` 托管互操作项目,包含显式平台解析、
`LibraryImport`、`SafeHandle`、错误码转换和结果复制。
- Rust 样条、ICSP、FFI 测试,以及 C#/Rust `1e-9` 自动对拍测试。
- `build/Copy-NativePlanningArtifacts.ps1`,用于初始化 Windows MSVC 环境、
构建原生库并复制到测试或发布目录。
- Windows x64 已实际生成并加载 `flyshot_planning.dll` 完成规划。
当前上线边界保持不变:
- 默认生产路径继续使用 C# `ICspPlanner`。
- Rust 规划器只允许显式选择,库缺失、路径不一致或架构不支持时返回明确错误。
- Linux x64 的源码、库名和构建脚本边界已落地,但 `.so` 的实际加载验证必须在
Linux x64 构建环境执行后才能标记完成。