diff --git a/VectoCore/Configuration/Constants.cs b/VectoCore/Configuration/Constants.cs index 2c4e309c58e19108ffbdbd1d8d09b835ac94e260..d148d26c72a5c9c0800af65dc9204d0155d0c65c 100644 --- a/VectoCore/Configuration/Constants.cs +++ b/VectoCore/Configuration/Constants.cs @@ -66,6 +66,8 @@ namespace TUGraz.VectoCore.Configuration public const double CluchNormSpeed = 0.03; public static readonly MeterPerSquareSecond MinimumAcceleration = 0.1.SI<MeterPerSquareSecond>(); + + public static Meter DriverActionDistanceTolerance = 0.25.SI<Meter>(); } } } \ No newline at end of file diff --git a/VectoCore/Models/Connector/Ports/Impl/Response.cs b/VectoCore/Models/Connector/Ports/Impl/Response.cs index 3826459d99f144c4ef13d7600a7900e72b5557e4..118db603af567061db9f21dba431a70f8eacc777 100644 --- a/VectoCore/Models/Connector/Ports/Impl/Response.cs +++ b/VectoCore/Models/Connector/Ports/Impl/Response.cs @@ -37,7 +37,7 @@ namespace TUGraz.VectoCore.Models.Connector.Ports.Impl /// </summary> public class ResponseFailOverload : AbstractResponse { - public double Delta { get; set; } + public Watt Delta { get; set; } public double Gradient { get; set; } public override ResponseType ResponseType @@ -71,8 +71,8 @@ namespace TUGraz.VectoCore.Models.Connector.Ports.Impl internal class ResponseDryRun : AbstractResponse { - public double DeltaFullLoad { get; set; } - public double DeltaDragLoad { get; set; } + public Watt DeltaFullLoad { get; set; } + public Watt DeltaDragLoad { get; set; } public override ResponseType ResponseType { diff --git a/VectoCore/Models/Simulation/DataBus/IDataBus.cs b/VectoCore/Models/Simulation/DataBus/IDataBus.cs index f3c7bedf5758971ef05952d0ae287f18cbcac4c6..49a7772a9c723eb9497fe4169a2c4ed3f68845f3 100644 --- a/VectoCore/Models/Simulation/DataBus/IDataBus.cs +++ b/VectoCore/Models/Simulation/DataBus/IDataBus.cs @@ -1,7 +1,9 @@ -namespace TUGraz.VectoCore.Models.Simulation.DataBus +using TUGraz.VectoCore.Models.SimulationComponent; + +namespace TUGraz.VectoCore.Models.Simulation.DataBus { /// <summary> /// Defines interfaces for all different cockpits to access shared data of the powertrain. /// </summary> - public interface IDataBus : IGearboxInfo, IEngineInfo, IVehicleInfo, IMileageCounter, IRoadLookAhead {} + public interface IDataBus : IGearboxInfo, IEngineInfo, IVehicleInfo, IMileageCounter, IBreaks, IRoadLookAhead {} } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs index 574307bda1bf3cd288bbdf2a4db6700d9147bd2a..93f24f01b8ad1745a068c01b7571052a575cc54e 100644 --- a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs +++ b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs @@ -18,6 +18,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl internal IEngineInfo Engine; internal IGearboxInfo Gearbox; internal IVehicleInfo Vehicle; + internal IBreaks Breaks; internal IMileageCounter MilageCounter; @@ -121,6 +122,11 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl MilageCounter = milage; } + var breaks = component as IBreaks; + if (breaks != null) { + Breaks = breaks; + } + var road = component as IRoadLookAhead; if (road != null) { Road = road; @@ -171,5 +177,11 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl { return Road.LookAhead(time); } + + public Watt BreakPower + { + get { return Breaks.BreakPower; } + set { Breaks.BreakPower = value; } + } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs b/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs index bc429a3f29fc25e2aee419ad7de7010784c67a70..75a67c9565d5b293ce9e3562206673817822773b 100644 --- a/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs +++ b/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs @@ -4,6 +4,7 @@ using System.Data; using System.Linq; using Common.Logging; using Newtonsoft.Json; +using NLog.Fluent; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Utils; @@ -103,8 +104,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox public NewtonMeter GearboxInTorque(PerSecond angularVelocity, NewtonMeter gbxOutTorque) { try { - return VectoMath.Max(_lossMap.Interpolate(angularVelocity.Value(), gbxOutTorque.Value()).SI<NewtonMeter>(), - 0.SI<NewtonMeter>()); + var gbxInTorque = _lossMap.Interpolate(angularVelocity.Value(), gbxOutTorque.Value()).SI<NewtonMeter>(); + LogManager.GetLogger(this.GetType()).DebugFormat("GearboxLoss: {0}", gbxInTorque); + // Torque at input of the geabox must be greater than or equal to the torque at the output + // (i.e. no 'torque-gain' in the transmission due to interpolation etc.) + return VectoMath.Max(gbxInTorque, gbxOutTorque); } catch (Exception e) { throw new VectoSimulationException( string.Format("Failed to interpolate in TransmissionLossMap. angularVelocity: {0}, torque: {1}", angularVelocity, diff --git a/VectoCore/Models/SimulationComponent/IBreaks.cs b/VectoCore/Models/SimulationComponent/IBreaks.cs new file mode 100644 index 0000000000000000000000000000000000000000..d32488fe2e3c7dec835ccd1baf73d48103dc2da5 --- /dev/null +++ b/VectoCore/Models/SimulationComponent/IBreaks.cs @@ -0,0 +1,9 @@ +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent +{ + public interface IBreaks + { + Watt BreakPower { get; set; } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/AxleGear.cs b/VectoCore/Models/SimulationComponent/Impl/AxleGear.cs index 371e0ecd0893a3babc4dff0312a64972f44f1cf1..a8da825462f8fd75012eaa6f9ab43e057a8b9a51 100644 --- a/VectoCore/Models/SimulationComponent/Impl/AxleGear.cs +++ b/VectoCore/Models/SimulationComponent/Impl/AxleGear.cs @@ -34,6 +34,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public IResponse Request(Second absTime, Second dt, NewtonMeter torque, PerSecond angularVelocity, bool dryRun = false) { + Log.DebugFormat("request: torque: {0}, angularVelocity: {1}", torque, angularVelocity); return _nextComponent.Request(absTime, dt, _gearData.LossMap.GearboxInTorque(angularVelocity * _gearData.Ratio, torque), angularVelocity * _gearData.Ratio, dryRun); diff --git a/VectoCore/Models/SimulationComponent/Impl/Breaks.cs b/VectoCore/Models/SimulationComponent/Impl/Breaks.cs new file mode 100644 index 0000000000000000000000000000000000000000..95881f82885ca294baffb4b2e235dc34a6a1d62b --- /dev/null +++ b/VectoCore/Models/SimulationComponent/Impl/Breaks.cs @@ -0,0 +1,60 @@ +using System; +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + public class Breaks : VectoSimulationComponent, IPowerTrainComponent, ITnOutPort, ITnInPort, IBreaks + { + protected ITnOutPort Next; + + protected NewtonMeter BreakTorque; + + public Breaks(IVehicleContainer dataBus) : base(dataBus) {} + + + public ITnInPort InPort() + { + return this; + } + + public ITnOutPort OutPort() + { + return this; + } + + public Watt BreakPower { get; set; } + + public IResponse Request(Second absTime, Second dt, NewtonMeter torque, PerSecond angularVelocity, bool dryRun = false) + { + BreakTorque = Formulas.PowerToTorque(BreakPower, angularVelocity); + return Next.Request(absTime, dt, torque - BreakTorque, angularVelocity, dryRun); + } + + public IResponse Initialize(NewtonMeter torque, PerSecond angularVelocity) + { + BreakPower = 0.SI<Watt>(); + BreakTorque = 0.SI<NewtonMeter>(); + return Next.Initialize(torque, angularVelocity); + } + + + public void Connect(ITnOutPort other) + { + Next = other; + } + + protected override void DoWriteModalResults(IModalDataWriter writer) + { + writer[ModalResultField.Pbrake] = BreakPower; + } + + protected override void DoCommitSimulationStep() + { + BreakPower = 0.SI<Watt>(); + BreakTorque = 0.SI<NewtonMeter>(); + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs index d2a77ca383001ad3c933176e4dc57aa722739407..47362afce6df81f7b450133b6e6d068ef854fc9b 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs @@ -84,6 +84,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl private void AddClutchLoss(NewtonMeter torque, PerSecond angularVelocity, out NewtonMeter torqueIn, out PerSecond engineSpeedIn) { + Log.DebugFormat("from Wheels: torque: {0}, angularVelocity: {1}, power {2}", torque, angularVelocity, + Formulas.TorqueToPower(torque, angularVelocity)); torqueIn = torque; engineSpeedIn = angularVelocity; @@ -109,6 +111,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl _clutchState = ClutchState.ClutchClosed; } } + Log.DebugFormat("to Engine: torque: {0}, angularVelocity: {1}, power {2}", torqueIn, engineSpeedIn, + Formulas.TorqueToPower(torqueIn, engineSpeedIn)); } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 0bedf3609b3299c449a8c689d6329494761ce18f..909f2e2f21b94040ec516d3966fbbc8d38ddcff8 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -93,13 +93,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (dryRun) { return new ResponseDryRun() { - DeltaFullLoad = (requestedEnginePower - _currentState.DynamicFullLoadPower).Value(), - DeltaDragLoad = (requestedEnginePower - _currentState.FullDragPower).Value() + DeltaFullLoad = (requestedEnginePower - _currentState.DynamicFullLoadPower), + DeltaDragLoad = (requestedEnginePower - _currentState.FullDragPower) }; } if (!_currentState.EnginePower.IsEqual(requestedEnginePower, Constants.SimulationSettings.EngineFLDPowerTolerance)) { - return new ResponseFailOverload() { Delta = (requestedEnginePower - _currentState.EnginePower).Value() }; + return new ResponseFailOverload() { Delta = (requestedEnginePower - _currentState.EnginePower) }; } UpdateEngineState(_currentState.EnginePower); diff --git a/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/Models/SimulationComponent/Impl/Driver.cs index f4f0710890538d3df56c08889e4678e60a6d1bd2..27222b0721f12b20c19c2552170ad72bacab10ec 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -1,6 +1,7 @@ using System; using System.CodeDom; using System.Collections.Generic; +using System.Configuration; using System.Linq; using System.Security.Cryptography.X509Certificates; using TUGraz.VectoCore.Configuration; @@ -35,6 +36,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl //OverSpeed, } + public class DrivingBehaviorEntry + { + public DrivingBehavior Action; + public MeterPerSecond NextTargetSpeed; + public Meter TriggerDistance; + public Meter ActionDistance; + } + public Driver(VehicleContainer container, DriverData driverData) : base(container) { DriverData = driverData; @@ -50,8 +59,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Next = other; } + + public IResponse Initialize(MeterPerSecond vehicleSpeed, Radian roadGradient) + { + return Next.Initialize(vehicleSpeed, roadGradient); + } + + public IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) { + Log.Debug("==== DRIVER Request ===="); + Log.DebugFormat( + "Request: absTime: {0}, ds: {1}, targetVelocity: {2}, gradient: {3} | distance: {4}, velocity: {5}", absTime, ds, + targetVelocity, + gradient, DataBus.Distance(), DataBus.VehicleSpeed()); + var retVal = DoHandleRequest(absTime, ds, targetVelocity, gradient); CurrentState.Response = retVal; @@ -64,13 +86,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return retVal; } - public IResponse Initialize(MeterPerSecond vehicleSpeed, Radian roadGradient) - { - return Next.Initialize(vehicleSpeed, roadGradient); - } public IResponse Request(Second absTime, Second dt, MeterPerSecond targetVelocity, Radian gradient) { + Log.Debug("==== DRIVER Request ===="); + Log.DebugFormat("Request: absTime: {0}, dt: {1}, targetVelocity: {2}, gradient: {3}", absTime, dt, targetVelocity + , gradient); var retVal = DoHandleRequest(absTime, dt, targetVelocity, gradient); CurrentState.Response = retVal; @@ -82,18 +103,42 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected IResponse DoHandleRequest(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) { - var tmp = GetNextDrivingAction(targetVelocity); var currentDistance = DataBus.Distance(); - if (tmp.Key < currentDistance + ds) { - return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = tmp.Key - currentDistance }; - } + var nextDrivingActions = GetNextDrivingActions(currentDistance); - var nextAction = DrivingBehavior.Drive; - if (tmp.Key.IsEqual(currentDistance + ds)) { - nextAction = tmp.Value; + if (CurrentState.DrivingAction.Action == DrivingBehavior.Stopped && targetVelocity >= DataBus.VehicleSpeed()) { + CurrentState.DrivingAction.Action = DrivingBehavior.Drive; } - switch (nextAction) { + if (nextDrivingActions.Count > 0) { + // if we exceeded the previous action (by accident), set the action anyway in case there is no 'next action'... + var previousActionss = nextDrivingActions.Where(x => x.Key < currentDistance).ToList(); + if (previousActionss.Count > 0) { + CurrentState.DrivingAction = previousActionss.Last().Value; + } + + var nextActions = nextDrivingActions.Where(x => x.Key >= currentDistance).ToList(); + var nextDrivingAction = nextActions.GetEnumerator(); + nextDrivingAction.MoveNext(); + var hasNextEntry = true; + + // if the current position matches the next action - set new action. + if (nextDrivingAction.Current.Key.IsEqual(currentDistance, + Constants.SimulationSettings.DriverActionDistanceTolerance.Value())) { + CurrentState.DrivingAction = nextDrivingAction.Current.Value; + hasNextEntry = nextDrivingAction.MoveNext(); // the current action has already been processed, look at next one... + } + // check if desired distance exeeds next acttion point + if (hasNextEntry && nextDrivingAction.Current.Key < currentDistance + ds) { + Log.DebugFormat( + "current Distance: {3} -- Simulation Distance {0} exceeds next DrivingAction at {1}, reducing interval to {2}", ds, + nextDrivingAction.Current.Key, nextDrivingAction.Current.Key - currentDistance, currentDistance); + return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = nextDrivingAction.Current.Key - currentDistance }; + } + } + Log.DebugFormat("DrivingAction: {0}", CurrentState.DrivingAction); + //CurrentState.DrivingAction = nextAction; + switch (CurrentState.DrivingAction.Action) { case DrivingBehavior.Accelerating: return DriveOrAccelerate(absTime, ds, targetVelocity, gradient); case DrivingBehavior.Drive: @@ -101,14 +146,100 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl case DrivingBehavior.Coasting: return DoCoast(absTime, ds, gradient); case DrivingBehavior.Breaking: - break; + return DoBreak(absTime, ds, gradient, CurrentState.DrivingAction.NextTargetSpeed); + } + throw new VectoSimulationException("unhandled driving action " + CurrentState.DrivingAction); + } + + private IResponse DoBreak(Second absTime, Meter ds, Radian gradient, MeterPerSecond nextTargetSpeed) + { + ComputeAcceleration(ref ds, nextTargetSpeed); + + // todo: still required? + if (CurrentState.dt.IsEqual(0)) { + return new ResponseFailTimeInterval(); + } + + var response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true); + + var dryRun = response as ResponseDryRun; + if (dryRun == null) { + throw new VectoSimulationException("Expected DryRunResponse after Dry-Run Request!"); + } + + if (dryRun.DeltaDragLoad > 0) { + throw new VectoSimulationException("No breaking necessary, still above full drag load!"); + } + + var newDs = ds; + var success = SearchBreakingPower(absTime, ref newDs, gradient, dryRun, true); + + if (!success) { + Log.ErrorFormat("Failed to find operating point for breaking!"); + throw new VectoSimulationException("Failed to find operating point for breaking!"); + } + + if (!ds.IsEqual(newDs)) { + Log.DebugFormat( + "SearchOperatingPoint Breaking reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!", newDs, + ds); + return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = newDs, SimulationInterval = CurrentState.dt }; + } + + Log.DebugFormat("Found operating point for breaking. dt: {0}, acceleration: {1}", CurrentState.dt, + CurrentState.Acceleration); + var retVal = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient); + CurrentState.Response = retVal; + switch (retVal.ResponseType) { + case ResponseType.Success: + retVal.SimulationInterval = CurrentState.dt; + return retVal; } - throw new VectoSimulationException("unhandled driving action " + nextAction); + Log.DebugFormat("unhandled response from powertrain: {0}", retVal); + return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt }; + } + + private bool SearchBreakingPower(Second absTime, ref Meter ds, Radian gradient, ResponseDryRun response, bool coasting) + { + var exceeded = new List<Watt>(); // only used while testing + var searchInterval = VectoMath.Abs(response.DeltaDragLoad); + var breakingPower = VectoMath.Abs(response.DeltaDragLoad); + + var originalDs = ds; + + do { + ds = originalDs; + var delta = -response.DeltaDragLoad; + + exceeded.Add(delta); + if (delta.IsEqual(0, Constants.SimulationSettings.EngineFLDPowerTolerance)) { + Log.DebugFormat("found operating point in {0} iterations, delta: {1}", exceeded.Count, delta); + return true; + } + if (delta > 0) { + breakingPower -= searchInterval; + } else { + breakingPower += searchInterval; + } + + searchInterval /= 2.0; + ComputeTimeInterval(CurrentState.Acceleration, ref ds, out CurrentState.dt); + DataBus.BreakPower = breakingPower; + response = (ResponseDryRun)Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true); + } while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold); + + Log.DebugFormat("Exceeded max iterations when searching for operating point!"); + //Log.DebugFormat("acceleration: {0} ... {1}", string.Join(", ", acceleration.Take(5)), + // string.Join(", ", acceleration.GetRange(acceleration.Count - 6, 5))); + Log.DebugFormat("exceeded: {0} ... {1}", string.Join(", ", exceeded.Take(5)), + string.Join(", ", exceeded.GetRange(exceeded.Count - 6, 5))); + + return false; } private IResponse DriveOrAccelerate(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) { - ComputeAcceleration(ds, targetVelocity); + ComputeAcceleration(ref ds, targetVelocity); if (CurrentState.dt.IsEqual(0)) { return new ResponseFailTimeInterval(); } @@ -120,7 +251,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl retVal.SimulationInterval = CurrentState.dt; return retVal; case ResponseType.FailOverload: - SearchOperatingPoint(absTime, ds, gradient, retVal); + SearchOperatingPoint(absTime, ref ds, gradient, retVal); break; } } while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold); @@ -128,33 +259,52 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt }; } - protected KeyValuePair<Meter, DrivingBehavior> GetNextDrivingAction(MeterPerSecond targetSpeed) + protected List<KeyValuePair<Meter, DrivingBehaviorEntry>> GetNextDrivingActions(Meter minDistance) { - // distance until halt: s = - v * v / (2 * a) var currentSpeed = DataBus.VehicleSpeed(); - - + // distance until halt: s = - v * v / (2 * a) var lookaheadDistance = (-currentSpeed * currentSpeed / (2 * DeclarationData.Driver.LookAhead.Deceleration)).Cast<Meter>(); var lookaheadData = DataBus.LookAhead(1.2 * lookaheadDistance); - var nextActions = new List<KeyValuePair<Meter, DrivingBehavior>>(); + Log.DebugFormat("Lookahead distance: {0} @ current speed {1}", lookaheadDistance, currentSpeed); + var nextActions = new List<KeyValuePair<Meter, DrivingBehaviorEntry>>(); for (var i = 0; i < lookaheadData.Count; i++) { var entry = lookaheadData[i]; if (entry.VehicleTargetSpeed < currentSpeed) { var breakingDistance = ComputeDecelerationDistance(entry.VehicleTargetSpeed); - nextActions.Add(new KeyValuePair<Meter, DrivingBehavior>(entry.Distance - breakingDistance, - DrivingBehavior.Breaking)); - nextActions.Add(new KeyValuePair<Meter, DrivingBehavior>(entry.Distance - lookaheadDistance, - DrivingBehavior.Coasting)); + Log.DebugFormat("distance to decelerate from {0} to {1}: {2}", currentSpeed, entry.VehicleTargetSpeed, + breakingDistance); + Log.DebugFormat("adding 'Braking' starting at distance {0}", entry.Distance - breakingDistance); + nextActions.Add(new KeyValuePair<Meter, DrivingBehaviorEntry>(entry.Distance - breakingDistance, + new DrivingBehaviorEntry() { + Action = DrivingBehavior.Breaking, + ActionDistance = entry.Distance - breakingDistance, + TriggerDistance = entry.Distance, + NextTargetSpeed = entry.VehicleTargetSpeed + })); + Log.DebugFormat("adding 'Coasting' starting at distance {0}", entry.Distance - lookaheadDistance); + nextActions.Add(new KeyValuePair<Meter, DrivingBehaviorEntry>(entry.Distance - lookaheadDistance, + new DrivingBehaviorEntry() { + Action = DrivingBehavior.Coasting, + ActionDistance = entry.Distance - lookaheadDistance, + TriggerDistance = entry.Distance, + NextTargetSpeed = entry.VehicleTargetSpeed + })); } if (entry.VehicleTargetSpeed > currentSpeed) { - nextActions.Add(new KeyValuePair<Meter, DrivingBehavior>(entry.Distance, DrivingBehavior.Accelerating)); + nextActions.Add(new KeyValuePair<Meter, DrivingBehaviorEntry>(entry.Distance, new DrivingBehaviorEntry() { + Action = DrivingBehavior.Accelerating, + NextTargetSpeed = entry.VehicleTargetSpeed, + TriggerDistance = entry.Distance, + ActionDistance = entry.Distance + })); } } - nextActions.Sort((x, y) => x.Value.CompareTo(y.Value)); - return nextActions.First(); + nextActions.Sort((x, y) => x.Key.CompareTo(y.Key)); + + return nextActions; } protected internal Meter ComputeDecelerationDistance(MeterPerSecond targetSpeed) @@ -164,19 +314,31 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected internal IResponse DoCoast(Second absTime, Meter ds, Radian gradient) { - ComputeAcceleration(ds, 0.SI<MeterPerSecond>()); + ComputeAcceleration(ref ds, 0.SI<MeterPerSecond>()); + // todo: still required? if (CurrentState.dt.IsEqual(0)) { return new ResponseFailTimeInterval(); } var response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true); - var distance = SearchOperatingPoint(absTime, ds, gradient, response, true); - if (!ds.IsEqual(distance)) { - return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = distance, SimulationInterval = CurrentState.dt }; + var newDs = ds; + var success = SearchOperatingPoint(absTime, ref newDs, gradient, response, true); + + if (!success) { + Log.ErrorFormat("Failed to find operating point for coasting!"); + throw new VectoSimulationException("Failed to find operating point for coasting!"); } + if (!ds.IsEqual(newDs)) { + Log.DebugFormat( + "SearchOperatingPoint reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!", newDs, ds); + return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = newDs, SimulationInterval = CurrentState.dt }; + } + + Log.DebugFormat("Found operating point for coasting. dt: {0}, acceleration: {1}", CurrentState.dt, + CurrentState.Acceleration); var retVal = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient); CurrentState.Response = retVal; switch (retVal.ResponseType) { @@ -184,21 +346,35 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl retVal.SimulationInterval = CurrentState.dt; return retVal; } - - return new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt }; + Log.DebugFormat("unhandled response from powertrain: {0}", retVal); + return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt }; } - private Meter SearchOperatingPoint(Second absTime, Meter ds, Radian gradient, + /// <summary> + /// search for the operating point where the engine's requested power is either on the full-load curve or on the drag curve (parameter 'coasting'). + /// before the search can be performed either a normal request or a dry-run request has to be made and the response is passed to this method. + /// perform a binary search starting with the currentState's acceleration value. + /// while searching it might be necessary to reduce the simulation distance because the vehicle already stopped before reaching the given ds. However, + /// it for every new iteration of the search the original distance is used. The resulting distance is returned. + /// After the search operation a normal request has to be made by the caller of this method. The final acceleration and time interval is stored in CurrentState. + /// </summary> + /// <param name="absTime">absTime from the original request</param> + /// <param name="ds">ds from the original request</param> + /// <param name="gradient">gradient from the original request</param> + /// <param name="response">response of the former request that resulted in an overload response (or a dry-run response)</param> + /// <param name="coasting">if true approach the drag-load curve, otherwise full-load curve</param> + /// <returns></returns> + private bool SearchOperatingPoint(Second absTime, ref Meter ds, Radian gradient, IResponse response, bool coasting = false) { - var exceeded = new List<double>(); // only used while testing + var exceeded = new List<Watt>(); // only used while testing var acceleration = new List<double>(); // only used while testing var searchInterval = CurrentState.Acceleration.Value() / 2.0; - Meter computedDs; + Meter originalDs = ds; do { - var delta = 0.0; - computedDs = ds; + var delta = 0.0.SI<Watt>(); + ds = originalDs; switch (response.ResponseType) { case ResponseType.FailOverload: delta = ((ResponseFailOverload)response).Delta; @@ -213,7 +389,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl exceeded.Add(delta); acceleration.Add(CurrentState.Acceleration.Value()); if (delta.IsEqual(0, Constants.SimulationSettings.EngineFLDPowerTolerance)) { - return computedDs; + Log.DebugFormat("found operating point in {0} iterations, delta: {1}", exceeded.Count, delta); + return true; } if (delta > 0) { CurrentState.Acceleration -= searchInterval; @@ -226,13 +403,29 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl throw new VectoSimulationException("Could not achieve minimum acceleration"); } searchInterval /= 2.0; - ComputeTimeInterval(CurrentState.Acceleration, ref computedDs, out CurrentState.dt); + ComputeTimeInterval(CurrentState.Acceleration, ref ds, out CurrentState.dt); response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true); } while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold); - return computedDs; + + Log.DebugFormat("Exceeded max iterations when searching for operating point!"); + Log.DebugFormat("acceleration: {0} ... {1}", string.Join(", ", acceleration.Take(5)), + string.Join(", ", acceleration.GetRange(acceleration.Count - 6, 5))); + Log.DebugFormat("exceeded: {0} ... {1}", string.Join(", ", exceeded.Take(5)), + string.Join(", ", exceeded.GetRange(exceeded.Count - 6, 5))); + + return false; } - private void ComputeAcceleration(Meter ds, MeterPerSecond targetVelocity) + /// <summary> + /// compute the acceleration and time-interval such that the vehicle's velocity approaches the given target velocity + /// - first compute the acceleration to reach the targetVelocity within the given distance + /// - limit the acceleration/deceleration by the driver's acceleration curve + /// - compute the time interval required to drive the given distance with the computed acceleration + /// computed acceleration and time interval are stored in CurrentState! + /// </summary> + /// <param name="ds"></param> + /// <param name="targetVelocity"></param> + private void ComputeAcceleration(ref Meter ds, MeterPerSecond targetVelocity) { var currentSpeed = DataBus.VehicleSpeed(); @@ -249,14 +442,36 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } CurrentState.Acceleration = requiredAcceleration; + var tmpDs = ds; ComputeTimeInterval(CurrentState.Acceleration, ref ds, out CurrentState.dt); + if (!ds.IsEqual(tmpDs)) { + Log.ErrorFormat( + "Unexpected Condition: Distance has been adjusted from {0} to {1}, currentVelocity: {2} acceleration: {3}, targetVelocity: {4}", + tmpDs, ds, currentSpeed, CurrentState.Acceleration, targetVelocity); + throw new VectoSimulationException("Simulation distance unexpectedly adjusted!"); + } } + /// <summary> + /// compute the time interval for driving the given distance ds with the vehicle's current speed and the given acceleration + /// if the given distance ds can not be reached (i.e., the vehicle would halt before ds is reached) then the distance parameter is adjusted + /// the computed time interval is returned via the out parameter dt + /// </summary> + /// <param name="acceleration"></param> + /// <param name="ds"></param> + /// <param name="dt"></param> private void ComputeTimeInterval(MeterPerSquareSecond acceleration, ref Meter ds, out Second dt) { + if (!(ds > 0)) { + throw new VectoSimulationException("distance has to be greater than 0!"); + } var currentSpeed = DataBus.VehicleSpeed(); if (acceleration.IsEqual(0)) { + if (!(currentSpeed > 0)) { + Log.ErrorFormat("vehicle speed is {0}, acceleration is {1}", currentSpeed, acceleration); + throw new VectoSimulationException("vehicle speed has to be > 0 if acceleration = 0"); + } dt = (ds / currentSpeed).Cast<Second>(); return; } @@ -267,16 +482,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -ds.Value()); if (solutions.Count == 0) { - // no real-valued solutions, required distance can not be reached (vehicle stopped?), adapt ds... + // no real-valued solutions, required distance can not be reached (vehicle stopped), adapt ds... dt = -(currentSpeed / acceleration).Cast<Second>(); var stopDistance = (currentSpeed * dt + acceleration / 2 * dt * dt).Cast<Meter>(); if (stopDistance > ds) { Log.WarnFormat( "Could not find solution for computing required time interval to drive distance {0}. currentSpeed: {1}, acceleration: {2}", ds, currentSpeed, acceleration); - dt = 0.SI<Second>(); - return; + throw new VectoSimulationException("Could not find solution"); } + Log.InfoFormat( + "Adjusted distance when computing time interval: currentSpeed: {0}, acceleration: {1}, distance: {2} -> {3}, timeInterval: {4}", + currentSpeed, acceleration, ds, stopDistance, dt); ds = stopDistance; return; } @@ -321,11 +538,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public class DriverState { + public DriverState() + { + DrivingAction = new DrivingBehaviorEntry(); + } + public Second dt; public MeterPerSquareSecond Acceleration; public IResponse Response; public int RetryCount; - public DrivingBehavior activity; + public DrivingBehaviorEntry DrivingAction; } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs index 25c7f7fc3dd7ce48c7e378263015c80f3f4f2582..e06403c69327b4d0e0841117d314588a3e2e8b41 100644 --- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs @@ -31,8 +31,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (dryRun) { return new ResponseDryRun() { - DeltaFullLoad = (requestedEnginePower - _currentState.DynamicFullLoadPower).Value(), - DeltaDragLoad = (requestedEnginePower - _currentState.FullDragPower).Value() + DeltaFullLoad = (requestedEnginePower - _currentState.DynamicFullLoadPower), + DeltaDragLoad = (requestedEnginePower - _currentState.FullDragPower) }; } diff --git a/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs b/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs index e8ec0490a13c68cb751418d6438af3716bb7c81b..3df8170f80f20145d935c632ca8deda14adf71e7 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs @@ -63,6 +63,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public IResponse Request(Second absTime, Second dt, MeterPerSquareSecond accelleration, Radian gradient, bool dryRun = false) { + Log.DebugFormat("from Wheels: acceleration: {0}", accelleration); _currentState.Velocity = (_previousState.Velocity + (accelleration * dt)).Cast<MeterPerSecond>(); _currentState.dt = dt; _currentState.Distance = _previousState.Distance + @@ -87,9 +88,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected Newton RollingResistance(Radian gradient) { - return (Math.Cos(gradient.Value()) * _data.TotalVehicleWeight() * - Physics.GravityAccelleration * - _data.TotalRollResistanceCoefficient).Cast<Newton>(); + var retVal = (Math.Cos(gradient.Value()) * _data.TotalVehicleWeight() * + Physics.GravityAccelleration * + _data.TotalRollResistanceCoefficient).Cast<Newton>(); + Log.DebugFormat("RollingResistance: {0}", retVal); + return retVal; } @@ -125,7 +128,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl //break; } - return (CdA * Physics.AirDensity / 2 * vAir * vAir).Cast<Newton>(); + var retVal = (CdA * Physics.AirDensity / 2 * vAir * vAir).Cast<Newton>(); + Log.DebugFormat("AirDragResistance: {0}", retVal); + return retVal; } private double AirDragInterpolate(IEnumerable<Point> curve, MeterPerSecond x) @@ -187,13 +192,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected Newton DriverAcceleration(MeterPerSquareSecond accelleration) { - return ((_data.TotalVehicleWeight() + _data.ReducedMassWheels) * accelleration).Cast<Newton>(); + var retVal = ((_data.TotalVehicleWeight() + _data.ReducedMassWheels) * accelleration).Cast<Newton>(); + Log.DebugFormat("DriverAcceleration: {0}", retVal); + return retVal; } protected Newton SlopeResistance(Radian gradient) { - return (_data.TotalVehicleWeight() * Physics.GravityAccelleration * Math.Sin(gradient.Value())).Cast<Newton>(); + var retVal = (_data.TotalVehicleWeight() * Physics.GravityAccelleration * Math.Sin(gradient.Value())).Cast<Newton>(); + Log.DebugFormat("SlopeResistance: {0}", retVal); + return retVal; } public MeterPerSecond VehicleSpeed() diff --git a/VectoCore/Models/SimulationComponent/Impl/Wheels.cs b/VectoCore/Models/SimulationComponent/Impl/Wheels.cs index 6df4dd11e971d475b0f1d07e615c6c1a5e843532..84e9d43470476be1fdf00a2cc1700bf08d73a9a7 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Wheels.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Wheels.cs @@ -39,6 +39,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl IResponse IFvOutPort.Request(Second absTime, Second dt, Newton force, MeterPerSecond velocity, bool dryRun) { + Log.DebugFormat("request: force: {0}, velocity: {1}", force, velocity); var torque = force * _dynamicWheelRadius; var angularVelocity = velocity / _dynamicWheelRadius; return _outPort.Request(absTime, dt, torque, angularVelocity, dryRun); diff --git a/VectoCore/VectoCore.csproj b/VectoCore/VectoCore.csproj index 89a35be5d8229e99ae22673368454c5dfa981052..39e053c48ebe7ab42af70b56f736e600e81c2290 100644 --- a/VectoCore/VectoCore.csproj +++ b/VectoCore/VectoCore.csproj @@ -157,7 +157,9 @@ <Compile Include="Models\Declaration\Mission.cs" /> <Compile Include="Models\Declaration\MissionType.cs" /> <Compile Include="Models\SimulationComponent\Data\Engine\PT1Curve.cs" /> + <Compile Include="Models\SimulationComponent\IBreaks.cs" /> <Compile Include="Models\SimulationComponent\IDrivingCycleInfo.cs" /> + <Compile Include="Models\SimulationComponent\Impl\Breaks.cs" /> <Compile Include="Models\SimulationComponent\Impl\EngineOnlyCombustionEngine.cs" /> <Compile Include="Models\SimulationComponent\Impl\MappingAuxiliaryData.cs" /> <Compile Include="Models\Simulation\Data\AuxiliaryDemandType.cs" /> diff --git a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs index b631ec5fdc391ce2d4fde0f25e08de022dccfd71..7753f01cdea34e5c955cb2298273f11426bd4dec 100644 --- a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs @@ -87,6 +87,62 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent modalWriter.Finish(); } + [TestMethod] + public void DriverCoastingTest2() + { + var engineData = EngineeringModeSimulationDataReader.CreateEngineDataFromFile(EngineFile); + + var vehicleData = CreateVehicleData(33000.SI<Kilogram>()); + + var driverData = CreateDriverData(); + + var modalWriter = new ModalDataWriter("Coach_MinimalPowertrain_Coasting.vmod", false); //new TestModalDataWriter(); + var sumWriter = new TestSumWriter(); + var vehicleContainer = new VehicleContainer(modalWriter, sumWriter); + + var driver = new Driver(vehicleContainer, driverData); + var engine = new CombustionEngine(vehicleContainer, engineData); + + dynamic tmp = AddComponent(driver, new Vehicle(vehicleContainer, vehicleData)); + tmp = AddComponent(tmp, new Wheels(vehicleContainer, vehicleData.DynamicTyreRadius)); + tmp = AddComponent(tmp, new Clutch(vehicleContainer, engineData)); + AddComponent(tmp, engine); + + var gbx = new DummyGearbox(vehicleContainer); + gbx.CurrentGear = 1; + + var driverPort = driver.OutPort(); + + var gradient = VectoMath.InclinationToAngle(-0.020237973 / 100.0); + driverPort.Initialize(5.SI<MeterPerSecond>(), gradient); + + var absTime = 0.SI<Second>(); + + var response = driver.DoCoast(absTime, 1.SI<Meter>(), gradient); + + Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); + + vehicleContainer.CommitSimulationStep(absTime, response.SimulationInterval); + absTime += response.SimulationInterval; + + Assert.AreEqual(4.9812, vehicleContainer.VehicleSpeed().Value(), Tolerance); + Assert.AreEqual(0.2004, response.SimulationInterval.Value(), Tolerance); + Assert.AreEqual(engine._previousState.FullDragPower.Value(), engine._previousState.EnginePower.Value(), + Constants.SimulationSettings.EngineFLDPowerTolerance); + + while (vehicleContainer.VehicleSpeed() > 1) { + response = driver.DoCoast(absTime, 1.SI<Meter>(), gradient); + + Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); + + vehicleContainer.CommitSimulationStep(absTime, response.SimulationInterval); + absTime += response.SimulationInterval; + modalWriter.Finish(); + } + modalWriter.Finish(); + } + + [TestMethod] public void DriverOverloadTest() { diff --git a/VectoCoreTest/app.config b/VectoCoreTest/app.config index 148fbbb86e307afbb7c8a9264b898e9cfd96834b..d1ca176ab548d2fd255b31c7a4004cd34ff80d7e 100644 --- a/VectoCoreTest/app.config +++ b/VectoCoreTest/app.config @@ -29,7 +29,10 @@ layout="${longdate} [${processid}@${machinename}] ${callsite} ${level:uppercase=true}: ${message}" /> </targets> <rules> - <logger name="*" minlevel="Info" writeTo="ConsoleLogger" /> + <logger name="TUGraz.VectoCore.Models.SimulationComponent.Impl.Vehicle" minlevel="Info" writeTo="ConsoleLogger" /> + <logger name="TUGraz.VectoCore.Models.SimulationComponent.Impl.Clutch" minlevel="Info" writeTo="ConsoleLogger" /> + <logger name="TUGraz.VectoCore.Models.SimulationComponent.Impl.AxleGear" minlevel="Info" writeTo="ConsoleLogger" /> + <logger name="TUGraz.VectoCore.Models.SimulationComponent.Impl.Driver" minlevel="Debug" writeTo="ConsoleLogger" /> </rules> </nlog> </configuration> \ No newline at end of file