mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 01:15:12 +03:00
A few traffic signal cleanups: (#512)
- Stop alerting when slow pedestrians can't make it through the minimum crosswalk time - Simpler iteration style in lagging_green.rs - Totally delete the old brute force signal config code; it never worked well, and the improved heuristics eliminate the need for it anyway - Make a Duration::max function and use it in one case
This commit is contained in:
parent
aacc0fa4d5
commit
03538faf3f
@ -729,13 +729,12 @@ impl ContextualActions for Actions {
|
||||
}
|
||||
|
||||
fn find_bad_signals(app: &App) {
|
||||
println!("Bad traffic signals:");
|
||||
error!("Bad traffic signals:");
|
||||
for i in app.primary.map.all_intersections() {
|
||||
if i.is_traffic_signal() {
|
||||
let first = &ControlTrafficSignal::get_possible_policies(&app.primary.map, i.id)[0].0;
|
||||
if first == "stage per road" || first == "arbitrary assignment" {
|
||||
println!("- {}", i.id);
|
||||
ControlTrafficSignal::brute_force(&app.primary.map, i.id);
|
||||
error!("- {}", i.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +222,24 @@ impl Duration {
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Returns the largest of the two inputs.
|
||||
pub fn max(self, other: Duration) -> Duration {
|
||||
if self >= other {
|
||||
self
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the smallest of the two inputs.
|
||||
pub fn min(self, other: Duration) -> Duration {
|
||||
if self <= other {
|
||||
self
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Duration {
|
||||
|
@ -1,39 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
/// Temporary experiment to group all movements into the smallest number of stages.
|
||||
pub fn make_traffic_signal(map: &Map, i: IntersectionID) {
|
||||
let movements: Vec<Movement> = Movement::for_i(i, map)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter_map(|(id, m)| if id.crosswalk { None } else { Some(m) })
|
||||
.collect();
|
||||
let indices: Vec<usize> = (0..movements.len()).collect();
|
||||
for num_stages in 1..=movements.len() {
|
||||
println!(
|
||||
"For {} turn movements, looking for solution with {} stages",
|
||||
movements.len(),
|
||||
num_stages
|
||||
);
|
||||
for partition in helper(&indices, num_stages) {
|
||||
if okay_partition(movements.iter().collect(), partition) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn okay_partition(movements: Vec<&Movement>, partition: Partition) -> bool {
|
||||
for stage in partition.0 {
|
||||
let mut protected: Vec<&Movement> = Vec::new();
|
||||
for idx in stage {
|
||||
let m = movements[idx];
|
||||
if protected.iter().any(|other| m.conflicts_with(other)) {
|
||||
return false;
|
||||
}
|
||||
protected.push(m);
|
||||
}
|
||||
}
|
||||
println!("found one that works! :O");
|
||||
true
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
|
||||
///
|
||||
/// Create a traffic signal which has a stage that is: protected straight, protected right,
|
||||
/// unprotected left, unprotected right on red. Followed by a variable stage that has protected
|
||||
/// left, unprotected right on red. With a last stage that is all-walk and variable.
|
||||
@ -57,15 +56,11 @@ fn optimize(mut ts: ControlTrafficSignal) -> Option<ControlTrafficSignal> {
|
||||
fn make_crosswalk_variable(ts: &mut ControlTrafficSignal) {
|
||||
const MIN_CROSSWALK_TIME: Duration = Duration::const_seconds(15.0);
|
||||
for mut s in ts.stages.iter_mut() {
|
||||
if let Some(mut duration) = s.max_crosswalk_time(&ts.movements) {
|
||||
if let Some(duration) = s.max_crosswalk_time(&ts.movements) {
|
||||
match s.stage_type {
|
||||
StageType::Fixed(_) => {
|
||||
// get the minimum duration, but never less than 15 seconds
|
||||
if duration < MIN_CROSSWALK_TIME {
|
||||
duration = MIN_CROSSWALK_TIME;
|
||||
}
|
||||
s.stage_type = StageType::Variable(
|
||||
duration,
|
||||
duration.max(MIN_CROSSWALK_TIME),
|
||||
Duration::const_seconds(1.0),
|
||||
Duration::const_seconds(1.0),
|
||||
)
|
||||
@ -157,20 +152,20 @@ fn multi_way_stages(map: &Map, id: IntersectionID) -> Option<ControlTrafficSigna
|
||||
// Insert the straight movements, followed by the right and then the left.
|
||||
stage1.protected_movements.insert(m1.clone());
|
||||
stage1.protected_movements.insert(m2.clone());
|
||||
turns(&m1.from.id, &right).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&m2.from.id, &right).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&m1.from.id, &left).iter().for_each(|t| {
|
||||
stage1.yield_movements.insert(*t);
|
||||
stage2.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&m2.from.id, &left).iter().for_each(|t| {
|
||||
stage1.yield_movements.insert(*t);
|
||||
stage2.protected_movements.insert(*t);
|
||||
});
|
||||
stage1
|
||||
.protected_movements
|
||||
.extend(turns(&m1.from.id, &right));
|
||||
stage1
|
||||
.protected_movements
|
||||
.extend(turns(&m2.from.id, &right));
|
||||
for t in turns(&m1.from.id, &left) {
|
||||
stage1.yield_movements.insert(t);
|
||||
stage2.protected_movements.insert(t);
|
||||
}
|
||||
for t in turns(&m2.from.id, &left) {
|
||||
stage1.yield_movements.insert(t);
|
||||
stage2.protected_movements.insert(t);
|
||||
}
|
||||
add_stage(&mut ts, stage1);
|
||||
if let Some((m1, m2)) = is_conflict(&ts, &stage2) {
|
||||
// We've hit the case where oncoming left turns can't both be protected.
|
||||
@ -203,33 +198,25 @@ fn multi_way_stages(map: &Map, id: IntersectionID) -> Option<ControlTrafficSigna
|
||||
}
|
||||
|
||||
// add right turns
|
||||
turns(&r1, &right).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&r2, &right).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
stage1.protected_movements.extend(turns(&r1, &right));
|
||||
stage1.protected_movements.extend(turns(&r2, &right));
|
||||
|
||||
// add left turns
|
||||
turns(&r1, &left).iter().for_each(|t| {
|
||||
stage1.yield_movements.insert(*t);
|
||||
stage2.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&r2, &left).iter().for_each(|t| {
|
||||
stage1.yield_movements.insert(*t);
|
||||
stage2.protected_movements.insert(*t);
|
||||
});
|
||||
for t in turns(&r1, &left) {
|
||||
stage1.yield_movements.insert(t);
|
||||
stage2.protected_movements.insert(t);
|
||||
}
|
||||
for t in turns(&r2, &left) {
|
||||
stage1.yield_movements.insert(t);
|
||||
stage2.protected_movements.insert(t);
|
||||
}
|
||||
// add the stages
|
||||
add_stage(&mut ts, stage1);
|
||||
add_stage(&mut ts, stage2);
|
||||
} else {
|
||||
// single stage without lagging left turns
|
||||
turns(&r1, &right).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
turns(&r1, &left).iter().for_each(|t| {
|
||||
stage1.protected_movements.insert(*t);
|
||||
});
|
||||
stage1.protected_movements.extend(turns(&r1, &right));
|
||||
stage1.protected_movements.extend(turns(&r1, &left));
|
||||
add_stage(&mut ts, stage1);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use crate::{
|
||||
};
|
||||
use geom::Duration;
|
||||
|
||||
pub mod brute_force;
|
||||
mod lagging_green;
|
||||
|
||||
/// Applies a bunch of heuristics to a single intersection, returning the valid results in
|
||||
@ -446,41 +445,6 @@ fn make_stages_filtered(
|
||||
}
|
||||
}
|
||||
|
||||
// Technically, a set of sets; order doesn't matter
|
||||
#[derive(Clone)]
|
||||
struct Partition(Vec<Vec<usize>>);
|
||||
|
||||
// Extremely hasty port of https://stackoverflow.com/a/30903689
|
||||
fn helper(items: &[usize], max_size: usize) -> Vec<Partition> {
|
||||
if items.len() < max_size || max_size == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
if max_size == 1 {
|
||||
return vec![Partition(vec![items.to_vec()])];
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
let prev1 = helper(&items[0..items.len() - 1], max_size);
|
||||
for i in 0..prev1.len() {
|
||||
for j in 0..prev1[i].0.len() {
|
||||
let mut partition: Vec<Vec<usize>> = Vec::new();
|
||||
for inner in &prev1[i].0 {
|
||||
partition.push(inner.clone());
|
||||
}
|
||||
partition[j].push(*items.last().unwrap());
|
||||
results.push(Partition(partition));
|
||||
}
|
||||
}
|
||||
|
||||
let set = vec![*items.last().unwrap()];
|
||||
for mut partition in helper(&items[0..items.len() - 1], max_size - 1) {
|
||||
partition.0.push(set.clone());
|
||||
results.push(partition);
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// Simple second-pass after generating all signals. Find pairs of traffic signals very close to
|
||||
/// each other with 2 stages each, see if the primary movement of the first stages lead to each
|
||||
/// other, and flip the order of stages if not. This is often wrong when the most common movement is
|
||||
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
use abstutil::{deserialize_btreemap, retain_btreeset, serialize_btreemap};
|
||||
use geom::{Distance, Duration, Speed};
|
||||
|
||||
use crate::make::traffic_signals::{brute_force, get_possible_policies};
|
||||
use crate::make::traffic_signals::get_possible_policies;
|
||||
use crate::raw::OriginalRoad;
|
||||
use crate::{
|
||||
osm, CompressedMovementID, DirectedRoadID, Direction, IntersectionID, Map, Movement,
|
||||
@ -89,10 +89,6 @@ impl ControlTrafficSignal {
|
||||
// signal config.
|
||||
get_possible_policies(map, id, false)
|
||||
}
|
||||
// TODO tmp
|
||||
pub fn brute_force(map: &Map, id: IntersectionID) {
|
||||
brute_force::make_traffic_signal(map, id)
|
||||
}
|
||||
|
||||
pub fn get_min_crossing_time(&self, idx: usize) -> Duration {
|
||||
let mut max_distance = Distance::meters(0.0);
|
||||
|
@ -839,16 +839,10 @@ impl IntersectionSimState {
|
||||
// it wrong, that's fine -- block the box a bit.
|
||||
let time_to_cross = turn.geom.length() / speed;
|
||||
if time_to_cross > remaining_stage_time {
|
||||
// Actually, we might have bigger problems...
|
||||
if time_to_cross > full_stage_duration {
|
||||
self.events.push(Event::Alert(
|
||||
AlertLocation::Intersection(req.turn.parent),
|
||||
format!(
|
||||
"{:?} is impossible to fit into stage duration of {}",
|
||||
req, full_stage_duration
|
||||
),
|
||||
));
|
||||
} else {
|
||||
// Signals enforce a minimum crosswalk time, but some pedestrians are configured to
|
||||
// walk very slowly. In that case, allow them to go anyway and wind up in the crosswalk
|
||||
// during a red. This matches reality reasonably.
|
||||
if time_to_cross <= full_stage_duration {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user