part 1 of of 'dont block the box'. disabled, because something's broken,

but a solid start.
This commit is contained in:
Dustin Carlino 2019-08-10 15:32:07 -07:00
parent 1ca1f571d0
commit a37b3fe58c
6 changed files with 121 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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
}
}

View File

@ -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(

View File

@ -445,6 +445,7 @@ impl Pedestrian {
now,
map,
scheduler,
None,
) {
return false;
}