- 改为高优先级 J519 接收线程与复用缓冲区发送链路 - 增加稠密执行前的 J519 就绪重试与状态诊断 - 修正程序状态响应字段顺序与 EnableRobot 默认参数 - 为飞拍轨迹补充平滑起停时间轴与首尾整形验证 - 补充真实运行配置、报警窗口与边界对比测试 - 同步更新限值文档、分析脚本与 .NET 8 SDK 固定配置
137 lines
4.6 KiB
C#
137 lines
4.6 KiB
C#
using Flyshot.Core.Domain;
|
||
using Flyshot.Core.Planning.Kinematics;
|
||
|
||
namespace Flyshot.Core.Planning.Sampling;
|
||
|
||
/// <summary>
|
||
/// 从规划轨迹生成稠密关节/笛卡尔采样序列。
|
||
///
|
||
/// 为什么采样周期默认 0.016s?
|
||
/// ---
|
||
/// 真机伺服周期是 8ms(125Hz),但规划层通常以 16ms(约 60Hz)输出到上层。
|
||
/// 0.016 是一个经验默认值,与真机观测到的稠密轨迹间隔一致。
|
||
/// </summary>
|
||
public static class TrajectorySampler
|
||
{
|
||
/// <summary>
|
||
/// 生成稠密关节轨迹采样,每行格式:[time, j1, j2, ..., jN],保留 6 位小数。
|
||
/// </summary>
|
||
public static IReadOnlyList<IReadOnlyList<double>> SampleJointTrajectory(
|
||
PlannedTrajectory trajectory,
|
||
double samplePeriod = 0.016,
|
||
int decimals = 6,
|
||
bool smoothStartStop = false)
|
||
{
|
||
var spline = RebuildSpline(trajectory);
|
||
double duration = trajectory.WaypointTimes[^1];
|
||
var times = GenerateSampleTimes(duration, samplePeriod);
|
||
var result = new List<IReadOnlyList<double>>(times.Count);
|
||
|
||
foreach (var t in times)
|
||
{
|
||
var evaluationTime = smoothStartStop
|
||
? MapSmoothStartStopEvaluationTime(t, duration)
|
||
: t;
|
||
var pos = spline.Evaluate(evaluationTime);
|
||
var row = new List<double>(pos.Length + 1);
|
||
row.Add(Math.Round(t, decimals));
|
||
foreach (var value in pos)
|
||
{
|
||
row.Add(Math.Round(value, decimals));
|
||
}
|
||
|
||
result.Add(row);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成稠密笛卡尔轨迹采样,每行格式:[time, x, y, z, qx, qy, qz, qw],保留 6 位小数。
|
||
/// </summary>
|
||
public static IReadOnlyList<IReadOnlyList<double>> SampleCartesianTrajectory(
|
||
PlannedTrajectory trajectory,
|
||
RobotKinematicsModel kinematicsModel,
|
||
double samplePeriod = 0.016,
|
||
int decimals = 6,
|
||
bool smoothStartStop = false)
|
||
{
|
||
var spline = RebuildSpline(trajectory);
|
||
double duration = trajectory.WaypointTimes[^1];
|
||
var times = GenerateSampleTimes(duration, samplePeriod);
|
||
var result = new List<IReadOnlyList<double>>(times.Count);
|
||
|
||
foreach (var t in times)
|
||
{
|
||
var evaluationTime = smoothStartStop
|
||
? MapSmoothStartStopEvaluationTime(t, duration)
|
||
: t;
|
||
var jointPos = spline.Evaluate(evaluationTime);
|
||
var pose = RobotKinematics.ForwardKinematics(kinematicsModel, jointPos);
|
||
var row = new List<double>(pose.Length + 1);
|
||
row.Add(Math.Round(t, decimals));
|
||
foreach (var value in pose)
|
||
{
|
||
row.Add(Math.Round(value, decimals));
|
||
}
|
||
|
||
result.Add(row);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从规划轨迹重建 CubicSplineInterpolator 用于稠密采样。
|
||
/// </summary>
|
||
private static CubicSplineInterpolator RebuildSpline(PlannedTrajectory trajectory)
|
||
{
|
||
var times = trajectory.WaypointTimes.ToArray();
|
||
var values = trajectory.PlannedWaypoints.Select(wp => wp.Positions.ToArray()).ToArray();
|
||
return new CubicSplineInterpolator(times, values);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成采样时间点,强制包含终点。
|
||
/// </summary>
|
||
private static IReadOnlyList<double> GenerateSampleTimes(double duration, double samplePeriod)
|
||
{
|
||
var times = new List<double>();
|
||
double t = 0.0;
|
||
while (t < duration - 1e-12)
|
||
{
|
||
times.Add(t);
|
||
t += samplePeriod;
|
||
}
|
||
|
||
if (times.Count == 0 || Math.Abs(times[^1] - duration) > 1e-12)
|
||
{
|
||
times.Add(duration);
|
||
}
|
||
|
||
return times;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 把线性采样时间映射为整段平滑起停的评估时间。
|
||
/// 使用 7 次 smootherstep 时间律,让起点和终点的一到三阶导都自然收敛到 0。
|
||
/// </summary>
|
||
private static double MapSmoothStartStopEvaluationTime(double sampleTime, double duration)
|
||
{
|
||
if (duration <= 0.0)
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
var normalizedTime = Math.Clamp(sampleTime / duration, 0.0, 1.0);
|
||
var u2 = normalizedTime * normalizedTime;
|
||
var u3 = u2 * normalizedTime;
|
||
var u4 = u3 * normalizedTime;
|
||
var u5 = u4 * normalizedTime;
|
||
var u6 = u5 * normalizedTime;
|
||
var u7 = u6 * normalizedTime;
|
||
var progress = (35.0 * u4) - (84.0 * u5) + (70.0 * u6) - (20.0 * u7);
|
||
return duration * progress;
|
||
}
|
||
}
|