diff --git a/VectoCore/Configuration/Constants.cs b/VectoCore/Configuration/Constants.cs index a8463409283b85243848f7af3b4b5d155cd008c1..0922ef2f257ca43c03d0b206df89eab64741ec1a 100644 --- a/VectoCore/Configuration/Constants.cs +++ b/VectoCore/Configuration/Constants.cs @@ -36,7 +36,7 @@ namespace TUGraz.VectoCore.Configuration { public static class Constants { - public const double RPMToRad = 2 * Math.PI / 60; + public const double RPMToRad = 2 * Math.PI / 60; public const double Kilo = 1000; public const double MeterPerSecondToKMH = 3.6; public const double SecondsPerHour = 3600; @@ -84,6 +84,13 @@ namespace TUGraz.VectoCore.Configuration /// </summary> public static readonly Second TargetTimeInterval = 0.5.SI<Second>(); + + /// <summary> + /// maximum time interval for the simulation in measured speed mode. + /// </summary> + public static readonly Second MeasuredSpeedTargetTimeInterval = 1.SI<Second>(); + + /// <summary> /// simulation interval if the vehicle stands still /// </summary> diff --git a/VectoCore/Models/Simulation/Impl/TimeRun.cs b/VectoCore/Models/Simulation/Impl/TimeRun.cs index 5a9ae27680bb7d30b39863a55bd1e82d514b7ea8..33a51a679659593752030abc3fea61fcc664d929 100644 --- a/VectoCore/Models/Simulation/Impl/TimeRun.cs +++ b/VectoCore/Models/Simulation/Impl/TimeRun.cs @@ -44,7 +44,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl protected override IResponse DoSimulationStep() { - dt = Constants.SimulationSettings.TargetTimeInterval; + dt = Constants.SimulationSettings.MeasuredSpeedTargetTimeInterval; var debug = new List<dynamic>(); var loopCount = 0; diff --git a/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/Models/SimulationComponent/Impl/Driver.cs index 95e0dee42cb3d6be7770ce9ef2e5719611f1ac99..24ddac5c9b7dac4411af0fe5139141a038e5d328 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -30,6 +30,7 @@ */ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using NLog; @@ -493,6 +494,83 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Log.Info("Disabling logging during search iterations"); LogManager.DisableLogging(); + var debug = new List<dynamic>(); // only used while testing + + var operatingPoint = new OperatingPoint() { SimulationDistance = ds, Acceleration = acceleration }; + Watt origDelta = null; + initialResponse.Switch(). + Case<ResponseGearShift>(r => origDelta = r.GearboxPowerRequest). + Case<ResponseUnderload>(r => origDelta = DataBus.ClutchClosed(absTime) + ? r.Delta + : r.GearboxPowerRequest). + Default(r => { throw new UnexpectedResponseException("cannot use response for searching braking power!", r); }); + + // braking power is in the range of the exceeding delta. set searching range to 2/3 so that + // the target point is approximately in the center of the second interval + var searchInterval = origDelta.Abs() * 2 / 3; + + debug.Add(new { brakePower = 0.SI<Watt>(), searchInterval, delta = origDelta, operatingPoint }); + + var brakePower = searchInterval * -origDelta.Sign(); + + var modeBinarySearch = false; + var intervalFactor = 1.0; + var retryCount = 0; + + do { + operatingPoint = ComputeTimeInterval(operatingPoint.Acceleration, ds); + DataBus.BrakePower = brakePower; + var response = + (ResponseDryRun) + NextComponent.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient, true); + var delta = DataBus.ClutchClosed(absTime) ? response.DeltaDragLoad : response.GearboxPowerRequest; + + if (delta.IsEqual(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)) { + LogManager.EnableLogging(); + Log.Debug("found operating point in {0} iterations, delta: {1}", debug.Count, delta); + return operatingPoint; + } + + debug.Add(new { brakePower, searchInterval, delta, operatingPoint }); + + // check if a correct searchInterval was found (when the delta changed signs, we stepped through the 0-point) + // from then on the searchInterval can be bisected. + if (origDelta.Sign() != delta.Sign()) { + intervalFactor = 0.5; + if (!modeBinarySearch) { + modeBinarySearch = true; + retryCount = 0; // again max. 100 iterations for the binary search... + } + } + + searchInterval *= intervalFactor; + brakePower += searchInterval * -delta.Sign(); + } while (retryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold); + + LogManager.EnableLogging(); + Log.Warn("Exceeded max iterations when searching for operating point!"); + Log.Warn("exceeded: {0} ... {1}", ", ".Join(debug.Take(5)), ", ".Join(debug.Slice(-6))); + Log.Error("Failed to find operating point for breaking!"); + throw new VectoSearchFailedException("Failed to find operating point for breaking!exceeded: {0} ... {1}", + ", ".Join(debug.Take(5)), ", ".Join(debug.Slice(-6))); + } + + /// <summary> + /// Performs a search for the required braking power such that the vehicle accelerates with the given acceleration. + /// Returns a new operating point (a, ds, dt) where ds may be shorter due to vehicle stopping + /// </summary> + /// <param name="absTime"></param> + /// <param name="ds"></param> + /// <param name="gradient"></param> + /// <param name="acceleration"></param> + /// <param name="initialResponse"></param> + /// <returns>operating point (a, ds, dt) such that the vehicle accelerates with the given acceleration.</returns> + private OperatingPoint SearchBrakingPowerInterpol(Second absTime, Meter ds, Radian gradient, + MeterPerSquareSecond acceleration, IResponse initialResponse) + { + Log.Info("Disabling logging during search iterations"); + LogManager.DisableLogging(); + var operatingPoint = ComputeTimeInterval(acceleration, ds); var dt = operatingPoint.SimulationInterval; Watt brakePower = null; @@ -535,6 +613,92 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Log.Info("Disabling logging during search iterations"); LogManager.DisableLogging(); + var debug = new List<dynamic>(); + + var retVal = new OperatingPoint { Acceleration = acceleration, SimulationDistance = ds }; + + var actionRoll = !DataBus.ClutchClosed(absTime); + + var searchInterval = Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating; + var intervalFactor = 1.0; + + Watt origDelta = null; + if (actionRoll) { + initialResponse.Switch(). + Case<ResponseDryRun>(r => origDelta = r.GearboxPowerRequest). + Case<ResponseFailTimeInterval>(r => origDelta = r.GearboxPowerRequest). + Default(r => { throw new UnexpectedResponseException("Unknown response type.", r); }); + } else { + initialResponse.Switch(). + Case<ResponseOverload>(r => origDelta = r.Delta). // search operating point in drive action after overload + Case<ResponseDryRun>(r => origDelta = coasting ? r.DeltaDragLoad : r.DeltaFullLoad). + Default(r => { throw new UnexpectedResponseException("Unknown response type.", r); }); + } + var delta = origDelta; + + var retryCount = 0; + do { + debug.Add(new { delta, acceleration = retVal.Acceleration, searchInterval }); + + // check if a correct searchInterval was found (when the delta changed signs, we stepped through the 0-point) + // from then on the searchInterval can be bisected. + if (origDelta.Sign() != delta.Sign()) { + intervalFactor = 0.5; + } + + searchInterval *= intervalFactor; + retVal.Acceleration += searchInterval * -delta.Sign(); + + if (retVal.Acceleration < (10 * DriverData.AccelerationCurve.MaxDeceleration() - searchInterval) + || retVal.Acceleration > (DriverData.AccelerationCurve.MaxAcceleration() + searchInterval)) { + LogManager.EnableLogging(); + Log.Warn("Operating Point outside driver acceleration limits: a: {0}", retVal.Acceleration); + LogManager.DisableLogging(); + } + + var tmp = ComputeTimeInterval(retVal.Acceleration, ds); + retVal.SimulationInterval = tmp.SimulationInterval; + retVal.SimulationDistance = tmp.SimulationDistance; + + var response = + (ResponseDryRun)NextComponent.Request(absTime, retVal.SimulationInterval, retVal.Acceleration, gradient, true); + delta = actionRoll ? response.GearboxPowerRequest : (coasting ? response.DeltaDragLoad : response.DeltaFullLoad); + + if (response is ResponseEngineSpeedTooLow) { + LogManager.EnableLogging(); + Log.Debug("Got EngineSpeedTooLow during SearchOperatingPoint. Aborting!"); + throw new VectoSimulationException("EngineSpeed too low during search."); + } + + if (delta.IsEqual(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)) { + LogManager.EnableLogging(); + Log.Debug("found operating point in {0} iterations. Engine Power req: {2}, Gearbox Power req: {3} delta: {1}", + debug.Count, delta, response.EnginePowerRequest, response.GearboxPowerRequest); + return retVal; + } + } while (retryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold); + + LogManager.EnableLogging(); + Log.Error("Exceeded max iterations when searching for operating point!"); + Log.Error("acceleration: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.acceleration)), + ", ".Join(debug.Slice(-6).Select(x => x.acceleration))); + Log.Error("exceeded: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.delta)), + ", ".Join(debug.Slice(-6).Select(x => x.delta))); + // issue request once more for logging... + NextComponent.Request(absTime, retVal.SimulationInterval, retVal.Acceleration, gradient, true); + Log.Error("Failed to find operating point! absTime: {0}", absTime); + throw new VectoSearchFailedException("Failed to find operating point! exceeded: {0} ... {1}", + ", ".Join(debug.Take(5).Select(x => x.delta)), + ", ".Join(debug.Slice(-6).Select(x => x.delta))); + } + + + protected OperatingPoint SearchOperatingPointInterpol(Second absTime, Meter ds, Radian gradient, + MeterPerSquareSecond acceleration, IResponse initialResponse, bool coasting = false) + { + Log.Info("Disabling logging during search iterations"); + LogManager.DisableLogging(); + var retVal = new OperatingPoint { Acceleration = acceleration, SimulationDistance = ds }; var actionRoll = !DataBus.ClutchClosed(absTime); diff --git a/VectoCore/Models/SimulationComponent/Impl/PowertrainDrivingCycle.cs b/VectoCore/Models/SimulationComponent/Impl/PowertrainDrivingCycle.cs index 2cec2d745593133316cc213799ad784b6f8d8233..fafe79e58bdccd19d389d6a44dad86714a78b498 100644 --- a/VectoCore/Models/SimulationComponent/Impl/PowertrainDrivingCycle.cs +++ b/VectoCore/Models/SimulationComponent/Impl/PowertrainDrivingCycle.cs @@ -119,38 +119,41 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected IResponse DoHandleRequest(Second absTime, Second dt, PerSecond angularVelocity) { - var response = NextComponent.Request(absTime, dt, LeftSample.Current.Torque, angularVelocity); + var debug = new List<dynamic>(); - if (response is ResponseGearShift) { + IResponse response; + var responseCount = 0; + do { response = NextComponent.Request(absTime, dt, LeftSample.Current.Torque, angularVelocity); - } - - response.Switch() - .Case<ResponseUnderload>(r => { - var torqueInterval = -r.Delta / (angularVelocity.IsEqual(0) ? 10.RPMtoRad() : angularVelocity); - var torque = SearchAlgorithm.Search(LeftSample.Current.Torque, r.Delta, torqueInterval, - getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, - evaluateFunction: t => NextComponent.Request(absTime, dt, t, angularVelocity, true), - criterion: y => - ((ResponseDryRun)y).DeltaDragLoad.IsEqual(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)); - response = NextComponent.Request(absTime, dt, torque, angularVelocity); - }) - .Case<ResponseOverload>(r => { - angularVelocity = SearchAlgorithm.Search(angularVelocity, r.Delta, 50.RPMtoRad(), - getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, - evaluateFunction: n => NextComponent.Request(absTime, dt, LeftSample.Current.Torque, n, true), - criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Abs() < Constants.SimulationSettings.EnginePowerSearchTolerance); - response = NextComponent.Request(absTime, dt, LeftSample.Current.Torque, angularVelocity); - }) - .Case<ResponseSuccess>(() => { }) - .Default( - r => { throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle received an unexpected response.", r); }); - - if (!(response is ResponseSuccess)) { - throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle received an unexpected response.", response); - } + debug.Add(response); + response.Switch() + .Case<ResponseGearShift>( + () => response = NextComponent.Request(absTime, dt, LeftSample.Current.Torque, angularVelocity)) + .Case<ResponseUnderload>(r => { + var torqueInterval = -r.Delta / (angularVelocity.IsEqual(0) ? 10.RPMtoRad() : angularVelocity); + var torque = SearchAlgorithm.Search(LeftSample.Current.Torque, r.Delta, torqueInterval, + getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, + evaluateFunction: t => NextComponent.Request(absTime, dt, t, angularVelocity, true), + criterion: y => + ((ResponseDryRun)y).DeltaDragLoad.IsEqual(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)); + response = NextComponent.Request(absTime, dt, torque, angularVelocity); + }) + .Case<ResponseOverload>(r => { + angularVelocity = SearchAlgorithm.Search(angularVelocity, r.Delta, 50.RPMtoRad(), + getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, + evaluateFunction: n => NextComponent.Request(absTime, dt, LeftSample.Current.Torque, n, true), + criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Abs() < Constants.SimulationSettings.EnginePowerSearchTolerance); + response = NextComponent.Request(absTime, dt, LeftSample.Current.Torque, angularVelocity); + }) + .Case<ResponseFailTimeInterval>(r => { dt = r.DeltaT; }) + .Case<ResponseSuccess>(() => { }) + .Default( + r => { throw new UnexpectedResponseException("PowertrainDrivingCycle received an unexpected response.", r); }); + } while (!(response is ResponseSuccess || response is ResponseFailTimeInterval) && (++responseCount < 10)); AbsTime = absTime + dt; + response.SimulationInterval = dt; + debug.Add(response); return response; } diff --git a/VectoCore/Utils/SearchAlgorithm.cs b/VectoCore/Utils/SearchAlgorithm.cs index fb92a8f643eab55fcbb358687ff9b20139ce6e17..94e7c602e68f02bad72509cca95fcf42685ef168 100644 --- a/VectoCore/Utils/SearchAlgorithm.cs +++ b/VectoCore/Utils/SearchAlgorithm.cs @@ -33,7 +33,6 @@ using System; using System.Collections.Generic; using System.Linq; using NLog; -using NLog.Fluent; using TUGraz.VectoCore.Exceptions; namespace TUGraz.VectoCore.Utils @@ -46,7 +45,7 @@ namespace TUGraz.VectoCore.Utils //#if DEBUG var res = InterpolateLinear(x, y, interval, getYValue, evaluateFunction, criterion); //#else -// var res = SearchBinary(x, y, interval, getYValue, evaluateFunction, criterion); + //var res = SearchBinary(x, y, interval, getYValue, evaluateFunction, criterion); //#endif return res; } diff --git a/VectoCoreTest/Integration/DriverStrategy/DriverStrategyTestTruck.cs b/VectoCoreTest/Integration/DriverStrategy/DriverStrategyTestTruck.cs index e95db1e443bb2bfccf394b0812f5c00e108cbd6b..9e8510e57eb29b8e39c6e8d37bc27a899326a040 100644 --- a/VectoCoreTest/Integration/DriverStrategy/DriverStrategyTestTruck.cs +++ b/VectoCoreTest/Integration/DriverStrategy/DriverStrategyTestTruck.cs @@ -118,7 +118,7 @@ namespace TUGraz.VectoCore.Tests.Integration.DriverStrategy @"..\..\TestData\Integration\DriverStrategy\Vecto2.2\40t Truck\40t_Long_Haul_Truck_Cycle_Accelerate_20_60_downhill_25.vmod"); } - [TestMethod] + [TestMethod, TestCategory("LongRunning")] public void Truck_Accelerate_20_60_uphill_15() { var cycle = SimpleDrivingCycles.CreateCycleData(SimpleDrivingCycles.CycleAccelerate_20_60_uphill_15);