mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-01 10:57:17 +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
|
## 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
|
- cleanup after the cutover
|
||||||
- explicit tests making cars park at 0 and max_dist, peds walk to 0 and max_dist
|
- explicit tests making cars park at 0 and max_dist, peds walk to 0 and max_dist
|
||||||
- proper intersection policies, by seeing full view
|
- proper intersection policies, by seeing full view
|
||||||
@ -117,13 +108,5 @@
|
|||||||
- converting goal into router and such
|
- converting goal into router and such
|
||||||
|
|
||||||
- perf
|
- 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?
|
- dig into individual events, still too many?
|
||||||
- for laggy heads, often round down and try slightly too early
|
- 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
|
vehicle is still in the Crossing state, so we need to notify intersections
|
||||||
ahead of time of intended turns and an ETA.
|
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
|
## Demand data
|
||||||
|
|
||||||
The input to a traffic simulation consists of a list of trips -- start from
|
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);
|
car.state = car.crossing_state(params.start_dist, now, map);
|
||||||
}
|
}
|
||||||
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||||
self.queues
|
{
|
||||||
.get_mut(&Traversable::Lane(first_lane))
|
let queue = self.queues.get_mut(&Traversable::Lane(first_lane)).unwrap();
|
||||||
.unwrap()
|
queue.cars.insert(idx, car.vehicle.id);
|
||||||
.cars
|
// Don't use try_to_reserve_entry -- it's overly conservative.
|
||||||
.insert(idx, car.vehicle.id);
|
// 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);
|
self.cars.insert(car.vehicle.id, car);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -307,6 +309,10 @@ impl DrivingSimState {
|
|||||||
now,
|
now,
|
||||||
map,
|
map,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
Some((
|
||||||
|
self.queues.get_mut(&Traversable::Lane(t.dst)).unwrap(),
|
||||||
|
&car,
|
||||||
|
)),
|
||||||
) {
|
) {
|
||||||
// Don't schedule a retry here.
|
// Don't schedule a retry here.
|
||||||
return false;
|
return false;
|
||||||
@ -484,7 +490,7 @@ impl DrivingSimState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// We might be vanishing while partly clipping into other stuff.
|
// 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.
|
// We might've scheduled one of those using BLIND_RETRY_TO_CREEP_FORWARDS.
|
||||||
scheduler.cancel(Command::UpdateLaggyHead(car.vehicle.id));
|
scheduler.cancel(Command::UpdateLaggyHead(car.vehicle.id));
|
||||||
@ -569,7 +575,7 @@ impl DrivingSimState {
|
|||||||
|
|
||||||
// Argh, fight the borrow checker.
|
// Argh, fight the borrow checker.
|
||||||
let mut car = self.cars.remove(&id).unwrap();
|
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);
|
self.cars.insert(id, car);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,6 +585,7 @@ impl DrivingSimState {
|
|||||||
car: &mut Car,
|
car: &mut Car,
|
||||||
intersections: &mut IntersectionSimState,
|
intersections: &mut IntersectionSimState,
|
||||||
scheduler: &mut Scheduler,
|
scheduler: &mut Scheduler,
|
||||||
|
map: &Map,
|
||||||
) {
|
) {
|
||||||
// If we were blocking a few short lanes, should be better now. Very last one might have
|
// If we were blocking a few short lanes, should be better now. Very last one might have
|
||||||
// somebody to wake up.
|
// somebody to wake up.
|
||||||
@ -588,11 +595,17 @@ impl DrivingSimState {
|
|||||||
let old_queue = self.queues.get_mut(&on).unwrap();
|
let old_queue = self.queues.get_mut(&on).unwrap();
|
||||||
assert_eq!(old_queue.laggy_head, Some(car.vehicle.id));
|
assert_eq!(old_queue.laggy_head, Some(car.vehicle.id));
|
||||||
old_queue.laggy_head = None;
|
old_queue.laggy_head = None;
|
||||||
|
match on {
|
||||||
if let Traversable::Turn(t) = on {
|
Traversable::Turn(t) => {
|
||||||
intersections.turn_finished(now, AgentID::Car(car.vehicle.id), *t, scheduler);
|
intersections.turn_finished(now, AgentID::Car(car.vehicle.id), *t, scheduler);
|
||||||
car.last_completed_turn = now;
|
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 {
|
if idx == last_steps.len() - 1 {
|
||||||
// Wake up the follower
|
// Wake up the follower
|
||||||
if let Some(follower_id) = old_queue.cars.front() {
|
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 crate::{AgentID, Command, Scheduler, Speed};
|
||||||
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
@ -71,8 +73,19 @@ impl IntersectionSimState {
|
|||||||
// finished and could let another one in.
|
// finished and could let another one in.
|
||||||
|
|
||||||
for req in state.waiting.keys() {
|
for req in state.waiting.keys() {
|
||||||
// TODO Use update because multiple agents could finish a turn at the same time, before
|
// Use update because multiple agents could finish a turn at the same time, before the
|
||||||
// the waiting one has a chance to try again.
|
// 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));
|
scheduler.update(now, Command::update_agent(req.agent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,18 +127,19 @@ impl IntersectionSimState {
|
|||||||
now: Duration,
|
now: Duration,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
scheduler: &mut Scheduler,
|
scheduler: &mut Scheduler,
|
||||||
|
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let req = Request { agent, turn };
|
let req = Request { agent, turn };
|
||||||
let state = self.state.get_mut(&turn.parent).unwrap();
|
let state = self.state.get_mut(&turn.parent).unwrap();
|
||||||
state.waiting.entry(req.clone()).or_insert(now);
|
state.waiting.entry(req.clone()).or_insert(now);
|
||||||
|
|
||||||
let allowed = if let Some(ref signal) = map.maybe_get_traffic_signal(state.id) {
|
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) {
|
} 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 {
|
} else {
|
||||||
// TODO This never gets called right now
|
// TODO This never gets called right now
|
||||||
state.freeform_policy(&req, map)
|
state.freeform_policy(&req, map, maybe_car_and_target_queue)
|
||||||
};
|
};
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
@ -166,12 +180,25 @@ impl State {
|
|||||||
.any(|req| map.get_t(req.turn).conflicts_with(turn))
|
.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
|
// Allow concurrent turns that don't conflict, don't prevent target lane from spilling
|
||||||
// over.
|
// over.
|
||||||
if self.any_accepted_conflict_with(req.turn, map) {
|
if self.any_accepted_conflict_with(req.turn, map) {
|
||||||
return false;
|
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
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +230,7 @@ impl State {
|
|||||||
now: Duration,
|
now: Duration,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
scheduler: &mut Scheduler,
|
scheduler: &mut Scheduler,
|
||||||
|
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.any_accepted_conflict_with(req.turn, map) {
|
if self.any_accepted_conflict_with(req.turn, map) {
|
||||||
return false;
|
return false;
|
||||||
@ -245,6 +273,13 @@ impl State {
|
|||||||
// TODO Make sure we can optimistically finish this turn before an approaching
|
// TODO Make sure we can optimistically finish this turn before an approaching
|
||||||
// higher-priority vehicle wants to begin.
|
// 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
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,6 +289,7 @@ impl State {
|
|||||||
new_req: &Request,
|
new_req: &Request,
|
||||||
speed: Speed,
|
speed: Speed,
|
||||||
time: Duration,
|
time: Duration,
|
||||||
|
maybe_car_and_target_queue: Option<(&mut Queue, &Car)>,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let (_, cycle, remaining_cycle_time) = signal.current_cycle_and_remaining_time(time);
|
let (_, cycle, remaining_cycle_time) = signal.current_cycle_and_remaining_time(time);
|
||||||
@ -290,6 +326,13 @@ impl State {
|
|||||||
return false;
|
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
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,16 +13,22 @@ pub struct Queue {
|
|||||||
pub laggy_head: Option<CarID>,
|
pub laggy_head: Option<CarID>,
|
||||||
|
|
||||||
pub geom_len: Distance,
|
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 {
|
impl Queue {
|
||||||
pub fn new(id: Traversable, map: &Map) -> Queue {
|
pub fn new(id: Traversable, map: &Map) -> Queue {
|
||||||
let len = id.length(map);
|
|
||||||
Queue {
|
Queue {
|
||||||
id,
|
id,
|
||||||
cars: VecDeque::new(),
|
cars: VecDeque::new(),
|
||||||
laggy_head: None,
|
laggy_head: None,
|
||||||
geom_len: len,
|
geom_len: id.length(map),
|
||||||
|
reserved_length: Distance::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +160,35 @@ impl Queue {
|
|||||||
|
|
||||||
Some(idx)
|
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(
|
fn validate_positions(
|
||||||
|
@ -445,6 +445,7 @@ impl Pedestrian {
|
|||||||
now,
|
now,
|
||||||
map,
|
map,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
None,
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user