增加车队轨迹控制核心与轮组自转模式
This commit is contained in:
@@ -12,15 +12,30 @@ using MultiWheelC.StateEstimation;
|
||||
namespace MultiWheelC
|
||||
{
|
||||
/// <summary>
|
||||
/// 将四个舵轮准备到自转姿态并按世界航向闭环旋转,正常完成后等待舵轮回正。
|
||||
/// 指定原地自转使用Detour绝对航向或轮组相对角度反馈。
|
||||
/// </summary>
|
||||
public enum InPlaceRotationFeedbackMode
|
||||
{
|
||||
DetourAbsoluteHeading,
|
||||
RelativeWheelOdometry
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将四个舵轮准备到自转姿态,按所选反馈旋转并在完成后等待舵轮回正。
|
||||
/// </summary>
|
||||
public class MultiWheelRotateInPlace : MovementDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 旋转目标角度
|
||||
/// Detour模式表示世界目标航向,轮组模式表示有符号相对旋转角度,单位deg。
|
||||
/// </summary>
|
||||
public float AngleTarget;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置原地自转反馈模式;默认保持现有Detour绝对航向闭环。
|
||||
/// </summary>
|
||||
public InPlaceRotationFeedbackMode FeedbackMode =
|
||||
InPlaceRotationFeedbackMode.DetourAbsoluteHeading;
|
||||
|
||||
// 留作标定或单元测试时显式替换;为空时使用配置化Detour与电机反馈组合状态源。
|
||||
public Func<float> ThetaReader;
|
||||
|
||||
@@ -152,10 +167,32 @@ namespace MultiWheelC
|
||||
adapter.LastFailureReason);
|
||||
}
|
||||
|
||||
var useRelativeWheelOdometry =
|
||||
FeedbackMode ==
|
||||
InPlaceRotationFeedbackMode
|
||||
.RelativeWheelOdometry;
|
||||
var wheelStateProvider =
|
||||
useRelativeWheelOdometry
|
||||
? stateProvider as
|
||||
WheelFeedbackVehicleStateProvider
|
||||
: null;
|
||||
if (useRelativeWheelOdometry &&
|
||||
wheelStateProvider == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"轮组相对角度自转需要" +
|
||||
"WheelFeedbackVehicleStateProvider。");
|
||||
}
|
||||
|
||||
var targetAngle =
|
||||
(float)AngleMath.NormalizeDegrees(AngleTarget);
|
||||
useRelativeWheelOdometry
|
||||
? AngleTarget
|
||||
: (float)AngleMath.NormalizeDegrees(
|
||||
AngleTarget);
|
||||
var currentAngle =
|
||||
ReadCurrentAngleDegrees(stateProvider);
|
||||
useRelativeWheelOdometry
|
||||
? 0f
|
||||
: ReadCurrentAngleDegrees(stateProvider);
|
||||
var cachedCurrentAngle = currentAngle;
|
||||
thPid = new PIDController(
|
||||
() => cachedCurrentAngle,
|
||||
@@ -170,6 +207,10 @@ namespace MultiWheelC
|
||||
pidParameters.SpeedAccPerSec);
|
||||
var lastCommandTime = DateTime.Now;
|
||||
var rotationStarted = DateTime.Now;
|
||||
var accumulatedWheelAngleRadians = 0.0;
|
||||
var previousWheelOmegaRadiansPerSecond = 0.0;
|
||||
var previousWheelTimestampSeconds = 0.0;
|
||||
var hasPreviousWheelSample = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -181,15 +222,74 @@ namespace MultiWheelC
|
||||
$"原地自转超过{rotationTimeoutSeconds:F1}s仍未到位。");
|
||||
}
|
||||
|
||||
currentAngle =
|
||||
ReadCurrentAngleDegrees(stateProvider);
|
||||
if (useRelativeWheelOdometry)
|
||||
{
|
||||
if (!wheelStateProvider.TryGetWheelTwist(
|
||||
out var wheelTwist,
|
||||
out var wheelTimestampSeconds))
|
||||
{
|
||||
CommandAngularSpeedObserver?.Invoke(0f);
|
||||
adapter
|
||||
.StopXYThDrivePreserveSteeringState();
|
||||
|
||||
if (hasPreviousWheelSample)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"原地自转期间轮组角速度不可用:" +
|
||||
wheelStateProvider.LastFailureReason);
|
||||
}
|
||||
|
||||
yield return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasPreviousWheelSample)
|
||||
{
|
||||
var wheelDeltaTimeSeconds =
|
||||
wheelTimestampSeconds -
|
||||
previousWheelTimestampSeconds;
|
||||
if (!NumericGuard.IsFinite(
|
||||
wheelDeltaTimeSeconds) ||
|
||||
wheelDeltaTimeSeconds <= 0.0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"轮组角速度采样时间没有单调递增。");
|
||||
}
|
||||
|
||||
accumulatedWheelAngleRadians +=
|
||||
0.5 *
|
||||
(previousWheelOmegaRadiansPerSecond +
|
||||
wheelTwist
|
||||
.OmegaRadiansPerSecond) *
|
||||
wheelDeltaTimeSeconds;
|
||||
}
|
||||
|
||||
previousWheelOmegaRadiansPerSecond =
|
||||
wheelTwist.OmegaRadiansPerSecond;
|
||||
previousWheelTimestampSeconds =
|
||||
wheelTimestampSeconds;
|
||||
hasPreviousWheelSample = true;
|
||||
currentAngle =
|
||||
(float)AngleMath.RadiansToDegrees(
|
||||
accumulatedWheelAngleRadians);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentAngle =
|
||||
ReadCurrentAngleDegrees(stateProvider);
|
||||
}
|
||||
|
||||
cachedCurrentAngle = currentAngle;
|
||||
var s = thPid.GetResponse(targetAngle, true);
|
||||
var s = thPid.GetResponse(
|
||||
targetAngle,
|
||||
!useRelativeWheelOdometry);
|
||||
var angleErrorDegrees =
|
||||
(float)AngleMath
|
||||
.ShortestDifferenceDegrees(
|
||||
targetAngle,
|
||||
currentAngle);
|
||||
useRelativeWheelOdometry
|
||||
? targetAngle - currentAngle
|
||||
: (float)AngleMath
|
||||
.ShortestDifferenceDegrees(
|
||||
targetAngle,
|
||||
currentAngle);
|
||||
|
||||
// PID进入到位死区后等待其0.3s稳定确认;等待期间
|
||||
// 只清零驱动速度,不清除已经准备好的自转舵角状态。
|
||||
@@ -255,7 +355,8 @@ namespace MultiWheelC
|
||||
CommandAngularSpeedObserver?.Invoke(0f);
|
||||
adapter.StopXYThDrivePreserveSteeringState();
|
||||
|
||||
if (IsLocalizationRecoveryPending(
|
||||
if (!useRelativeWheelOdometry &&
|
||||
IsLocalizationRecoveryPending(
|
||||
stateProvider))
|
||||
{
|
||||
BeginPostRotationPositionRecovery(
|
||||
@@ -309,7 +410,10 @@ namespace MultiWheelC
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$"final rotate to {targetAngle}, wheels forward");
|
||||
useRelativeWheelOdometry
|
||||
? "final relative wheel rotate to " +
|
||||
$"{currentAngle:F2}deg, wheels forward"
|
||||
: $"final rotate to {targetAngle}, wheels forward");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -345,6 +449,33 @@ namespace MultiWheelC
|
||||
rotationTimeoutSeconds,
|
||||
nameof(RotationTimeoutSeconds));
|
||||
|
||||
if (float.IsNaN(AngleTarget) ||
|
||||
float.IsInfinity(AngleTarget))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(AngleTarget),
|
||||
"原地自转目标角度必须是有限值。");
|
||||
}
|
||||
|
||||
if (!Enum.IsDefined(
|
||||
typeof(InPlaceRotationFeedbackMode),
|
||||
FeedbackMode))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(FeedbackMode),
|
||||
"原地自转反馈模式无效。");
|
||||
}
|
||||
|
||||
if (FeedbackMode ==
|
||||
InPlaceRotationFeedbackMode
|
||||
.RelativeWheelOdometry &&
|
||||
Math.Abs(AngleTarget) >= 180f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(AngleTarget),
|
||||
"轮组相对自转角度必须满足-180° < angle < 180°。");
|
||||
}
|
||||
|
||||
if (pidParameters == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
||||
Reference in New Issue
Block a user