控制器测试横向跟踪,增加记录
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -47,11 +47,11 @@ namespace MultiWheelC.Control.Execution
|
|||||||
ILongitudinalController longitudinalController,
|
ILongitudinalController longitudinalController,
|
||||||
GcpCommandAllocator gcpAllocator,
|
GcpCommandAllocator gcpAllocator,
|
||||||
GcpCommandExecutor commandExecutor,
|
GcpCommandExecutor commandExecutor,
|
||||||
double finishDistanceMeters = 0.03,
|
double finishDistanceMeters = 0.04,
|
||||||
double finishSpeedMetersPerSecond = 0.02,
|
double finishSpeedMetersPerSecond = 0.02,
|
||||||
double finishHeadingToleranceRadians =
|
double finishHeadingToleranceRadians =
|
||||||
3.0 * Math.PI / 180.0,
|
3.0 * Math.PI / 180.0,
|
||||||
double maximumDistanceToTrajectoryMeters = 0.50)
|
double maximumDistanceToTrajectoryMeters = 0.30)
|
||||||
{
|
{
|
||||||
_stateProvider = stateProvider ??
|
_stateProvider = stateProvider ??
|
||||||
throw new ArgumentNullException(
|
throw new ArgumentNullException(
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace MultiWheelC.Control.Lateral
|
|||||||
public double MinimumSpeedMetersPerSecond { get; }
|
public double MinimumSpeedMetersPerSecond { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取是否优先使用Detour估算的实际纵向速度计算横向修正。
|
/// 获取是否优先使用当前状态源提供的实际纵向速度计算横向修正。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseActualSpeedForGain { get; }
|
public bool UseActualSpeedForGain { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace MultiWheelC
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 测试平滑左转轨迹、停车原地左转90°和再次直行的组合运动执行过程。
|
/// 测试平滑左转轨迹、停车原地左转90°和再次直行的组合运动执行过程。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MovementTest(name = "新版控制器:曲线-停车自转-直线组合测试")]
|
[MovementTest(name = "新版控制器:直线-圆弧-折线组合测试")]
|
||||||
public sealed class CompositeStopTurnGoTest : MovementTest
|
public sealed class CompositeStopTurnGoTest : MovementTest
|
||||||
{
|
{
|
||||||
private const float MillimetersPerMeter = 1000f;
|
private const float MillimetersPerMeter = 1000f;
|
||||||
@@ -52,6 +52,13 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!TrajectoryExperimentInput
|
||||||
|
.TryReadLateralOffsetMeters(
|
||||||
|
out var lateralOffsetMeters))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!MovementTestPreparation.AreWheelsForward())
|
if (!MovementTestPreparation.AreWheelsForward())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -65,19 +72,30 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stateProvider = new DetourVehicleStateProvider();
|
var detourStateProvider =
|
||||||
if (!stateProvider.TryGetState(out var initialState))
|
new DetourVehicleStateProvider();
|
||||||
|
if (!detourStateProvider.TryGetState(
|
||||||
|
out var initialState))
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"无法读取组合运动起点位姿:" +
|
"无法读取组合运动起点位姿:" +
|
||||||
stateProvider.LastFailureReason);
|
detourStateProvider.LastFailureReason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stateProvider =
|
||||||
|
new WheelFeedbackVehicleStateProvider(
|
||||||
|
detourStateProvider,
|
||||||
|
chassis);
|
||||||
|
|
||||||
|
var planStartPose =
|
||||||
|
TrajectoryExperimentInput.OffsetPoseLaterally(
|
||||||
|
initialState.PoseInWorld,
|
||||||
|
lateralOffsetMeters);
|
||||||
var firstTrajectory =
|
var firstTrajectory =
|
||||||
TestTrajectoryFactory
|
TestTrajectoryFactory
|
||||||
.CreateStraightSmoothLeftTurnStraight(
|
.CreateStraightSmoothLeftTurnStraight(
|
||||||
initialState.PoseInWorld,
|
planStartPose,
|
||||||
StraightLengthMeters,
|
StraightLengthMeters,
|
||||||
TurnRadiusMeters,
|
TurnRadiusMeters,
|
||||||
AngleMath.DegreesToRadians(
|
AngleMath.DegreesToRadians(
|
||||||
@@ -133,7 +151,9 @@ namespace MultiWheelC
|
|||||||
_recorder = new TrackingExperimentRecorder(
|
_recorder = new TrackingExperimentRecorder(
|
||||||
controllerName: "NewStanleyPidComposite",
|
controllerName: "NewStanleyPidComposite",
|
||||||
trajectoryName:
|
trajectoryName:
|
||||||
|
TrajectoryExperimentInput.BuildTrajectoryName(
|
||||||
"SmoothTurnStopRotateStraight",
|
"SmoothTurnStopRotateStraight",
|
||||||
|
lateralOffsetMeters),
|
||||||
trialNumber: TrialNumber,
|
trialNumber: TrialNumber,
|
||||||
referenceStart: ToMillimeterVector(
|
referenceStart: ToMillimeterVector(
|
||||||
firstTrajectory.StartPoint.PoseInWorld),
|
firstTrajectory.StartPoint.PoseInWorld),
|
||||||
@@ -145,7 +165,8 @@ namespace MultiWheelC
|
|||||||
referenceAccelerationMetersPerSecondSquared:
|
referenceAccelerationMetersPerSecondSquared:
|
||||||
(float)AccelerationMetersPerSecondSquared,
|
(float)AccelerationMetersPerSecondSquared,
|
||||||
referenceDecelerationMetersPerSecondSquared:
|
referenceDecelerationMetersPerSecondSquared:
|
||||||
(float)DecelerationMetersPerSecondSquared);
|
(float)DecelerationMetersPerSecondSquared,
|
||||||
|
diagnosticChassis: chassis);
|
||||||
_recorder.Start();
|
_recorder.Start();
|
||||||
|
|
||||||
var controlPointRadiusMeters =
|
var controlPointRadiusMeters =
|
||||||
@@ -157,6 +178,7 @@ namespace MultiWheelC
|
|||||||
StateProvider = stateProvider,
|
StateProvider = stateProvider,
|
||||||
ConfigureTrackingMovement = tracking =>
|
ConfigureTrackingMovement = tracking =>
|
||||||
{
|
{
|
||||||
|
tracking.StanleyUsesActualSpeed = true;
|
||||||
tracking.MaximumCommandSpeedMetersPerSecond =
|
tracking.MaximumCommandSpeedMetersPerSecond =
|
||||||
StraightMaximumSpeedMetersPerSecond;
|
StraightMaximumSpeedMetersPerSecond;
|
||||||
tracking
|
tracking
|
||||||
@@ -166,6 +188,7 @@ namespace MultiWheelC
|
|||||||
SegmentStarted = (index, segment) =>
|
SegmentStarted = (index, segment) =>
|
||||||
{
|
{
|
||||||
_recorder?.ClearControlReference();
|
_recorder?.ClearControlReference();
|
||||||
|
_recorder?.ClearGcpCommand();
|
||||||
_recorder?.UpdateCommand(0f, 0f);
|
_recorder?.UpdateCommand(0f, 0f);
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"组合运动开始第{index + 1}段:" +
|
$"组合运动开始第{index + 1}段:" +
|
||||||
@@ -174,7 +197,8 @@ namespace MultiWheelC
|
|||||||
TrackingCycleObserver = (index, controller) =>
|
TrackingCycleObserver = (index, controller) =>
|
||||||
RecordTrackingCycle(
|
RecordTrackingCycle(
|
||||||
controller,
|
controller,
|
||||||
controlPointRadiusMeters),
|
controlPointRadiusMeters,
|
||||||
|
stateProvider),
|
||||||
RotationCommandObserver = (index, omega) =>
|
RotationCommandObserver = (index, omega) =>
|
||||||
_recorder?.UpdateCommand(
|
_recorder?.UpdateCommand(
|
||||||
0f,
|
0f,
|
||||||
@@ -276,7 +300,8 @@ namespace MultiWheelC
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void RecordTrackingCycle(
|
private void RecordTrackingCycle(
|
||||||
ParkingGeometricController controller,
|
ParkingGeometricController controller,
|
||||||
double controlPointRadiusMeters)
|
double controlPointRadiusMeters,
|
||||||
|
WheelFeedbackVehicleStateProvider stateProvider)
|
||||||
{
|
{
|
||||||
if (controller.LastVehicleState.HasValue)
|
if (controller.LastVehicleState.HasValue)
|
||||||
{
|
{
|
||||||
@@ -284,6 +309,21 @@ namespace MultiWheelC
|
|||||||
controller.LastVehicleState.Value);
|
controller.LastVehicleState.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stateProvider.TryGetLatestVelocityDiagnostics(
|
||||||
|
out var detourBodyVx,
|
||||||
|
out var detourVelocityValid,
|
||||||
|
out var rawWheelBodyVx,
|
||||||
|
out var filteredWheelBodyVx,
|
||||||
|
out var wheelVelocityValid))
|
||||||
|
{
|
||||||
|
_recorder?.UpdateVelocityDiagnostics(
|
||||||
|
detourBodyVx,
|
||||||
|
detourVelocityValid,
|
||||||
|
rawWheelBodyVx,
|
||||||
|
filteredWheelBodyVx,
|
||||||
|
wheelVelocityValid);
|
||||||
|
}
|
||||||
|
|
||||||
if (!controller.LastCommand.HasValue)
|
if (!controller.LastCommand.HasValue)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -305,6 +345,9 @@ namespace MultiWheelC
|
|||||||
}
|
}
|
||||||
|
|
||||||
var command = controller.LastCommand.Value;
|
var command = controller.LastCommand.Value;
|
||||||
|
_recorder?.UpdateGcpCommand(
|
||||||
|
command.FrontAngleRadians,
|
||||||
|
command.RearAngleRadians);
|
||||||
var curvaturePerMeter = Math.Tan(
|
var curvaturePerMeter = Math.Tan(
|
||||||
command.FrontAngleRadians) /
|
command.FrontAngleRadians) /
|
||||||
controlPointRadiusMeters;
|
controlPointRadiusMeters;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ClumsyCore;
|
using ClumsyCore;
|
||||||
@@ -13,6 +14,82 @@ using MyParking.Shared;
|
|||||||
|
|
||||||
namespace MultiWheelC
|
namespace MultiWheelC
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一读取轨迹实验的有符号横向偏移,并将车体局部偏移转换到世界坐标系。
|
||||||
|
/// </summary>
|
||||||
|
internal static class TrajectoryExperimentInput
|
||||||
|
{
|
||||||
|
private const double MaximumOffsetCentimeters = 30.0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从Clumsy输入框读取车体左正右负的横向偏移,单位转换为m。
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryReadLateralOffsetMeters(
|
||||||
|
out double lateralOffsetMeters)
|
||||||
|
{
|
||||||
|
lateralOffsetMeters = 0.0;
|
||||||
|
var input = UI.GetInput(
|
||||||
|
"输入轨迹横向偏移(cm,左正右负,范围-30~30):");
|
||||||
|
var parsed = double.TryParse(
|
||||||
|
input,
|
||||||
|
NumberStyles.Float,
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
out var offsetCentimeters) ||
|
||||||
|
double.TryParse(
|
||||||
|
input,
|
||||||
|
NumberStyles.Float,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
out offsetCentimeters);
|
||||||
|
|
||||||
|
if (!parsed ||
|
||||||
|
double.IsNaN(offsetCentimeters) ||
|
||||||
|
double.IsInfinity(offsetCentimeters) ||
|
||||||
|
Math.Abs(offsetCentimeters) >
|
||||||
|
MaximumOffsetCentimeters)
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"轨迹横向偏移必须是-30~30cm之间的有限数值,测试未启动。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lateralOffsetMeters =
|
||||||
|
offsetCentimeters / 100.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 沿初始车体左方向平移参考轨迹起点,同时保持世界坐标航向不变。
|
||||||
|
/// </summary>
|
||||||
|
public static Pose2D OffsetPoseLaterally(
|
||||||
|
Pose2D poseInWorld,
|
||||||
|
double lateralOffsetMeters)
|
||||||
|
{
|
||||||
|
var yawRadians = poseInWorld.YawRadians;
|
||||||
|
return new Pose2D(
|
||||||
|
poseInWorld.XMeters -
|
||||||
|
Math.Sin(yawRadians) *
|
||||||
|
lateralOffsetMeters,
|
||||||
|
poseInWorld.YMeters +
|
||||||
|
Math.Cos(yawRadians) *
|
||||||
|
lateralOffsetMeters,
|
||||||
|
yawRadians);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成带毫米偏移标识的实验轨迹名称。
|
||||||
|
/// </summary>
|
||||||
|
public static string BuildTrajectoryName(
|
||||||
|
string baseName,
|
||||||
|
double lateralOffsetMeters)
|
||||||
|
{
|
||||||
|
return baseName +
|
||||||
|
"_Offset" +
|
||||||
|
(lateralOffsetMeters * 1000.0)
|
||||||
|
.ToString("+0;-0;0", CultureInfo.InvariantCulture) +
|
||||||
|
"mm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从当前Detour位姿开始执行新版控制器4m直线跟踪并保存实验数据。
|
/// 从当前Detour位姿开始执行新版控制器4m直线跟踪并保存实验数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -27,7 +104,7 @@ namespace MultiWheelC
|
|||||||
|
|
||||||
private DriveTask _task;
|
private DriveTask _task;
|
||||||
private TrackingExperimentRecorder _recorder;
|
private TrackingExperimentRecorder _recorder;
|
||||||
private DetourVehicleStateProvider _stateProvider;
|
private IVehicleStateProvider _stateProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置本次测试编号,用于区分重复实验CSV。
|
/// 获取或设置本次测试编号,用于区分重复实验CSV。
|
||||||
@@ -37,7 +114,7 @@ namespace MultiWheelC
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置4m直线的巡航参考速度,单位为m/s。
|
/// 获取或设置4m直线的巡航参考速度,单位为m/s。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double CruiseSpeedMetersPerSecond = 0.30;
|
public double CruiseSpeedMetersPerSecond = 0.40;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置参考速度加速度,单位为m/s²。
|
/// 获取或设置参考速度加速度,单位为m/s²。
|
||||||
@@ -66,6 +143,13 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!TrajectoryExperimentInput
|
||||||
|
.TryReadLateralOffsetMeters(
|
||||||
|
out var lateralOffsetMeters))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!MovementTestPreparation.AreWheelsForward())
|
if (!MovementTestPreparation.AreWheelsForward())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -80,21 +164,30 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateProvider =
|
var detourStateProvider =
|
||||||
new DetourVehicleStateProvider();
|
new DetourVehicleStateProvider();
|
||||||
if (!_stateProvider.TryGetState(
|
if (!detourStateProvider.TryGetState(
|
||||||
out var initialState))
|
out var initialState))
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"无法读取有效Detour起点位姿:" +
|
"无法读取有效Detour起点位姿:" +
|
||||||
_stateProvider.LastFailureReason);
|
detourStateProvider.LastFailureReason);
|
||||||
_stateProvider = null;
|
_stateProvider = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_stateProvider =
|
||||||
|
new WheelFeedbackVehicleStateProvider(
|
||||||
|
detourStateProvider,
|
||||||
|
chassis);
|
||||||
|
|
||||||
|
var trajectoryStartPose =
|
||||||
|
TrajectoryExperimentInput.OffsetPoseLaterally(
|
||||||
|
initialState.PoseInWorld,
|
||||||
|
lateralOffsetMeters);
|
||||||
var trajectory =
|
var trajectory =
|
||||||
TestTrajectoryFactory.CreateStraight4Meters(
|
TestTrajectoryFactory.CreateStraight4Meters(
|
||||||
initialState.PoseInWorld,
|
trajectoryStartPose,
|
||||||
CruiseSpeedMetersPerSecond,
|
CruiseSpeedMetersPerSecond,
|
||||||
AccelerationMetersPerSecondSquared,
|
AccelerationMetersPerSecondSquared,
|
||||||
DecelerationMetersPerSecondSquared,
|
DecelerationMetersPerSecondSquared,
|
||||||
@@ -109,7 +202,10 @@ namespace MultiWheelC
|
|||||||
var recorder =
|
var recorder =
|
||||||
new TrackingExperimentRecorder(
|
new TrackingExperimentRecorder(
|
||||||
controllerName: "NewStanleyPid",
|
controllerName: "NewStanleyPid",
|
||||||
trajectoryName: "ProfiledStraight4m",
|
trajectoryName:
|
||||||
|
TrajectoryExperimentInput.BuildTrajectoryName(
|
||||||
|
"ProfiledStraight4m",
|
||||||
|
lateralOffsetMeters),
|
||||||
trialNumber: TrialNumber,
|
trialNumber: TrialNumber,
|
||||||
referenceStart: referenceStart,
|
referenceStart: referenceStart,
|
||||||
referenceEnd: referenceEnd,
|
referenceEnd: referenceEnd,
|
||||||
@@ -119,7 +215,8 @@ namespace MultiWheelC
|
|||||||
referenceAccelerationMetersPerSecondSquared:
|
referenceAccelerationMetersPerSecondSquared:
|
||||||
(float)AccelerationMetersPerSecondSquared,
|
(float)AccelerationMetersPerSecondSquared,
|
||||||
referenceDecelerationMetersPerSecondSquared:
|
referenceDecelerationMetersPerSecondSquared:
|
||||||
(float)DecelerationMetersPerSecondSquared);
|
(float)DecelerationMetersPerSecondSquared,
|
||||||
|
diagnosticChassis: chassis);
|
||||||
_recorder = recorder;
|
_recorder = recorder;
|
||||||
|
|
||||||
var controlPointRadiusMeters =
|
var controlPointRadiusMeters =
|
||||||
@@ -130,12 +227,15 @@ namespace MultiWheelC
|
|||||||
{
|
{
|
||||||
Trajectory = trajectory,
|
Trajectory = trajectory,
|
||||||
StateProvider = _stateProvider,
|
StateProvider = _stateProvider,
|
||||||
|
StanleyUsesActualSpeed = true,
|
||||||
MaximumCommandSpeedMetersPerSecond = 0.50,
|
MaximumCommandSpeedMetersPerSecond = 0.50,
|
||||||
CycleObserver = controller =>
|
CycleObserver = controller =>
|
||||||
RecordControlCycle(
|
RecordControlCycle(
|
||||||
recorder,
|
recorder,
|
||||||
controller,
|
controller,
|
||||||
controlPointRadiusMeters)
|
controlPointRadiusMeters,
|
||||||
|
_stateProvider as
|
||||||
|
WheelFeedbackVehicleStateProvider)
|
||||||
};
|
};
|
||||||
|
|
||||||
recorder.Start();
|
recorder.Start();
|
||||||
@@ -239,7 +339,8 @@ namespace MultiWheelC
|
|||||||
private static void RecordControlCycle(
|
private static void RecordControlCycle(
|
||||||
TrackingExperimentRecorder recorder,
|
TrackingExperimentRecorder recorder,
|
||||||
ParkingGeometricController controller,
|
ParkingGeometricController controller,
|
||||||
double controlPointRadiusMeters)
|
double controlPointRadiusMeters,
|
||||||
|
WheelFeedbackVehicleStateProvider stateProvider)
|
||||||
{
|
{
|
||||||
if (controller.LastVehicleState.HasValue)
|
if (controller.LastVehicleState.HasValue)
|
||||||
{
|
{
|
||||||
@@ -247,6 +348,10 @@ namespace MultiWheelC
|
|||||||
controller.LastVehicleState.Value);
|
controller.LastVehicleState.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateVelocityDiagnostics(
|
||||||
|
recorder,
|
||||||
|
stateProvider);
|
||||||
|
|
||||||
if (!controller.LastCommand.HasValue)
|
if (!controller.LastCommand.HasValue)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -267,6 +372,9 @@ namespace MultiWheelC
|
|||||||
}
|
}
|
||||||
|
|
||||||
var command = controller.LastCommand.Value;
|
var command = controller.LastCommand.Value;
|
||||||
|
recorder.UpdateGcpCommand(
|
||||||
|
command.FrontAngleRadians,
|
||||||
|
command.RearAngleRadians);
|
||||||
var curvaturePerMeter = Math.Tan(
|
var curvaturePerMeter = Math.Tan(
|
||||||
command.FrontAngleRadians) /
|
command.FrontAngleRadians) /
|
||||||
controlPointRadiusMeters;
|
controlPointRadiusMeters;
|
||||||
@@ -279,6 +387,32 @@ namespace MultiWheelC
|
|||||||
(float)angularSpeedRadiansPerSecond);
|
(float)angularSpeedRadiansPerSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将同一周期的Detour速度和轮速解算速度写入实验记录器。
|
||||||
|
/// </summary>
|
||||||
|
private static void UpdateVelocityDiagnostics(
|
||||||
|
TrackingExperimentRecorder recorder,
|
||||||
|
WheelFeedbackVehicleStateProvider stateProvider)
|
||||||
|
{
|
||||||
|
if (stateProvider == null ||
|
||||||
|
!stateProvider.TryGetLatestVelocityDiagnostics(
|
||||||
|
out var detourBodyVx,
|
||||||
|
out var detourVelocityValid,
|
||||||
|
out var rawWheelBodyVx,
|
||||||
|
out var filteredWheelBodyVx,
|
||||||
|
out var wheelVelocityValid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder.UpdateVelocityDiagnostics(
|
||||||
|
detourBodyVx,
|
||||||
|
detourVelocityValid,
|
||||||
|
rawWheelBodyVx,
|
||||||
|
filteredWheelBodyVx,
|
||||||
|
wheelVelocityValid);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将Shared世界坐标系米制位姿转换为Clumsy绘图和旧记录器使用的毫米坐标。
|
/// 将Shared世界坐标系米制位姿转换为Clumsy绘图和旧记录器使用的毫米坐标。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -293,6 +427,7 @@ namespace MultiWheelC
|
|||||||
poseInWorld.YMeters *
|
poseInWorld.YMeters *
|
||||||
MillimetersPerMeter));
|
MillimetersPerMeter));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -310,7 +445,7 @@ namespace MultiWheelC
|
|||||||
|
|
||||||
private DriveTask _task;
|
private DriveTask _task;
|
||||||
private TrackingExperimentRecorder _recorder;
|
private TrackingExperimentRecorder _recorder;
|
||||||
private DetourVehicleStateProvider _stateProvider;
|
private IVehicleStateProvider _stateProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置本次测试编号,用于区分重复实验CSV。
|
/// 获取或设置本次测试编号,用于区分重复实验CSV。
|
||||||
@@ -369,6 +504,13 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!TrajectoryExperimentInput
|
||||||
|
.TryReadLateralOffsetMeters(
|
||||||
|
out var lateralOffsetMeters))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!MovementTestPreparation.AreWheelsForward())
|
if (!MovementTestPreparation.AreWheelsForward())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -383,22 +525,31 @@ namespace MultiWheelC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateProvider =
|
var detourStateProvider =
|
||||||
new DetourVehicleStateProvider();
|
new DetourVehicleStateProvider();
|
||||||
if (!_stateProvider.TryGetState(
|
if (!detourStateProvider.TryGetState(
|
||||||
out var initialState))
|
out var initialState))
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"无法读取有效Detour起点位姿:" +
|
"无法读取有效Detour起点位姿:" +
|
||||||
_stateProvider.LastFailureReason);
|
detourStateProvider.LastFailureReason);
|
||||||
_stateProvider = null;
|
_stateProvider = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_stateProvider =
|
||||||
|
new WheelFeedbackVehicleStateProvider(
|
||||||
|
detourStateProvider,
|
||||||
|
chassis);
|
||||||
|
|
||||||
|
var trajectoryStartPose =
|
||||||
|
TrajectoryExperimentInput.OffsetPoseLaterally(
|
||||||
|
initialState.PoseInWorld,
|
||||||
|
lateralOffsetMeters);
|
||||||
var trajectory =
|
var trajectory =
|
||||||
TestTrajectoryFactory
|
TestTrajectoryFactory
|
||||||
.CreateStraightLeftSemicircleStraight(
|
.CreateStraightLeftSemicircleStraight(
|
||||||
initialState.PoseInWorld,
|
trajectoryStartPose,
|
||||||
StraightLengthMeters,
|
StraightLengthMeters,
|
||||||
TurnRadiusMeters,
|
TurnRadiusMeters,
|
||||||
CurvatureTransitionLengthMeters,
|
CurvatureTransitionLengthMeters,
|
||||||
@@ -418,7 +569,9 @@ namespace MultiWheelC
|
|||||||
new TrackingExperimentRecorder(
|
new TrackingExperimentRecorder(
|
||||||
controllerName: "NewStanleyPid",
|
controllerName: "NewStanleyPid",
|
||||||
trajectoryName:
|
trajectoryName:
|
||||||
|
TrajectoryExperimentInput.BuildTrajectoryName(
|
||||||
"ProfiledStraightSmoothLeftTurnStraight",
|
"ProfiledStraightSmoothLeftTurnStraight",
|
||||||
|
lateralOffsetMeters),
|
||||||
trialNumber: TrialNumber,
|
trialNumber: TrialNumber,
|
||||||
referenceStart: referenceStart,
|
referenceStart: referenceStart,
|
||||||
referenceEnd: referenceEnd,
|
referenceEnd: referenceEnd,
|
||||||
@@ -428,7 +581,8 @@ namespace MultiWheelC
|
|||||||
referenceAccelerationMetersPerSecondSquared:
|
referenceAccelerationMetersPerSecondSquared:
|
||||||
(float)AccelerationMetersPerSecondSquared,
|
(float)AccelerationMetersPerSecondSquared,
|
||||||
referenceDecelerationMetersPerSecondSquared:
|
referenceDecelerationMetersPerSecondSquared:
|
||||||
(float)DecelerationMetersPerSecondSquared);
|
(float)DecelerationMetersPerSecondSquared,
|
||||||
|
diagnosticChassis: chassis);
|
||||||
_recorder = recorder;
|
_recorder = recorder;
|
||||||
|
|
||||||
var controlPointRadiusMeters =
|
var controlPointRadiusMeters =
|
||||||
@@ -439,13 +593,16 @@ namespace MultiWheelC
|
|||||||
{
|
{
|
||||||
Trajectory = trajectory,
|
Trajectory = trajectory,
|
||||||
StateProvider = _stateProvider,
|
StateProvider = _stateProvider,
|
||||||
|
StanleyUsesActualSpeed = true,
|
||||||
MaximumCommandSpeedMetersPerSecond =
|
MaximumCommandSpeedMetersPerSecond =
|
||||||
StraightMaximumSpeedMetersPerSecond,
|
StraightMaximumSpeedMetersPerSecond,
|
||||||
CycleObserver = controller =>
|
CycleObserver = controller =>
|
||||||
RecordControlCycle(
|
RecordControlCycle(
|
||||||
recorder,
|
recorder,
|
||||||
controller,
|
controller,
|
||||||
controlPointRadiusMeters)
|
controlPointRadiusMeters,
|
||||||
|
_stateProvider as
|
||||||
|
WheelFeedbackVehicleStateProvider)
|
||||||
};
|
};
|
||||||
|
|
||||||
recorder.Start();
|
recorder.Start();
|
||||||
@@ -549,7 +706,8 @@ namespace MultiWheelC
|
|||||||
private static void RecordControlCycle(
|
private static void RecordControlCycle(
|
||||||
TrackingExperimentRecorder recorder,
|
TrackingExperimentRecorder recorder,
|
||||||
ParkingGeometricController controller,
|
ParkingGeometricController controller,
|
||||||
double controlPointRadiusMeters)
|
double controlPointRadiusMeters,
|
||||||
|
WheelFeedbackVehicleStateProvider stateProvider)
|
||||||
{
|
{
|
||||||
if (controller.LastVehicleState.HasValue)
|
if (controller.LastVehicleState.HasValue)
|
||||||
{
|
{
|
||||||
@@ -557,6 +715,10 @@ namespace MultiWheelC
|
|||||||
controller.LastVehicleState.Value);
|
controller.LastVehicleState.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateVelocityDiagnostics(
|
||||||
|
recorder,
|
||||||
|
stateProvider);
|
||||||
|
|
||||||
if (!controller.LastCommand.HasValue)
|
if (!controller.LastCommand.HasValue)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -577,6 +739,9 @@ namespace MultiWheelC
|
|||||||
}
|
}
|
||||||
|
|
||||||
var command = controller.LastCommand.Value;
|
var command = controller.LastCommand.Value;
|
||||||
|
recorder.UpdateGcpCommand(
|
||||||
|
command.FrontAngleRadians,
|
||||||
|
command.RearAngleRadians);
|
||||||
var curvaturePerMeter = Math.Tan(
|
var curvaturePerMeter = Math.Tan(
|
||||||
command.FrontAngleRadians) /
|
command.FrontAngleRadians) /
|
||||||
controlPointRadiusMeters;
|
controlPointRadiusMeters;
|
||||||
@@ -589,6 +754,32 @@ namespace MultiWheelC
|
|||||||
(float)angularSpeedRadiansPerSecond);
|
(float)angularSpeedRadiansPerSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将同一周期的Detour速度和轮速解算速度写入实验记录器。
|
||||||
|
/// </summary>
|
||||||
|
private static void UpdateVelocityDiagnostics(
|
||||||
|
TrackingExperimentRecorder recorder,
|
||||||
|
WheelFeedbackVehicleStateProvider stateProvider)
|
||||||
|
{
|
||||||
|
if (stateProvider == null ||
|
||||||
|
!stateProvider.TryGetLatestVelocityDiagnostics(
|
||||||
|
out var detourBodyVx,
|
||||||
|
out var detourVelocityValid,
|
||||||
|
out var rawWheelBodyVx,
|
||||||
|
out var filteredWheelBodyVx,
|
||||||
|
out var wheelVelocityValid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder.UpdateVelocityDiagnostics(
|
||||||
|
detourBodyVx,
|
||||||
|
detourVelocityValid,
|
||||||
|
rawWheelBodyVx,
|
||||||
|
filteredWheelBodyVx,
|
||||||
|
wheelVelocityValid);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将Shared世界坐标系米制位姿转换为Clumsy绘图和记录器使用的毫米坐标。
|
/// 将Shared世界坐标系米制位姿转换为Clumsy绘图和记录器使用的毫米坐标。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using CommonUsage.Chassis;
|
||||||
using MyParking.Shared;
|
using MyParking.Shared;
|
||||||
using MultiWheelC.StateEstimation;
|
using MultiWheelC.StateEstimation;
|
||||||
|
|
||||||
@@ -47,6 +48,28 @@ namespace MultiWheelC
|
|||||||
public double ControlHeadingErrorRadians;
|
public double ControlHeadingErrorRadians;
|
||||||
public double ControlDistanceToTrajectoryMeters;
|
public double ControlDistanceToTrajectoryMeters;
|
||||||
public double ControlRemainingDistanceMeters;
|
public double ControlRemainingDistanceMeters;
|
||||||
|
|
||||||
|
// 并列保存Detour速度与轮速解算速度,避免StateBodyVx的数据来源产生歧义。
|
||||||
|
public bool HasVelocityDiagnostics;
|
||||||
|
public double DetourEstimatedBodyVxMetersPerSecond;
|
||||||
|
public bool DetourVelocityEstimateValid;
|
||||||
|
public double WheelFeedbackRawBodyVxMetersPerSecond;
|
||||||
|
public double WheelFeedbackFilteredBodyVxMetersPerSecond;
|
||||||
|
public bool WheelFeedbackVelocityEstimateValid;
|
||||||
|
|
||||||
|
// 四舵轮机械角使用deg,前后虚拟GCP命令角使用rad。
|
||||||
|
public bool HasSteeringDiagnostics;
|
||||||
|
public double TargetSteerLeftFrontDegrees;
|
||||||
|
public double TargetSteerLeftRearDegrees;
|
||||||
|
public double TargetSteerRightFrontDegrees;
|
||||||
|
public double TargetSteerRightRearDegrees;
|
||||||
|
public double ActualSteerLeftFrontDegrees;
|
||||||
|
public double ActualSteerLeftRearDegrees;
|
||||||
|
public double ActualSteerRightFrontDegrees;
|
||||||
|
public double ActualSteerRightRearDegrees;
|
||||||
|
public bool HasGcpCommand;
|
||||||
|
public double CommandFrontGcpAngleRadians;
|
||||||
|
public double CommandRearGcpAngleRadians;
|
||||||
}
|
}
|
||||||
|
|
||||||
// C层实验工具:统一采集并保存轨迹跟踪实验数据。
|
// C层实验工具:统一采集并保存轨迹跟踪实验数据。
|
||||||
@@ -63,6 +86,7 @@ namespace MultiWheelC
|
|||||||
private readonly float _referenceAccelerationMetersPerSecondSquared;
|
private readonly float _referenceAccelerationMetersPerSecondSquared;
|
||||||
private readonly float _referenceDecelerationMetersPerSecondSquared;
|
private readonly float _referenceDecelerationMetersPerSecondSquared;
|
||||||
private readonly int _sampleIntervalMs;
|
private readonly int _sampleIntervalMs;
|
||||||
|
private readonly MultiWheelChassis _diagnosticChassis;
|
||||||
|
|
||||||
private readonly List<TrackingSample> _samples =
|
private readonly List<TrackingSample> _samples =
|
||||||
new List<TrackingSample>();
|
new List<TrackingSample>();
|
||||||
@@ -97,6 +121,15 @@ namespace MultiWheelC
|
|||||||
private double _controlHeadingErrorRadians;
|
private double _controlHeadingErrorRadians;
|
||||||
private double _controlDistanceToTrajectoryMeters;
|
private double _controlDistanceToTrajectoryMeters;
|
||||||
private double _controlRemainingDistanceMeters;
|
private double _controlRemainingDistanceMeters;
|
||||||
|
private bool _hasVelocityDiagnostics;
|
||||||
|
private double _detourEstimatedBodyVxMetersPerSecond;
|
||||||
|
private bool _detourVelocityEstimateValid;
|
||||||
|
private double _wheelFeedbackRawBodyVxMetersPerSecond;
|
||||||
|
private double _wheelFeedbackFilteredBodyVxMetersPerSecond;
|
||||||
|
private bool _wheelFeedbackVelocityEstimateValid;
|
||||||
|
private bool _hasGcpCommand;
|
||||||
|
private double _commandFrontGcpAngleRadians;
|
||||||
|
private double _commandRearGcpAngleRadians;
|
||||||
|
|
||||||
public TrackingExperimentRecorder(
|
public TrackingExperimentRecorder(
|
||||||
string controllerName,
|
string controllerName,
|
||||||
@@ -109,7 +142,8 @@ namespace MultiWheelC
|
|||||||
int sampleIntervalMs = 50,
|
int sampleIntervalMs = 50,
|
||||||
float referenceMotionFrameYawDegrees = 0f,
|
float referenceMotionFrameYawDegrees = 0f,
|
||||||
float referenceAccelerationMetersPerSecondSquared = 0f,
|
float referenceAccelerationMetersPerSecondSquared = 0f,
|
||||||
float referenceDecelerationMetersPerSecondSquared = 0f)
|
float referenceDecelerationMetersPerSecondSquared = 0f,
|
||||||
|
MultiWheelChassis diagnosticChassis = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(controllerName))
|
if (string.IsNullOrWhiteSpace(controllerName))
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
@@ -140,6 +174,7 @@ namespace MultiWheelC
|
|||||||
_referenceDecelerationMetersPerSecondSquared =
|
_referenceDecelerationMetersPerSecondSquared =
|
||||||
referenceDecelerationMetersPerSecondSquared;
|
referenceDecelerationMetersPerSecondSquared;
|
||||||
_sampleIntervalMs = sampleIntervalMs;
|
_sampleIntervalMs = sampleIntervalMs;
|
||||||
|
_diagnosticChassis = diagnosticChassis;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存成功后的CSV绝对路径;尚未保存时为空。
|
// 保存成功后的CSV绝对路径;尚未保存时为空。
|
||||||
@@ -221,6 +256,60 @@ namespace MultiWheelC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存同一控制周期的Detour纵向速度以及轮速解算的原始和滤波纵向速度。
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateVelocityDiagnostics(
|
||||||
|
double detourEstimatedBodyVxMetersPerSecond,
|
||||||
|
bool detourVelocityEstimateValid,
|
||||||
|
double wheelFeedbackRawBodyVxMetersPerSecond,
|
||||||
|
double wheelFeedbackFilteredBodyVxMetersPerSecond,
|
||||||
|
bool wheelFeedbackVelocityEstimateValid)
|
||||||
|
{
|
||||||
|
lock (_stateSyncRoot)
|
||||||
|
{
|
||||||
|
_detourEstimatedBodyVxMetersPerSecond =
|
||||||
|
detourEstimatedBodyVxMetersPerSecond;
|
||||||
|
_detourVelocityEstimateValid =
|
||||||
|
detourVelocityEstimateValid;
|
||||||
|
_wheelFeedbackRawBodyVxMetersPerSecond =
|
||||||
|
wheelFeedbackRawBodyVxMetersPerSecond;
|
||||||
|
_wheelFeedbackFilteredBodyVxMetersPerSecond =
|
||||||
|
wheelFeedbackFilteredBodyVxMetersPerSecond;
|
||||||
|
_wheelFeedbackVelocityEstimateValid =
|
||||||
|
wheelFeedbackVelocityEstimateValid;
|
||||||
|
_hasVelocityDiagnostics = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存经过GCP角速度限制后实际交给底盘的前后虚拟控制点转角。
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateGcpCommand(
|
||||||
|
double frontGcpAngleRadians,
|
||||||
|
double rearGcpAngleRadians)
|
||||||
|
{
|
||||||
|
lock (_stateSyncRoot)
|
||||||
|
{
|
||||||
|
_commandFrontGcpAngleRadians =
|
||||||
|
frontGcpAngleRadians;
|
||||||
|
_commandRearGcpAngleRadians =
|
||||||
|
rearGcpAngleRadians;
|
||||||
|
_hasGcpCommand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除上一轨迹段的GCP命令,避免停车或原地自转阶段沿用旧角度。
|
||||||
|
/// </summary>
|
||||||
|
public void ClearGcpCommand()
|
||||||
|
{
|
||||||
|
lock (_stateSyncRoot)
|
||||||
|
{
|
||||||
|
_hasGcpCommand = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存新版控制器本周期实际使用的轨迹投影、误差和参考速度。
|
/// 保存新版控制器本周期实际使用的轨迹投影、误差和参考速度。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -331,6 +420,15 @@ namespace MultiWheelC
|
|||||||
double controlHeadingErrorRadians;
|
double controlHeadingErrorRadians;
|
||||||
double controlDistanceToTrajectoryMeters;
|
double controlDistanceToTrajectoryMeters;
|
||||||
double controlRemainingDistanceMeters;
|
double controlRemainingDistanceMeters;
|
||||||
|
bool hasVelocityDiagnostics;
|
||||||
|
double detourEstimatedBodyVxMetersPerSecond;
|
||||||
|
bool detourVelocityEstimateValid;
|
||||||
|
double wheelFeedbackRawBodyVxMetersPerSecond;
|
||||||
|
double wheelFeedbackFilteredBodyVxMetersPerSecond;
|
||||||
|
bool wheelFeedbackVelocityEstimateValid;
|
||||||
|
bool hasGcpCommand;
|
||||||
|
double commandFrontGcpAngleRadians;
|
||||||
|
double commandRearGcpAngleRadians;
|
||||||
|
|
||||||
lock (_commandSyncRoot)
|
lock (_commandSyncRoot)
|
||||||
{
|
{
|
||||||
@@ -382,6 +480,23 @@ namespace MultiWheelC
|
|||||||
_controlDistanceToTrajectoryMeters;
|
_controlDistanceToTrajectoryMeters;
|
||||||
controlRemainingDistanceMeters =
|
controlRemainingDistanceMeters =
|
||||||
_controlRemainingDistanceMeters;
|
_controlRemainingDistanceMeters;
|
||||||
|
hasVelocityDiagnostics =
|
||||||
|
_hasVelocityDiagnostics;
|
||||||
|
detourEstimatedBodyVxMetersPerSecond =
|
||||||
|
_detourEstimatedBodyVxMetersPerSecond;
|
||||||
|
detourVelocityEstimateValid =
|
||||||
|
_detourVelocityEstimateValid;
|
||||||
|
wheelFeedbackRawBodyVxMetersPerSecond =
|
||||||
|
_wheelFeedbackRawBodyVxMetersPerSecond;
|
||||||
|
wheelFeedbackFilteredBodyVxMetersPerSecond =
|
||||||
|
_wheelFeedbackFilteredBodyVxMetersPerSecond;
|
||||||
|
wheelFeedbackVelocityEstimateValid =
|
||||||
|
_wheelFeedbackVelocityEstimateValid;
|
||||||
|
hasGcpCommand = _hasGcpCommand;
|
||||||
|
commandFrontGcpAngleRadians =
|
||||||
|
_commandFrontGcpAngleRadians;
|
||||||
|
commandRearGcpAngleRadians =
|
||||||
|
_commandRearGcpAngleRadians;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sample = new TrackingSample
|
var sample = new TrackingSample
|
||||||
@@ -411,9 +526,28 @@ namespace MultiWheelC
|
|||||||
ControlDistanceToTrajectoryMeters =
|
ControlDistanceToTrajectoryMeters =
|
||||||
controlDistanceToTrajectoryMeters,
|
controlDistanceToTrajectoryMeters,
|
||||||
ControlRemainingDistanceMeters =
|
ControlRemainingDistanceMeters =
|
||||||
controlRemainingDistanceMeters
|
controlRemainingDistanceMeters,
|
||||||
|
HasVelocityDiagnostics =
|
||||||
|
hasVelocityDiagnostics,
|
||||||
|
DetourEstimatedBodyVxMetersPerSecond =
|
||||||
|
detourEstimatedBodyVxMetersPerSecond,
|
||||||
|
DetourVelocityEstimateValid =
|
||||||
|
detourVelocityEstimateValid,
|
||||||
|
WheelFeedbackRawBodyVxMetersPerSecond =
|
||||||
|
wheelFeedbackRawBodyVxMetersPerSecond,
|
||||||
|
WheelFeedbackFilteredBodyVxMetersPerSecond =
|
||||||
|
wheelFeedbackFilteredBodyVxMetersPerSecond,
|
||||||
|
WheelFeedbackVelocityEstimateValid =
|
||||||
|
wheelFeedbackVelocityEstimateValid,
|
||||||
|
HasGcpCommand = hasGcpCommand,
|
||||||
|
CommandFrontGcpAngleRadians =
|
||||||
|
commandFrontGcpAngleRadians,
|
||||||
|
CommandRearGcpAngleRadians =
|
||||||
|
commandRearGcpAngleRadians
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CaptureSteeringDiagnostics(sample);
|
||||||
|
|
||||||
if (processedState.HasValue)
|
if (processedState.HasValue)
|
||||||
{
|
{
|
||||||
var state = processedState.Value;
|
var state = processedState.Value;
|
||||||
@@ -452,6 +586,90 @@ namespace MultiWheelC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按舵轮物理安装位置记录四轮目标角和实际反馈角。
|
||||||
|
/// </summary>
|
||||||
|
private void CaptureSteeringDiagnostics(
|
||||||
|
TrackingSample sample)
|
||||||
|
{
|
||||||
|
if (_diagnosticChassis == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS0612, CS0618
|
||||||
|
var wheels = _diagnosticChassis.GetSteerWheels();
|
||||||
|
#pragma warning restore CS0612, CS0618
|
||||||
|
|
||||||
|
var leftFront = FindWheel(
|
||||||
|
wheels,
|
||||||
|
requireFront: true,
|
||||||
|
requireLeft: true);
|
||||||
|
var leftRear = FindWheel(
|
||||||
|
wheels,
|
||||||
|
requireFront: false,
|
||||||
|
requireLeft: true);
|
||||||
|
var rightFront = FindWheel(
|
||||||
|
wheels,
|
||||||
|
requireFront: true,
|
||||||
|
requireLeft: false);
|
||||||
|
var rightRear = FindWheel(
|
||||||
|
wheels,
|
||||||
|
requireFront: false,
|
||||||
|
requireLeft: false);
|
||||||
|
|
||||||
|
if (leftFront == null ||
|
||||||
|
leftRear == null ||
|
||||||
|
rightFront == null ||
|
||||||
|
rightRear == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.HasSteeringDiagnostics = true;
|
||||||
|
sample.TargetSteerLeftFrontDegrees =
|
||||||
|
leftFront.GetSendAngle();
|
||||||
|
sample.TargetSteerLeftRearDegrees =
|
||||||
|
leftRear.GetSendAngle();
|
||||||
|
sample.TargetSteerRightFrontDegrees =
|
||||||
|
rightFront.GetSendAngle();
|
||||||
|
sample.TargetSteerRightRearDegrees =
|
||||||
|
rightRear.GetSendAngle();
|
||||||
|
sample.ActualSteerLeftFrontDegrees =
|
||||||
|
leftFront.ReadAngle();
|
||||||
|
sample.ActualSteerLeftRearDegrees =
|
||||||
|
leftRear.ReadAngle();
|
||||||
|
sample.ActualSteerRightFrontDegrees =
|
||||||
|
rightFront.ReadAngle();
|
||||||
|
sample.ActualSteerRightRearDegrees =
|
||||||
|
rightRear.ReadAngle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据车体X向前、Y向左的物理坐标查找指定象限中的舵轮。
|
||||||
|
/// </summary>
|
||||||
|
private static SteerWheel FindWheel(
|
||||||
|
IReadOnlyList<SteerWheel> wheels,
|
||||||
|
bool requireFront,
|
||||||
|
bool requireLeft)
|
||||||
|
{
|
||||||
|
foreach (var wheel in wheels)
|
||||||
|
{
|
||||||
|
var isFront =
|
||||||
|
wheel.PhysicalPosition.X >= 0f;
|
||||||
|
var isLeft =
|
||||||
|
wheel.PhysicalPosition.Y >= 0f;
|
||||||
|
|
||||||
|
if (isFront == requireFront &&
|
||||||
|
isLeft == requireLeft)
|
||||||
|
{
|
||||||
|
return wheel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 将内存中的采样数据写入CSV。
|
// 将内存中的采样数据写入CSV。
|
||||||
private void SaveCsv()
|
private void SaveCsv()
|
||||||
{
|
{
|
||||||
@@ -523,7 +741,25 @@ namespace MultiWheelC
|
|||||||
"ControlLateralErrorMeters," +
|
"ControlLateralErrorMeters," +
|
||||||
"ControlHeadingErrorRadians," +
|
"ControlHeadingErrorRadians," +
|
||||||
"ControlDistanceToTrajectoryMeters," +
|
"ControlDistanceToTrajectoryMeters," +
|
||||||
"ControlRemainingDistanceMeters");
|
"ControlRemainingDistanceMeters," +
|
||||||
|
"HasVelocityDiagnostics," +
|
||||||
|
"DetourEstimatedBodyVxMetersPerSecond," +
|
||||||
|
"DetourVelocityEstimateValid," +
|
||||||
|
"WheelFeedbackRawBodyVxMetersPerSecond," +
|
||||||
|
"WheelFeedbackFilteredBodyVxMetersPerSecond," +
|
||||||
|
"WheelFeedbackVelocityEstimateValid," +
|
||||||
|
"HasSteeringDiagnostics," +
|
||||||
|
"TargetSteerLeftFrontDegrees," +
|
||||||
|
"TargetSteerLeftRearDegrees," +
|
||||||
|
"TargetSteerRightFrontDegrees," +
|
||||||
|
"TargetSteerRightRearDegrees," +
|
||||||
|
"ActualSteerLeftFrontDegrees," +
|
||||||
|
"ActualSteerLeftRearDegrees," +
|
||||||
|
"ActualSteerRightFrontDegrees," +
|
||||||
|
"ActualSteerRightRearDegrees," +
|
||||||
|
"HasGcpCommand," +
|
||||||
|
"CommandFrontGcpAngleRadians," +
|
||||||
|
"CommandRearGcpAngleRadians");
|
||||||
|
|
||||||
foreach (var sample in snapshot)
|
foreach (var sample in snapshot)
|
||||||
{
|
{
|
||||||
@@ -610,7 +846,65 @@ namespace MultiWheelC
|
|||||||
sample.ControlDistanceToTrajectoryMeters),
|
sample.ControlDistanceToTrajectoryMeters),
|
||||||
FormatOptional(
|
FormatOptional(
|
||||||
sample.HasControlReference,
|
sample.HasControlReference,
|
||||||
sample.ControlRemainingDistanceMeters)));
|
sample.ControlRemainingDistanceMeters),
|
||||||
|
sample.HasVelocityDiagnostics
|
||||||
|
? "1"
|
||||||
|
: "0",
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasVelocityDiagnostics,
|
||||||
|
sample.DetourEstimatedBodyVxMetersPerSecond),
|
||||||
|
sample.HasVelocityDiagnostics
|
||||||
|
? sample.DetourVelocityEstimateValid
|
||||||
|
? "1"
|
||||||
|
: "0"
|
||||||
|
: string.Empty,
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasVelocityDiagnostics,
|
||||||
|
sample.WheelFeedbackRawBodyVxMetersPerSecond),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasVelocityDiagnostics,
|
||||||
|
sample.WheelFeedbackFilteredBodyVxMetersPerSecond),
|
||||||
|
sample.HasVelocityDiagnostics
|
||||||
|
? sample.WheelFeedbackVelocityEstimateValid
|
||||||
|
? "1"
|
||||||
|
: "0"
|
||||||
|
: string.Empty,
|
||||||
|
sample.HasSteeringDiagnostics
|
||||||
|
? "1"
|
||||||
|
: "0",
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.TargetSteerLeftFrontDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.TargetSteerLeftRearDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.TargetSteerRightFrontDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.TargetSteerRightRearDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.ActualSteerLeftFrontDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.ActualSteerLeftRearDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.ActualSteerRightFrontDegrees),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasSteeringDiagnostics,
|
||||||
|
sample.ActualSteerRightRearDegrees),
|
||||||
|
sample.HasGcpCommand
|
||||||
|
? "1"
|
||||||
|
: "0",
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasGcpCommand,
|
||||||
|
sample.CommandFrontGcpAngleRadians),
|
||||||
|
FormatOptional(
|
||||||
|
sample.HasGcpCommand,
|
||||||
|
sample.CommandRearGcpAngleRadians)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace MultiWheelC
|
|||||||
public double StanleyMinimumSpeedMetersPerSecond = 0.15;
|
public double StanleyMinimumSpeedMetersPerSecond = 0.15;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置Stanley是否优先使用Detour估算的实际速度。
|
/// 获取或设置Stanley是否优先使用当前状态源提供的实际纵向速度。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool StanleyUsesActualSpeed = true;
|
public bool StanleyUsesActualSpeed = true;
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ namespace MultiWheelC
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 车辆允许偏离参考轨迹的最大欧氏距离,单位为m。
|
/// 车辆允许偏离参考轨迹的最大欧氏距离,单位为m。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaximumDistanceToTrajectoryMeters = 0.50;
|
public double MaximumDistanceToTrajectoryMeters = 0.30;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 单次轨迹动作允许的最长执行时间,单位为s。
|
/// 单次轨迹动作允许的最长执行时间,单位为s。
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class PilotConfig : MultiWheelPilotConfig
|
|||||||
|
|
||||||
#region 单车-临时
|
#region 单车-临时
|
||||||
[FieldMember(desc = "原地旋转Kp")]
|
[FieldMember(desc = "原地旋转Kp")]
|
||||||
public float InPlaceRotateKp = 1.1f;
|
public float InPlaceRotateKp = 1.05f;
|
||||||
|
|
||||||
[FieldMember(desc = "原地旋转Ki")]
|
[FieldMember(desc = "原地旋转Ki")]
|
||||||
public float InPlaceRotateKi = 0f;
|
public float InPlaceRotateKi = 0f;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,17 +0,0 @@
|
|||||||
第一次:
|
|
||||||
ok
|
|
||||||
|
|
||||||
第二次:
|
|
||||||
: * (Exception):DriveTask failed, msg=车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.031m,航向误差=0.03°。, stack:
|
|
||||||
at ClumsyCore.DriveTask.Wait() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 205
|
|
||||||
at MultiWheelC.NewControllerStraight4mTest.Test() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Experiments\NewControllerTrackingTests.cs:line 146
|
|
||||||
at ClumsyLite.ClumsyLiteUI.<>c__DisplayClass19_1.<MainPanelHandler>b__21() in D:\MDCS\Source\Core\Clumsy\ClumsyLite\ClumsyLiteUI.cs:line 592
|
|
||||||
|
|
||||||
*p.InnerException * (InvalidOperationException):车辆已在终点零速参考处停稳,但终点精度不满足要求:位置误差=0.031m,航向误差=0.03°。, stack:
|
|
||||||
at MultiWheelC.TrajectoryTrackingMovement.Get()+MoveNext() in D:\Users\Desktop\入职培训\停车机器人\MyParking\MultiWheelC\Movements\TrajectoryTrackingMovement.cs:line 257
|
|
||||||
at ClumsyCore.DriveTask.<>c__DisplayClass9_0.<.ctor>b__2() in D:\MDCS\Source\Core\Clumsy\ClumsyCore\DriveTask.cs:line 112
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
第三次:
|
|
||||||
ok
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""为新版4m直线控制器实验CSV生成轨迹、横向/航向误差和速度响应图。"""
|
"""为新版控制器实验CSV生成包含轨迹、误差、速度和转角的六子图总图。"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -293,13 +293,87 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
|
|||||||
)
|
)
|
||||||
if np.any(has_control_reference):
|
if np.any(has_control_reference):
|
||||||
reference_speed[~has_control_reference] = np.nan
|
reference_speed[~has_control_reference] = np.nan
|
||||||
actual_speed = numeric_column(frame, "StateBodyVxMetersPerSecond")
|
state_body_vx = numeric_column(frame, "StateBodyVxMetersPerSecond")
|
||||||
velocity_valid = (
|
velocity_valid = (
|
||||||
numeric_column(frame, "StateVelocityEstimateValid", 0.0) > 0.5
|
numeric_column(frame, "StateVelocityEstimateValid", 0.0) > 0.5
|
||||||
)
|
)
|
||||||
actual_speed[~velocity_valid] = np.nan
|
state_body_vx[~velocity_valid] = np.nan
|
||||||
|
has_velocity_diagnostics = (
|
||||||
|
numeric_column(frame, "HasVelocityDiagnostics", 0.0) > 0.5
|
||||||
|
)
|
||||||
|
detour_speed = numeric_column(
|
||||||
|
frame,
|
||||||
|
"DetourEstimatedBodyVxMetersPerSecond",
|
||||||
|
)
|
||||||
|
detour_speed_valid = (
|
||||||
|
has_velocity_diagnostics
|
||||||
|
& (
|
||||||
|
numeric_column(
|
||||||
|
frame,
|
||||||
|
"DetourVelocityEstimateValid",
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
> 0.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
detour_speed[~detour_speed_valid] = np.nan
|
||||||
|
wheel_raw_speed = numeric_column(
|
||||||
|
frame,
|
||||||
|
"WheelFeedbackRawBodyVxMetersPerSecond",
|
||||||
|
)
|
||||||
|
wheel_filtered_speed = numeric_column(
|
||||||
|
frame,
|
||||||
|
"WheelFeedbackFilteredBodyVxMetersPerSecond",
|
||||||
|
)
|
||||||
|
wheel_speed_valid = (
|
||||||
|
has_velocity_diagnostics
|
||||||
|
& (
|
||||||
|
numeric_column(
|
||||||
|
frame,
|
||||||
|
"WheelFeedbackVelocityEstimateValid",
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
> 0.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
wheel_raw_speed[~wheel_speed_valid] = np.nan
|
||||||
|
wheel_filtered_speed[~wheel_speed_valid] = np.nan
|
||||||
|
actual_speed = np.where(
|
||||||
|
np.isfinite(wheel_filtered_speed),
|
||||||
|
wheel_filtered_speed,
|
||||||
|
state_body_vx,
|
||||||
|
)
|
||||||
command_speed = numeric_column(frame, "CommandSpeed")
|
command_speed = numeric_column(frame, "CommandSpeed")
|
||||||
|
|
||||||
|
has_steering_diagnostics = (
|
||||||
|
numeric_column(frame, "HasSteeringDiagnostics", 0.0) > 0.5
|
||||||
|
)
|
||||||
|
steering_angles_degrees = {}
|
||||||
|
for wheel_name in (
|
||||||
|
"LeftFront",
|
||||||
|
"LeftRear",
|
||||||
|
"RightFront",
|
||||||
|
"RightRear",
|
||||||
|
):
|
||||||
|
values = numeric_column(
|
||||||
|
frame,
|
||||||
|
f"ActualSteer{wheel_name}Degrees",
|
||||||
|
)
|
||||||
|
values[~has_steering_diagnostics] = np.nan
|
||||||
|
steering_angles_degrees[wheel_name] = values
|
||||||
|
|
||||||
|
has_gcp_command = (
|
||||||
|
numeric_column(frame, "HasGcpCommand", 0.0) > 0.5
|
||||||
|
)
|
||||||
|
front_gcp_degrees = np.rad2deg(
|
||||||
|
numeric_column(frame, "CommandFrontGcpAngleRadians")
|
||||||
|
)
|
||||||
|
rear_gcp_degrees = np.rad2deg(
|
||||||
|
numeric_column(frame, "CommandRearGcpAngleRadians")
|
||||||
|
)
|
||||||
|
front_gcp_degrees[~has_gcp_command] = np.nan
|
||||||
|
rear_gcp_degrees[~has_gcp_command] = np.nan
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"frame": frame,
|
"frame": frame,
|
||||||
"time": time_seconds,
|
"time": time_seconds,
|
||||||
@@ -316,7 +390,13 @@ def load_experiment(csv_path: Path) -> dict[str, object]:
|
|||||||
"heading_error": heading_error,
|
"heading_error": heading_error,
|
||||||
"reference_speed": reference_speed,
|
"reference_speed": reference_speed,
|
||||||
"actual_speed": actual_speed,
|
"actual_speed": actual_speed,
|
||||||
|
"detour_speed": detour_speed,
|
||||||
|
"wheel_raw_speed": wheel_raw_speed,
|
||||||
|
"wheel_filtered_speed": wheel_filtered_speed,
|
||||||
"command_speed": command_speed,
|
"command_speed": command_speed,
|
||||||
|
"steering_angles_degrees": steering_angles_degrees,
|
||||||
|
"front_gcp_degrees": front_gcp_degrees,
|
||||||
|
"rear_gcp_degrees": rear_gcp_degrees,
|
||||||
"controller_name": first_text(
|
"controller_name": first_text(
|
||||||
frame,
|
frame,
|
||||||
"ControllerName",
|
"ControllerName",
|
||||||
@@ -342,7 +422,7 @@ def save_figure(
|
|||||||
show: bool,
|
show: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""保存并关闭一张实验图。"""
|
"""保存并关闭一张实验图。"""
|
||||||
fig.tight_layout()
|
fig.tight_layout(rect=(0.0, 0.0, 1.0, 0.97))
|
||||||
fig.savefig(destination, dpi=300, bbox_inches="tight")
|
fig.savefig(destination, dpi=300, bbox_inches="tight")
|
||||||
if show:
|
if show:
|
||||||
plt.show()
|
plt.show()
|
||||||
@@ -354,14 +434,16 @@ def plot_experiment(
|
|||||||
output_directory: Path,
|
output_directory: Path,
|
||||||
show: bool,
|
show: bool,
|
||||||
) -> list[Path]:
|
) -> list[Path]:
|
||||||
"""为单份新版控制器CSV生成四类对比图。"""
|
"""为单份新版控制器CSV生成一张包含六个子图的实验总图。"""
|
||||||
data = load_experiment(csv_path)
|
data = load_experiment(csv_path)
|
||||||
output_directory.mkdir(parents=True, exist_ok=True)
|
output_directory.mkdir(parents=True, exist_ok=True)
|
||||||
title = f"{data['controller_name']} - {data['trajectory_name']}"
|
title = f"{data['controller_name']} - {data['trajectory_name']}"
|
||||||
destinations: list[Path] = []
|
fig, axes = plt.subplots(3, 2, figsize=(18.0, 16.0))
|
||||||
|
fig.suptitle(title, fontsize=16)
|
||||||
|
|
||||||
|
# 1. 期望轨迹与实际轨迹。
|
||||||
|
axis = axes[0, 0]
|
||||||
valid_position = data["valid_position"]
|
valid_position = data["valid_position"]
|
||||||
fig, axis = plt.subplots(figsize=(9.0, 6.5))
|
|
||||||
valid_reference_position = data["valid_reference_position"]
|
valid_reference_position = data["valid_reference_position"]
|
||||||
if np.count_nonzero(valid_reference_position) >= 2:
|
if np.count_nonzero(valid_reference_position) >= 2:
|
||||||
axis.plot(
|
axis.plot(
|
||||||
@@ -390,47 +472,42 @@ def plot_experiment(
|
|||||||
axis.set_aspect("equal", adjustable="box")
|
axis.set_aspect("equal", adjustable="box")
|
||||||
axis.set_xlabel("世界坐标X / m")
|
axis.set_xlabel("世界坐标X / m")
|
||||||
axis.set_ylabel("世界坐标Y / m")
|
axis.set_ylabel("世界坐标Y / m")
|
||||||
axis.set_title(f"期望轨迹与实际轨迹对比\n{title}")
|
axis.set_title("期望轨迹与实际轨迹对比")
|
||||||
axis.grid(True, alpha=0.3)
|
axis.grid(True, alpha=0.3)
|
||||||
axis.legend()
|
axis.legend(fontsize=8)
|
||||||
destination = output_directory / f"{csv_path.stem}_trajectory.png"
|
|
||||||
save_figure(fig, destination, show)
|
|
||||||
destinations.append(destination)
|
|
||||||
|
|
||||||
|
# 2. 横向误差。
|
||||||
lateral_mm = data["lateral_error"] * 1000.0
|
lateral_mm = data["lateral_error"] * 1000.0
|
||||||
lateral_rmse_mm = finite_rmse(lateral_mm)
|
lateral_rmse_mm = finite_rmse(lateral_mm)
|
||||||
fig, axis = plt.subplots(figsize=(10.0, 5.5))
|
axis = axes[0, 1]
|
||||||
axis.plot(data["time"], lateral_mm, linewidth=1.5)
|
axis.plot(data["time"], lateral_mm, linewidth=1.5)
|
||||||
axis.axhline(0.0, color="black", linewidth=0.8)
|
axis.axhline(0.0, color="black", linewidth=0.8)
|
||||||
axis.set_xlabel("时间 / s")
|
axis.set_xlabel("时间 / s")
|
||||||
axis.set_ylabel("横向误差 / mm")
|
axis.set_ylabel("横向误差 / mm")
|
||||||
axis.set_title(
|
axis.set_title(
|
||||||
f"横向误差(轨迹在车辆左侧为正)\n{title},RMSE={lateral_rmse_mm:.2f}mm"
|
"横向误差(轨迹在车辆左侧为正)\n"
|
||||||
|
f"RMSE={lateral_rmse_mm:.2f}mm"
|
||||||
)
|
)
|
||||||
axis.grid(True, alpha=0.3)
|
axis.grid(True, alpha=0.3)
|
||||||
destination = output_directory / f"{csv_path.stem}_lateral_error.png"
|
|
||||||
save_figure(fig, destination, show)
|
|
||||||
destinations.append(destination)
|
|
||||||
|
|
||||||
|
# 3. 航向误差。
|
||||||
heading_degrees = np.rad2deg(data["heading_error"])
|
heading_degrees = np.rad2deg(data["heading_error"])
|
||||||
heading_rmse_degrees = finite_rmse(heading_degrees)
|
heading_rmse_degrees = finite_rmse(heading_degrees)
|
||||||
fig, axis = plt.subplots(figsize=(10.0, 5.5))
|
axis = axes[1, 0]
|
||||||
axis.plot(data["time"], heading_degrees, linewidth=1.5)
|
axis.plot(data["time"], heading_degrees, linewidth=1.5)
|
||||||
axis.axhline(0.0, color="black", linewidth=0.8)
|
axis.axhline(0.0, color="black", linewidth=0.8)
|
||||||
axis.set_xlabel("时间 / s")
|
axis.set_xlabel("时间 / s")
|
||||||
axis.set_ylabel("航向角偏差 / °")
|
axis.set_ylabel("航向角偏差 / °")
|
||||||
axis.set_title(
|
axis.set_title(
|
||||||
"航向角偏差:参考轨迹航向-实际车体航向(逆时针为正)\n"
|
"航向角偏差:参考轨迹航向-实际车体航向(逆时针为正)\n"
|
||||||
f"{title},RMSE={heading_rmse_degrees:.3f}°"
|
f"RMSE={heading_rmse_degrees:.3f}°"
|
||||||
)
|
)
|
||||||
axis.grid(True, alpha=0.3)
|
axis.grid(True, alpha=0.3)
|
||||||
destination = output_directory / f"{csv_path.stem}_heading_error.png"
|
|
||||||
save_figure(fig, destination, show)
|
|
||||||
destinations.append(destination)
|
|
||||||
|
|
||||||
|
# 4. 参考、命令、Detour估计和轮速解算速度。
|
||||||
speed_error = data["actual_speed"] - data["reference_speed"]
|
speed_error = data["actual_speed"] - data["reference_speed"]
|
||||||
speed_rmse = finite_rmse(speed_error)
|
speed_rmse = finite_rmse(speed_error)
|
||||||
fig, axis = plt.subplots(figsize=(10.0, 5.8))
|
axis = axes[1, 1]
|
||||||
axis.plot(
|
axis.plot(
|
||||||
data["time"],
|
data["time"],
|
||||||
data["reference_speed"],
|
data["reference_speed"],
|
||||||
@@ -444,29 +521,118 @@ def plot_experiment(
|
|||||||
linewidth=1.3,
|
linewidth=1.3,
|
||||||
label="纵向控制器下发速度",
|
label="纵向控制器下发速度",
|
||||||
)
|
)
|
||||||
|
if np.any(np.isfinite(data["detour_speed"])):
|
||||||
|
axis.plot(
|
||||||
|
data["time"],
|
||||||
|
data["detour_speed"],
|
||||||
|
":",
|
||||||
|
linewidth=1.2,
|
||||||
|
label="Detour估计Vx",
|
||||||
|
)
|
||||||
|
if np.any(np.isfinite(data["wheel_filtered_speed"])):
|
||||||
|
axis.plot(
|
||||||
|
data["time"],
|
||||||
|
data["wheel_filtered_speed"],
|
||||||
|
linewidth=1.5,
|
||||||
|
label="轮速解算滤波Vx(控制使用)",
|
||||||
|
)
|
||||||
|
else:
|
||||||
axis.plot(
|
axis.plot(
|
||||||
data["time"],
|
data["time"],
|
||||||
data["actual_speed"],
|
data["actual_speed"],
|
||||||
linewidth=1.5,
|
linewidth=1.5,
|
||||||
label="状态估计实际车体纵向速度",
|
label="控制器实际纵向速度",
|
||||||
)
|
)
|
||||||
axis.set_xlabel("时间 / s")
|
axis.set_xlabel("时间 / s")
|
||||||
axis.set_ylabel("速度 / (m/s)")
|
axis.set_ylabel("速度 / (m/s)")
|
||||||
axis.set_title(f"参考速度与实际速度对比\n{title},RMSE={speed_rmse:.4f}m/s")
|
axis.set_title(
|
||||||
|
"参考速度、控制命令与观测速度\n"
|
||||||
|
f"轮速Vx相对参考速度RMSE={speed_rmse:.4f}m/s"
|
||||||
|
)
|
||||||
axis.grid(True, alpha=0.3)
|
axis.grid(True, alpha=0.3)
|
||||||
axis.legend()
|
axis.legend(fontsize=8)
|
||||||
destination = output_directory / f"{csv_path.stem}_speed_response.png"
|
|
||||||
|
# 5. 四个舵轮的实际机械转角。
|
||||||
|
axis = axes[2, 0]
|
||||||
|
wheel_labels = {
|
||||||
|
"LeftFront": "左前轮",
|
||||||
|
"LeftRear": "左后轮",
|
||||||
|
"RightFront": "右前轮",
|
||||||
|
"RightRear": "右后轮",
|
||||||
|
}
|
||||||
|
steering_data_available = False
|
||||||
|
for wheel_name, wheel_label in wheel_labels.items():
|
||||||
|
wheel_angles = data["steering_angles_degrees"][wheel_name]
|
||||||
|
if np.any(np.isfinite(wheel_angles)):
|
||||||
|
steering_data_available = True
|
||||||
|
axis.plot(
|
||||||
|
data["time"],
|
||||||
|
wheel_angles,
|
||||||
|
linewidth=1.2,
|
||||||
|
label=wheel_label,
|
||||||
|
)
|
||||||
|
if steering_data_available:
|
||||||
|
axis.axhline(0.0, color="black", linewidth=0.8)
|
||||||
|
axis.legend(fontsize=8, ncol=2)
|
||||||
|
else:
|
||||||
|
axis.text(
|
||||||
|
0.5,
|
||||||
|
0.5,
|
||||||
|
"CSV不含四舵轮转角诊断数据",
|
||||||
|
ha="center",
|
||||||
|
va="center",
|
||||||
|
transform=axis.transAxes,
|
||||||
|
)
|
||||||
|
axis.set_xlabel("时间 / s")
|
||||||
|
axis.set_ylabel("实际舵角 / °")
|
||||||
|
axis.set_title("四个舵轮实际反馈转角")
|
||||||
|
axis.grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
# 6. 经过角速度限制后实际发送的前、后虚拟GCP转角。
|
||||||
|
axis = axes[2, 1]
|
||||||
|
gcp_data_available = (
|
||||||
|
np.any(np.isfinite(data["front_gcp_degrees"]))
|
||||||
|
or np.any(np.isfinite(data["rear_gcp_degrees"]))
|
||||||
|
)
|
||||||
|
if gcp_data_available:
|
||||||
|
axis.plot(
|
||||||
|
data["time"],
|
||||||
|
data["front_gcp_degrees"],
|
||||||
|
linewidth=1.4,
|
||||||
|
label="前GCP",
|
||||||
|
)
|
||||||
|
axis.plot(
|
||||||
|
data["time"],
|
||||||
|
data["rear_gcp_degrees"],
|
||||||
|
linewidth=1.4,
|
||||||
|
label="后GCP",
|
||||||
|
)
|
||||||
|
axis.axhline(0.0, color="black", linewidth=0.8)
|
||||||
|
axis.legend(fontsize=8)
|
||||||
|
else:
|
||||||
|
axis.text(
|
||||||
|
0.5,
|
||||||
|
0.5,
|
||||||
|
"CSV不含前后GCP转角数据",
|
||||||
|
ha="center",
|
||||||
|
va="center",
|
||||||
|
transform=axis.transAxes,
|
||||||
|
)
|
||||||
|
axis.set_xlabel("时间 / s")
|
||||||
|
axis.set_ylabel("GCP命令角 / °")
|
||||||
|
axis.set_title("前后虚拟GCP实际发送转角")
|
||||||
|
axis.grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
destination = output_directory / f"{csv_path.stem}_summary_6plots.png"
|
||||||
save_figure(fig, destination, show)
|
save_figure(fig, destination, show)
|
||||||
destinations.append(destination)
|
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"{csv_path.name}: 横向RMSE={lateral_rmse_mm:.3f}mm, "
|
f"{csv_path.name}: 横向RMSE={lateral_rmse_mm:.3f}mm, "
|
||||||
f"航向RMSE={heading_rmse_degrees:.4f}°, "
|
f"航向RMSE={heading_rmse_degrees:.4f}°, "
|
||||||
f"速度RMSE={speed_rmse:.5f}m/s"
|
f"速度RMSE={speed_rmse:.5f}m/s"
|
||||||
)
|
)
|
||||||
for destination in destinations:
|
print(f"已生成六子图总图:{destination}")
|
||||||
print(f"已生成:{destination}")
|
return [destination]
|
||||||
return destinations
|
|
||||||
|
|
||||||
|
|
||||||
def discover_csv_files(arguments: list[str]) -> list[Path]:
|
def discover_csv_files(arguments: list[str]) -> list[Path]:
|
||||||
@@ -487,7 +653,7 @@ def discover_csv_files(arguments: list[str]) -> list[Path]:
|
|||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""解析命令行并批量处理新版控制器实验CSV。"""
|
"""解析命令行并批量处理新版控制器实验CSV。"""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="绘制新版控制器轨迹实验的四类对比图。"
|
description="绘制新版控制器轨迹实验的六子图总图。"
|
||||||
)
|
)
|
||||||
parser.add_argument("csv", nargs="*", help="需要处理的CSV文件路径。")
|
parser.add_argument("csv", nargs="*", help="需要处理的CSV文件路径。")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user