182 lines
5.8 KiB
C#
182 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using MyParking.Shared;
|
|
// 夹紧后只执行一次 → 建立固定布局
|
|
namespace MultiWheelC.Fleet
|
|
{
|
|
// 建立编队时使用的一辆成员车世界位姿快照。
|
|
public readonly struct FleetMemberPose
|
|
{
|
|
public FleetMemberPose(
|
|
int vehicleId,
|
|
Pose2D poseInWorld)
|
|
{
|
|
if (vehicleId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(vehicleId),
|
|
"编队成员车号必须大于零。");
|
|
}
|
|
|
|
NumericGuard.EnsureFinite(
|
|
poseInWorld,
|
|
nameof(poseInWorld));
|
|
|
|
VehicleId = vehicleId;
|
|
PoseInWorld = new Pose2D(
|
|
poseInWorld.XMeters,
|
|
poseInWorld.YMeters,
|
|
AngleMath.NormalizeRadians(
|
|
poseInWorld.YawRadians));
|
|
}
|
|
|
|
public int VehicleId { get; }
|
|
|
|
public Pose2D PoseInWorld { get; }
|
|
}
|
|
|
|
// 保存布局建立时的车队世界位姿和固定成员布局。
|
|
public readonly struct FleetLayoutCaptureResult
|
|
{
|
|
public FleetLayoutCaptureResult(
|
|
Pose2D fleetPoseInWorld,
|
|
FleetLayout layout)
|
|
{
|
|
NumericGuard.EnsureFinite(
|
|
fleetPoseInWorld,
|
|
nameof(fleetPoseInWorld));
|
|
|
|
FleetPoseInWorld = new Pose2D(
|
|
fleetPoseInWorld.XMeters,
|
|
fleetPoseInWorld.YMeters,
|
|
AngleMath.NormalizeRadians(
|
|
fleetPoseInWorld.YawRadians));
|
|
Layout = layout ??
|
|
throw new ArgumentNullException(
|
|
nameof(layout));
|
|
}
|
|
|
|
public Pose2D FleetPoseInWorld { get; }
|
|
|
|
public FleetLayout Layout { get; }
|
|
}
|
|
|
|
// 根据同一世界坐标系中的成员位姿建立车队几何中心和固定布局。
|
|
public static class FleetLayoutCapture
|
|
{
|
|
public static FleetLayoutCaptureResult Capture(
|
|
IReadOnlyList<FleetMemberPose> memberPoses,
|
|
int leaderVehicleId)
|
|
{
|
|
if (memberPoses == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(memberPoses));
|
|
}
|
|
|
|
if (memberPoses.Count == 0)
|
|
{
|
|
throw new ArgumentException(
|
|
"建立编队布局至少需要一辆成员车。",
|
|
nameof(memberPoses));
|
|
}
|
|
|
|
if (leaderVehicleId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(leaderVehicleId),
|
|
"主车车号必须大于零。");
|
|
}
|
|
|
|
var vehicleIds = new HashSet<int>();
|
|
var centerXMeters = 0.0;
|
|
var centerYMeters = 0.0;
|
|
var leaderFound = false;
|
|
var leaderYawRadians = 0.0;
|
|
|
|
for (var index = 0;
|
|
index < memberPoses.Count;
|
|
index++)
|
|
{
|
|
var memberPose = memberPoses[index];
|
|
if (memberPose.VehicleId <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(memberPoses),
|
|
$"第{index}辆成员车的车号必须大于零。");
|
|
}
|
|
|
|
NumericGuard.EnsureFinite(
|
|
memberPose.PoseInWorld,
|
|
$"{nameof(memberPoses)}[{index}]." +
|
|
nameof(FleetMemberPose.PoseInWorld));
|
|
|
|
if (!vehicleIds.Add(memberPose.VehicleId))
|
|
{
|
|
throw new ArgumentException(
|
|
$"成员位姿包含重复车号{memberPose.VehicleId}。",
|
|
nameof(memberPoses));
|
|
}
|
|
|
|
centerXMeters +=
|
|
memberPose.PoseInWorld.XMeters;
|
|
centerYMeters +=
|
|
memberPose.PoseInWorld.YMeters;
|
|
|
|
if (memberPose.VehicleId == leaderVehicleId)
|
|
{
|
|
leaderFound = true;
|
|
leaderYawRadians =
|
|
memberPose.PoseInWorld.YawRadians;
|
|
}
|
|
}
|
|
|
|
if (!leaderFound)
|
|
{
|
|
throw new ArgumentException(
|
|
$"成员位姿中不存在主车{leaderVehicleId}。",
|
|
nameof(leaderVehicleId));
|
|
}
|
|
|
|
centerXMeters /= memberPoses.Count;
|
|
centerYMeters /= memberPoses.Count;
|
|
NumericGuard.EnsureFinite(
|
|
centerXMeters,
|
|
nameof(centerXMeters));
|
|
NumericGuard.EnsureFinite(
|
|
centerYMeters,
|
|
nameof(centerYMeters));
|
|
|
|
var fleetPoseInWorld = new Pose2D(
|
|
centerXMeters,
|
|
centerYMeters,
|
|
AngleMath.NormalizeRadians(
|
|
leaderYawRadians));
|
|
var worldPoseInFleet =
|
|
FrameTransform2D.Inverse(
|
|
fleetPoseInWorld);
|
|
var vehicleLayouts =
|
|
new VehicleLayout[memberPoses.Count];
|
|
|
|
for (var index = 0;
|
|
index < memberPoses.Count;
|
|
index++)
|
|
{
|
|
var memberPose = memberPoses[index];
|
|
var poseInFleet =
|
|
FrameTransform2D.Compose(
|
|
worldPoseInFleet,
|
|
memberPose.PoseInWorld);
|
|
vehicleLayouts[index] =
|
|
new VehicleLayout(
|
|
memberPose.VehicleId,
|
|
poseInFleet);
|
|
}
|
|
|
|
return new FleetLayoutCaptureResult(
|
|
fleetPoseInWorld,
|
|
new FleetLayout(vehicleLayouts));
|
|
}
|
|
}
|
|
}
|