Files
ParkingRobot/MultiWheelC/Old/CrabMotionFrameTracker.cs
T

818 lines
32 KiB
C#
Raw 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.
// using ClumsyCore;
// using ClumsyCore.DTools;
// using ClumsyCore.Interfaces;
// using ClumsyCore.Pilot;
// using CommonUsage.Chassis;
// using MyParking.Shared;
// using System;
// using System.Collections.Generic;
// using System.Numerics;
// namespace MultiWheelC
// {
// // C层单车测试:在可配置的运动坐标系中统一跟踪直线、圆弧或S型曲线。
// public sealed class CrabMotionFrameTracker : MovementDefinition
// {
// public enum ReferencePathKind
// {
// Straight = 0,
// LeftArc = 1,
// SCurve = 2
// }
// public enum ChassisCommandBackend
// {
// SendXYThSpeed = 0,
// SendMotion = 1
// }
// public ReferencePathKind PathKind;
// public ChassisCommandBackend CommandBackend =
// ChassisCommandBackend.SendMotion;
// public Vector2 StartPosition;
// public double InitialBodyYawRadians;
// public float LengthMillimeters = 4000f;
// public float RadiusMillimeters = 2000f;
// public float SCurveLateralOffsetMillimeters = 400f;
// public double ArcSweepRadians = Math.PI / 2.0;
// public float CruiseSpeed = 0.2f;
// public float SlowDistanceMillimeters = 600f;
// public float FinishDistanceMillimeters = 30f;
// public float MinimumSpeed = 0.04f;
// public double LateralGainPerSecond = 0.8;
// public double MaximumLateralCorrection = 0.12;
// public double HeadingGainPerSecond = 1.5;
// public double MaximumAngularSpeedRadiansPerSecond =
// AngleMath.DegreesToRadians(30.0);
// public double MaximumVirtualSteeringRadians =
// AngleMath.DegreesToRadians(30.0);
// public float WheelAlignmentToleranceDegrees = 2f;
// public float WheelAlignmentStableSeconds = 0.3f;
// public float WheelAlignmentTimeoutSeconds = 10f;
// public float TrackingTimeoutSeconds = 60f;
// public Action<float, float, float> CommandObserver;
// // 运动坐标系相对车体坐标系的朝向:普通模式为0,蟹行为π/2。
// public double MotionFrameYawInBodyRadians = Math.PI / 2.0;
// private double _lastSCurveProgress;
// public override IEnumerable<bool> Get()
// {
// ValidateParameters();
// var chassis =
// PilotDefinition.Chassis as MultiWheelChassis;
// if (chassis == null)
// throw new InvalidOperationException(
// "当前底盘不是MultiWheelChassis,无法执行运动坐标系轨迹测试。");
// var adapter = new MultiWheelChassisAdapter(
// chassis,
// PilotDefinition.Self.CarNum);
// adapter.ResetToBodyFrame();
// var lastCommandTime = DateTime.Now;
// try
// {
// // 模式切换阶段只转舵轮,驱动速度始终保持为零。
// var alignmentStarted = DateTime.Now;
// DateTime? stableSince = null;
// while (true)
// {
// if (!adapter.PrepareParallelDirection(
// MotionFrameYawInBodyRadians))
// throw new InvalidOperationException(
// "无法生成运动坐标系对应的舵轮准备姿态。");
// var aligned =
// adapter.AreParallelWheelsAligned(
// MotionFrameYawInBodyRadians,
// AngleMath.DegreesToRadians(
// WheelAlignmentToleranceDegrees));
// if (aligned)
// {
// if (stableSince == null)
// stableSince = DateTime.Now;
// if ((DateTime.Now - stableSince.Value)
// .TotalSeconds >=
// WheelAlignmentStableSeconds)
// break;
// }
// else
// {
// stableSince = null;
// }
// if ((DateTime.Now - alignmentStarted)
// .TotalSeconds >
// WheelAlignmentTimeoutSeconds)
// throw new TimeoutException(
// "舵轮在限定时间内未稳定到达运动坐标系初始方向。");
// yield return true;
// }
// if (CommandBackend ==
// ChassisCommandBackend.SendMotion)
// {
// // 舵轮已按真实机械角度完成预对齐;
// // 现在由Shared适配层激活SendMotion虚拟运动坐标系。
// adapter.ActivateMotionFrame(
// MotionFrameYawInBodyRadians);
// }
// var trackingStarted = DateTime.Now;
// while (true)
// {
// if ((DateTime.Now - trackingStarted)
// .TotalSeconds >
// TrackingTimeoutSeconds)
// throw new TimeoutException(
// "蟹行轨迹在限定时间内未完成。");
// var location =
// DetourInterface.getCartLocation();
// if (!IsFinite(location.x) ||
// !IsFinite(location.y) ||
// !IsFinite(location.th))
// throw new InvalidOperationException(
// "蟹行轨迹测试期间Detour位姿无效。");
// var currentPosition = new Vector2(
// (float)location.x,
// (float)location.y);
// var currentBodyYaw =
// AngleMath.DegreesToRadians(location.th);
// CalculateReference(
// currentPosition,
// out var tangentYaw,
// out var referencePoint,
// out var remainingMillimeters,
// out var referenceCurvature);
// if (remainingMillimeters <=
// FinishDistanceMillimeters)
// break;
// var speed =
// CalculateSpeed(remainingMillimeters);
// var tangent = new Vector2(
// (float)Math.Cos(tangentYaw),
// (float)Math.Sin(tangentYaw));
// var leftNormal = new Vector2(
// -tangent.Y,
// tangent.X);
// var positionError =
// currentPosition - referencePoint;
// var lateralErrorMeters =
// Vector2.Dot(
// positionError,
// leftNormal) / 1000.0;
// var normalCorrection =
// Limit(
// -LateralGainPerSecond *
// lateralErrorMeters,
// MaximumLateralCorrection);
// // 先在世界坐标中组合切向速度与横向纠偏速度。
// var worldVx =
// tangent.X * speed +
// leftNormal.X * (float)normalCorrection;
// var worldVy =
// tangent.Y * speed +
// leftNormal.Y * (float)normalCorrection;
// // 将世界速度表达为当前蟹行运动坐标系速度。
// var motionYaw =
// currentBodyYaw +
// MotionFrameYawInBodyRadians;
// var motionCos = Math.Cos(motionYaw);
// var motionSin = Math.Sin(motionYaw);
// var vxInMotion =
// motionCos * worldVx +
// motionSin * worldVy;
// var vyInMotion =
// -motionSin * worldVx +
// motionCos * worldVy;
// var desiredBodyYaw =
// tangentYaw -
// MotionFrameYawInBodyRadians;
// var headingError =
// AngleMath.ShortestDifferenceRadians(
// desiredBodyYaw,
// currentBodyYaw);
// var omega =
// speed * referenceCurvature +
// HeadingGainPerSecond * headingError;
// omega = Limit(
// omega,
// MaximumAngularSpeedRadiansPerSecond);
// var now = DateTime.Now;
// var interval = now - lastCommandTime;
// lastCommandTime = now;
// bool commandAccepted;
// Twist2D bodyTwist;
// if (CommandBackend ==
// ChassisCommandBackend.SendMotion)
// {
// // 运动坐标系相对车体系旋转+90°:
// // 运动系正向速度会转换成车体系+Y速度。
// bodyTwist =
// FrameTransform2D
// .TransformTwistAtSamePoint(
// new Pose2D(
// 0.0,
// 0.0,
// MotionFrameYawInBodyRadians),
// new Twist2D(
// vxInMotion,
// vyInMotion,
// omega));
// // 将运动坐标系原点和前后几何控制点处的速度,
// // 转换为SendMotion需要的前后轴方向。
// var controlPointRadiusMeters =
// Math.Max(
// chassis.ControlPointRadius /
// 1000.0,
// 0.001);
// var frontVelocityY =
// vyInMotion +
// omega *
// controlPointRadiusMeters;
// var rearVelocityY =
// vyInMotion -
// omega *
// controlPointRadiusMeters;
// var frontSteeringRadians =
// Math.Atan2(
// frontVelocityY,
// vxInMotion);
// var rearSteeringRadians =
// Math.Atan2(
// rearVelocityY,
// vxInMotion);
// // 蟹行测试绕过M层ManualControl并直接调用SendMotion
// // 因此需要在C层同步应用蟹行虚拟几何比例和转向符号。
// if (IsCrabMotionFrame())
// {
// var geometryRatio =
// adapter.HalfTrackWidthMeters /
// adapter.HalfWheelBaseMeters;
// frontSteeringRadians =
// ConvertToCrabSteering(
// frontSteeringRadians,
// geometryRatio);
// rearSteeringRadians =
// ConvertToCrabSteering(
// rearSteeringRadians,
// geometryRatio);
// }
// var frontThetaDegrees =
// (float)AngleMath.RadiansToDegrees(
// frontSteeringRadians);
// var rearThetaDegrees =
// (float)AngleMath.RadiansToDegrees(
// rearSteeringRadians);
// var motionSpeed =
// (float)Math.Sqrt(
// vxInMotion * vxInMotion +
// vyInMotion * vyInMotion);
// commandAccepted =
// chassis.SendMotion(
// motionSpeed,
// frontThetaDegrees,
// rearThetaDegrees,
// interval);
// }
// else if (CommandBackend ==
// ChassisCommandBackend
// .SendXYThSpeed)
// {
// // 安全XYTh后端根据舵角误差统一压低驱动轮速。
// bodyTwist =
// FrameTransform2D
// .TransformTwistAtSamePoint(
// new Pose2D(
// 0.0,
// 0.0,
// MotionFrameYawInBodyRadians),
// new Twist2D(
// vxInMotion,
// vyInMotion,
// omega));
// var command = new ChassisCommand(
// PilotDefinition.Self.CarNum,
// bodyTwist);
// commandAccepted =
// adapter.Send(
// command,
// interval);
// }
// else
// {
// throw new InvalidOperationException(
// $"不支持的底盘命令后端:{CommandBackend}。");
// }
// if (!commandAccepted)
// throw new InvalidOperationException(
// "运动坐标系轨迹底盘解算失败:" +
// chassis
// .LastMotionDecomposeFailureReason);
// CommandObserver?.Invoke(
// (float)bodyTwist.VxMetersPerSecond,
// (float)bodyTwist.VyMetersPerSecond,
// (float)bodyTwist
// .OmegaRadiansPerSecond);
// yield return true;
// }
// }
// finally
// {
// adapter.StopImmediately();
// if (CommandBackend ==
// ChassisCommandBackend.SendMotion)
// {
// // 测试退出后恢复真实车体坐标系,避免影响后续测试。
// adapter.ResetToBodyFrame();
// }
// CommandObserver?.Invoke(0f, 0f, 0f);
// }
// yield return false;
// }
// // 判断当前运动坐标系是否为车体左侧朝前的蟹行坐标系。
// private bool IsCrabMotionFrame()
// {
// return Math.Abs(
// AngleMath.ShortestDifferenceRadians(
// Math.PI / 2.0,
// MotionFrameYawInBodyRadians)) <
// 1e-6;
// }
// // 按车体几何比例缩小蟹行转角。
// // +90°运动坐标系已经完成方向映射,此处不能再次反号。
// private double ConvertToCrabSteering(
// double normalSteeringRadians,
// double geometryRatio)
// {
// var crabSteeringRadians =
// Math.Atan(
// geometryRatio *
// Math.Tan(
// normalSteeringRadians));
// return Limit(
// crabSteeringRadians,
// MaximumVirtualSteeringRadians);
// }
// // 计算当前点在直线或圆弧上的参考点、切线和剩余距离。
// private void CalculateReference(
// Vector2 currentPosition,
// out double tangentYaw,
// out Vector2 referencePoint,
// out float remainingMillimeters,
// out double curvaturePerMeter)
// {
// var initialMotionYaw =
// InitialBodyYawRadians +
// MotionFrameYawInBodyRadians;
// if (PathKind == ReferencePathKind.Straight)
// {
// var tangent = new Vector2(
// (float)Math.Cos(initialMotionYaw),
// (float)Math.Sin(initialMotionYaw));
// var relative = currentPosition - StartPosition;
// var progress =
// Vector2.Dot(relative, tangent);
// var clampedProgress =
// Math.Max(
// 0f,
// Math.Min(progress, LengthMillimeters));
// tangentYaw = initialMotionYaw;
// referencePoint =
// StartPosition +
// tangent * clampedProgress;
// remainingMillimeters =
// Math.Max(
// 0f,
// LengthMillimeters - progress);
// curvaturePerMeter = 0.0;
// return;
// }
// if (PathKind == ReferencePathKind.SCurve)
// {
// CalculateSCurveReference(
// currentPosition,
// initialMotionYaw,
// out tangentYaw,
// out referencePoint,
// out remainingMillimeters,
// out curvaturePerMeter);
// return;
// }
// var center = GetArcCenter();
// var startRadialYaw =
// initialMotionYaw - Math.PI / 2.0;
// var radial = currentPosition - center;
// var currentRadialYaw =
// Math.Atan2(radial.Y, radial.X);
// var progressRadians =
// AngleMath.NormalizeRadians(
// currentRadialYaw - startRadialYaw);
// // 测试圆弧只有+90°,起点附近的轻微负噪声按0处理。
// if (progressRadians < 0.0)
// progressRadians = 0.0;
// var clampedProgressRadians =
// Math.Min(
// progressRadians,
// ArcSweepRadians);
// var referenceRadialYaw =
// startRadialYaw +
// clampedProgressRadians;
// referencePoint = center + new Vector2(
// RadiusMillimeters *
// (float)Math.Cos(referenceRadialYaw),
// RadiusMillimeters *
// (float)Math.Sin(referenceRadialYaw));
// tangentYaw =
// referenceRadialYaw + Math.PI / 2.0;
// remainingMillimeters =
// (float)Math.Max(
// 0.0,
// (ArcSweepRadians - progressRadians) *
// RadiusMillimeters);
// curvaturePerMeter =
// 1000.0 / RadiusMillimeters;
// }
// // 通过离散最近点和解析导数计算两段三次贝塞尔S曲线的参考状态。
// private void CalculateSCurveReference(
// Vector2 currentPosition,
// double initialMotionYaw,
// out double tangentYaw,
// out Vector2 referencePoint,
// out float remainingMillimeters,
// out double curvaturePerMeter)
// {
// const int nearestPointSamples = 200;
// var searchStart =
// Math.Max(
// 0.0,
// _lastSCurveProgress - 0.02);
// var bestProgress = _lastSCurveProgress;
// var bestDistanceSquared = double.MaxValue;
// for (var i = 0;
// i <= nearestPointSamples;
// i++)
// {
// var progress =
// searchStart +
// (1.0 - searchStart) *
// i / nearestPointSamples;
// EvaluateSCurve(
// progress,
// out var localPoint,
// out _,
// out _);
// var worldPoint =
// LocalPathPointToWorld(
// localPoint,
// initialMotionYaw);
// var distanceSquared =
// Vector2.DistanceSquared(
// currentPosition,
// worldPoint);
// if (distanceSquared <
// bestDistanceSquared)
// {
// bestDistanceSquared =
// distanceSquared;
// bestProgress = progress;
// }
// }
// // 轨迹进度不允许因定位噪声倒退,防止控制目标跳回上一段曲线。
// _lastSCurveProgress =
// Math.Max(
// _lastSCurveProgress,
// bestProgress);
// EvaluateSCurve(
// _lastSCurveProgress,
// out var bestLocalPoint,
// out var firstDerivative,
// out var secondDerivative);
// referencePoint =
// LocalPathPointToWorld(
// bestLocalPoint,
// initialMotionYaw);
// tangentYaw =
// initialMotionYaw +
// Math.Atan2(
// firstDerivative.Y,
// firstDerivative.X);
// var derivativeMagnitude =
// Math.Sqrt(
// firstDerivative.X *
// firstDerivative.X +
// firstDerivative.Y *
// firstDerivative.Y);
// if (derivativeMagnitude < 1e-6)
// {
// curvaturePerMeter = 0.0;
// }
// else
// {
// // 导数单位为mm,乘1000后将曲率从1/mm转换成1/m。
// curvaturePerMeter =
// (firstDerivative.X *
// secondDerivative.Y -
// firstDerivative.Y *
// secondDerivative.X) *
// 1000.0 /
// Math.Pow(
// derivativeMagnitude,
// 3.0);
// }
// remainingMillimeters =
// ApproximateSCurveRemainingLength(
// _lastSCurveProgress);
// }
// // 计算与普通4m S型测试完全一致的三段三次贝塞尔完整S曲线。
// private void EvaluateSCurve(
// double progress,
// out Vector2 point,
// out Vector2 firstDerivative,
// out Vector2 secondDerivative)
// {
// progress =
// Math.Max(
// 0.0,
// Math.Min(progress, 1.0));
// Vector2 p0;
// Vector2 p1;
// Vector2 p2;
// Vector2 p3;
// double t;
// if (progress <= 0.25)
// {
// t = progress * 4.0;
// p0 = new Vector2(0f, 0f);
// p1 = new Vector2(
// LengthMillimeters / 12f,
// 0f);
// p2 = new Vector2(
// LengthMillimeters / 6f,
// SCurveLateralOffsetMillimeters);
// p3 = new Vector2(
// LengthMillimeters * 0.25f,
// SCurveLateralOffsetMillimeters);
// }
// else if (progress <= 0.75)
// {
// t = (progress - 0.25) * 2.0;
// p0 = new Vector2(
// LengthMillimeters * 0.25f,
// SCurveLateralOffsetMillimeters);
// p1 = new Vector2(
// LengthMillimeters / 3f,
// SCurveLateralOffsetMillimeters);
// p2 = new Vector2(
// LengthMillimeters * 2f / 3f,
// -SCurveLateralOffsetMillimeters);
// p3 = new Vector2(
// LengthMillimeters * 0.75f,
// -SCurveLateralOffsetMillimeters);
// }
// else
// {
// t = (progress - 0.75) * 4.0;
// p0 = new Vector2(
// LengthMillimeters * 0.75f,
// -SCurveLateralOffsetMillimeters);
// p1 = new Vector2(
// LengthMillimeters * 5f / 6f,
// -SCurveLateralOffsetMillimeters);
// p2 = new Vector2(
// LengthMillimeters * 11f / 12f,
// 0f);
// p3 = new Vector2(
// LengthMillimeters,
// 0f);
// }
// var oneMinusT = 1.0 - t;
// point =
// p0 * (float)(
// oneMinusT *
// oneMinusT *
// oneMinusT) +
// p1 * (float)(
// 3.0 *
// oneMinusT *
// oneMinusT *
// t) +
// p2 * (float)(
// 3.0 *
// oneMinusT *
// t *
// t) +
// p3 * (float)(t * t * t);
// firstDerivative =
// (p1 - p0) *
// (float)(
// 3.0 *
// oneMinusT *
// oneMinusT) +
// (p2 - p1) *
// (float)(
// 6.0 *
// oneMinusT *
// t) +
// (p3 - p2) *
// (float)(3.0 * t * t);
// secondDerivative =
// (p2 - 2f * p1 + p0) *
// (float)(6.0 * oneMinusT) +
// (p3 - 2f * p2 + p1) *
// (float)(6.0 * t);
// }
// // 通过分段采样估算从当前S曲线进度到终点的实际弧长。
// private float ApproximateSCurveRemainingLength(
// double startProgress)
// {
// const int lengthSamples = 100;
// EvaluateSCurve(
// startProgress,
// out var previousPoint,
// out _,
// out _);
// var length = 0f;
// for (var i = 1;
// i <= lengthSamples;
// i++)
// {
// var progress =
// startProgress +
// (1.0 - startProgress) *
// i / lengthSamples;
// EvaluateSCurve(
// progress,
// out var point,
// out _,
// out _);
// length +=
// Vector2.Distance(
// previousPoint,
// point);
// previousPoint = point;
// }
// return length;
// }
// // 将以初始蟹行方向为X轴的局部路径点转换到Detour世界坐标。
// private Vector2 LocalPathPointToWorld(
// Vector2 localPoint,
// double initialMotionYaw)
// {
// var cos =
// (float)Math.Cos(initialMotionYaw);
// var sin =
// (float)Math.Sin(initialMotionYaw);
// return StartPosition + new Vector2(
// localPoint.X * cos -
// localPoint.Y * sin,
// localPoint.X * sin +
// localPoint.Y * cos);
// }
// // 获取蟹行左转圆弧圆心;它位于初始运动方向的左侧。
// public Vector2 GetArcCenter()
// {
// var initialMotionYaw =
// InitialBodyYawRadians +
// MotionFrameYawInBodyRadians;
// return StartPosition + new Vector2(
// -RadiusMillimeters *
// (float)Math.Sin(initialMotionYaw),
// RadiusMillimeters *
// (float)Math.Cos(initialMotionYaw));
// }
// // 获取圆弧测试的理论终点。
// public Vector2 GetArcDestination()
// {
// var initialMotionYaw =
// InitialBodyYawRadians +
// MotionFrameYawInBodyRadians;
// var startRadialYaw =
// initialMotionYaw - Math.PI / 2.0;
// var endRadialYaw =
// startRadialYaw + ArcSweepRadians;
// var center = GetArcCenter();
// return center + new Vector2(
// RadiusMillimeters *
// (float)Math.Cos(endRadialYaw),
// RadiusMillimeters *
// (float)Math.Sin(endRadialYaw));
// }
// // 根据剩余路径长度生成终点减速速度。
// private float CalculateSpeed(
// float remainingMillimeters)
// {
// if (remainingMillimeters >=
// SlowDistanceMillimeters)
// return CruiseSpeed;
// var ratio =
// remainingMillimeters /
// Math.Max(
// SlowDistanceMillimeters,
// 1f);
// return Math.Max(
// MinimumSpeed,
// CruiseSpeed * ratio);
// }
// private void ValidateParameters()
// {
// if (CruiseSpeed <= 0f ||
// !IsFinite(CruiseSpeed) ||
// LengthMillimeters <= 0f ||
// !IsFinite(LengthMillimeters) ||
// RadiusMillimeters <= 0f ||
// !IsFinite(RadiusMillimeters) ||
// SCurveLateralOffsetMillimeters <= 0f ||
// !IsFinite(
// SCurveLateralOffsetMillimeters) ||
// ArcSweepRadians <= 0.0 ||
// !IsFinite(ArcSweepRadians) ||
// SlowDistanceMillimeters <= 0f ||
// !IsFinite(SlowDistanceMillimeters) ||
// FinishDistanceMillimeters < 0f ||
// !IsFinite(FinishDistanceMillimeters) ||
// TrackingTimeoutSeconds <= 0f ||
// !IsFinite(TrackingTimeoutSeconds) ||
// MaximumVirtualSteeringRadians <= 0.0 ||
// MaximumVirtualSteeringRadians >=
// Math.PI / 2.0 ||
// !IsFinite(
// MaximumVirtualSteeringRadians))
// throw new ArgumentOutOfRangeException(
// "蟹行轨迹测试参数无效。");
// }
// private static double Limit(
// double value,
// double absoluteLimit)
// {
// return Math.Max(
// -absoluteLimit,
// Math.Min(value, absoluteLimit));
// }
// private static bool IsFinite(double value)
// {
// return
// !double.IsNaN(value) &&
// !double.IsInfinity(value);
// }
// }
// }