💥 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:
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`。" }
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user