Fix fleet rotate synchronization
This commit is contained in:
@@ -212,7 +212,7 @@ public class FleetRotateInPlace : MovementDefinition
|
|||||||
if (accel > 1e-3) estDuration += maxOmega / (2 * accel);
|
if (accel > 1e-3) estDuration += maxOmega / (2 * accel);
|
||||||
|
|
||||||
DLog.Log(
|
DLog.Log(
|
||||||
$"START target={TargetDeltaDeg:0.0} dir={dir} omega={maxOmega:0.0} accel={accel:0.0} " +
|
$"REQUEST target={TargetDeltaDeg:0.0} dir={dir} omega={maxOmega:0.0} accel={accel:0.0} " +
|
||||||
$"slowDeg={slowDeg:0.0} minOmega={minOmega:0.0} useDetourHeading={hasPos} startTh={startTh:0.00} " +
|
$"slowDeg={slowDeg:0.0} minOmega={minOmega:0.0} useDetourHeading={hasPos} startTh={startTh:0.00} " +
|
||||||
$"estDuration={estDuration:0.00}s syncUseDetour={conf.MultiVehicleSyncUseDetour}",
|
$"estDuration={estDuration:0.00}s syncUseDetour={conf.MultiVehicleSyncUseDetour}",
|
||||||
"FleetRotateDbg");
|
"FleetRotateDbg");
|
||||||
@@ -224,6 +224,35 @@ public class FleetRotateInPlace : MovementDefinition
|
|||||||
self.MultiVehicleScriptMode = 2;
|
self.MultiVehicleScriptMode = 2;
|
||||||
self.MultiVehicleScriptVth = 0;
|
self.MultiVehicleScriptVth = 0;
|
||||||
self.MultiVehicleScriptEnabled = true;
|
self.MultiVehicleScriptEnabled = true;
|
||||||
|
self.MultiVehicleRotateWheelsReady = false;
|
||||||
|
self.MultiVehicleRotateFleetReady = false;
|
||||||
|
|
||||||
|
DLog.Log("WAIT_ALIGN fleet rotate wheels", "FleetRotateDbg");
|
||||||
|
while (!self.MultiVehicleRotateFleetReady)
|
||||||
|
{
|
||||||
|
self.MultiVehicleScriptVx = 0;
|
||||||
|
self.MultiVehicleScriptVy = 0;
|
||||||
|
self.MultiVehicleScriptMode = 2;
|
||||||
|
self.MultiVehicleScriptVth = 0;
|
||||||
|
self.MultiVehicleScriptEnabled = true;
|
||||||
|
Hedingben.ToastText("车队原地旋转舵轮预对齐中", "FleetRotate");
|
||||||
|
yield return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPos)
|
||||||
|
{
|
||||||
|
prevTh = (float)DetourInterface.getCartLocation().th;
|
||||||
|
startTh = prevTh;
|
||||||
|
}
|
||||||
|
accumulated = 0f;
|
||||||
|
start = DateTime.Now;
|
||||||
|
lastTime = start;
|
||||||
|
lastLog = DateTime.MinValue;
|
||||||
|
cmdMag = 0f;
|
||||||
|
DLog.Log(
|
||||||
|
$"START target={TargetDeltaDeg:0.0} dir={dir} omega={maxOmega:0.0} startTh={startTh:0.00} " +
|
||||||
|
$"fleetAligned={self.MultiVehicleRotateFleetReady}",
|
||||||
|
"FleetRotateDbg");
|
||||||
|
|
||||||
var stopReason = "stop()";
|
var stopReason = "stop()";
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using ClumsyCore.Pilot;
|
|||||||
using ClumsyCore.Utilities;
|
using ClumsyCore.Utilities;
|
||||||
using CommonUsage;
|
using CommonUsage;
|
||||||
using CommonUsage.Chassis;
|
using CommonUsage.Chassis;
|
||||||
|
using CommonUsage.Mathematics;
|
||||||
using FundamentalLib;
|
using FundamentalLib;
|
||||||
using FundamentalLib.Utilities;
|
using FundamentalLib.Utilities;
|
||||||
using MDCSToolBox.Clumsy.Pilot.MultiWheel;
|
using MDCSToolBox.Clumsy.Pilot.MultiWheel;
|
||||||
@@ -99,6 +100,10 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
private long _multiVehicleAppliedSeq = -1;
|
private long _multiVehicleAppliedSeq = -1;
|
||||||
|
|
||||||
[AsLowerIO(desc = "车号")] public int CarNum = 1;
|
[AsLowerIO(desc = "车号")] public int CarNum = 1;
|
||||||
|
[FieldMember(desc = "多车联动:原地旋转本车舵轮已对齐")]
|
||||||
|
public bool MultiVehicleRotateWheelsReady = true;
|
||||||
|
[FieldMember(desc = "多车联动:原地旋转整队舵轮已对齐")]
|
||||||
|
public bool MultiVehicleRotateFleetReady = true;
|
||||||
|
|
||||||
#region 夹臂变量
|
#region 夹臂变量
|
||||||
[AsUpperIO(desc = "左夹臂下发速度")] public float SpeedLeftArm;
|
[AsUpperIO(desc = "左夹臂下发速度")] public float SpeedLeftArm;
|
||||||
@@ -138,6 +143,10 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
private bool _multiVehicleMotionFeasible = true;
|
private bool _multiVehicleMotionFeasible = true;
|
||||||
private string _multiVehicleMotionInfeasibleReason = "";
|
private string _multiVehicleMotionInfeasibleReason = "";
|
||||||
private DateTime _multiVehicleStopLastLog = DateTime.MinValue;
|
private DateTime _multiVehicleStopLastLog = DateTime.MinValue;
|
||||||
|
private bool _multiVehicleRotateModeActive;
|
||||||
|
private float _multiVehicleRotateDirectionHint = 1f;
|
||||||
|
private string _multiVehicleRotateAlignDetail = "";
|
||||||
|
private DateTime _multiVehicleRotateAlignLastLog = DateTime.MinValue;
|
||||||
|
|
||||||
// 诊断日志:节流计时 + 最近一次检测几何(中心/朝向/距离),用于定位剧烈运动来源
|
// 诊断日志:节流计时 + 最近一次检测几何(中心/朝向/距离),用于定位剧烈运动来源
|
||||||
private DateTime _mvDbgLastLog = DateTime.MinValue;
|
private DateTime _mvDbgLastLog = DateTime.MinValue;
|
||||||
@@ -167,6 +176,7 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
// 互识别:邻车两腿检测的滑动窗口(最近 1s,最多 10 帧),用于平滑抖动
|
// 互识别:邻车两腿检测的滑动窗口(最近 1s,最多 10 帧),用于平滑抖动
|
||||||
private DateTime _mvRemoteInputLastLog = DateTime.MinValue;
|
private DateTime _mvRemoteInputLastLog = DateTime.MinValue;
|
||||||
private DateTime _mvRemoteDecisionLastLog = DateTime.MinValue;
|
private DateTime _mvRemoteDecisionLastLog = DateTime.MinValue;
|
||||||
|
private DateTime _mvNotifyApplyLastLog = DateTime.MinValue;
|
||||||
|
|
||||||
private void LogMultiVehicleRemoteInput(bool isMaster, bool scriptOn, bool manualEnabled, bool autoEnabled,
|
private void LogMultiVehicleRemoteInput(bool isMaster, bool scriptOn, bool manualEnabled, bool autoEnabled,
|
||||||
int manualMode, float manualVx, float manualVy, float manualVth)
|
int manualMode, float manualVx, float manualVy, float manualVth)
|
||||||
@@ -204,6 +214,118 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
_multiVehicleMotionInfeasibleReason = feasible ? "" : (reason ?? "");
|
_multiVehicleMotionInfeasibleReason = feasible ? "" : (reason ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float ClampFloat(float value, float min, float max)
|
||||||
|
{
|
||||||
|
if (value < min) return min;
|
||||||
|
if (value > max) return max;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMultiVehicleRotateModeState(int fleetMode, bool active, float requestedOmega)
|
||||||
|
{
|
||||||
|
var rotateActive = active && fleetMode == 2;
|
||||||
|
if (!rotateActive)
|
||||||
|
{
|
||||||
|
_multiVehicleRotateModeActive = false;
|
||||||
|
MultiVehicleRotateWheelsReady = true;
|
||||||
|
MultiVehicleRotateFleetReady = true;
|
||||||
|
_multiVehicleRotateDirectionHint = 1f;
|
||||||
|
_multiVehicleRotateAlignDetail = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_multiVehicleRotateModeActive)
|
||||||
|
{
|
||||||
|
MultiVehicleRotateWheelsReady = false;
|
||||||
|
MultiVehicleRotateFleetReady = false;
|
||||||
|
_multiVehicleRotateAlignDetail = "rotate mode just entered";
|
||||||
|
}
|
||||||
|
|
||||||
|
_multiVehicleRotateModeActive = true;
|
||||||
|
if (Math.Abs(requestedOmega) > Conf.MultiVehicleRotateActiveOmega)
|
||||||
|
_multiVehicleRotateDirectionHint = Math.Sign(requestedOmega);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool PrepareMultiVehicleRotateWheels(MultiWheelChassis chassis, float requestedOmega,
|
||||||
|
float localCompensateX = 0f, float localCompensateY = 0f, float localCompensateTh = 0f,
|
||||||
|
bool rampStop = true)
|
||||||
|
{
|
||||||
|
var hint = Math.Abs(requestedOmega) > Conf.MultiVehicleRotateActiveOmega
|
||||||
|
? Math.Sign(requestedOmega)
|
||||||
|
: Math.Sign(_multiVehicleRotateDirectionHint);
|
||||||
|
if (hint == 0) hint = 1;
|
||||||
|
_multiVehicleRotateDirectionHint = hint;
|
||||||
|
|
||||||
|
// Reuse SendRotateMotion's decomposition and angle-limit checks, but keep wheel speed at zero.
|
||||||
|
if (rampStop)
|
||||||
|
chassis.RampStop();
|
||||||
|
var alignOmega = Math.Abs(requestedOmega) > Conf.MultiVehicleRotateActiveOmega
|
||||||
|
? requestedOmega
|
||||||
|
: 0.01f * hint;
|
||||||
|
var motionOk = chassis.SendRotateMotion(alignOmega, TimeSpan.Zero,
|
||||||
|
localCompensateX: localCompensateX, localCompensateY: localCompensateY,
|
||||||
|
localCompensateTh: localCompensateTh);
|
||||||
|
var wheelAligned = TryCheckRotateWheelAlignment(chassis, Conf.InPlaceRotateWheelAlignDeg,
|
||||||
|
out var alignDetail);
|
||||||
|
var aligned = motionOk && wheelAligned;
|
||||||
|
if (!motionOk)
|
||||||
|
alignDetail = string.IsNullOrWhiteSpace(chassis.LastMotionDecomposeFailureReason)
|
||||||
|
? alignDetail
|
||||||
|
: chassis.LastMotionDecomposeFailureReason;
|
||||||
|
|
||||||
|
MultiVehicleRotateWheelsReady = aligned;
|
||||||
|
_multiVehicleRotateAlignDetail = alignDetail;
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
if ((now - _multiVehicleRotateAlignLastLog).TotalMilliseconds >= 200)
|
||||||
|
{
|
||||||
|
_multiVehicleRotateAlignLastLog = now;
|
||||||
|
DLog.Log(
|
||||||
|
$"car{CarNum} ROTATE_PREPARE omegaReq={requestedOmega:0.000} alignOmega={alignOmega:0.000} " +
|
||||||
|
$"hint={hint} comp=({localCompensateX:0.0},{localCompensateY:0.0},{localCompensateTh:0.000}) " +
|
||||||
|
$"rampStop={rampStop} ok={motionOk} aligned={aligned} detail={alignDetail} " +
|
||||||
|
$"chassisReason={chassis.LastMotionDecomposeFailureReason}",
|
||||||
|
"MultiVehicleRemoteDbg");
|
||||||
|
FleetDiag(
|
||||||
|
$"ROTATE_PREPARE omegaReq={requestedOmega:0.000} alignOmega={alignOmega:0.000} hint={hint} " +
|
||||||
|
$"comp=({localCompensateX:0.0},{localCompensateY:0.0},{localCompensateTh:0.000}) " +
|
||||||
|
$"ok={motionOk} aligned={aligned} detail={alignDetail}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Hedingben.ToastText(
|
||||||
|
aligned ? "车队原地旋转舵轮已对齐" : $"车队原地旋转舵轮预对齐中 {alignDetail}",
|
||||||
|
$"MultiVehicle{CarNum}-rotate-align");
|
||||||
|
|
||||||
|
return motionOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryCheckRotateWheelAlignment(MultiWheelChassis chassis, float toleranceDeg, out string detail)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0612, CS0618
|
||||||
|
var wheels = chassis.GetSteerWheels();
|
||||||
|
#pragma warning restore CS0612, CS0618
|
||||||
|
var maxDth = 0f;
|
||||||
|
var parts = new List<string>();
|
||||||
|
for (var i = 0; i < wheels.Count; i++)
|
||||||
|
{
|
||||||
|
var sw = wheels[i];
|
||||||
|
var dth = Math.Abs(CommonMath.ThDiff(sw.ReadAngle(), sw.GetSendAngle()));
|
||||||
|
maxDth = Math.Max(maxDth, dth);
|
||||||
|
parts.Add($"w{i}:{dth:0.0}");
|
||||||
|
}
|
||||||
|
|
||||||
|
detail = $"maxDth={maxDth:0.0} tol={toleranceDeg:0.0} {string.Join(",", parts)}";
|
||||||
|
return maxDth <= toleranceDeg;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
detail = $"read alignment failed: {e.Message}";
|
||||||
|
return chassis.LastRotateAligned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void LogMultiVehicleStop(string reason, bool force = false)
|
private void LogMultiVehicleStop(string reason, bool force = false)
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
@@ -354,6 +476,17 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
MultiVehicleNotification = notification;
|
MultiVehicleNotification = notification;
|
||||||
_multiVehicleLastNotifyTime = DateTime.Now;
|
_multiVehicleLastNotifyTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
var applyNow = DateTime.Now;
|
||||||
|
if ((applyNow - _mvNotifyApplyLastLog).TotalMilliseconds >= 200)
|
||||||
|
{
|
||||||
|
_mvNotifyApplyLastLog = applyNow;
|
||||||
|
DLog.Log(
|
||||||
|
$"car{CarNum} NOTIFY_APPLY seq={notification.Seq} mode={notification.Mode} " +
|
||||||
|
$"omega={notification.FleetOmega:0.000} reqOmega={notification.RequestedFleetOmega:0.000} " +
|
||||||
|
$"released={notification.FleetMotionReleased} stop={notification.FleetStopActive} " +
|
||||||
|
$"reason={notification.FleetStopReason}",
|
||||||
|
"MultiVehicleRemoteDbg");
|
||||||
|
}
|
||||||
return JsonConvert.SerializeObject(new { code = 200, message = "ok" });
|
return JsonConvert.SerializeObject(new { code = 200, message = "ok" });
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -472,6 +605,7 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
LogMultiVehicleStop("fleet control disabled; ramp stop previous fleet command", true);
|
LogMultiVehicleStop("fleet control disabled; ramp stop previous fleet command", true);
|
||||||
}
|
}
|
||||||
_multiVehicleWasActive = false;
|
_multiVehicleWasActive = false;
|
||||||
|
UpdateMultiVehicleRotateModeState(0, false, 0);
|
||||||
LogMultiVehicleRemoteDecision(
|
LogMultiVehicleRemoteDecision(
|
||||||
$"RETURN_IDLE master={isMaster} rawEn={MultiVehicleManualEnabled} scriptOn={scriptOn} auto={autoEnabled} " +
|
$"RETURN_IDLE master={isMaster} rawEn={MultiVehicleManualEnabled} scriptOn={scriptOn} auto={autoEnabled} " +
|
||||||
$"rawMode={MultiVehicleManualMode} rawVx={MultiVehicleManualVx:0.000} rawVy={MultiVehicleManualVy:0.000} rawVth={MultiVehicleManualVth:0.000}");
|
$"rawMode={MultiVehicleManualMode} rawVx={MultiVehicleManualVx:0.000} rawVy={MultiVehicleManualVy:0.000} rawVth={MultiVehicleManualVth:0.000}");
|
||||||
@@ -535,6 +669,8 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
var notificationStopActive = false;
|
var notificationStopActive = false;
|
||||||
var notificationStopReason = "";
|
var notificationStopReason = "";
|
||||||
var notificationStopSourceCar = 0;
|
var notificationStopSourceCar = 0;
|
||||||
|
var notificationRequestedFleetOmega = 0f;
|
||||||
|
var notificationMotionReleased = true;
|
||||||
float crabInputVx = 0, crabInputVy = 0, crabRawAngle = 0;
|
float crabInputVx = 0, crabInputVy = 0, crabRawAngle = 0;
|
||||||
float crabSteerLimit = 0;
|
float crabSteerLimit = 0;
|
||||||
var crabReverseEquivalent = false;
|
var crabReverseEquivalent = false;
|
||||||
@@ -548,7 +684,8 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
if (fleetMode == 2)
|
if (fleetMode == 2)
|
||||||
{
|
{
|
||||||
// 原地旋转:摇杆左右 → 绕车队中心角速度(deg/s)。底盘 SetOriginBias 已设为车队中心。
|
// 原地旋转:摇杆左右 → 绕车队中心角速度(deg/s)。底盘 SetOriginBias 已设为车队中心。
|
||||||
fleetOmega = manualVth * Conf.ManualCarSyncVthFac;
|
var rotateInput = scriptOn ? manualVth : ClampFloat(manualVth, -1f, 1f);
|
||||||
|
fleetOmega = scriptOn ? rotateInput : rotateInput * Conf.ManualCarSyncVthFac;
|
||||||
fleetVx = 0;
|
fleetVx = 0;
|
||||||
fleetFrontTh = 0;
|
fleetFrontTh = 0;
|
||||||
fleetRearTh = 0;
|
fleetRearTh = 0;
|
||||||
@@ -631,6 +768,8 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
fleetRearTh = notification.FleetRearTh;
|
fleetRearTh = notification.FleetRearTh;
|
||||||
fleetMode = notification.Mode;
|
fleetMode = notification.Mode;
|
||||||
fleetOmega = notification.FleetOmega;
|
fleetOmega = notification.FleetOmega;
|
||||||
|
notificationRequestedFleetOmega = notification.RequestedFleetOmega;
|
||||||
|
notificationMotionReleased = notification.FleetMotionReleased;
|
||||||
notificationStopActive = notification.FleetStopActive;
|
notificationStopActive = notification.FleetStopActive;
|
||||||
notificationStopReason = notification.FleetStopReason ?? "";
|
notificationStopReason = notification.FleetStopReason ?? "";
|
||||||
notificationStopSourceCar = notification.FleetStopSourceCar;
|
notificationStopSourceCar = notification.FleetStopSourceCar;
|
||||||
@@ -641,6 +780,13 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
MultiVehicleAutoIdealTh = notification.IdealTh;
|
MultiVehicleAutoIdealTh = notification.IdealTh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var requestedFleetOmega = !isMaster && fleetMode == 2
|
||||||
|
? ((Math.Abs(notificationRequestedFleetOmega) > 1e-6f || !notificationMotionReleased)
|
||||||
|
? notificationRequestedFleetOmega
|
||||||
|
: fleetOmega)
|
||||||
|
: fleetOmega;
|
||||||
|
UpdateMultiVehicleRotateModeState(fleetMode, manualEnabled || autoEnabled, requestedFleetOmega);
|
||||||
|
|
||||||
var (layoutX, layoutY, layoutTh) = GetLayoutPose(syncTh, syncDistance);
|
var (layoutX, layoutY, layoutTh) = GetLayoutPose(syncTh, syncDistance);
|
||||||
|
|
||||||
if (isMaster)
|
if (isMaster)
|
||||||
@@ -716,6 +862,7 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
bool othersDetectOk;
|
bool othersDetectOk;
|
||||||
string otherDetectLostCars;
|
string otherDetectLostCars;
|
||||||
List<KeyValuePair<int, VehicleSyncInfo>> motionInfeasibleMembers;
|
List<KeyValuePair<int, VehicleSyncInfo>> motionInfeasibleMembers;
|
||||||
|
List<KeyValuePair<int, VehicleSyncInfo>> rotateUnalignedMembers;
|
||||||
lock (FleetLock)
|
lock (FleetLock)
|
||||||
{
|
{
|
||||||
othersDetectOk = MultiVehicleFleet.Where(kv => kv.Key != CarNum).All(kv => kv.Value.DetectOk);
|
othersDetectOk = MultiVehicleFleet.Where(kv => kv.Key != CarNum).All(kv => kv.Value.DetectOk);
|
||||||
@@ -725,7 +872,21 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
motionInfeasibleMembers = MultiVehicleFleet
|
motionInfeasibleMembers = MultiVehicleFleet
|
||||||
.Where(kv => kv.Key != CarNum && !kv.Value.MotionFeasible)
|
.Where(kv => kv.Key != CarNum && !kv.Value.MotionFeasible)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
rotateUnalignedMembers = MultiVehicleFleet
|
||||||
|
.Where(kv => !kv.Value.RotateWheelsAligned)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
if (!MultiVehicleRotateWheelsReady && rotateUnalignedMembers.All(kv => kv.Key != CarNum))
|
||||||
|
{
|
||||||
|
rotateUnalignedMembers.Add(new KeyValuePair<int, VehicleSyncInfo>(CarNum, new VehicleSyncInfo
|
||||||
|
{
|
||||||
|
RotateWheelsAligned = false,
|
||||||
|
RotateWheelAlignDetail = _multiVehicleRotateAlignDetail
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
var rotateUnalignedCars = string.Join(",", rotateUnalignedMembers.Select(kv => kv.Key.ToString()));
|
||||||
|
var rotateFleetWheelsAligned = fleetMode != 2 || (fleetReady && rotateUnalignedMembers.Count == 0);
|
||||||
|
MultiVehicleRotateFleetReady = rotateFleetWheelsAligned;
|
||||||
|
|
||||||
var fleetStopActive = false;
|
var fleetStopActive = false;
|
||||||
var fleetStopReason = "";
|
var fleetStopReason = "";
|
||||||
@@ -794,6 +955,26 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
Hedingben.ToastText("车队检测正常", $"MultiVehicle{CarNum}-stop");
|
Hedingben.ToastText("车队检测正常", $"MultiVehicle{CarNum}-stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rotateHoldForAlignment = false;
|
||||||
|
if (fleetMode == 2 && fleetReady && !fleetStopActive && !notificationMotionReleased)
|
||||||
|
{
|
||||||
|
rotateHoldForAlignment = true;
|
||||||
|
fleetOmega = 0;
|
||||||
|
Hedingben.ToastText("车队原地旋转等待主车释放速度", $"MultiVehicle{CarNum}-rotate-align");
|
||||||
|
LogMultiVehicleRemoteDecision(
|
||||||
|
$"ROTATE_HOLD_RELEASE master={isMaster} manual={manualEnabled} auto={autoEnabled} " +
|
||||||
|
$"requestedOmega={requestedFleetOmega:0.000}", true);
|
||||||
|
}
|
||||||
|
else if (fleetMode == 2 && fleetReady && !fleetStopActive && !rotateFleetWheelsAligned)
|
||||||
|
{
|
||||||
|
rotateHoldForAlignment = true;
|
||||||
|
fleetOmega = 0;
|
||||||
|
Hedingben.ToastText($"车队原地旋转舵轮预对齐中 car=[{rotateUnalignedCars}]", $"MultiVehicle{CarNum}-rotate-align");
|
||||||
|
LogMultiVehicleRemoteDecision(
|
||||||
|
$"ROTATE_HOLD_ALIGN master={isMaster} manual={manualEnabled} auto={autoEnabled} " +
|
||||||
|
$"requestedOmega={requestedFleetOmega:0.000} unalignedCars=[{rotateUnalignedCars}]", true);
|
||||||
|
}
|
||||||
|
|
||||||
if (fleetStopActive)
|
if (fleetStopActive)
|
||||||
LogMultiVehicleStop(fleetStopReason);
|
LogMultiVehicleStop(fleetStopReason);
|
||||||
|
|
||||||
@@ -814,6 +995,12 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
{
|
{
|
||||||
chassis.RampStop();
|
chassis.RampStop();
|
||||||
SetMultiVehicleMotionFeasible(true);
|
SetMultiVehicleMotionFeasible(true);
|
||||||
|
if (fleetMode == 2)
|
||||||
|
{
|
||||||
|
MultiVehicleRotateWheelsReady = false;
|
||||||
|
MultiVehicleRotateFleetReady = false;
|
||||||
|
_multiVehicleRotateAlignDetail = $"fleet stop: {fleetStopReason}";
|
||||||
|
}
|
||||||
LogMultiVehicleRemoteDecision(
|
LogMultiVehicleRemoteDecision(
|
||||||
$"RAMP_STOP master={isMaster} manual={manualEnabled} auto={autoEnabled} ready={fleetReady} " +
|
$"RAMP_STOP master={isMaster} manual={manualEnabled} auto={autoEnabled} ready={fleetReady} " +
|
||||||
$"reason={fleetStopReason}", true);
|
$"reason={fleetStopReason}", true);
|
||||||
@@ -926,8 +1113,45 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
_mvRotCompVy = rotCompVy;
|
_mvRotCompVy = rotCompVy;
|
||||||
_mvRotCompOmega = rotCompOmega;
|
_mvRotCompOmega = rotCompOmega;
|
||||||
|
|
||||||
var motionOk = chassis.SendRotateMotion(fleetOmega,
|
bool motionOk;
|
||||||
localCompensateX: rotCompVx, localCompensateY: rotCompVy, localCompensateTh: rotCompOmega);
|
if (rotateHoldForAlignment)
|
||||||
|
{
|
||||||
|
motionOk = PrepareMultiVehicleRotateWheels(chassis, requestedFleetOmega,
|
||||||
|
rotCompVx, rotCompVy, rotCompOmega);
|
||||||
|
}
|
||||||
|
else if (rotating)
|
||||||
|
{
|
||||||
|
motionOk = PrepareMultiVehicleRotateWheels(chassis, requestedFleetOmega,
|
||||||
|
rotCompVx, rotCompVy, rotCompOmega, rampStop: false);
|
||||||
|
if (motionOk && MultiVehicleRotateWheelsReady)
|
||||||
|
{
|
||||||
|
motionOk = chassis.SendRotateMotion(fleetOmega,
|
||||||
|
localCompensateX: rotCompVx, localCompensateY: rotCompVy,
|
||||||
|
localCompensateTh: rotCompOmega);
|
||||||
|
MultiVehicleRotateWheelsReady = motionOk &&
|
||||||
|
TryCheckRotateWheelAlignment(chassis,
|
||||||
|
Conf.InPlaceRotateWheelAlignDeg,
|
||||||
|
out _multiVehicleRotateAlignDetail);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rotateHoldForAlignment = true;
|
||||||
|
fleetOmega = 0;
|
||||||
|
chassis.RampStop();
|
||||||
|
LogMultiVehicleRemoteDecision(
|
||||||
|
$"ROTATE_HOLD_LOCAL_ALIGN master={isMaster} manual={manualEnabled} auto={autoEnabled} " +
|
||||||
|
$"requestedOmega={requestedFleetOmega:0.000} align={_multiVehicleRotateAlignDetail}", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
motionOk = chassis.SendRotateMotion(fleetOmega,
|
||||||
|
localCompensateX: rotCompVx, localCompensateY: rotCompVy,
|
||||||
|
localCompensateTh: rotCompOmega);
|
||||||
|
MultiVehicleRotateWheelsReady = motionOk &&
|
||||||
|
TryCheckRotateWheelAlignment(chassis, Conf.InPlaceRotateWheelAlignDeg,
|
||||||
|
out _multiVehicleRotateAlignDetail);
|
||||||
|
}
|
||||||
SetMultiVehicleMotionFeasible(motionOk, motionOk ? "" : chassis.LastMotionDecomposeFailureReason);
|
SetMultiVehicleMotionFeasible(motionOk, motionOk ? "" : chassis.LastMotionDecomposeFailureReason);
|
||||||
if (!motionOk)
|
if (!motionOk)
|
||||||
{
|
{
|
||||||
@@ -938,7 +1162,9 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
}
|
}
|
||||||
LogMultiVehicleRemoteDecision(
|
LogMultiVehicleRemoteDecision(
|
||||||
$"SEND_ROTATE master={isMaster} manual={manualEnabled} canMove={canMove} ready={fleetReady} ok={motionOk} " +
|
$"SEND_ROTATE master={isMaster} manual={manualEnabled} canMove={canMove} ready={fleetReady} ok={motionOk} " +
|
||||||
$"mode={fleetMode} omega={fleetOmega:0.000} comp=({rotCompVx:0.0},{rotCompVy:0.0},{rotCompOmega:0.000}) " +
|
$"holdAlign={rotateHoldForAlignment} wheelReady={MultiVehicleRotateWheelsReady} fleetReady={MultiVehicleRotateFleetReady} " +
|
||||||
|
$"mode={fleetMode} omegaReq={requestedFleetOmega:0.000} omega={fleetOmega:0.000} " +
|
||||||
|
$"comp=({rotCompVx:0.0},{rotCompVy:0.0},{rotCompOmega:0.000}) align={_multiVehicleRotateAlignDetail} " +
|
||||||
$"fleetCnt={fleetCount}/{Conf.MultiVehicleFleetNum}");
|
$"fleetCnt={fleetCount}/{Conf.MultiVehicleFleetNum}");
|
||||||
|
|
||||||
// 仅主车:读取两车实际 sim 位姿,量化"开环横向滑移"来源(节流 ~200ms)。
|
// 仅主车:读取两车实际 sim 位姿,量化"开环横向滑移"来源(节流 ~200ms)。
|
||||||
@@ -950,6 +1176,9 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
_mvRotCompVx = 0;
|
_mvRotCompVx = 0;
|
||||||
_mvRotCompVy = 0;
|
_mvRotCompVy = 0;
|
||||||
_mvRotCompOmega = 0;
|
_mvRotCompOmega = 0;
|
||||||
|
MultiVehicleRotateWheelsReady = true;
|
||||||
|
MultiVehicleRotateFleetReady = true;
|
||||||
|
_multiVehicleRotateAlignDetail = "";
|
||||||
// 退出原地旋转:清空 PI 积分与计时、位姿诊断片段,下次进入重新起算。
|
// 退出原地旋转:清空 PI 积分与计时、位姿诊断片段,下次进入重新起算。
|
||||||
_rotIntegX = _rotIntegY = _rotIntegTh = 0;
|
_rotIntegX = _rotIntegY = _rotIntegTh = 0;
|
||||||
_rotPiLastTime = DateTime.MinValue;
|
_rotPiLastTime = DateTime.MinValue;
|
||||||
@@ -1003,7 +1232,9 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
$"bias({posBiasX:F0},{posBiasY:F0},{posBiasTh:F1}) -> comp x:{xPosCompensate:F1} y:{yPosCompensate:F1} th:{thPosCompensate:F2} " +
|
$"bias({posBiasX:F0},{posBiasY:F0},{posBiasTh:F1}) -> comp x:{xPosCompensate:F1} y:{yPosCompensate:F1} th:{thPosCompensate:F2} " +
|
||||||
$"| LAYOUT({layoutX:F0},{layoutY:F0},{layoutTh:F0}) R:{syncDistance / 2f:F0} " +
|
$"| LAYOUT({layoutX:F0},{layoutY:F0},{layoutTh:F0}) R:{syncDistance / 2f:F0} " +
|
||||||
$"| SEND mode:{fleetMode} omega:{fleetOmega:F1} vx:{fleetVx:F3} fTh:{fleetFrontTh:F2} rTh:{fleetRearTh:F2} cx:{cx:F1} cy:{cy:F1} cth:{cth:F2} " +
|
$"| SEND mode:{fleetMode} omega:{fleetOmega:F1} vx:{fleetVx:F3} fTh:{fleetFrontTh:F2} rTh:{fleetRearTh:F2} cx:{cx:F1} cy:{cy:F1} cth:{cth:F2} " +
|
||||||
$"rotComp(vx:{_mvRotCompVx:F1} vy:{_mvRotCompVy:F1} om:{_mvRotCompOmega:F2})";
|
$"rotComp(vx:{_mvRotCompVx:F1} vy:{_mvRotCompVy:F1} om:{_mvRotCompOmega:F2}) " +
|
||||||
|
$"| ROT_ALIGN hold:{rotateHoldForAlignment} wheelReady:{MultiVehicleRotateWheelsReady} fleetReady:{MultiVehicleRotateFleetReady} " +
|
||||||
|
$"unaligned:[{rotateUnalignedCars}] reqOmega:{requestedFleetOmega:F1} detail:{_multiVehicleRotateAlignDetail}";
|
||||||
|
|
||||||
DLog.Log(dbg, "MultiVehicleDbg");
|
DLog.Log(dbg, "MultiVehicleDbg");
|
||||||
FleetDiag(dbg);
|
FleetDiag(dbg);
|
||||||
@@ -1046,6 +1277,8 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
FleetRearTh = fleetStopActive ? 0 : fleetRearTh,
|
FleetRearTh = fleetStopActive ? 0 : fleetRearTh,
|
||||||
Mode = fleetMode,
|
Mode = fleetMode,
|
||||||
FleetOmega = fleetStopActive ? 0 : fleetOmega,
|
FleetOmega = fleetStopActive ? 0 : fleetOmega,
|
||||||
|
RequestedFleetOmega = fleetMode == 2 && !fleetStopActive ? requestedFleetOmega : 0,
|
||||||
|
FleetMotionReleased = !fleetStopActive && !(fleetMode == 2 && rotateHoldForAlignment),
|
||||||
FleetStopActive = fleetStopActive,
|
FleetStopActive = fleetStopActive,
|
||||||
FleetStopReason = fleetStopReason,
|
FleetStopReason = fleetStopReason,
|
||||||
FleetStopSourceCar = fleetStopSourceCar,
|
FleetStopSourceCar = fleetStopSourceCar,
|
||||||
@@ -1066,6 +1299,12 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogMultiVehicleRemoteDecision(
|
||||||
|
$"NOTIFY_SEND seq={notification.Seq} mode={notification.Mode} " +
|
||||||
|
$"omega={notification.FleetOmega:0.000} reqOmega={notification.RequestedFleetOmega:0.000} " +
|
||||||
|
$"released={notification.FleetMotionReleased} stop={notification.FleetStopActive} " +
|
||||||
|
$"reason={notification.FleetStopReason} fleetCnt={notification.Fleet.Count}/{Conf.MultiVehicleFleetNum}");
|
||||||
|
|
||||||
foreach (var kv in notification.Fleet)
|
foreach (var kv in notification.Fleet)
|
||||||
{
|
{
|
||||||
var ip = kv.Value.Ip;
|
var ip = kv.Value.Ip;
|
||||||
@@ -1240,7 +1479,9 @@ public class PilotDefinition : MultiWheelPilotDefinition<PilotConfig, PilotDefin
|
|||||||
Aligned = aligned,
|
Aligned = aligned,
|
||||||
DetectOk = detectOk,
|
DetectOk = detectOk,
|
||||||
MotionFeasible = _multiVehicleMotionFeasible,
|
MotionFeasible = _multiVehicleMotionFeasible,
|
||||||
MotionInfeasibleReason = _multiVehicleMotionInfeasibleReason
|
MotionInfeasibleReason = _multiVehicleMotionInfeasibleReason,
|
||||||
|
RotateWheelsAligned = MultiVehicleRotateWheelsReady,
|
||||||
|
RotateWheelAlignDetail = _multiVehicleRotateAlignDetail
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public class VehicleSyncInfo
|
|||||||
[JsonProperty("DetectOk")] public bool DetectOk { get; set; }
|
[JsonProperty("DetectOk")] public bool DetectOk { get; set; }
|
||||||
[JsonProperty("MotionFeasible")] public bool MotionFeasible { get; set; } = true;
|
[JsonProperty("MotionFeasible")] public bool MotionFeasible { get; set; } = true;
|
||||||
[JsonProperty("MotionInfeasibleReason")] public string MotionInfeasibleReason { get; set; } = "";
|
[JsonProperty("MotionInfeasibleReason")] public string MotionInfeasibleReason { get; set; } = "";
|
||||||
|
[JsonProperty("RotateWheelsAligned")] public bool RotateWheelsAligned { get; set; } = true;
|
||||||
|
[JsonProperty("RotateWheelAlignDetail")] public string RotateWheelAlignDetail { get; set; } = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleSyncNotification
|
public class VehicleSyncNotification
|
||||||
@@ -38,6 +40,8 @@ public class VehicleSyncNotification
|
|||||||
[JsonProperty("Mode")] public int Mode { get; set; }
|
[JsonProperty("Mode")] public int Mode { get; set; }
|
||||||
// 原地旋转角速度(deg/s,逆时针为正),仅 Mode==2 有效
|
// 原地旋转角速度(deg/s,逆时针为正),仅 Mode==2 有效
|
||||||
[JsonProperty("FleetOmega")] public float FleetOmega { get; set; }
|
[JsonProperty("FleetOmega")] public float FleetOmega { get; set; }
|
||||||
|
[JsonProperty("RequestedFleetOmega")] public float RequestedFleetOmega { get; set; }
|
||||||
|
[JsonProperty("FleetMotionReleased")] public bool FleetMotionReleased { get; set; } = true;
|
||||||
[JsonProperty("FleetStopActive")] public bool FleetStopActive { get; set; }
|
[JsonProperty("FleetStopActive")] public bool FleetStopActive { get; set; }
|
||||||
[JsonProperty("FleetStopReason")] public string FleetStopReason { get; set; } = "";
|
[JsonProperty("FleetStopReason")] public string FleetStopReason { get; set; } = "";
|
||||||
[JsonProperty("FleetStopSourceCar")] public int FleetStopSourceCar { get; set; }
|
[JsonProperty("FleetStopSourceCar")] public int FleetStopSourceCar { get; set; }
|
||||||
|
|||||||
Reference in New Issue
Block a user