贯通车队运行链并支持轨迹自动推导β
This commit is contained in:
@@ -21,6 +21,11 @@ namespace MultiWheelC
|
||||
public sealed class TrajectoryTrackingMovement
|
||||
: MovementDefinition
|
||||
{
|
||||
private const double ReferenceSpeedDeadbandMetersPerSecond =
|
||||
1e-6;
|
||||
private const double FixedMotionDirectionToleranceRadians =
|
||||
3.0 * Math.PI / 180.0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置本次动作需要跟踪的世界坐标系轨迹。
|
||||
/// </summary>
|
||||
@@ -43,9 +48,18 @@ namespace MultiWheelC
|
||||
public Action<ParkingGeometricController> CycleObserver;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置本动作运动坐标系X轴在车体系中的方向,单位为rad;0表示车头方向。
|
||||
/// 获取或设置本动作运动坐标系X轴在车体系中的方向,单位为rad;为空时从轨迹自动推导。
|
||||
/// </summary>
|
||||
public double MotionDirectionInBodyRadians;
|
||||
public double? MotionDirectionInBodyRadians = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取本次执行最终采用的运动坐标系方向,动作尚未开始时为空。
|
||||
/// </summary>
|
||||
public double? ResolvedMotionDirectionInBodyRadians
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置轨迹正常完成后是否停车并将舵轮主动恢复到车头方向。
|
||||
@@ -270,6 +284,12 @@ namespace MultiWheelC
|
||||
config.ParkingExecutionTimeoutSeconds;
|
||||
|
||||
ValidateParameters(executionTimeoutSeconds);
|
||||
var motionDirectionInBodyRadians =
|
||||
MotionDirectionInBodyRadians ??
|
||||
ResolveFixedMotionDirectionInBodyRadians(
|
||||
Trajectory);
|
||||
ResolvedMotionDirectionInBodyRadians =
|
||||
motionDirectionInBodyRadians;
|
||||
|
||||
var chassis =
|
||||
PilotDefinition.Chassis as MultiWheelChassis;
|
||||
@@ -283,7 +303,7 @@ namespace MultiWheelC
|
||||
new PrepareWheelsForward
|
||||
{
|
||||
DirectionRadians =
|
||||
MotionDirectionInBodyRadians
|
||||
motionDirectionInBodyRadians
|
||||
};
|
||||
foreach (var keepRunning in wheelPreparation.Get())
|
||||
{
|
||||
@@ -306,7 +326,7 @@ namespace MultiWheelC
|
||||
PilotDefinition.Self.CarNum);
|
||||
|
||||
adapter.ActivateMotionFrame(
|
||||
MotionDirectionInBodyRadians);
|
||||
motionDirectionInBodyRadians);
|
||||
|
||||
var stateProvider =
|
||||
StateProvider ??
|
||||
@@ -348,7 +368,7 @@ namespace MultiWheelC
|
||||
new GcpCommandExecutor(
|
||||
adapter,
|
||||
maximumGcpAngleRateRadiansPerSecond,
|
||||
MotionDirectionInBodyRadians);
|
||||
motionDirectionInBodyRadians);
|
||||
|
||||
Controller = new ParkingGeometricController(
|
||||
stateProvider,
|
||||
@@ -366,7 +386,7 @@ namespace MultiWheelC
|
||||
maximumTerminalApproachSpeedMetersPerSecond,
|
||||
stanleyCurvaturePreviewSeconds,
|
||||
stanleyMaximumCurvaturePreviewMeters,
|
||||
MotionDirectionInBodyRadians);
|
||||
motionDirectionInBodyRadians);
|
||||
|
||||
var clock = Stopwatch.StartNew();
|
||||
var previousCycleSeconds =
|
||||
@@ -478,9 +498,12 @@ namespace MultiWheelC
|
||||
"新版轨迹跟踪动作没有设置Trajectory。");
|
||||
}
|
||||
|
||||
NumericGuard.EnsureFinite(
|
||||
MotionDirectionInBodyRadians,
|
||||
nameof(MotionDirectionInBodyRadians));
|
||||
if (MotionDirectionInBodyRadians.HasValue)
|
||||
{
|
||||
NumericGuard.EnsureFinite(
|
||||
MotionDirectionInBodyRadians.Value,
|
||||
nameof(MotionDirectionInBodyRadians));
|
||||
}
|
||||
|
||||
if (double.IsNaN(executionTimeoutSeconds) ||
|
||||
double.IsInfinity(executionTimeoutSeconds) ||
|
||||
@@ -491,5 +514,114 @@ namespace MultiWheelC
|
||||
"轨迹跟踪超时时间必须是正有限值。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据轨迹切线、参考车身航向和速度符号推导整段轨迹共同使用的固定运动方向。
|
||||
/// </summary>
|
||||
private static double ResolveFixedMotionDirectionInBodyRadians(
|
||||
Trajectory2D trajectory)
|
||||
{
|
||||
double? resolvedDirectionRadians = null;
|
||||
|
||||
for (var index = 0;
|
||||
index < trajectory.Count - 1;
|
||||
index++)
|
||||
{
|
||||
var segmentStart = trajectory[index];
|
||||
var segmentEnd = trajectory[index + 1];
|
||||
var travelDirection = ResolveSegmentTravelDirection(
|
||||
segmentStart.ReferenceSpeedMetersPerSecond,
|
||||
segmentEnd.ReferenceSpeedMetersPerSecond,
|
||||
index);
|
||||
if (travelDirection == 0.0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var tangentYawRadians = Math.Atan2(
|
||||
segmentEnd.PoseInWorld.YMeters -
|
||||
segmentStart.PoseInWorld.YMeters,
|
||||
segmentEnd.PoseInWorld.XMeters -
|
||||
segmentStart.PoseInWorld.XMeters);
|
||||
var positiveMotionAxisYawRadians =
|
||||
travelDirection > 0.0
|
||||
? tangentYawRadians
|
||||
: AngleMath.NormalizeRadians(
|
||||
tangentYawRadians + Math.PI);
|
||||
var referenceBodyYawRadians =
|
||||
AngleMath.LerpRadians(
|
||||
segmentStart.PoseInWorld.YawRadians,
|
||||
segmentEnd.PoseInWorld.YawRadians,
|
||||
0.5);
|
||||
var candidateDirectionRadians =
|
||||
AngleMath.ShortestDifferenceRadians(
|
||||
positiveMotionAxisYawRadians,
|
||||
referenceBodyYawRadians);
|
||||
|
||||
if (!resolvedDirectionRadians.HasValue)
|
||||
{
|
||||
resolvedDirectionRadians =
|
||||
candidateDirectionRadians;
|
||||
continue;
|
||||
}
|
||||
|
||||
var directionDifferenceRadians = Math.Abs(
|
||||
AngleMath.ShortestDifferenceRadians(
|
||||
candidateDirectionRadians,
|
||||
resolvedDirectionRadians.Value));
|
||||
if (directionDifferenceRadians >
|
||||
FixedMotionDirectionToleranceRadians)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"轨迹无法由一个固定运动坐标系执行:" +
|
||||
$"第{index + 1}段需要的方向与起始方向相差" +
|
||||
$"{AngleMath.RadiansToDegrees(directionDifferenceRadians):F2}°。" +
|
||||
"请拆分轨迹,或显式指定并验证MotionDirectionInBodyRadians。");
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedDirectionRadians.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"轨迹没有非零参考速度线段,无法自动确定运动坐标系方向。");
|
||||
}
|
||||
|
||||
return resolvedDirectionRadians.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从相邻轨迹点的有符号参考速度确定该线段的执行方向。
|
||||
/// </summary>
|
||||
private static double ResolveSegmentTravelDirection(
|
||||
double startSpeedMetersPerSecond,
|
||||
double endSpeedMetersPerSecond,
|
||||
int segmentStartIndex)
|
||||
{
|
||||
var hasStartDirection =
|
||||
Math.Abs(startSpeedMetersPerSecond) >
|
||||
ReferenceSpeedDeadbandMetersPerSecond;
|
||||
var hasEndDirection =
|
||||
Math.Abs(endSpeedMetersPerSecond) >
|
||||
ReferenceSpeedDeadbandMetersPerSecond;
|
||||
|
||||
if (hasStartDirection &&
|
||||
hasEndDirection &&
|
||||
Math.Sign(startSpeedMetersPerSecond) !=
|
||||
Math.Sign(endSpeedMetersPerSecond))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"轨迹第{segmentStartIndex + 1}段内参考速度发生正负切换," +
|
||||
"无法自动确定固定运动坐标系;请在零速点拆分动作段。");
|
||||
}
|
||||
|
||||
if (hasStartDirection)
|
||||
{
|
||||
return Math.Sign(startSpeedMetersPerSecond);
|
||||
}
|
||||
|
||||
return hasEndDirection
|
||||
? Math.Sign(endSpeedMetersPerSecond)
|
||||
: 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user