using System; using System.Numerics; using ClumsyCore; using ClumsyCore.Interfaces; using ClumsyCore.Pilot; using FundamentalLib; using MDCSToolBox.Clumsy.MotionControllers; using MDCSToolBox.Clumsy.Movements; using MDCSToolBox.Clumsy.Pilot; namespace MultiWheelC; public class ChassisController : MovementDefinition { public float BaseSpeed = Configuration.conf.basicSpeed; private DateTime _sendMotionDbgLast = DateTime.MinValue; public override MultiWheelGeometricController Get() { return new MultiWheelGeometricController { Chassis = BasicPilotBase.Chassis, BaseSpeed = BaseSpeed, SlowDistance = PilotDefinition.Conf.SlowDistance, SlowingPow = PilotDefinition.Conf.SlowingPow, FinishDistance = PilotDefinition.Conf.FinishDistance, FinishSpeed = PilotDefinition.Conf.FinishSpeed, FirstThAccuracy = PilotDefinition.Conf.FirstThAccuracy, FirstRotateSpeedFac = PilotDefinition.Conf.FirstRotateSpeedFac, FirstRotateMaxSpeed = PilotDefinition.Conf.FirstRotateMaxSpeed, NotContinuousAngle = PilotDefinition.Conf.NotContinuousAngle, DebugMode = PilotDefinition.Conf.MotionDebugPrint, DebugCurvature = PilotDefinition.Conf.DebugCurvature, PowerSteeringLookAhead = PilotDefinition.Conf.PowerSteeringLookAhead, SpeedLookAhead = PilotDefinition.Conf.SpeedLookAhead, SpeedLookAheadCurveDiff = PilotDefinition.Conf.SpeedLookAheadCurveDiff, SpeedLookBackCurveDiff = PilotDefinition.Conf.SpeedLookBackCurveDiff, SpeedLimitCurveDiffMin = PilotDefinition.Conf.SpeedLimitCurveDiffMin, SpeedLimitCurveMin = PilotDefinition.Conf.SpeedLimitCurveMin, MaxRotateSpeed = PilotDefinition.Conf.MaxRotateSpeedCurveLimit, MaxRotateAcc = PilotDefinition.Conf.MaxRotateAccCurveLimit, GcpThetaThreshold = PilotDefinition.Conf.GcpThetaThreshold, DthLinearFac = PilotDefinition.Conf.DthLinearFac, DthLinearThreshold = PilotDefinition.Conf.DthLinearThreshold, BiasFac = PilotDefinition.Conf.BiasFac, BiasThreshold = PilotDefinition.Conf.BiasThreshold, MultiVehicleSendMotion = (speed, frontTh, rearTh, idealPos, idealAngle) => { var self = PilotDefinition.Self; self.MultiVehicleAutoEnabled = true; // A: 用固定锁对象(不再锁会被替换的字段引用)。 int fleetCnt; lock (self.FleetLock) fleetCnt = self.MultiVehicleFleet.Count; // 诊断(节流 ~300ms):确认回调被调用、编队是否就绪、是否因数量不符提前 return(导致不下发速度)。 if ((DateTime.Now - _sendMotionDbgLast).TotalMilliseconds >= 300) { _sendMotionDbgLast = DateTime.Now; DLog.Log( $"SENDMOTION speed={speed:0.000} fTh={frontTh:0.0} rTh={rearTh:0.0} " + $"ideal=({idealPos.X:0},{idealPos.Y:0},{idealAngle:0.0}) " + $"editCnt={fleetCnt}/{PilotDefinition.Conf.MultiVehicleFleetNum} " + $"earlyReturn={fleetCnt != PilotDefinition.Conf.MultiVehicleFleetNum}", "FleetCrabDbg"); } if (fleetCnt != PilotDefinition.Conf.MultiVehicleFleetNum) return; self.MultiVehicleAutoVx = speed; self.MultiVehicleAutoFrontTh = frontTh; self.MultiVehicleAutoRearTh = rearTh; // D: 透传路径控制器算出的理想车队中心位姿(此前被丢弃),供各车按 layout 做前馈。 self.MultiVehicleAutoIdealX = idealPos.X; self.MultiVehicleAutoIdealY = idealPos.Y; self.MultiVehicleAutoIdealTh = idealAngle; self.MultiVehicleAutoHasIdeal = true; // B: 标记命令新鲜度。路径结束/早退/卡顿不再刷新此时刻 → 主车超时后清零速度,避免滑行。 self.MultiVehicleAutoCmdTime = DateTime.Now; }, // G: 读取车队中心原子快照,避免跨线程读到撕裂的 x/y/th 组合。 MultiVehicleGetFleetPos = () => { var snap = PilotDefinition.Self.GetFleetCenterSnapshot(); return new Location { x = snap.X, y = snap.Y, th = snap.Th, l_step = 1, tick = DateTime.Now.Ticks }; } }; } }