mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-09 11:22:25 +01:00
AI braking: plan approaches from the consist brake, stop 8-15 m short
The new braking curve below plans the stop at the signal itself unless the stopping point moves with it (in testing, trains stopped 0-3 m past the signal), so both go in one change. 1. The braking curve. The braking curve towards a lower speed or a stop assumed a constant deceleration from the present speed over the whole distance. A pneumatic brake does not work like that: - it acts on the state the train is in once it has applied; while the train is still speeding up (downhill), plan from the speed it will have reached by then, over the distance left by then; - cast iron blocks grip harder as the train slows, so a constant deceleration plan asks for too much at the start and then releases; follow the shape of the consist brake table (fBrake_a0/a1) over speed instead (shaped_braking_demand), over the distance left once the brake has had its fill time; - no power while the train is still above the speed it is braking for. IncBrake() only adds braking when the brake table says the present position is not enough. When the table overestimates the consist (see the previous rapid step fix, or a descent), the train keeps slowing too little with the handle held. After the full fill time with the handle unchanged, believe the train rather than the table (fBrakeHeldTime). When the AI asks for no braking and the train is still slowing down more than that, release. With the gentler plan above the demand eases off early, and without this a heavy freight train in G came to a stand a few hundred metres before a W4 with its brake still on (seen in testing). 2. The stopping window. In train running the final approach to a stop signal began at fMaxProximityDist (15-40 m, cargo 25-50 m), aimed at half of fMinProximityDist (10-15 m, cargo 20-25 m), and set VelDesired = 0, which asks for hard braking whatever the distance left. Where a train came to a stand depended on how it entered that phase: on the test sceneries anywhere from 1 to 45 m short of the signal on the flat and 25-42 m uphill for heavy freight. For train running only (a W4 stop point, shunting and coupling keep the old margins and the old final approach): - stop 8-15 m short of the signal whatever the length of the train, aim at the middle of that window (stopping_margin) both with the braking curve and in the final approach, and ask there only for what the distance left takes, following the consist brake table; - start the final approach 50 m out (cargo 60 m) whether the train is moving or standing; it used to be 15-40 m while moving and 50 m once stopped, and with the gentler final approach above a train that came to a stand between the two released to creep closer and, uphill, rolled back and tried again; - uphill, while the gradient alone would bring the train to a stand short of the window, keep it rolling at no more than the speed the gradient can take off by then, with power if needed; - uphill, put the train brake on as the train comes to a stand, keep it on while it stands, and apply it for real if the train rolls back. This is done at the very end of pick_optimal_speed(): the adjustments made before it assume a train moving forward and can raise the demand back to "no braking" (with the rule placed earlier, EMUs rolled back up to 90 m in testing). The EMU branch of control_braking_force() gets the "slipping back" condition the wagon branch has always had. 3. Known limitation. Heavy trains of wagons with the ESt3AL2 distributor (413S, 601W and alike) respond more slowly than the AI expects: with the present brake model the distributor fills the AL2 relay chamber, whose capacity is set to 2 in TNESt3::SetSize (Przekladniki[i]->CreateCap(i)), through orifices sized for about 1 l, so these wagons need 34 s to reach 95 % cylinder pressure in G against 21 s for a plain ESt3. On a 20 per mille descent an ET22 with 20 loaded 413S wagons overran the stop signal by up to 6.5 m in 6 of 24 runs at x20 with this change. With the relay chamber at 1 l and the release orifices at about 80 % of their diameter, as the author of the brake model suggested, none of them did (0 of 16).
This commit is contained in:
@@ -3122,6 +3122,17 @@ bool TController::IncBrake()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( BrakeCtrlPosition > 0.0
|
||||
&& AccDesired < 0.0 // only while braking is asked for, not when called for a train slipping back while starting
|
||||
&& fBrakeHeldTime > (
|
||||
mvOccupied->BrakeDelayFlag > bdelay_G ?
|
||||
mvOccupied->BrakeDelay[ 1 ] :
|
||||
mvOccupied->BrakeDelay[ 3 ] ) ) {
|
||||
// the table says the present position is enough, but the brake has had its full fill
|
||||
// time since the handle last moved and the train is still not slowing down as asked:
|
||||
// believe the train rather than the table
|
||||
OK = BrakeLevelAdd( BrakingLevelIncrease );
|
||||
}
|
||||
else
|
||||
OK = false;
|
||||
}
|
||||
@@ -3781,6 +3792,7 @@ void TController::BrakeLevelSet(double b)
|
||||
if (BrakeCtrlPosition == b)
|
||||
return; // nie przeliczać, jak nie ma zmiany
|
||||
BrakeCtrlPosition = std::clamp(b, (double)gbh_MIN, (double)gbh_MAX);
|
||||
fBrakeHeldTime = 0.0;
|
||||
}
|
||||
|
||||
bool TController::BrakeLevelAdd(double b)
|
||||
@@ -5911,6 +5923,7 @@ TController::update_timers( double dt ) {
|
||||
ElapsedTime += dt;
|
||||
WaitingTime += dt;
|
||||
fBrakeTime -= dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca
|
||||
fBrakeHeldTime += dt;
|
||||
if( mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos( bh_FS ) ) {
|
||||
// brake charging timeout starts after charging ends
|
||||
BrakeChargingCooldown += dt;
|
||||
@@ -6659,12 +6672,27 @@ TController::determine_proximity_ranges() {
|
||||
// na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody
|
||||
// jeśli pociąg
|
||||
if( is_train() ) {
|
||||
fMinProximityDist = std::clamp( 5 + iVehicles, 10, 15 );
|
||||
fMaxProximityDist = std::clamp( 10 + iVehicles, 15, 40 );
|
||||
if( true == IsAtPassengerStop ) {
|
||||
// a stop point keeps the margins its W4 parameters (head or middle of the consist,
|
||||
// platform length) were set up against
|
||||
fMinProximityDist = std::clamp( 5 + iVehicles, 10, 15 );
|
||||
fMaxProximityDist = std::clamp( 10 + iVehicles, 15, 40 );
|
||||
if( IsCargoTrain ) {
|
||||
fMinProximityDist += 10.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// a train stops 8-15 m short of the signal whatever its length; closer than the lower
|
||||
// edge it stops at once, the braking aims at the middle of the window, 3.5 m further out
|
||||
fMinProximityDist = 8.0;
|
||||
// the final approach begins far enough out to slow from the coasting speed down to the window,
|
||||
// and at the same distance whether the train is moving or standing, so it does not flip
|
||||
// between creeping closer and holding still when it comes to a stand short of the window
|
||||
fMaxProximityDist = 50.0;
|
||||
}
|
||||
|
||||
if( IsCargoTrain ) {
|
||||
// increase distances for cargo trains to take into account slower reaction to brakes
|
||||
fMinProximityDist += 10.0;
|
||||
// start the final approach earlier for cargo trains, their brakes react more slowly
|
||||
fMaxProximityDist += 10.0;
|
||||
/*
|
||||
if( IsHeavyCargoTrain ) {
|
||||
@@ -6683,7 +6711,7 @@ TController::determine_proximity_ranges() {
|
||||
|
||||
if( mvOccupied->Vel < 0.1 ) {
|
||||
// jak stanie za daleko, to niech nie dociąga paru metrów
|
||||
fMaxProximityDist = 50.0;
|
||||
fMaxProximityDist = std::max( fMaxProximityDist, 50.0 );
|
||||
}
|
||||
|
||||
if( iDrivigFlags & moveLate ) {
|
||||
@@ -7314,6 +7342,26 @@ TController::pick_optimal_speed( double const Range ) {
|
||||
is_car() ? -2.0 : -0.9,
|
||||
is_car() ? 2.0 : 0.9 );
|
||||
|
||||
if( ( true == is_train() )
|
||||
&& ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
||||
&& ( VelNext == 0.0 )
|
||||
&& ( ActualProximityDist <= fMaxProximityDist )
|
||||
&& ( fAccGravity < -0.025 ) ) {
|
||||
// uphill, the gradient alone brings a slow train to a stand within metres, far sooner than a released
|
||||
// brake applies; so the train brake goes on while the train is coming to a stand, stays on while it
|
||||
// stands, and is applied for real if the train has started to roll back. done last, so that none of
|
||||
// the adjustments above, made for a train moving forward, can undo it
|
||||
auto const vel { DirectionalVel() };
|
||||
if( std::abs( vel ) < EU07_AI_NOMOVEMENT ) {
|
||||
VelDesired = 0.0;
|
||||
AccDesired = std::min( AccDesired, EU07_AI_NOACCELERATION );
|
||||
}
|
||||
else if( ( vel < 0.0 )
|
||||
|| ( ( vel < 5.0 ) && ( AccDesired < 0.0 ) ) ) {
|
||||
AccDesired = std::min( AccDesired, fAccGravity - 0.15 );
|
||||
}
|
||||
}
|
||||
|
||||
// if the route ahead is blocked we might need to head the other way
|
||||
check_route_behind( 1000 ); // NOTE: legacy scan range value
|
||||
}
|
||||
@@ -7502,6 +7550,55 @@ brake_response_horizon( TMoverParameters const *Vehicle, TBrakeSystem const Brak
|
||||
return horizon;
|
||||
}
|
||||
|
||||
// distance [m] in which the gradient alone, with no brake and no power, brings a train rolling uphill at Velocity [km/h] to a stand
|
||||
static double
|
||||
uphill_stopping_distance( double const Velocity, double const Gravity ) {
|
||||
return ( Velocity / 3.6 ) * ( Velocity / 3.6 ) / ( 2.0 * std::max( 0.01, -Gravity ) );
|
||||
}
|
||||
|
||||
// speed [km/h] from which the gradient alone brings a train rolling uphill to a stand in Distance [m]
|
||||
static double
|
||||
uphill_coasting_speed( double const Distance, double const Gravity ) {
|
||||
return 3.6 * std::sqrt( 2.0 * std::max( 0.01, -Gravity ) * std::max( 0.0, Distance ) );
|
||||
}
|
||||
|
||||
// resultant acceleration [m/s2] to ask for now so that a steady application of the pneumatic brake takes the train
|
||||
// from Velocity down to Target [km/h] over Distance [m], with the brake force following the speed the way the
|
||||
// consist brake table says it does (cast iron blocks grip harder as the train slows)
|
||||
static double
|
||||
shaped_braking_demand(
|
||||
double const Velocity, double const Target, double const Distance, double const Gravity,
|
||||
double const Vmax, double const *Brakea0, double const *Brakea1, int const Tablesize ) {
|
||||
|
||||
auto const fullbraking = [&]( double const Kmh ) {
|
||||
auto const index { std::clamp( static_cast<int>( Tablesize * Kmh / std::max( 1.0, Vmax ) ), 1, Tablesize ) };
|
||||
return std::max( 0.01, Brakea0[ index ] + 12.0 * Brakea1[ index ] ); };
|
||||
auto const travel = [&]( double const Ratio ) {
|
||||
auto const steps { 24 };
|
||||
auto const step { ( Velocity - Target ) / steps };
|
||||
auto distance { 0.0 };
|
||||
for( auto i { 0 }; i < steps; ++i ) {
|
||||
auto const kmh { Target + ( i + 0.5 ) * step };
|
||||
auto const deceleration { Ratio * fullbraking( kmh ) - Gravity };
|
||||
if( deceleration <= 0.001 ) {
|
||||
return std::numeric_limits<double>::max();
|
||||
}
|
||||
distance += ( kmh / 3.6 ) * ( step / 3.6 ) / deceleration;
|
||||
}
|
||||
return distance; };
|
||||
|
||||
auto low { 0.0 };
|
||||
auto high { 3.0 };
|
||||
if( travel( high ) > Distance ) {
|
||||
return Gravity - high * fullbraking( Velocity );
|
||||
}
|
||||
for( auto i { 0 }; i < 30; ++i ) {
|
||||
auto const middle { 0.5 * ( low + high ) };
|
||||
( travel( middle ) > Distance ? low : high ) = middle;
|
||||
}
|
||||
return Gravity - high * fullbraking( Velocity );
|
||||
}
|
||||
|
||||
void
|
||||
TController::adjust_desired_speed_for_target_speed( double const Range ) {
|
||||
// ustalanie zadanego przyspieszenia
|
||||
@@ -7552,9 +7649,33 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
|
||||
}
|
||||
else {
|
||||
// hamowanie tak, aby stanąć
|
||||
VelDesired = VelNext;
|
||||
AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) );
|
||||
AccDesired = std::min( AccDesired, fAccThreshold );
|
||||
if( ( OrderCurrentGet() & Obey_train ) == 0 ) {
|
||||
VelDesired = VelNext;
|
||||
AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) );
|
||||
AccDesired = std::min( AccDesired, fAccThreshold );
|
||||
}
|
||||
else {
|
||||
// a train keeps its desired speed until the lower edge of the stopping window (a desired
|
||||
// speed of 0 here asks for hard braking whatever the distance left), aims at the middle
|
||||
// of the window, and asks only for what that takes
|
||||
auto const stoppingroom { std::max( 0.5, ActualProximityDist - stopping_margin() ) };
|
||||
AccDesired = (
|
||||
BrakeSystem == TBrakeSystem::Pneumatic ?
|
||||
// cast iron blocks grip harder as the train comes to a stand
|
||||
shaped_braking_demand(
|
||||
vel, VelNext, stoppingroom, fAccGravity,
|
||||
mvOccupied->Vmax, fBrake_a0, fBrake_a1, BrakeAccTableSize ) :
|
||||
( VelNext * VelNext - vel * vel ) / ( 25.92 * stoppingroom ) );
|
||||
if( ( fAccGravity < -0.025 )
|
||||
&& ( AccDesired >= fAccGravity - 0.001 ) // no brake needed at all
|
||||
&& ( ActualProximityDist - uphill_stopping_distance( vel, fAccGravity ) > stopping_margin() ) ) {
|
||||
// uphill the gradient alone would bring the train to a stand short of the
|
||||
// middle of the stopping window; keep it rolling at the present speed until that changes,
|
||||
// but no faster than the gradient alone can take off by the middle of the window
|
||||
AccDesired = std::min( 0.0, AccPreferred );
|
||||
VelDesired = min_speed( VelDesired, uphill_coasting_speed( ActualProximityDist - stopping_margin(), fAccGravity ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -7578,17 +7699,57 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
|
||||
if( brakingdistance + std::max(slowdowndistance, fMaxProximityDist) >= ActualProximityDist - fMaxProximityDist ) {
|
||||
// don't slow down prematurely; as long as we have room to come to a full stop at a safe distance, we're good
|
||||
// ensure some minimal coasting speed, otherwise a vehicle entering this zone at very low speed will be crawling forever
|
||||
auto const brakingpointoffset = VelNext * braking_distance_multiplier( VelNext );
|
||||
AccDesired = std::min(
|
||||
AccDesired,
|
||||
( VelNext * VelNext - vel * vel )
|
||||
/ ( 25.92
|
||||
* std::max(
|
||||
ActualProximityDist - brakingpointoffset,
|
||||
std::min(
|
||||
ActualProximityDist,
|
||||
brakingpointoffset ) )
|
||||
+ 0.1 ) ); // najpierw hamuje mocniej, potem zluzuje
|
||||
auto const brakingpointoffset = (
|
||||
( VelNext == 0.0 ) && is_train() && ( OrderCurrentGet() & Obey_train ) ?
|
||||
stopping_margin() : // the same stopping point as the final approach
|
||||
VelNext * braking_distance_multiplier( VelNext ) );
|
||||
// a pneumatic brake acts on the state the train will be in once it has responded; while the
|
||||
// train is still speeding up, plan the deceleration from there, so more is asked for at once
|
||||
auto velocity { vel };
|
||||
auto distance { ActualProximityDist };
|
||||
if( ( BrakeSystem == TBrakeSystem::Pneumatic )
|
||||
&& ( AbsAccS > 0.0 ) ) {
|
||||
auto const horizon { brake_response_horizon( mvOccupied, BrakeSystem, AbsAccS, fBrake_a0[ 0 ] ) };
|
||||
velocity = vel + 3.6 * horizon * AbsAccS;
|
||||
distance = std::max( 0.5 * ActualProximityDist, ActualProximityDist - ( vel + velocity ) * 0.5 / 3.6 * horizon );
|
||||
}
|
||||
auto const brakingroom {
|
||||
std::max(
|
||||
distance - brakingpointoffset,
|
||||
std::min(
|
||||
distance,
|
||||
brakingpointoffset ) ) };
|
||||
auto deceleration { ( VelNext * VelNext - velocity * velocity ) / ( 25.92 * brakingroom + 0.1 ) };
|
||||
if( ( BrakeSystem == TBrakeSystem::Pneumatic ) && ( velocity > VelNext ) ) {
|
||||
// follow the shape of the consist brake force over speed rather than assume a constant
|
||||
// deceleration, over the distance left once the brake has had time to fill (while the
|
||||
// train is still speeding up, the prediction above has taken that off already)
|
||||
auto room { brakingroom };
|
||||
if( ( AbsAccS <= 0.0 )
|
||||
&& ( ( VelNext > 0.0 ) || ( BrakeCtrlPosition <= 0.0 ) ) ) { // for a stop, only while the brake is still to be applied
|
||||
room = std::max(
|
||||
0.5 * brakingroom,
|
||||
brakingroom - vel / 3.6 * brake_response_horizon( mvOccupied, BrakeSystem, 1.0, fBrake_a0[ 0 ] ) );
|
||||
}
|
||||
deceleration = shaped_braking_demand(
|
||||
velocity, VelNext, room + 0.1 / 25.92, fAccGravity,
|
||||
mvOccupied->Vmax, fBrake_a0, fBrake_a1, BrakeAccTableSize );
|
||||
}
|
||||
AccDesired = std::min( AccDesired, deceleration ); // najpierw hamuje mocniej, potem zluzuje
|
||||
if( vel > VelNext ) {
|
||||
if( deceleration < fAccGravity - 0.001 ) {
|
||||
// no power while still above the speed being braked for
|
||||
AccDesired = std::min( AccDesired, EU07_AI_NOACCELERATION );
|
||||
}
|
||||
else if( ( fAccGravity < -0.025 )
|
||||
&& ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
||||
&& ( ActualProximityDist - uphill_stopping_distance( vel, fAccGravity ) > brakingpointoffset ) ) {
|
||||
// uphill the gradient alone slows the train down more than it is asked to;
|
||||
// keep it rolling at the present speed until that changes
|
||||
AccDesired = std::min( 0.0, AccPreferred );
|
||||
VelDesired = min_speed( VelDesired, uphill_coasting_speed( ActualProximityDist - brakingpointoffset, fAccGravity ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7959,7 +8120,13 @@ void TController::control_tractive_force() {
|
||||
if( ActualProximityDist > (is_car() ? fMinProximityDist : // cars are allowed to move within min proximity distance
|
||||
fMaxProximityDist) ? // other vehicle types keep wider margin
|
||||
true :
|
||||
velocity + 1.0 < VelNext ) {
|
||||
( velocity + 1.0 < VelNext )
|
||||
// uphill, a train told to hold its speed on the way to a stop needs the power to do it
|
||||
|| ( ( VelNext == 0.0 )
|
||||
&& ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
||||
&& ( AccDesired >= 0.0 )
|
||||
&& ( fAccGravity < -0.025 )
|
||||
&& ( ActualProximityDist > fMinProximityDist ) ) ) {
|
||||
// ...to można przyspieszyć
|
||||
increase_tractive_force();
|
||||
}
|
||||
@@ -8039,9 +8206,10 @@ void TController::control_braking_force() {
|
||||
auto const AccMax{ std::min(fBrake_a0[0] + 12 * fBrake_a1[0], mvOccupied->MED_amax) };
|
||||
auto const accmargin = AccMax > 1.1 * AccDesired && fAccGravity < 0.025 ?
|
||||
0.05 : 0.0;
|
||||
if( AccDesired < accthreshold // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold
|
||||
&& ( AbsAccS > accdesiredresultant + accmargin
|
||||
|| BrakeCtrlPosition < 0 ) ) {
|
||||
if( ( AccDesired < accthreshold // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold
|
||||
&& ( AbsAccS > accdesiredresultant + accmargin
|
||||
|| BrakeCtrlPosition < 0 ) )
|
||||
|| ( fAccGravity < -0.05 && velocity < -0.1 && AccDesired < 0.0 ) ) { // also brake if uphill and slipping back
|
||||
// hamować bardziej, gdy aktualne opóźnienie hamowania mniejsze niż (AccDesired)
|
||||
cue_action( driver_hint::brakingforceincrease );
|
||||
}
|
||||
@@ -8084,7 +8252,8 @@ void TController::control_braking_force() {
|
||||
* 0.5 ); // Ra: tymczasowo, bo przeżyna S1
|
||||
}
|
||||
}
|
||||
if ( accdesiredresultant < fAccGravity - 0.05
|
||||
if ( ( accdesiredresultant < fAccGravity - 0.05
|
||||
|| AccDesired >= EU07_AI_NOACCELERATION ) // no braking asked for, yet the train is slowing down more than that
|
||||
&& accdesiredresultant - fBrake_a1[0] * 0.51 - AbsAccS > 0.05 ) {
|
||||
// jak hamuje, to nie tykaj kranu za często
|
||||
// yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2
|
||||
|
||||
Reference in New Issue
Block a user