mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
part 1 of of 'dont block the box'. disabled, because something's broken,
but a solid start.
This commit is contained in:
parent
1ca1f571d0
commit
a37b3fe58c
@ -98,15 +98,6 @@
|
||||
|
||||
## Discrete-event sim model
|
||||
|
||||
- gridlock
|
||||
- get_car_positions will recurse?
|
||||
- laggy head stuff will be inefficient without prevention?
|
||||
- preventish... dont start turn unless target WILL have capacity
|
||||
- stronger is room_at_end now, but thats too strict
|
||||
- best-case static capacity wont help (we might have longer buses and over-commit)
|
||||
- worst-case static capacity will stop shorter cars from going when they legit could
|
||||
- look at current occupants, find where the back WILL eventually wind up (just add up lengths + following dist, actually), then reserve after that.
|
||||
|
||||
- cleanup after the cutover
|
||||
- explicit tests making cars park at 0 and max_dist, peds walk to 0 and max_dist
|
||||
- proper intersection policies, by seeing full view
|
||||
@ -117,13 +108,5 @@
|
||||
- converting goal into router and such
|
||||
|
||||
- perf
|
||||
- speed up Scheduler
|
||||
- BinaryHeap
|
||||
- binheapplus not serde
|
||||
- std::cmp::Reverse not serde
|
||||
- so have to manually impl ord
|
||||
- BinaryHeap isnt PartialEq, which sim det stuff needs!
|
||||
- update() is very hard
|
||||
- priority_queue crate... internally uses hash, so serialization and determinism probably borked
|
||||
- dig into individual events, still too many?
|
||||
- for laggy heads, often round down and try slightly too early
|
||||
|
@ -138,6 +138,16 @@ section interesting:
|
||||
vehicle is still in the Crossing state, so we need to notify intersections
|
||||
ahead of time of intended turns and an ETA.
|
||||
|
||||
One contributor to permanent gridlock is cars and bikes being stuck in an
|
||||
intersection, preventing conflicting turns from being performed. To help avoid
|
||||
this, one of the last checks that stop signs and traffic signals perform before
|
||||
accepting a new turn request is that the target lane has enough space for the
|
||||
new vehicle. This is "reserved" space, not necessarily currently occupied by
|
||||
vehicles in that lane. This accounts for other vehicles performing a turn bound
|
||||
for that lane. See `try_to_reserve_entry` in `mechanics/queue.rs`. When a car
|
||||
completely leaves a lane (determined by the "laggy head" described above), this
|
||||
space is freed, and blocked cars are woken up.
|
||||
|
||||
## Demand data
|
||||
|
||||
The input to a traffic simulation consists of a list of trips -- start from
|
||||
|
@ -110,11 +110,13 @@ impl DrivingSimState {
|
||||
car.state = car.crossing_state(params.start_dist, now, map);
|
||||
}
|
||||
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||
self.queues
|
||||
.get_mut(&Traversable::Lane(first_lane))
|
||||
.unwrap()
|
||||
.cars
|
||||
.insert(idx, car.vehicle.id);
|
||||
{
|
||||
let queue = self.queues.get_mut(&Traversable::Lane(first_lane)).unwrap();
|
||||
queue.cars.insert(idx, car.vehicle.id);
|
||||
// Don't use try_to_reserve_entry -- it's overly conservative.
|
||||
// get_idx_to_insert_car does a more detailed check of the current space usage.
|
||||
queue.reserved_length += car.vehicle.length + FOLLOWING_DISTANCE;
|
||||
}
|
||||
self.cars.insert(car.vehicle.id, car);
|
||||
return true;
|
||||
}
|
||||
@ -307,6 +309,10 @@ impl DrivingSimState {
|
||||
now,
|
||||
map,
|
||||
scheduler,
|
||||
Some((
|
||||
self.queues.get_mut(&Traversable::Lane(t.dst)).unwrap(),
|
||||
&car,
|
||||
)),
|
||||
) {
|
||||
// Don't schedule a retry here.
|
||||
return false;
|
||||
@ -484,7 +490,7 @@ impl DrivingSimState {
|
||||
);
|
||||
|
||||
// We might be vanishing while partly clipping into other stuff.
|
||||
self.clear_last_steps(now, car, intersections, scheduler);
|
||||
self.clear_last_steps(now, car, intersections, scheduler, map);
|
||||
|
||||
// We might've scheduled one of those using BLIND_RETRY_TO_CREEP_FORWARDS.
|
||||
scheduler.cancel(Command::UpdateLaggyHead(car.vehicle.id));
|
||||
@ -569,7 +575,7 @@ impl DrivingSimState {
|
||||
|
||||
// Argh, fight the borrow checker.
|
||||
let mut car = self.cars.remove(&id).unwrap();
|
||||
self.clear_last_steps(now, &mut car, intersections, scheduler);
|
||||
self.clear_last_steps(now, &mut car, intersections, scheduler, map);
|
||||
self.cars.insert(id, car);
|
||||
}
|
||||
|
||||
@ -579,6 +585,7 @@ impl DrivingSimState {
|
||||
car: &mut Car,
|
||||
intersections: &mut IntersectionSimState,
|
||||
scheduler: &mut Scheduler,
|
||||
map: &Map,
|
||||
) {
|
||||
// If we were blocking a few short lanes, should be better now. Very last one might have
|
||||
// somebody to wake up.
|
||||
@ -588,11 +595,17 @@ impl DrivingSimState {
|
||||
let old_queue = self.queues.get_mut(&on).unwrap();
|
||||
assert_eq!(old_queue.laggy_head, Some(car.vehicle.id));
|
||||
old_queue.laggy_head = None;
|
||||
|
||||
if let Traversable::Turn(t) = on {
|
||||
intersections.turn_finished(now, AgentID::Car(car.vehicle.id), *t, scheduler);
|
||||
car.last_completed_turn = now;
|
||||
match on {
|
||||
Traversable::Turn(t) => {
|
||||
intersections.turn_finished(now, AgentID::Car(car.vehicle.id), *t, scheduler);
|
||||
car.last_completed_turn = now;
|
||||
}
|
||||
Traversable::Lane(l) => {
|
||||
old_queue.free_reserved_space(car);
|
||||
intersections.space_freed(now, map.get_l(*l).src_i, scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
if idx == last_steps.len() - 1 {
|
||||
// Wake up the follower
|
||||
if let Some(follower_id) = old_queue.cars.front() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::mechanics::car::Car;
|
||||
use crate::mechanics::queue::Queue;
|
||||
use crate::{AgentID, Command, Scheduler, Speed};
|
||||
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||
use geom::Duration;
|
||||
@ -71,8 +73,19 @@ impl IntersectionSimState {
|
||||
// finished and could let another one in.
|
||||
|
||||
for req in state.waiting.keys() {
|
||||
// TODO Use update because multiple agents could finish a turn at the same time, before
|
||||
// the waiting one has a chance to try again.
|
||||
// Use update because multiple agents could finish a turn at the same time, before the
|
||||
// waiting one has a chance to try again.
|
||||
scheduler.update(now, Command::update_agent(req.agent));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn space_freed(&mut self, now: Duration, i: IntersectionID, scheduler: &mut Scheduler) {
|
||||
let state = self.state.get_mut(&i).unwrap();
|
||||
|
||||
// TODO Be smarter and wake up less people here too.
|
||||
|
||||
for req in state.waiting.keys() {
|
||||
// Use update for similar reasons to turn_finished.
|
||||
scheduler.update(now, Command::update_agent(req.agent));
|
||||
}
|
||||
}
|
||||
@ -114,18 +127,19 @@ impl IntersectionSimState {
|
||||
now: Duration,
|
||||
map: &Map,
|
||||
scheduler: &mut Scheduler,
|
||||
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||
) -> bool {
|
||||
let req = Request { agent, turn };
|
||||
let state = self.state.get_mut(&turn.parent).unwrap();
|
||||
state.waiting.entry(req.clone()).or_insert(now);
|
||||
|
||||
let allowed = if let Some(ref signal) = map.maybe_get_traffic_signal(state.id) {
|
||||
state.traffic_signal_policy(signal, &req, speed, now, map)
|
||||
state.traffic_signal_policy(signal, &req, speed, now, maybe_car_and_target_queue, map)
|
||||
} else if let Some(ref sign) = map.maybe_get_stop_sign(state.id) {
|
||||
state.stop_sign_policy(sign, &req, now, map, scheduler)
|
||||
state.stop_sign_policy(sign, &req, now, map, scheduler, maybe_car_and_target_queue)
|
||||
} else {
|
||||
// TODO This never gets called right now
|
||||
state.freeform_policy(&req, map)
|
||||
state.freeform_policy(&req, map, maybe_car_and_target_queue)
|
||||
};
|
||||
|
||||
if allowed {
|
||||
@ -166,12 +180,25 @@ impl State {
|
||||
.any(|req| map.get_t(req.turn).conflicts_with(turn))
|
||||
}
|
||||
|
||||
fn freeform_policy(&self, req: &Request, map: &Map) -> bool {
|
||||
fn freeform_policy(
|
||||
&self,
|
||||
req: &Request,
|
||||
map: &Map,
|
||||
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||
) -> bool {
|
||||
// Allow concurrent turns that don't conflict, don't prevent target lane from spilling
|
||||
// over.
|
||||
if self.any_accepted_conflict_with(req.turn, map) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't block the box
|
||||
if let Some((queue, car)) = maybe_car_and_target_queue {
|
||||
if !queue.try_to_reserve_entry(car) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -203,6 +230,7 @@ impl State {
|
||||
now: Duration,
|
||||
map: &Map,
|
||||
scheduler: &mut Scheduler,
|
||||
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||
) -> bool {
|
||||
if self.any_accepted_conflict_with(req.turn, map) {
|
||||
return false;
|
||||
@ -245,6 +273,13 @@ impl State {
|
||||
// TODO Make sure we can optimistically finish this turn before an approaching
|
||||
// higher-priority vehicle wants to begin.
|
||||
|
||||
// Don't block the box
|
||||
if let Some((queue, car)) = maybe_car_and_target_queue {
|
||||
if !queue.try_to_reserve_entry(car) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -254,6 +289,7 @@ impl State {
|
||||
new_req: &Request,
|
||||
speed: Speed,
|
||||
time: Duration,
|
||||
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||
map: &Map,
|
||||
) -> bool {
|
||||
let (_, cycle, remaining_cycle_time) = signal.current_cycle_and_remaining_time(time);
|
||||
@ -290,6 +326,13 @@ impl State {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't block the box
|
||||
if let Some((queue, car)) = maybe_car_and_target_queue {
|
||||
if !queue.try_to_reserve_entry(car) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -13,16 +13,22 @@ pub struct Queue {
|
||||
pub laggy_head: Option<CarID>,
|
||||
|
||||
pub geom_len: Distance,
|
||||
// When a car's turn is accepted, reserve the vehicle length + FOLLOWING_DISTANCE for the
|
||||
// target lane. When the car completely leaves (stops being the laggy_head), free up that
|
||||
// space. To prevent blocking the box for possibly scary amounts of time, allocate some of this
|
||||
// length first. This is unused for turns themselves. This value can exceed geom_len (for the
|
||||
// edge case of ONE long car on a short queue).
|
||||
pub reserved_length: Distance,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn new(id: Traversable, map: &Map) -> Queue {
|
||||
let len = id.length(map);
|
||||
Queue {
|
||||
id,
|
||||
cars: VecDeque::new(),
|
||||
laggy_head: None,
|
||||
geom_len: len,
|
||||
geom_len: id.length(map),
|
||||
reserved_length: Distance::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +160,35 @@ impl Queue {
|
||||
|
||||
Some(idx)
|
||||
}
|
||||
|
||||
// If true, there's room and the car must actually start the turn (because the space is
|
||||
// reserved).
|
||||
pub fn try_to_reserve_entry(&mut self, car: &Car) -> bool {
|
||||
// TODO Enable.
|
||||
if true {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sometimes a car + FOLLOWING_DISTANCE might be longer than the geom_len entirely. In that
|
||||
// case, it just means the car won't totally fit on the queue at once, which is fine.
|
||||
// Reserve the normal amount of space; the next car trying to enter will get rejected.
|
||||
let dist = car.vehicle.length + FOLLOWING_DISTANCE;
|
||||
if self.reserved_length + dist < self.geom_len || self.reserved_length == Distance::ZERO {
|
||||
self.reserved_length += dist;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn free_reserved_space(&mut self, car: &Car) {
|
||||
if true {
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(self.laggy_head.is_none());
|
||||
self.reserved_length -= car.vehicle.length + FOLLOWING_DISTANCE;
|
||||
assert!(self.reserved_length >= Distance::ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_positions(
|
||||
|
@ -445,6 +445,7 @@ impl Pedestrian {
|
||||
now,
|
||||
map,
|
||||
scheduler,
|
||||
None,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user