Files
FlyShotHost/src/Flyshot.Core.Planning/ICSP-算法说明.md
yunxiao.zhu 1779067b5c feat(fanuc): 打通飞拍轨迹完整执行链路
* 增加 J519 稠密发送采样校验与保姿回发逻辑
* 调整 saveTrajectory 导出与 sequence buffer 行为
* 补充 10010 解析脚本、ICSP 说明和回归测试
2026-05-08 13:25:02 +08:00

7.0 KiB
Raw Blame History

ICSP 算法说明(ICspPlanner

本文档用于解释 Flyshot.Core.Planning.ICspPlanner 当前实现的 ICSP 规划算法在本仓库中的真实含义与计算逻辑,便于与逆向结论对照、以及指导后续改造(例如“按约束生成中间点位”)。

适用范围:本文描述的是当前 C# 实现的 “CubicSpline + 逐段时间缩放迭代retiming 版本。
重要澄清:ICspPlanner 的主要输出是 时间轴(每个示教点的时间戳),而不是直接输出固定周期的稠密点序列;稠密点在后续采样层生成。


1. 名词与数据形态

1.1 输入

  • 示教点(路点)request.Program.Waypoints
    每个路点是关节空间向量 (q_i \in \mathbb{R}^{dof})。
  • 关节约束request.Robot.JointLimits[d] 提供每轴上限:
    • 速度上限 (v_{lim}[d])
    • 加速度上限 (a_{lim}[d])
    • 跃度jerk上限 (j_{lim}[d])

1.2 输出(PlannedTrajectory

ICspPlanner 的输出 不是稠密轨迹点序列,而是:

  • PlannedWaypoints:规划后路点(对于普通 icsp,与输入示教点相同;补点发生在 SelfAdaptIcspPlanner
  • WaypointTimes:每个路点的绝对时间 (t_i)(秒)
  • SegmentDurations:每段时长 (T_i = t_{i+1}-t_i)(秒)
  • SegmentScales:每段缩放因子 scale_i
  • Iterations / Threshold:收敛信息

后续模块会基于 PlannedWaypoints + WaypointTimes 重建样条并采样,生成稠密点:

  • 规划层稠密采样:TrajectorySampler.SampleJointTrajectory(...)
  • 运行时 J519 重采样(速度倍率映射 + rad->degJ519SendTrajectorySampler.SampleDenseJointTrajectory(...)

2. 算法总体目标retiming

给定一组关节示教点 ({q_i}{i=0}^{N-1}),在不改变路点位置的前提下,为每段分配时长 ({T_i}{i=0}^{N-2}),使得用 clamped-zero 三次样条连接后的轨迹在每段上满足:

  • (\max|\dot q_d(t)| \le v_{lim}[d])
  • (\max|\ddot q_d(t)| \le a_{lim}[d])
  • (\max|\dddot q_d(t)| \le j_{lim}[d])

实现策略是“逐段缩放时长”的迭代法:每轮用当前 ({T_i}) 构造样条并解析求导峰值,再根据超限程度把相应段时长乘以缩放因子,使峰值回落。


3. 计算步骤(与代码一致)

3.1 前置条件

  • 路点数 (N \ge 4)(否则抛异常)

3.2 初始段时长

段数 (nseg = N-1)。

初始段时长取相邻路点关节空间欧氏距离:

[ T_i^{(0)} = |q_{i+1}-q_i|_2 ]

3.3 由段时长构造绝对时间轴

[ t_0 = 0,\quad t_{i+1} = t_i + T_i ]

3.4 用 clamped-zero 边界构造三次样条

以 ((t_i, q_i)) 为节点构造分段三次多项式:

[ S_i(t) = a_i t^3 + b_i t^2 + c_i t + d_i,\quad t \in [t_i, t_{i+1}] ]

边界条件为 clamped-zero(起点/终点一阶导为 0用于与逆向锁定的参考行为对齐。

3.5 解析计算每段导数峰值

对每段、每轴,解析求最大绝对值:

  • 一阶导(速度)是二次函数:端点与顶点候选取最大
  • 二阶导(加速度)是一次函数:端点取最大
  • 三阶导(跃度)是常数:直接取绝对值

得到三张矩阵:

  • maxDq[seg,d] = max_t |dq/dt|
  • maxDdq[seg,d] = max_t |d²q/dt²|
  • maxDddq[seg,d] = max_t |d³q/dt³|

3.6 计算每段缩放因子(核心公式)

对段 seg,对每个关节 d 计算三类“超限比”:

[ s_v = \left|\frac{maxDq[seg,d]}{v_{lim}[d]}\right| ]

[ s_a = \sqrt{\left|\frac{maxDdq[seg,d]}{a_{lim}[d]}\right|} ]

[ s_j = \sqrt[3]{\left|\frac{maxDddq[seg,d]}{j_{lim}[d]}\right|} ]

段缩放因子取所有轴、三类约束的最大值:

[ scale_{seg}=\max_d \max(s_v, s_a, s_j) ]

指数来源:时间拉长 (k) 倍时,速度按 (1/k) 缩小、加速度按 (1/k^2) 缩小、跃度按 (1/k^3) 缩小,因此超限比需要分别取一次方/平方根/立方根来求“应当拉长多少倍”。

3.7 收敛指标与最优解保存

每轮用如下指标衡量“离约束满足还差多少”:

[ threshold = \sum_{seg} |scale_{seg} - 1| ]

若本轮 threshold 小于历史最佳,则保存当前解作为 best(包含 bestDurations / bestScales / bestWaypointTimes 等)。

3.8 收敛判定与段时长更新

  • threshold < _threshold(默认 1e-3),认为收敛并提前结束迭代
  • 否则更新每段时长:

[ T_{seg} \leftarrow T_{seg} \cdot scale_{seg} ]

并进入下一轮。


4. 最终判定global_scale

迭代结束后取历史最优缩放因子的最大值:

[ globalScale=\max_{seg}(scale_{seg}) ]

若启用强制判定(enforceFinalScale=true)且:

[ globalScale > 1 + \text{finalScaleTolerance} ]

则判定“未收敛/不可执行”并抛异常。默认容差 finalScaleTolerance=1e-2,用于容忍 C# spline 与参考实现间的小量数值差异。


5. 与“补点/中间点位”的关系(常见误解澄清)

5.1 ICspPlanner 不负责生成固定周期的中间点位

ICspPlanner 的核心工作是 时间轴规划retiming:在不改变示教点位置的情况下,通过缩放每段时长让样条导数峰值满足约束。

固定周期(例如 8ms/16ms的“中间点位序列”属于 采样层

  • TrajectorySampler:按 samplePeriod 在样条上取样,得到 [time, j1..jN](关节单位仍为 rad
  • J519SendTrajectorySampler:按 servoPeriod 生成真实发送序列,用 speedRatiosendTime 映射到 trajectoryTime 并线性插值,再做 rad -> deg

5.2 SelfAdaptIcspPlanner 才包含“补点”逻辑,但它很粗

self-adapt-icsp 的补点策略在 SelfAdaptIcspPlanner 中:当某些段 scale > 1 + tolerance 时,对这些段插入关节空间中点再重规划。该策略的目的主要是“救收敛”,不是生成最终稠密序列。


6. 后续改造建议(定位落点)

如果需求是“根据示教点 + v/a/j 限制,直接生成可下发的稠密点位序列”,通常有两条路径:

  1. 保留 ICSP retiming:继续用 ICspPlanner 求时间轴,再在采样层按固定周期生成中间点位(当前架构就是这条路)。此时需要讨论的是采样周期、速度倍率映射、以及是否要对采样序列再做约束校验或二次整形。
  2. 做真正的自适应插点/细分:把“插点策略”升级为基于约束的细分(而不只是插中点),这更自然的落点是 SelfAdaptIcspPlanner 或新增一个“约束驱动细分器”,而不是把稠密点生成塞进 ICspPlanner

7. 关联实现位置(便于跳转)

  • 算法入口:src/Flyshot.Core.Planning/ICspPlanner.cs
  • 自适应补点:src/Flyshot.Core.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