From a39c3f3464ee4d930be39c71a3b52e14b5d2c30a Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Thu, 3 Sep 2015 17:30:23 +0200 Subject: [PATCH] more work on driver modes, braking phases --- .../Models/Connector/Ports/Impl/Response.cs | 20 ++--- .../SimulationComponent/IDriverActions.cs | 10 ++- .../Impl/CombustionEngine.cs | 8 +- .../Impl/DefaultDriverStrategy.cs | 76 ++++++++++++++++--- .../Models/SimulationComponent/Impl/Driver.cs | 57 +++++++++----- .../Models/SimulationComponent/DriverTest.cs | 14 ++-- .../Models/SimulationComponent/GearboxTest.cs | 10 +-- 7 files changed, 139 insertions(+), 56 deletions(-) diff --git a/VectoCore/Models/Connector/Ports/Impl/Response.cs b/VectoCore/Models/Connector/Ports/Impl/Response.cs index a16ebbb921..854cde1cc1 100644 --- a/VectoCore/Models/Connector/Ports/Impl/Response.cs +++ b/VectoCore/Models/Connector/Ports/Impl/Response.cs @@ -1,4 +1,5 @@ using System.Linq; +using TUGraz.VectoCore.Models.SimulationComponent; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.Connector.Ports.Impl @@ -19,10 +20,13 @@ namespace TUGraz.VectoCore.Models.Connector.Ports.Impl public Watt VehiclePowerRequest { get; set; } + public VectoSimulationComponent Source { get; set; } + public override string ToString() { var t = GetType(); - return string.Format("{0}{{{1}}}", t.Name, ", ".Join(t.GetProperties().Select(p => string.Format("{0}: {1}", p.Name, p.GetValue(this))))); + return string.Format("{0}{{{1}}}", t.Name, + ", ".Join(t.GetProperties().Select(p => string.Format("{0}: {1}", p.Name, p.GetValue(this))))); } } @@ -37,23 +41,21 @@ namespace TUGraz.VectoCore.Models.Connector.Ports.Impl public class ResponseSuccess : AbstractResponse {} /// <summary> - /// Response when the request resulted in an engine overload. + /// Response when the request resulted in an engine or gearbox overload. /// </summary> - public class ResponseEngineOverload : AbstractResponse + public class ResponseOverload : AbstractResponse { public Watt Delta { get; set; } public double Gradient { get; set; } } /// <summary> - /// Response when the request resulted in an engine overload. + /// Response when the request resulted in an engine under-load. /// </summary> - public class ResponseGearboxOverload : AbstractResponse - { - public Watt Delta { get; set; } - public double Gradient { get; set; } - } + public class ResponseUnderload : ResponseOverload {} + + public class ResponseSpeedLimitExceeded : AbstractResponse {} /// <summary> /// Response when the request should have another time interval. diff --git a/VectoCore/Models/SimulationComponent/IDriverActions.cs b/VectoCore/Models/SimulationComponent/IDriverActions.cs index a56d36b780..014ad52344 100644 --- a/VectoCore/Models/SimulationComponent/IDriverActions.cs +++ b/VectoCore/Models/SimulationComponent/IDriverActions.cs @@ -24,9 +24,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> + /// <param name="maxVelocity"></param> /// <param name="gradient"></param> /// <returns></returns> - IResponse DrivingActionCoast(Second absTime, Meter ds, Radian gradient); + IResponse DrivingActionCoast(Second absTime, Meter ds, MeterPerSecond maxVelocity, Radian gradient); /// <summary> /// perform a 'brake driving action', i.e. decelerate the vehicle by using the mechanical brakes to the next target speed @@ -34,10 +35,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> - /// <param name="gradient"></param> /// <param name="nextTargetSpeed"></param> + /// <param name="gradient"></param> /// <returns></returns> - IResponse DrivingActionBrake(Second absTime, Meter ds, Radian gradient, MeterPerSecond nextTargetSpeed); + IResponse DrivingActionBrake(Second absTime, Meter ds, MeterPerSecond nextTargetSpeed, Radian gradient); /// <summary> /// perform a 'roll driving action', i.e., the clutch is open and the vehicle rolls without motoring. adjust the acceleration @@ -45,9 +46,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> + /// <param name="maxVelocity"></param> /// <param name="gradient"></param> /// <returns></returns> - IResponse DrivingActionRoll(Second absTime, Meter ds, Radian gradient); + IResponse DrivingActionRoll(Second absTime, Meter ds, MeterPerSecond maxVelocity, Radian gradient); /// <summary> diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 4003a11121..b8c6a6a166 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -105,10 +105,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } if (!_currentState.EnginePower.IsEqual(requestedEnginePower, Constants.SimulationSettings.EngineFLDPowerTolerance)) { - return new ResponseEngineOverload { - Delta = (requestedEnginePower - _currentState.EnginePower), - EnginePowerRequest = requestedEnginePower - }; + var delta = (requestedEnginePower - _currentState.EnginePower); + return delta > 0 + ? new ResponseOverload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this } + : new ResponseUnderload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this }; } UpdateEngineState(_currentState.EnginePower); diff --git a/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs b/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs index ae94a14b43..5a6a80b241 100644 --- a/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs +++ b/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; +using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; @@ -39,7 +40,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public IDriverActions Driver { get; set; } - protected Meter BrakeTriggerDistance { get; set; } + protected internal DrivingBehaviorEntry BrakeTrigger { get; set; } public IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) @@ -50,7 +51,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var nextAction = GetNextDrivingAction(currentDistance); if (currentDistance.IsEqual(nextAction.ActionDistance)) { CurrentDrivingMode = DrivingMode.DrivingModeBrake; - BrakeTriggerDistance = nextAction.TriggerDistance; + BrakeTrigger = nextAction; break; } if (currentDistance + ds > nextAction.ActionDistance) { @@ -60,7 +61,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } break; case DrivingMode.DrivingModeBrake: - if (Driver.DataBus.Distance() >= BrakeTriggerDistance) { + if (Driver.DataBus.Distance() >= BrakeTrigger.TriggerDistance) { CurrentDrivingMode = DrivingMode.DrivingModeDrive; } break; @@ -151,20 +152,20 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl response = DriverStrategy.Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); response.Switch(). Case<ResponseGearShift>(() => { - response = DriverStrategy.Driver.DrivingActionRoll(absTime, ds, gradient); + response = DriverStrategy.Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); response.Switch(). - Case<ResponseOverload>(() => { + Case<ResponseUnderload>(() => { // overload may happen if driver limits acceleration when rolling downhill response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient); }); }). - Case<ResponseOverload>(() => { + Case<ResponseUnderload>(() => { response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient); }); } else { - response = DriverStrategy.Driver.DrivingActionRoll(absTime, ds, gradient); + response = DriverStrategy.Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); response.Switch(). - Case<ResponseOverload>(() => { + Case<ResponseUnderload>(() => { response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient); }); } @@ -176,11 +177,68 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public class DriverModeBrake : IDriverMode { + protected enum BrakingPhase + { + Coast, + Brake + } + public DefaultDriverStrategy DriverStrategy { get; set; } + protected BrakingPhase Phase; + public IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) { - throw new System.NotImplementedException(); + IResponse response = null; + if (DriverStrategy.Driver.DataBus.VehicleSpeed() <= DriverStrategy.BrakeTrigger.NextTargetSpeed) { + response = DriverStrategy.Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient); + response.Switch(). + Case<ResponseGearShift>(() => { + response = DriverStrategy.Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); + response.Switch(). + Case<ResponseOverload>(() => { + // overload may happen if driver limits acceleration when rolling downhill + response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient); + }); + }). + Case<ResponseOverload>(() => { + response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient); + }); + return response; + } + var currentDistance = DriverStrategy.Driver.DataBus.Distance(); + if (Phase == BrakingPhase.Coast) { + var breakingDistance = DriverStrategy.Driver.ComputeDecelerationDistance(DriverStrategy.BrakeTrigger.NextTargetSpeed); + if (currentDistance + ds > DriverStrategy.BrakeTrigger.TriggerDistance - breakingDistance) { + return new ResponseDrivingCycleDistanceExceeded() { + MaxDistance = DriverStrategy.BrakeTrigger.TriggerDistance - breakingDistance - currentDistance + }; + } + if (DriverStrategy.BrakeTrigger.TriggerDistance - breakingDistance > + currentDistance + Constants.SimulationSettings.DriverActionDistanceTolerance) { + Phase = BrakingPhase.Brake; + } + } + switch (Phase) { + case BrakingPhase.Coast: + response = DriverStrategy.Driver.DrivingActionCoast(absTime, ds, targetVelocity, gradient); + response.Switch(). + Case<ResponseOverload>(() => { + // coast would decelerate more than driver's max deceleration => issue brakes to decelerate with driver's max deceleration + response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient); + Phase = BrakingPhase.Brake; + }); + break; + case BrakingPhase.Brake: + response = DriverStrategy.Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient); + break; + } + return response; } } diff --git a/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/Models/SimulationComponent/Impl/Driver.cs index ed665ff5b5..81b9ddb8d1 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -167,14 +167,19 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl r.SimulationInterval = operatingPoint.SimulationInterval; retVal = r; // => return }). - Case<ResponseEngineOverload>(r => { - if (r.Delta < 0) { - // if Delta is negative we are already below the Drag-load curve. activate breaks - retVal = r; // => return, strategy should brake - } + Case<ResponseOverload>(() => { + // do nothing, searchOperatingPoint is called later on }). - Case<ResponseGearShift>(r => { retVal = r; }). - Default(r => { throw new VectoException(string.Format("Unknown Response: {0}", r)); }); + Case<ResponseUnderload>(r => { + // Delta is negative we are already below the Drag-load curve. activate breaks + retVal = r; // => return, strategy should brake + }). + Case<ResponseGearShift>(r => { + retVal = r; + }). + Default(r => { + throw new VectoException(string.Format("Unknown Response: {0}", r)); + }); if (retVal == null) { // unhandled response (overload, delta > 0) - we need to search for a valid operating point.. @@ -199,11 +204,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> + /// <param name="maxVelocity"></param> /// <param name="gradient"></param> /// <returns></returns> - public IResponse DrivingActionCoast(Second absTime, Meter ds, Radian gradient) + public IResponse DrivingActionCoast(Second absTime, Meter ds, MeterPerSecond maxVelocity, Radian gradient) { - return CoastOrRollAction(absTime, ds, gradient); + return CoastOrRollAction(absTime, ds, maxVelocity, gradient); } @@ -212,14 +218,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> + /// <param name="maxVelocity"></param> /// <param name="gradient"></param> /// <returns></returns> - public IResponse DrivingActionRoll(Second absTime, Meter ds, Radian gradient) + public IResponse DrivingActionRoll(Second absTime, Meter ds, MeterPerSecond maxVelocity, Radian gradient) { - return CoastOrRollAction(absTime, ds, gradient); + return CoastOrRollAction(absTime, ds, maxVelocity, gradient); } - protected IResponse CoastOrRollAction(Second absTime, Meter ds, Radian gradient) + protected IResponse CoastOrRollAction(Second absTime, Meter ds, MeterPerSecond maxVelocity, Radian gradient) { var operatingPoint = ComputeAcceleration(ds, 0.SI<MeterPerSecond>()); @@ -250,6 +257,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl operatingPoint = LimitAccelerationByDriverModel(operatingPoint, true); + // compute speed at the end of the simulation interval. if it exceeds the limit -> return + var v2 = DataBus.VehicleSpeed() + operatingPoint.Acceleration * operatingPoint.SimulationInterval; + if (v2 > maxVelocity) { + return new ResponseSpeedLimitExceeded(); + } + CurrentState.dt = operatingPoint.SimulationInterval; CurrentState.Acceleration = operatingPoint.Acceleration; @@ -258,11 +271,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl retVal.Switch(). Case<ResponseSuccess>(r => r.SimulationInterval = CurrentState.dt). - Case<ResponseEngineOverload>(() => { + Case<ResponseOverload>(() => { /* an overload may occur due to limiting the acceleration. strategy has to handle this */ }). Case<ResponseGearShift>(r => retVal = r). - Default(() => { throw new VectoSimulationException("unhandled response from powertrain: {0}", retVal); }); + Default(() => { + throw new VectoSimulationException("unhandled response from powertrain: {0}", retVal); + }); return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt }; } @@ -273,10 +288,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// </summary> /// <param name="absTime"></param> /// <param name="ds"></param> - /// <param name="gradient"></param> /// <param name="nextTargetSpeed"></param> + /// <param name="gradient"></param> /// <returns></returns> - public IResponse DrivingActionBrake(Second absTime, Meter ds, Radian gradient, MeterPerSecond nextTargetSpeed) + public IResponse DrivingActionBrake(Second absTime, Meter ds, MeterPerSecond nextTargetSpeed, Radian gradient) { var operatingPoint = ComputeAcceleration(ds, nextTargetSpeed); @@ -437,12 +452,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (actionRoll) { initialResponse.Switch(). Case<ResponseDryRun>(r => origDelta = r.GearboxPowerRequest). - Default(r => { throw new VectoSimulationException("Unknown response type. {0}", r); }); + Default(r => { + throw new VectoSimulationException("Unknown response type. {0}", r); + }); } else { initialResponse.Switch(). - Case<ResponseEngineOverload>(r => origDelta = r.Delta). // search operating point in drive action after overload + 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 VectoSimulationException("Unknown response type. {0}", r); }); + Default(r => { + throw new VectoSimulationException("Unknown response type. {0}", r); + }); } var delta = origDelta; diff --git a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs index 28b9691e45..85dde68e33 100644 --- a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs @@ -58,11 +58,12 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var driverPort = driver.OutPort(); - driverPort.Initialize(5.SI<MeterPerSecond>(), 0.SI<Radian>()); + var velocity = 5.SI<MeterPerSecond>(); + driverPort.Initialize(velocity, 0.SI<Radian>()); var absTime = 0.SI<Second>(); - var response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), 0.SI<Radian>()); + var response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), velocity, 0.SI<Radian>()); Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); @@ -75,7 +76,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent Constants.SimulationSettings.EngineFLDPowerTolerance); while (vehicleContainer.VehicleSpeed() > 1) { - response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), 0.SI<Radian>()); + response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), velocity, 0.SI<Radian>()); Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); @@ -114,11 +115,12 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var driverPort = driver.OutPort(); var gradient = VectoMath.InclinationToAngle(-0.020237973 / 100.0); - driverPort.Initialize(5.SI<MeterPerSecond>(), gradient); + var velocity = 5.SI<MeterPerSecond>(); + driverPort.Initialize(velocity, gradient); var absTime = 0.SI<Second>(); - var response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), gradient); + var response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), velocity, gradient); Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); @@ -131,7 +133,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent Constants.SimulationSettings.EngineFLDPowerTolerance); while (vehicleContainer.VehicleSpeed() > 1) { - response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), gradient); + response = driver.DrivingActionCoast(absTime, 1.SI<Meter>(), velocity, gradient); Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); diff --git a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs index df3abe21e4..cb429490c1 100644 --- a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs @@ -86,7 +86,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var t = 2600.SI<NewtonMeter>(); var n = 1600.RPMtoRad(); var response = gearbox.OutPort().Request(absTime, dt, t * ratio, n / ratio); - Assert.IsInstanceOfType(response, typeof(ResponseGearboxOverload)); + Assert.IsInstanceOfType(response, typeof(ResponseOverload)); absTime += dt; t = -1300.SI<NewtonMeter>(); @@ -125,7 +125,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var angularVelocity = exp.n.RPMtoRad() / ratio; var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); - Assert.IsInstanceOfType(response, typeof(ResponseGearboxOverload)); + Assert.IsInstanceOfType(response, typeof(ResponseOverload)); } var expectedCorrect = new[] { @@ -159,11 +159,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var expected = new[] { new { gear = 1, t = 50, n = 800, loss = 10.108, responseType = typeof(ResponseSuccess) }, - new { gear = 1, t = 2450, n = 800, loss = 58.11, responseType = typeof(ResponseGearboxOverload) }, + new { gear = 1, t = 2450, n = 800, loss = 58.11, responseType = typeof(ResponseOverload) }, new { gear = 1, t = -1000, n = 800, loss = 29.11, responseType = typeof(ResponseSuccess) }, new { gear = 1, t = 850, n = 800, loss = 26.11, responseType = typeof(ResponseSuccess) }, new { gear = 1, t = 850, n = 0, loss = 22.06, responseType = typeof(ResponseSuccess) }, - new { gear = 1, t = 850, n = 200, loss = 23.07, responseType = typeof(ResponseGearboxOverload) }, + new { gear = 1, t = 850, n = 200, loss = 23.07, responseType = typeof(ResponseOverload) }, new { gear = 2, t = 50, n = 800, loss = 10.108, responseType = typeof(ResponseSuccess) }, new { gear = 2, t = 2450, n = 800, loss = 58.11, responseType = typeof(ResponseGearShift) }, new { gear = 2, t = -1000, n = 800, loss = 29.11, responseType = typeof(ResponseSuccess) }, @@ -177,7 +177,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent new { gear = 7, t = 850, n = 1200, loss = 15.382, responseType = typeof(ResponseSuccess) }, new { gear = 7, t = 850, n = 2000, loss = 19.43, responseType = typeof(ResponseGearShift) }, new { gear = 7, t = 2450, n = 0, loss = 17.31, responseType = typeof(ResponseGearShift) }, - new { gear = 7, t = 2450, n = 1200, loss = 23.382, responseType = typeof(ResponseGearboxOverload) } + new { gear = 7, t = 2450, n = 1200, loss = 23.382, responseType = typeof(ResponseOverload) } }; var absTime = 0.SI<Second>(); -- GitLab