💥 feat(planning)!: 拆分 C# ICSP 并默认 Rust
* 新增可选 Flyshot.CSharp.Planning 项目 * 将 icsp 映射到 Rust 后端并拒绝旧 icsp_rust * 补充后端注册、文档、调试页和回归测试 BREAKING CHANGE: method=icsp 现在默认调用 Rust 后端;C# ICSP 改为 method=icsp-csharp,旧 method=icsp_rust 不再作为正式入口。
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
@@ -27,8 +26,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyshot.ControllerClientCom
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyshot.Runtime.Fanuc", "src\Flyshot.Runtime.Fanuc\Flyshot.Runtime.Fanuc.csproj", "{B705FA6C-19CA-44A8-882C-6CE26A5379C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyshot.Native.Planning", "src\Flyshot.Native.Planning\Flyshot.Native.Planning.csproj", "{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyshot.Native.Planning", "src\Flyshot.Native.Planning\Flyshot.Native.Planning.csproj", "{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyshot.CSharp.Planning", "src\Flyshot.CSharp.Planning\Flyshot.CSharp.Planning.csproj", "{4717558C-0A09-4D4D-BB1A-C0FD9241070E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -159,18 +160,30 @@ Global
|
||||
{B705FA6C-19CA-44A8-882C-6CE26A5379C9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B705FA6C-19CA-44A8-882C-6CE26A5379C9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B705FA6C-19CA-44A8-882C-6CE26A5379C9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -186,6 +199,7 @@ Global
|
||||
{E4DDC34C-9AB6-4050-A927-3DF69804708A} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
{5B45CC23-3551-4D0F-B3CC-22659C2A8BA3} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
{B705FA6C-19CA-44A8-882C-6CE26A5379C9} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
{928CF396-C0E1-488E-BCC1-CF0279CF2DD0} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
{4717558C-0A09-4D4D-BB1A-C0FD9241070E} = {64EABE09-B1E0-4476-A213-32C93E46E7C3}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
12
README.md
12
README.md
@@ -16,8 +16,8 @@
|
||||
- 当前仓库不再恢复旧 `50001/TCP+JSON` 监听入口;旧 `ControllerClient` 逆向资料只作为接口语义参考,不作为运行时目标。
|
||||
- 宿主只保留 ASP.NET Core HTTP 控制器层,以及其后端 `Flyshot.ControllerClientCompat` 兼容服务。
|
||||
- `ExecuteTrajectory` 与 `ExecuteFlyShotTraj` 已经接入 `Planning + Triggering + Runtime` 链路;Web 状态页已通过 `/status` 和 `/api/status/snapshot` 暴露当前兼容层与运行时状态。
|
||||
- `Flyshot.Core.Planning` 的 ICSP / self-adapt-icsp 轨迹已经完成旧系统导出轨迹对齐;`doubles` 仍未实现。
|
||||
- `Flyshot.Native.Planning` 已提供显式 Rust ICSP 规划入口,通过 C ABI 调用 `flyshot_planning`;普通轨迹和飞拍轨迹可使用准确参数 `method="icsp_rust"` 选择该后端,默认 `icsp` 仍使用 C# `ICspPlanner`。
|
||||
- `Flyshot.Core.Planning` 保留规划公共模型、样条、采样、导出和运动学能力;C# ICSP / self-adapt-icsp 已拆分到可选项目 `Flyshot.CSharp.Planning`。
|
||||
- `Flyshot.Native.Planning` 已提供默认 Rust ICSP 规划入口,通过 C ABI 调用 `flyshot_planning`;普通轨迹和飞拍轨迹的默认 `method="icsp"` 使用 Rust 后端,`method="icsp-csharp"` 和 `method="self-adapt-icsp"` 只有显式注册 C# 后端时可用。
|
||||
- `Flyshot.Runtime.Fanuc` 已固化 `10010 / 10012 / 60015` 基础协议帧编解码。`10010` 状态通道以 `j519 协议.pcap` 和 `Rvbust/uttc-20260428/20260428.pcap` 真机抓包确认为 90B 固定帧。
|
||||
- 2026-04-28 UTTC 抓包确认:UDP 60015 命令 `target[0..5]` 为关节角度制 `deg`,`JointDetialTraj.txt` 为弧度制 `rad`,`speed_ratio=0.7` 体现为 UDP 下发时间轴约 `1.427730x` 拉伸;2026-04-30 实体机确认 `speed_ratio` 不影响生成的 `JointTraj.txt` 规划时长,当前实际生成约 `7.4s` 轨迹。
|
||||
- 2026-04-30 本机 `50001/TCP+JSON` 抓包确认:`ExecuteFlyShotTraj(save_traj=true,use_cache=false)` 请求只显式携带规划方法、保存、缓存和等待参数,不携带 `JointLimits / acc_limit / jerk_limit / velocity / acceleration / jerk`。因此旧系统不可见的有效规划限制不再继续假设来自公开链路,新系统按 replacement-only 内部参数限制规划加速度。
|
||||
@@ -65,7 +65,7 @@
|
||||
- [x] 打通最小宿主与 `/healthz`
|
||||
- [x] 建立领域模型与模块边界
|
||||
- [x] 落地配置兼容与机器人模型解析
|
||||
- [x] 落地 ICSP / self-adapt-icsp 轨迹规划与飞拍触发时间轴
|
||||
- [x] 落地 Rust ICSP、可选 C# ICSP / self-adapt-icsp 轨迹规划与飞拍触发时间轴
|
||||
- [x] 完成 ICSP 轨迹导出结果与旧系统对齐
|
||||
- [x] 增加 Rust `cdylib` ICSP 核心、C# P/Invoke 适配和 C#/Rust 自动对拍
|
||||
- [x] 将 `ExecuteTrajectory` / `ExecuteFlyShotTraj` 接入 FANUC 运行时链路
|
||||
@@ -81,9 +81,11 @@
|
||||
- [x] 完成真机优先与离线飞拍规划改造:`RealReady / OfflineReady / ModelLoadFailed` 状态、固定轨迹资产、完整离线规划响应和 Debug 页面
|
||||
- [x] 删除生产仿真切换 HTTP 入口及旧 `/init_mpc_robt`,统一使用 `/init_mpc_robot`
|
||||
|
||||
### Rust ICSP 原生库构建
|
||||
### 规划后端与 Rust ICSP 原生库构建
|
||||
|
||||
Rust 规划器通过普通轨迹或飞拍轨迹的准确参数 `method="icsp_rust"` 显式选择,不替换默认 `icsp` 使用的 C# `ICspPlanner`,也不会在原生库缺失时静默回退。
|
||||
普通轨迹或飞拍轨迹的默认参数 `method="icsp"` 会调用 Rust ICSP 后端。旧 `method="icsp_rust"` 已不再作为正式入口,调用时会提示改用 `method="icsp"`;Rust 原生库缺失时会明确报错,不会静默回退 C#。
|
||||
|
||||
C# `ICspPlanner` 与 `SelfAdaptIcspPlanner` 已拆分到 `Flyshot.CSharp.Planning.dll`。默认服务项目不引用该 DLL;只有在部署方显式引用并调用 `AddCSharpPlanningBackends()` 后,`method="icsp-csharp"` 与 `method="self-adapt-icsp"` 才可用。
|
||||
|
||||
Windows x64 需要安装:
|
||||
|
||||
|
||||
@@ -130,11 +130,11 @@
|
||||
以下内容是当前新实现的状态,不反推为旧系统事实:
|
||||
|
||||
- `Flyshot.ControllerClientCompat` 继续作为 HTTP 控制器后端兼容服务,不启动 `50001/TCP+JSON` 监听。
|
||||
- `ExecuteTrajectory` 会先通过 `ICspPlanner` 规划普通轨迹,再把 `TrajectoryResult` 和最终关节位置交给 `IControllerRuntime`。
|
||||
- `ExecuteFlyShotTraj` 会从上传轨迹目录取出轨迹,通过 `SelfAdaptIcspPlanner` 规划并用 `ShotTimelineBuilder` 生成 `ShotEvent` / `TrajectoryDoEvent`。
|
||||
- `ExecuteTrajectory` 会先通过默认 Rust `method="icsp"` 后端规划普通轨迹,再把 `TrajectoryResult` 和最终关节位置交给 `IControllerRuntime`。
|
||||
- `ExecuteFlyShotTraj` 会从上传轨迹目录取出轨迹,通过已注册的规划后端生成轨迹,并用 `ShotTimelineBuilder` 生成 `ShotEvent` / `TrajectoryDoEvent`。
|
||||
- HTTP 控制器已经按公开文档和抓包补齐 `ExecuteTrajectory(method, save_traj)` 与 `ExecuteFlyShotTraj(move_to_start, method, save_traj, use_cache, wait)` 参数,并继续兼容旧的裸 waypoint 数组和只传 `name` 的请求体。
|
||||
- 规划阶段会继续消费旧配置中的 `acc_limit / jerk_limit`。如果现场需要复现旧服务端不可见的保守约束,replacement 设计上使用内部 `planning_acceleration_scale` 限制规划加速度;该字段不属于旧 `ControllerClient` 公开 API,也不会通过 `50001` JSON 下发。
|
||||
- `method="icsp"` 与 `method="self-adapt-icsp"` 已接入当前规划器;`method="doubles"` 会被识别但返回显式未实现,不会静默降级成 ICSP。
|
||||
- `method="icsp"` 已接入默认 Rust 后端;`method="icsp-csharp"` 与 `method="self-adapt-icsp"` 只有显式注册 `Flyshot.CSharp.Planning` 后端时可用;`method="doubles"` 会被识别但返回显式未实现,不会静默降级成 ICSP。
|
||||
- `Flyshot.Runtime.Fanuc.Protocol` 已经固化 `10010` 状态帧、`10012` 命令帧和 `60015` J519 数据包的基础编解码,并使用逆向抓包样本覆盖最小测试;`10010` 当前现场确认固定 90B。
|
||||
- `Flyshot.Runtime.Fanuc` 已具备基础 Socket 客户端、程序启停、速度倍率/TCP/IO 参数命令和 J519 状态包驱动发送链路;稠密轨迹下发已按 `speed_ratio` 推进轨迹时间,并在收到机器人 UDP status 后按该 status sequence 回发命令。真实 R30iB 全流程现场联调仍需执行。
|
||||
- 2026-04-28 `UTTC_MS11` 抓包确认 J519 命令目标为 `deg`、导出 `JointDetialTraj.txt` 为 `rad`,`speed_ratio=0.5/0.7/1.0` 分别形成 `1851/1322/926` 个主运行 J519 包;实际执行不发送 464 行导出点,而是在 8ms 实发周期上按 `t_traj = t_send * speed_ratio` 采样,包数为 `floor(duration / (0.008 * speed_ratio)) + 1`。
|
||||
|
||||
@@ -428,7 +428,7 @@ function Install-ObfuscatedAssemblies
|
||||
} |
|
||||
Remove-Item -Force
|
||||
|
||||
# Rust ICSP 原生库必须随框架依赖包一起交付,否则 method=icsp_rust 会在运行时加载失败。
|
||||
# Rust ICSP 原生库必须随框架依赖包一起交付,否则默认 method=icsp 会在运行时加载失败。
|
||||
$nativePlanningLibraryPath = Join-Path $PublishedDirectory 'flyshot_planning.dll'
|
||||
if (-not (Test-Path -LiteralPath $nativePlanningLibraryPath -PathType Leaf))
|
||||
{
|
||||
|
||||
44
src/Flyshot.CSharp.Planning/CSharpIcspPlanningBackend.cs
Normal file
44
src/Flyshot.CSharp.Planning/CSharpIcspPlanningBackend.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.CSharp.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 将拆分后的 C# ICSP 规划器暴露为可选规划后端。
|
||||
/// </summary>
|
||||
public sealed class CSharpIcspPlanningBackend : IPlanningBackend
|
||||
{
|
||||
private readonly ICspPlanner _planner;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 C# ICSP 后端。
|
||||
/// </summary>
|
||||
/// <param name="logger">C# ICSP 规划器日志记录器;允许为空。</param>
|
||||
public CSharpIcspPlanningBackend(ILogger<ICspPlanner>? logger = null)
|
||||
{
|
||||
_planner = new ICspPlanner(logger: logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取外部调用 C# ICSP 后端时使用的准确方法名。
|
||||
/// </summary>
|
||||
public string MethodName => "icsp-csharp";
|
||||
|
||||
/// <summary>
|
||||
/// 获取 C# ICSP 后端写入结果对象的规划方法。
|
||||
/// </summary>
|
||||
public PlanningMethod ResultMethod => PlanningMethod.IcspCSharp;
|
||||
|
||||
/// <summary>
|
||||
/// 执行 C# ICSP 规划,并将结果标记为 C# 后端。
|
||||
/// </summary>
|
||||
/// <param name="request">包含机器人约束和示教路点的规划请求。</param>
|
||||
/// <param name="context">规划上下文;普通 C# ICSP 当前不使用该参数。</param>
|
||||
/// <returns>由 C# ICSP 规划器生成的轨迹结果。</returns>
|
||||
public PlannedTrajectory Plan(TrajectoryRequest request, PlanningBackendContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return _planner.Plan(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Flyshot.Core.Planning;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Flyshot.CSharp.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 提供可选 C# 规划算法后端的依赖注入注册入口。
|
||||
/// </summary>
|
||||
public static class CSharpPlanningServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 显式注册 C# ICSP 与 self-adapt-icsp 后端。
|
||||
/// </summary>
|
||||
/// <param name="services">当前宿主服务集合。</param>
|
||||
/// <returns>同一服务集合,便于链式调用。</returns>
|
||||
public static IServiceCollection AddCSharpPlanningBackends(this IServiceCollection services)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
services.AddSingleton<IPlanningBackend, CSharpIcspPlanningBackend>();
|
||||
services.AddSingleton<IPlanningBackend, SelfAdaptIcspPlanningBackend>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
13
src/Flyshot.CSharp.Planning/Flyshot.CSharp.Planning.csproj
Normal file
13
src/Flyshot.CSharp.Planning/Flyshot.CSharp.Planning.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flyshot.Core.Domain\Flyshot.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\Flyshot.Core.Planning\Flyshot.Core.Planning.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
# ICSP 算法说明(`ICspPlanner`)
|
||||
|
||||
本文档用于解释 `Flyshot.Core.Planning.ICspPlanner` 当前实现的 **ICSP 规划算法**在本仓库中的真实含义与计算逻辑,便于与逆向结论对照、以及指导后续改造(例如“按约束生成中间点位”)。
|
||||
本文档用于解释 `Flyshot.CSharp.Planning.ICspPlanner` 当前实现的 **C# ICSP 规划算法**在本仓库中的真实含义与计算逻辑,便于与逆向结论对照、以及指导后续改造(例如“按约束生成中间点位”)。
|
||||
|
||||
> 适用范围:本文描述的是当前 C# 实现的 **“CubicSpline + 逐段时间缩放迭代(retiming)”** 版本。
|
||||
> 重要澄清:`ICspPlanner` 的主要输出是 **时间轴**(每个示教点的时间戳),而不是直接输出固定周期的稠密点序列;稠密点在后续采样层生成。
|
||||
@@ -186,9 +186,8 @@ globalScale > 1 + \text{finalScaleTolerance}
|
||||
|
||||
## 7. 关联实现位置(便于跳转)
|
||||
|
||||
- 算法入口:`src/Flyshot.Core.Planning/ICspPlanner.cs`
|
||||
- 自适应补点:`src/Flyshot.Core.Planning/SelfAdaptIcspPlanner.cs`
|
||||
- 算法入口:`src/Flyshot.CSharp.Planning/ICspPlanner.cs`
|
||||
- 自适应补点:`src/Flyshot.CSharp.Planning/SelfAdaptIcspPlanner.cs`
|
||||
- 三次样条实现(clamped-zero + 解析导峰值):`src/Flyshot.Core.Planning/CubicSplineInterpolator.cs`
|
||||
- 规划层稠密采样:`src/Flyshot.Core.Planning/Sampling/TrajectorySampler.cs`
|
||||
- J519 实发重采样:`src/Flyshot.Core.Planning/Sampling/J519SendTrajectorySampler.cs`
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.Core.Planning;
|
||||
namespace Flyshot.CSharp.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 基于 CubicSpline + 逐段时间缩放迭代的 ICSP 规划器。
|
||||
@@ -196,7 +197,7 @@ public sealed class ICspPlanner
|
||||
waypointTimes: bestWaypointTimes,
|
||||
segmentDurations: bestDurations,
|
||||
segmentScales: bestScales,
|
||||
method: PlanningMethod.Icsp,
|
||||
method: PlanningMethod.IcspCSharp,
|
||||
iterations: bestIterations,
|
||||
threshold: bestThreshold);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.Core.Planning;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.CSharp.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 在 ICSP 外层包裹补中点策略,实现 self-adapt-icsp 行为。
|
||||
44
src/Flyshot.CSharp.Planning/SelfAdaptIcspPlanningBackend.cs
Normal file
44
src/Flyshot.CSharp.Planning/SelfAdaptIcspPlanningBackend.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.CSharp.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 将 C# self-adapt-icsp 规划器暴露为可选规划后端。
|
||||
/// </summary>
|
||||
public sealed class SelfAdaptIcspPlanningBackend : IPlanningBackend
|
||||
{
|
||||
private readonly SelfAdaptIcspPlanner _planner;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 C# self-adapt-icsp 后端。
|
||||
/// </summary>
|
||||
/// <param name="logger">self-adapt-icsp 规划器日志记录器;允许为空。</param>
|
||||
public SelfAdaptIcspPlanningBackend(ILogger<SelfAdaptIcspPlanner>? logger = null)
|
||||
{
|
||||
_planner = new SelfAdaptIcspPlanner(logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取外部调用 self-adapt-icsp 后端时使用的准确方法名。
|
||||
/// </summary>
|
||||
public string MethodName => "self-adapt-icsp";
|
||||
|
||||
/// <summary>
|
||||
/// 获取 self-adapt-icsp 后端写入结果对象的规划方法。
|
||||
/// </summary>
|
||||
public PlanningMethod ResultMethod => PlanningMethod.SelfAdaptIcsp;
|
||||
|
||||
/// <summary>
|
||||
/// 执行 C# 自适应 ICSP 规划,按上下文使用配置中的补点次数。
|
||||
/// </summary>
|
||||
/// <param name="request">包含机器人约束、示教路点和触发元数据的规划请求。</param>
|
||||
/// <param name="context">包含 self-adapt-icsp 最大补点次数的规划上下文。</param>
|
||||
/// <returns>由 self-adapt-icsp 规划器生成的轨迹结果。</returns>
|
||||
public PlannedTrajectory Plan(TrajectoryRequest request, PlanningBackendContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return _planner.Plan(request, context.AdaptIcspTryNum);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.Core.Planning;
|
||||
using Flyshot.Native.Planning;
|
||||
using Flyshot.Runtime.Common;
|
||||
using Flyshot.Runtime.Fanuc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -31,6 +33,7 @@ public static class ControllerClientCompatServiceCollectionExtensions
|
||||
services.AddSingleton<RobotModelLoader>();
|
||||
services.AddSingleton<RobotConfigLoader>();
|
||||
services.AddSingleton<ControllerClientCompatRobotCatalog>();
|
||||
services.AddSingleton<IPlanningBackend, RustIcspPlanningBackend>();
|
||||
services.AddSingleton<ControllerClientTrajectoryOrchestrator>();
|
||||
services.AddSingleton<FlyshotTrajectoryArtifactWriter>();
|
||||
services.AddSingleton<JsonFlyshotTrajectoryStore>();
|
||||
|
||||
@@ -29,9 +29,7 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
/// </summary>
|
||||
private const double SmoothStartStopBlend = 0.60;
|
||||
|
||||
private readonly ICspPlanner _icspPlanner;
|
||||
private readonly RustIcspPlanner _rustIcspPlanner;
|
||||
private readonly SelfAdaptIcspPlanner _selfAdaptIcspPlanner;
|
||||
private readonly IReadOnlyDictionary<string, IPlanningBackend> _planningBackends;
|
||||
private readonly ShotTimelineBuilder _shotTimelineBuilder = new(new WaypointTimestampResolver());
|
||||
private readonly Dictionary<string, PlannedExecutionBundle> _flyshotCache = new(StringComparer.Ordinal);
|
||||
private readonly ILogger<ControllerClientTrajectoryOrchestrator>? _logger;
|
||||
@@ -39,16 +37,16 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
/// <summary>
|
||||
/// 初始化轨迹编排器。
|
||||
/// </summary>
|
||||
/// <param name="planningBackends">显式注册的规划后端;为空时仅启用默认 Rust icsp 后端。</param>
|
||||
/// <param name="logger">日志记录器;允许 null。</param>
|
||||
/// <param name="loggerFactory">日志工厂;允许 null。</param>
|
||||
public ControllerClientTrajectoryOrchestrator(
|
||||
IEnumerable<IPlanningBackend>? planningBackends = null,
|
||||
ILogger<ControllerClientTrajectoryOrchestrator>? logger = null,
|
||||
ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
_logger = logger;
|
||||
_icspPlanner = new(logger: loggerFactory?.CreateLogger<ICspPlanner>());
|
||||
_rustIcspPlanner = new();
|
||||
_selfAdaptIcspPlanner = new(logger: loggerFactory?.CreateLogger<SelfAdaptIcspPlanner>());
|
||||
_planningBackends = BuildPlanningBackendMap(planningBackends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,14 +82,15 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
offsetValues: Enumerable.Repeat(0, waypoints.Count),
|
||||
addressGroups: Enumerable.Range(0, waypoints.Count).Select(static _ => Array.Empty<int>()));
|
||||
|
||||
var method = ParseOrdinaryMethod(options.Method);
|
||||
var methodName = ParseOrdinaryMethod(options.Method);
|
||||
var backend = ResolvePlanningBackend(methodName);
|
||||
var request = new TrajectoryRequest(
|
||||
robot: planningRobot,
|
||||
program: program,
|
||||
method: method,
|
||||
method: backend.ResultMethod,
|
||||
saveTrajectoryArtifacts: options.SaveTrajectory);
|
||||
|
||||
var plannedTrajectory = PlanByMethod(request, method);
|
||||
var plannedTrajectory = PlanByBackend(request, backend);
|
||||
var executionTrajectory = plannedTrajectory;
|
||||
var denseJointTrajectory = CreateLimitCompliantDenseTrajectory(ref executionTrajectory, shapeTrajectoryEdges: false);
|
||||
var shotTimeline = new ShotTimeline(Array.Empty<ShotEvent>(), Array.Empty<TrajectoryDoEvent>());
|
||||
@@ -155,7 +154,8 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
offsetValues: uploaded.OffsetValues,
|
||||
addressGroups: uploaded.AddressGroups);
|
||||
|
||||
var method = ParseFlyshotMethod(options.Method);
|
||||
var methodName = ParseFlyshotMethod(options.Method);
|
||||
var backend = ResolvePlanningBackend(methodName);
|
||||
var cacheKey = CreateFlyshotCacheKey(planningRobot, uploaded, options, settings, effectivePlanningSpeedScale);
|
||||
if (options.UseCache && _flyshotCache.TryGetValue(cacheKey, out var cachedBundle))
|
||||
{
|
||||
@@ -168,12 +168,12 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
var request = new TrajectoryRequest(
|
||||
robot: planningRobot,
|
||||
program: program,
|
||||
method: method,
|
||||
method: backend.ResultMethod,
|
||||
moveToStart: options.MoveToStart,
|
||||
saveTrajectoryArtifacts: options.SaveTrajectory,
|
||||
useCache: options.UseCache);
|
||||
|
||||
var plannedTrajectory = PlanByMethod(request, method, settings);
|
||||
var plannedTrajectory = PlanByBackend(request, backend, settings);
|
||||
var smoothedExecutionTrajectory = ApplyExecutionTiming(plannedTrajectory, settings);
|
||||
var denseJointTrajectory = CreateLimitCompliantDenseTrajectory(ref smoothedExecutionTrajectory, shapeTrajectoryEdges: false);
|
||||
var shotTimeline = _shotTimelineBuilder.Build(
|
||||
@@ -206,14 +206,15 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
/// </summary>
|
||||
/// <param name="method">旧 SDK 传入的方法名。</param>
|
||||
/// <returns>领域层规划方法。</returns>
|
||||
private static PlanningMethod ParseOrdinaryMethod(string method)
|
||||
private static string ParseOrdinaryMethod(string method)
|
||||
{
|
||||
var normalized = NormalizeMethod(method);
|
||||
return normalized switch
|
||||
{
|
||||
"icsp" => PlanningMethod.Icsp,
|
||||
"icsp_rust" => PlanningMethod.IcspRust,
|
||||
"doubles" => PlanningMethod.Doubles,
|
||||
"icsp" => normalized,
|
||||
"icsp-csharp" => normalized,
|
||||
"icsp_rust" => throw new ArgumentException("method=\"icsp_rust\" 已改名为 method=\"icsp\"。", nameof(method)),
|
||||
"doubles" => normalized,
|
||||
_ => throw new ArgumentException($"Unsupported ExecuteTrajectory method: {method}", nameof(method))
|
||||
};
|
||||
}
|
||||
@@ -223,35 +224,80 @@ public sealed class ControllerClientTrajectoryOrchestrator
|
||||
/// </summary>
|
||||
/// <param name="method">旧 SDK 传入的方法名。</param>
|
||||
/// <returns>领域层规划方法。</returns>
|
||||
private static PlanningMethod ParseFlyshotMethod(string method)
|
||||
private static string ParseFlyshotMethod(string method)
|
||||
{
|
||||
var normalized = NormalizeMethod(method);
|
||||
return normalized switch
|
||||
{
|
||||
"icsp" => PlanningMethod.Icsp,
|
||||
"icsp_rust" => PlanningMethod.IcspRust,
|
||||
"self-adapt-icsp" => PlanningMethod.SelfAdaptIcsp,
|
||||
"doubles" => PlanningMethod.Doubles,
|
||||
"icsp" => normalized,
|
||||
"icsp-csharp" => normalized,
|
||||
"icsp_rust" => throw new ArgumentException("method=\"icsp_rust\" 已改名为 method=\"icsp\"。", nameof(method)),
|
||||
"self-adapt-icsp" => normalized,
|
||||
"doubles" => normalized,
|
||||
_ => throw new ArgumentException($"Unsupported ExecuteFlyShotTraj method: {method}", nameof(method))
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按领域枚举分派到当前已经落地的规划器。
|
||||
/// 使用显式注册的规划后端执行规划。
|
||||
/// </summary>
|
||||
/// <param name="request">规划请求。</param>
|
||||
/// <param name="method">规划方法。</param>
|
||||
/// <param name="backend">已解析的规划后端。</param>
|
||||
/// <param name="settings">当前机器人兼容配置;为空时使用默认补点次数。</param>
|
||||
/// <returns>规划轨迹。</returns>
|
||||
private PlannedTrajectory PlanByMethod(TrajectoryRequest request, PlanningMethod method, CompatibilityRobotSettings? settings = null)
|
||||
private static PlannedTrajectory PlanByBackend(
|
||||
TrajectoryRequest request,
|
||||
IPlanningBackend backend,
|
||||
CompatibilityRobotSettings? settings = null)
|
||||
{
|
||||
return method switch
|
||||
var context = new PlanningBackendContext(settings?.AdaptIcspTryNum ?? 5);
|
||||
return backend.Plan(request, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从已注册后端中解析 method 对应的规划实现。
|
||||
/// </summary>
|
||||
/// <param name="methodName">已经归一化的外部 method 名称。</param>
|
||||
/// <returns>可执行该 method 的规划后端。</returns>
|
||||
private IPlanningBackend ResolvePlanningBackend(string methodName)
|
||||
{
|
||||
if (methodName == "doubles")
|
||||
{
|
||||
PlanningMethod.Icsp => _icspPlanner.Plan(request),
|
||||
PlanningMethod.IcspRust => _rustIcspPlanner.Plan(request),
|
||||
PlanningMethod.SelfAdaptIcsp => _selfAdaptIcspPlanner.Plan(request, settings?.AdaptIcspTryNum ?? 5),
|
||||
PlanningMethod.Doubles => throw new NotSupportedException("doubles 轨迹规划尚未落地。"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(method), method, "未知轨迹规划方法。")
|
||||
};
|
||||
throw new NotSupportedException("doubles 轨迹规划尚未落地。");
|
||||
}
|
||||
|
||||
if (_planningBackends.TryGetValue(methodName, out var backend))
|
||||
{
|
||||
return backend;
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"轨迹规划方法 {methodName} 未注册对应后端。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造规划后端查找表,未显式注册时只启用默认 Rust icsp 后端。
|
||||
/// </summary>
|
||||
/// <param name="planningBackends">调用方显式注册的规划后端集合。</param>
|
||||
/// <returns>按 method 名称索引的后端表。</returns>
|
||||
private static IReadOnlyDictionary<string, IPlanningBackend> BuildPlanningBackendMap(IEnumerable<IPlanningBackend>? planningBackends)
|
||||
{
|
||||
var backends = planningBackends?.ToArray() ?? [];
|
||||
if (backends.Length == 0)
|
||||
{
|
||||
backends = [new RustIcspPlanningBackend()];
|
||||
}
|
||||
|
||||
var map = new Dictionary<string, IPlanningBackend>(StringComparer.Ordinal);
|
||||
foreach (var backend in backends)
|
||||
{
|
||||
var methodName = NormalizeMethod(backend.MethodName);
|
||||
if (!map.TryAdd(methodName, backend))
|
||||
{
|
||||
throw new InvalidOperationException($"规划后端 method={methodName} 被重复注册。");
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,7 +9,7 @@ public sealed class FlyshotExecutionOptions
|
||||
/// 初始化飞拍轨迹执行参数。
|
||||
/// </summary>
|
||||
/// <param name="moveToStart">执行前是否自动移动到轨迹起点。</param>
|
||||
/// <param name="method">轨迹生成方法,支持 `icsp`、`icsp_rust`、`doubles` 或 `self-adapt-icsp`。</param>
|
||||
/// <param name="method">轨迹生成方法,支持默认 Rust `icsp`、可选 C# `icsp-csharp`、`self-adapt-icsp` 或 `doubles`。</param>
|
||||
/// <param name="saveTrajectory">是否保存轨迹信息。</param>
|
||||
/// <param name="useCache">是否优先复用已规划轨迹缓存。</param>
|
||||
/// <param name="wait">是否等待机器人执行完整条飞拍轨迹后再返回。</param>
|
||||
|
||||
@@ -8,7 +8,7 @@ public sealed class TrajectoryExecutionOptions
|
||||
/// <summary>
|
||||
/// 初始化普通轨迹执行参数。
|
||||
/// </summary>
|
||||
/// <param name="method">轨迹生成方法,支持 `icsp`、`icsp_rust` 或 `doubles`。</param>
|
||||
/// <param name="method">轨迹生成方法,支持默认 Rust `icsp`、可选 C# `icsp-csharp` 或 `doubles`。</param>
|
||||
/// <param name="saveTrajectory">是否保存轨迹信息。</param>
|
||||
public TrajectoryExecutionOptions(string method = "icsp", bool saveTrajectory = false)
|
||||
{
|
||||
|
||||
@@ -70,14 +70,14 @@ public sealed class TrajectoryRequest
|
||||
public enum PlanningMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses the cubic-spline ICSP planner.
|
||||
/// 使用正式 icsp 后端;当前默认实现为 Rust 原生 ICSP。
|
||||
/// </summary>
|
||||
Icsp,
|
||||
|
||||
/// <summary>
|
||||
/// 使用 Rust 原生库实现的 ICSP 规划器。
|
||||
/// 使用拆分到可选程序集中的 C# ICSP 规划器。
|
||||
/// </summary>
|
||||
IcspRust,
|
||||
IcspCSharp,
|
||||
|
||||
/// <summary>
|
||||
/// Uses the adaptive ICSP planner that may insert additional points.
|
||||
|
||||
27
src/Flyshot.Core.Planning/IPlanningBackend.cs
Normal file
27
src/Flyshot.Core.Planning/IPlanningBackend.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Flyshot.Core.Domain;
|
||||
|
||||
namespace Flyshot.Core.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 定义可由兼容层按 method 字符串分派的轨迹规划后端。
|
||||
/// </summary>
|
||||
public interface IPlanningBackend
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取外部 HTTP/ControllerClient method 参数使用的准确方法名。
|
||||
/// </summary>
|
||||
string MethodName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取该后端成功规划后写入结果对象的领域规划方法。
|
||||
/// </summary>
|
||||
PlanningMethod ResultMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 执行轨迹规划。
|
||||
/// </summary>
|
||||
/// <param name="request">包含机器人约束、轨迹程序和调用标志的规划请求。</param>
|
||||
/// <param name="context">兼容层传入的规划上下文参数。</param>
|
||||
/// <returns>规划后的轨迹时间轴、路点和收敛信息。</returns>
|
||||
PlannedTrajectory Plan(TrajectoryRequest request, PlanningBackendContext context);
|
||||
}
|
||||
21
src/Flyshot.Core.Planning/PlanningBackendContext.cs
Normal file
21
src/Flyshot.Core.Planning/PlanningBackendContext.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Flyshot.Core.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 表示规划后端执行时由兼容层补充的运行参数。
|
||||
/// </summary>
|
||||
public sealed class PlanningBackendContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化规划后端上下文。
|
||||
/// </summary>
|
||||
/// <param name="adaptIcspTryNum">self-adapt-icsp 允许的最大补点重试次数。</param>
|
||||
public PlanningBackendContext(int adaptIcspTryNum = 5)
|
||||
{
|
||||
AdaptIcspTryNum = adaptIcspTryNum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 self-adapt-icsp 允许的最大补点重试次数。
|
||||
/// </summary>
|
||||
public int AdaptIcspTryNum { get; }
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public sealed class RustIcspPlanner
|
||||
waypointTimes: waypointTimes,
|
||||
segmentDurations: segmentDurations,
|
||||
segmentScales: segmentScales,
|
||||
method: PlanningMethod.IcspRust,
|
||||
method: PlanningMethod.Icsp,
|
||||
iterations: nativeResult.Iterations,
|
||||
threshold: nativeResult.Threshold);
|
||||
}
|
||||
|
||||
43
src/Flyshot.Native.Planning/RustIcspPlanningBackend.cs
Normal file
43
src/Flyshot.Native.Planning/RustIcspPlanningBackend.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
|
||||
namespace Flyshot.Native.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 将 Rust ICSP 规划器暴露为默认正式 icsp 后端。
|
||||
/// </summary>
|
||||
public sealed class RustIcspPlanningBackend : IPlanningBackend
|
||||
{
|
||||
private readonly RustIcspPlanner _planner;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 Rust ICSP 后端。
|
||||
/// </summary>
|
||||
/// <param name="planner">可选的 Rust ICSP 规划器实例;为空时使用默认原生库解析规则。</param>
|
||||
public RustIcspPlanningBackend(RustIcspPlanner? planner = null)
|
||||
{
|
||||
_planner = planner ?? new RustIcspPlanner();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取外部调用 Rust ICSP 后端时使用的正式方法名。
|
||||
/// </summary>
|
||||
public string MethodName => "icsp";
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Rust ICSP 后端写入结果对象的规划方法。
|
||||
/// </summary>
|
||||
public PlanningMethod ResultMethod => PlanningMethod.Icsp;
|
||||
|
||||
/// <summary>
|
||||
/// 执行 Rust ICSP 规划。
|
||||
/// </summary>
|
||||
/// <param name="request">包含机器人约束和示教路点的规划请求。</param>
|
||||
/// <param name="context">规划上下文;Rust ICSP 当前不使用该参数。</param>
|
||||
/// <returns>由 Rust 原生库生成的轨迹结果。</returns>
|
||||
public PlannedTrajectory Plan(TrajectoryRequest request, PlanningBackendContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return _planner.Plan(request);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<Content Include="..\..\NLog.config" Link="NLog.config" CopyToOutputDirectory="PreserveNewest" />
|
||||
<!-- 运行时配置根目录固定为输出目录 Config,调试和发布都复制仓库内固化配置。 -->
|
||||
<Content Include="..\..\Config\**\*" Link="Config\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||
<!-- Rust ICSP 原生规划库:从 src/Libs 固定复制到运行与发布目录,供 method=icsp_rust 加载。 -->
|
||||
<!-- Rust ICSP 原生规划库:从 src/Libs 固定复制到运行与发布目录,供默认 method=icsp 加载。 -->
|
||||
<Content Include="..\Libs\flyshot_planning.dll" Link="flyshot_planning.dll" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="Exists('..\Libs\flyshot_planning.dll')" />
|
||||
<Content Include="..\Libs\libflyshot_planning.so" Link="libflyshot_planning.so" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="Exists('..\Libs\libflyshot_planning.so')" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
规划方法
|
||||
<select id="offline-planning-method">
|
||||
<option value="icsp">icsp</option>
|
||||
<option value="icsp-csharp">icsp-csharp</option>
|
||||
<option value="self-adapt-icsp">self-adapt-icsp</option>
|
||||
</select>
|
||||
</label>
|
||||
@@ -214,7 +215,7 @@
|
||||
"description": "执行普通关节轨迹。该接口兼容两种历史请求体形态:一是对象形态 `{ waypoints, method, save_traj }`,二是直接传纯数组路点;查询参数里的 `method` 和 `save_traj` 仍可覆盖请求体值。",
|
||||
"requestBodyFields": [
|
||||
{ "name": "waypoints", "type": "array<array<number>> | array<object>", "required": true, "description": "历史兼容对象形态中的轨迹路点集合;每个路点既可直接是关节数组,也可写成 `{ joints: [...] }`。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "对象形态中的轨迹规划方法,默认 `icsp`。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "对象形态中的轨迹规划方法,默认 `icsp`;`icsp-csharp` 需要显式注册 C# 后端。" },
|
||||
{ "name": "save_traj", "type": "boolean", "required": false, "description": "对象形态中的是否保存轨迹标记,默认 `false`。" },
|
||||
{ "name": "根数组", "type": "array<array<number>> | array<object>", "required": true, "description": "纯数组形态时,请直接提交路点数组本身,而不是对象包裹层。" },
|
||||
{ "name": "根数组[].joints", "type": "array<number>", "required": false, "description": "当路点元素是对象形态时,兼容层会读取其中的 `joints` 数组。" }
|
||||
@@ -237,7 +238,7 @@
|
||||
"requestBodyFields": [
|
||||
{ "name": "name", "type": "string", "required": true, "description": "要执行的飞拍轨迹名称。" },
|
||||
{ "name": "move_to_start", "type": "boolean", "required": false, "description": "是否先用临时 PTP 轨迹移动到飞拍起点,默认 `true`。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "飞拍轨迹生成方法,默认 `icsp`。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "飞拍轨迹生成方法,默认 `icsp`;`icsp-csharp` 和 `self-adapt-icsp` 需要显式注册 C# 后端。" },
|
||||
{ "name": "save_traj", "type": "boolean", "required": false, "description": "是否保存轨迹信息,默认 `true`。" },
|
||||
{ "name": "use_cache", "type": "boolean", "required": false, "description": "是否优先复用已缓存的轨迹结果,默认 `true`。" },
|
||||
{ "name": "wait", "type": "boolean", "required": false, "description": "是否等待整条飞拍轨迹执行完成后再返回,默认 `true`。" }
|
||||
@@ -248,7 +249,7 @@
|
||||
"description": "仅要求机器人模型已加载;完成规划、触发绑定、最终 8ms 序列生成和文件导出,并返回完整规划结果。",
|
||||
"requestBodyFields": [
|
||||
{ "name": "name", "type": "string", "required": true, "description": "目标飞拍轨迹名称。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`。" }
|
||||
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`;`icsp-csharp` 和 `self-adapt-icsp` 需要显式注册 C# 后端。" }
|
||||
]
|
||||
},
|
||||
"POST /is_flyShotTrajValid/": {
|
||||
@@ -256,7 +257,7 @@
|
||||
"description": "检查指定飞拍轨迹是否可生成有效执行结果,并返回轨迹时长;底层未实现时会返回 501。",
|
||||
"requestBodyFields": [
|
||||
{ "name": "name", "type": "string", "required": true, "description": "要校验的飞拍轨迹名称。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`。" },
|
||||
{ "name": "method", "type": "string", "required": false, "description": "轨迹规划方法,默认 `icsp`;`icsp-csharp` 和 `self-adapt-icsp` 需要显式注册 C# 后端。" },
|
||||
{ "name": "save_traj", "type": "boolean", "required": false, "description": "是否同时保存轨迹信息,默认 `true`。" }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<ProjectReference Include="..\..\src\Flyshot.Core.Config\Flyshot.Core.Config.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.Core.Domain\Flyshot.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.Core.Planning\Flyshot.Core.Planning.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.CSharp.Planning\Flyshot.CSharp.Planning.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.Core.Triggering\Flyshot.Core.Triggering.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.Native.Planning\Flyshot.Native.Planning.csproj" />
|
||||
<ProjectReference Include="..\..\src\Flyshot.Runtime.Fanuc\Flyshot.Runtime.Fanuc.csproj" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.CSharp.Planning;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Flyshot.Core.Planning.Export;
|
||||
@@ -55,7 +56,7 @@ public sealed class OfflinePlanTests
|
||||
var request = new TrajectoryRequest(
|
||||
robot: scaledProfile,
|
||||
program: program,
|
||||
method: useSelfAdapt ? PlanningMethod.SelfAdaptIcsp : PlanningMethod.Icsp);
|
||||
method: useSelfAdapt ? PlanningMethod.SelfAdaptIcsp : PlanningMethod.IcspCSharp);
|
||||
|
||||
PlannedTrajectory trajectory;
|
||||
if (useSelfAdapt)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.CSharp.Planning;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
using Flyshot.Core.Planning.Sampling;
|
||||
@@ -27,7 +28,7 @@ public sealed class PlanningCompatibilityTests
|
||||
new[] { 0.8, 0.3, 0.0, 0.0, 0.0, 0.0 },
|
||||
new[] { 1.0, 0.2, 0.0, 0.0, 0.0, 0.0 }
|
||||
}),
|
||||
method: PlanningMethod.Icsp);
|
||||
method: PlanningMethod.IcspCSharp);
|
||||
|
||||
var trajectory = new ICspPlanner().Plan(request);
|
||||
|
||||
@@ -51,7 +52,7 @@ public sealed class PlanningCompatibilityTests
|
||||
new[] { 20.0 },
|
||||
new[] { 30.0 }
|
||||
}),
|
||||
method: PlanningMethod.Icsp);
|
||||
method: PlanningMethod.IcspCSharp);
|
||||
|
||||
var planner = new ICspPlanner(maxIterations: 0);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flyshot.ControllerClientCompat;
|
||||
using Flyshot.CSharp.Planning;
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Core.Planning;
|
||||
@@ -16,6 +17,19 @@ namespace Flyshot.Core.Tests;
|
||||
/// </summary>
|
||||
public sealed class RuntimeOrchestrationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建显式注册 C# ICSP 后端的编排器,用于验证可选算法 DLL 的行为边界。
|
||||
/// </summary>
|
||||
/// <returns>包含 C# 普通 ICSP 与 self-adapt-icsp 后端的轨迹编排器。</returns>
|
||||
private static ControllerClientTrajectoryOrchestrator CreateOrchestratorWithCSharpBackends()
|
||||
{
|
||||
return new ControllerClientTrajectoryOrchestrator(
|
||||
[
|
||||
new CSharpIcspPlanningBackend(),
|
||||
new SelfAdaptIcspPlanningBackend()
|
||||
]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证真机运行时会把 TCP 10010 状态通道健康度映射为可诊断连接状态。
|
||||
/// </summary>
|
||||
@@ -107,54 +121,10 @@ public sealed class RuntimeOrchestrationTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证普通轨迹使用准确字串 icsp_rust 时会真实调用 Rust DLL,并在结果中标记原生后端。
|
||||
/// 验证普通轨迹默认 icsp 会真实调用 Rust DLL,并在结果中保留正式 icsp 方法标记。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_UsesRustPlanner()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
|
||||
var bundle = orchestrator.PlanOrdinaryTrajectory(
|
||||
robot,
|
||||
[
|
||||
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.001, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.002, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.003, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
],
|
||||
new TrajectoryExecutionOptions(method: "icsp_rust"));
|
||||
|
||||
Assert.Equal(PlanningMethod.IcspRust, bundle.PlannedTrajectory.Method);
|
||||
Assert.Equal(PlanningMethod.IcspRust, bundle.Result.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证飞拍轨迹使用准确字串 icsp_rust 时会真实调用 Rust DLL,同时保留触发时间轴。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanUploadedFlyshot_UsesRustPlanner()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
var uploaded = TestRobotFactory.CreateUploadedTrajectoryWithSingleShot();
|
||||
|
||||
var bundle = orchestrator.PlanUploadedFlyshot(
|
||||
robot,
|
||||
uploaded,
|
||||
new FlyshotExecutionOptions(method: "icsp_rust", useCache: false));
|
||||
|
||||
Assert.Equal(PlanningMethod.IcspRust, bundle.PlannedTrajectory.Method);
|
||||
Assert.Equal(PlanningMethod.IcspRust, bundle.Result.Method);
|
||||
Assert.Single(bundle.Result.ShotEvents);
|
||||
Assert.Single(bundle.Result.TriggerTimeline);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证普通轨迹默认 icsp 仍使用现有 C# 规划器。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_KeepsManagedIcspPlanner()
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_DefaultIcspUsesRustPlanner()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
@@ -174,12 +144,98 @@ public sealed class RuntimeOrchestrationTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 self-adapt-icsp 仍使用 C# 自适应规划器,不切换到 Rust。
|
||||
/// 验证旧的 icsp_rust 字串不再作为正式入口,并提示调用方改用 icsp。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanUploadedFlyshot_KeepsManagedSelfAdaptPlanner()
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_RejectsLegacyIcspRustName()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
|
||||
var exception = Assert.Throws<ArgumentException>(() => orchestrator.PlanOrdinaryTrajectory(
|
||||
robot,
|
||||
[
|
||||
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.001, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.002, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.003, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
],
|
||||
new TrajectoryExecutionOptions(method: "icsp_rust")));
|
||||
|
||||
Assert.Contains("icsp", exception.Message);
|
||||
Assert.Contains("icsp_rust", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证未注册 C# 后端时,icsp-csharp 会明确报错而不是静默回退。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_RejectsCSharpIcspWhenBackendIsMissing()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
|
||||
var exception = Assert.Throws<NotSupportedException>(() => orchestrator.PlanOrdinaryTrajectory(
|
||||
robot,
|
||||
[
|
||||
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.001, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.002, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.003, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
],
|
||||
new TrajectoryExecutionOptions(method: "icsp-csharp")));
|
||||
|
||||
Assert.Contains("icsp-csharp", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证显式注册 C# 后端后,icsp-csharp 使用拆分后的托管规划器。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_UsesRegisteredCSharpIcspPlanner()
|
||||
{
|
||||
var orchestrator = CreateOrchestratorWithCSharpBackends();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
|
||||
var bundle = orchestrator.PlanOrdinaryTrajectory(
|
||||
robot,
|
||||
[
|
||||
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.001, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.002, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||
[0.003, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
],
|
||||
new TrajectoryExecutionOptions(method: "icsp-csharp"));
|
||||
|
||||
Assert.Equal(PlanningMethod.IcspCSharp, bundle.PlannedTrajectory.Method);
|
||||
Assert.Equal(PlanningMethod.IcspCSharp, bundle.Result.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证未注册 C# 后端时,self-adapt-icsp 会明确报错而不是把行为藏在主服务里。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanUploadedFlyshot_RejectsSelfAdaptWhenBackendIsMissing()
|
||||
{
|
||||
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
||||
var robot = TestRobotFactory.CreateRobotProfile();
|
||||
var uploaded = TestRobotFactory.CreateUploadedTrajectoryWithSingleShot();
|
||||
|
||||
var exception = Assert.Throws<NotSupportedException>(() => orchestrator.PlanUploadedFlyshot(
|
||||
robot,
|
||||
uploaded,
|
||||
new FlyshotExecutionOptions(method: "self-adapt-icsp", useCache: false)));
|
||||
|
||||
Assert.Contains("self-adapt-icsp", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证显式注册 C# 后端后,self-adapt-icsp 仍使用 C# 自适应规划器。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ControllerClientTrajectoryOrchestrator_PlanUploadedFlyshot_UsesRegisteredManagedSelfAdaptPlanner()
|
||||
{
|
||||
var orchestrator = CreateOrchestratorWithCSharpBackends();
|
||||
var robot = new RobotProfile(
|
||||
name: "ManagedSelfAdaptTestRobot",
|
||||
modelPath: "Models/ManagedSelfAdaptTest.robot",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.CSharp.Planning;
|
||||
using Flyshot.Core.Planning;
|
||||
using Flyshot.Native.Planning;
|
||||
|
||||
@@ -35,7 +36,7 @@ public sealed class RustIcspPlannerInteropTests
|
||||
|
||||
var result = planner.Plan(CreateSimpleRequest());
|
||||
|
||||
Assert.Equal(PlanningMethod.IcspRust, result.Method);
|
||||
Assert.Equal(PlanningMethod.Icsp, result.Method);
|
||||
Assert.Equal(result.PlannedWaypointCount, result.WaypointTimes.Count);
|
||||
Assert.Equal(result.PlannedWaypointCount - 1, result.SegmentDurations.Count);
|
||||
Assert.Equal(result.SegmentDurations.Count, result.SegmentScales.Count);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.CSharp.Planning;
|
||||
using Flyshot.Core.Planning;
|
||||
using Flyshot.Native.Planning;
|
||||
|
||||
@@ -19,8 +20,8 @@ public sealed class RustIcspPlannerParityTests
|
||||
PlannedTrajectory managed = new ICspPlanner().Plan(request);
|
||||
PlannedTrajectory native = new RustIcspPlanner().Plan(request);
|
||||
|
||||
Assert.Equal(PlanningMethod.Icsp, managed.Method);
|
||||
Assert.Equal(PlanningMethod.IcspRust, native.Method);
|
||||
Assert.Equal(PlanningMethod.IcspCSharp, managed.Method);
|
||||
Assert.Equal(PlanningMethod.Icsp, native.Method);
|
||||
AssertTrajectoryMatches(managed, native, tolerance: 1e-9);
|
||||
}
|
||||
|
||||
@@ -35,8 +36,8 @@ public sealed class RustIcspPlannerParityTests
|
||||
PlannedTrajectory managed = new ICspPlanner(enforceFinalScale: false).Plan(request);
|
||||
PlannedTrajectory native = new RustIcspPlanner(options).Plan(request);
|
||||
|
||||
Assert.Equal(PlanningMethod.Icsp, managed.Method);
|
||||
Assert.Equal(PlanningMethod.IcspRust, native.Method);
|
||||
Assert.Equal(PlanningMethod.IcspCSharp, managed.Method);
|
||||
Assert.Equal(PlanningMethod.Icsp, native.Method);
|
||||
AssertTrajectoryMatches(managed, native, tolerance: 1e-9);
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ public sealed class RustIcspPlannerParityTests
|
||||
.Select(_ => new IoAddressGroup([]))
|
||||
.ToArray());
|
||||
|
||||
return new TrajectoryRequest(robot, program, PlanningMethod.Icsp);
|
||||
return new TrajectoryRequest(robot, program, PlanningMethod.IcspCSharp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user