mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-29 04:35:51 +03:00
very primitively tracking trip times, only showing in headless
This commit is contained in:
parent
c1427a044c
commit
4f3afa3894
@ -58,3 +58,28 @@ Originally there was a separate geometry layer, probably for stuff like this.
|
||||
## Scores
|
||||
|
||||
Alright, getting much closer to this being a game! Let's return to the idea of utility functions for agents.
|
||||
|
||||
- everyone cares about total trip time
|
||||
- everyone kind of cares about time spent waiting at intersections
|
||||
- drivers (anybody using a car for part of their trip)
|
||||
- easiness of parking... partly this is time spent walking (from start bldg or to goal bldg), and partly time spent driving after initially reaching destination lane
|
||||
- bikes (woops, not implemented yet :P)
|
||||
- climbing up hills
|
||||
- amount of time on busy roads
|
||||
- dedicated lanes are fine
|
||||
- even dedicated lanes too close to parking are bad -- stress from possibiliy of being doored
|
||||
- driving lanes with few actual cars passing are bad
|
||||
- peds
|
||||
- hills up OR down
|
||||
- amount of greenery along route
|
||||
- amount of amenities like cafes along route
|
||||
- Seattle greenways had more factors that make a road pleasant or not
|
||||
|
||||
Per agent, this score is some kind of a linear combination of factors. Coefficients vary per agent -- some people like hills, don't care about busy roads, etc.
|
||||
|
||||
But let's start super simple: just track total trip time for all agents. What's the live UI view we want?
|
||||
|
||||
- per population type (peds, drivers), number of pending and completed trips. sum score so far (can use time so far for pending trips)
|
||||
- note that sum score alone is a bit meaningless, even between population types. need to A/B test to meaningfully compare.
|
||||
- In headless mode, print scores at the end
|
||||
- in UI, have an optional OSD to pop up on the right with scores so far
|
||||
|
@ -80,6 +80,7 @@ fn main() {
|
||||
}),
|
||||
);
|
||||
sim::save_backtraces("call_graph.json");
|
||||
println!("{:?}", sim.get_score());
|
||||
}
|
||||
|
||||
// TODO This is copied from editor; dedupe how?
|
||||
|
@ -59,6 +59,7 @@ use map_model::{LaneID, Map, TurnID};
|
||||
pub use scenario::{Neighborhood, Scenario, SeedParkedCars, SpawnOverTime};
|
||||
pub use sim::{Benchmark, Sim};
|
||||
use std::fmt;
|
||||
pub use trips::ScoreSummary;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct CarID(pub usize);
|
||||
@ -213,6 +214,12 @@ impl std::ops::Add<Time> for Tick {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<Tick> for Tick {
|
||||
fn add_assign(&mut self, other: Tick) {
|
||||
*self = Tick(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Tick {
|
||||
type Output = Tick;
|
||||
|
||||
|
@ -22,8 +22,8 @@ use trips::TripManager;
|
||||
use view::WorldView;
|
||||
use walking::WalkingSimState;
|
||||
use {
|
||||
AgentID, CarID, CarState, DrawCarInput, DrawPedestrianInput, Event, PedestrianID, Tick,
|
||||
TIMESTEP,
|
||||
AgentID, CarID, CarState, DrawCarInput, DrawPedestrianInput, Event, PedestrianID, ScoreSummary,
|
||||
Tick, TIMESTEP,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Derivative)]
|
||||
@ -183,10 +183,14 @@ impl Sim {
|
||||
}
|
||||
|
||||
self.walking_state.populate_view(&mut view);
|
||||
for (ped, spot) in
|
||||
self.walking_state
|
||||
.step(&mut events, TIMESTEP, map, &mut self.intersection_state)?
|
||||
{
|
||||
for (ped, spot) in self.walking_state.step(
|
||||
&mut events,
|
||||
TIMESTEP,
|
||||
self.time,
|
||||
map,
|
||||
&mut self.intersection_state,
|
||||
&mut self.trips_state,
|
||||
)? {
|
||||
events.push(Event::PedReachedParkingSpot(ped, spot));
|
||||
capture_backtrace("PedReachedParkingSpot");
|
||||
self.spawner.ped_reached_parking_spot(
|
||||
@ -363,6 +367,10 @@ impl Sim {
|
||||
AgentID::Pedestrian(ped) => self.walking_state.get_current_route(ped),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_score(&self) -> ScoreSummary {
|
||||
self.trips_state.get_score(self.time)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Benchmark {
|
||||
|
@ -277,6 +277,7 @@ impl Spawner {
|
||||
self.enqueue_command(Command::Walk(
|
||||
at,
|
||||
trips.new_trip(
|
||||
at,
|
||||
map,
|
||||
ped_id,
|
||||
start_bldg,
|
||||
@ -307,6 +308,7 @@ impl Spawner {
|
||||
self.enqueue_command(Command::Walk(
|
||||
at,
|
||||
trips.new_trip(
|
||||
at,
|
||||
map,
|
||||
ped_id,
|
||||
start_bldg,
|
||||
@ -336,6 +338,7 @@ impl Spawner {
|
||||
self.enqueue_command(Command::Walk(
|
||||
at,
|
||||
trips.new_trip(
|
||||
at,
|
||||
map,
|
||||
ped_id,
|
||||
start_bldg,
|
||||
|
@ -2,7 +2,7 @@ use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||
use map_model::{BuildingID, BusStopID, Map};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use walking::SidewalkSpot;
|
||||
use {AgentID, CarID, ParkedCar, PedestrianID, RouteID, TripID};
|
||||
use {AgentID, CarID, ParkedCar, PedestrianID, RouteID, Tick, TripID};
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
|
||||
pub struct TripManager {
|
||||
@ -71,6 +71,21 @@ impl TripManager {
|
||||
(trip.id, *drive_to)
|
||||
}
|
||||
|
||||
pub fn ped_reached_building(&mut self, ped: PedestrianID, now: Tick) {
|
||||
let trip = &mut self.trips[self
|
||||
.active_trip_mode
|
||||
.remove(&AgentID::Pedestrian(ped))
|
||||
.unwrap()
|
||||
.0];
|
||||
match trip.legs.pop_front().unwrap() {
|
||||
TripLeg::Walk(_) => {}
|
||||
x => panic!("Last trip leg {:?} doesn't match ped_reached_building", x),
|
||||
};
|
||||
assert!(trip.legs.is_empty());
|
||||
assert!(!trip.finished_at.is_some());
|
||||
trip.finished_at = Some(now);
|
||||
}
|
||||
|
||||
// Combo query/transition from transit
|
||||
pub fn should_ped_board_bus(&mut self, ped: PedestrianID, route: RouteID) -> bool {
|
||||
let trip = &mut self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0];
|
||||
@ -125,6 +140,7 @@ impl TripManager {
|
||||
// Creation from the interactive part of spawner
|
||||
pub fn new_trip(
|
||||
&mut self,
|
||||
spawned_at: Tick,
|
||||
map: &Map,
|
||||
ped: PedestrianID,
|
||||
start_bldg: BuildingID,
|
||||
@ -143,9 +159,17 @@ impl TripManager {
|
||||
let id = TripID(self.trips.len());
|
||||
self.trips.push(Trip {
|
||||
id,
|
||||
spawned_at,
|
||||
finished_at: None,
|
||||
ped,
|
||||
start_bldg,
|
||||
goal_bldg,
|
||||
uses_car: legs
|
||||
.iter()
|
||||
.find(|l| match l {
|
||||
TripLeg::Drive(_, _) => true,
|
||||
_ => false,
|
||||
}).is_some(),
|
||||
legs: VecDeque::from(legs),
|
||||
});
|
||||
id
|
||||
@ -158,11 +182,47 @@ impl TripManager {
|
||||
.find(|t| t.legs.iter().find(|l| l.uses_car(car)).is_some())
|
||||
.map(|t| t.id)
|
||||
}
|
||||
|
||||
pub fn get_score(&self, now: Tick) -> ScoreSummary {
|
||||
let mut summary = ScoreSummary {
|
||||
pending_walking_trips: 0,
|
||||
total_walking_trips: 0,
|
||||
total_walking_trip_time: Tick::zero(),
|
||||
|
||||
pending_driving_trips: 0,
|
||||
total_driving_trips: 0,
|
||||
total_driving_trip_time: Tick::zero(),
|
||||
};
|
||||
// TODO or would it make more sense to aggregate events as they happen?
|
||||
for t in &self.trips {
|
||||
if t.uses_car {
|
||||
if let Some(at) = t.finished_at {
|
||||
summary.total_driving_trip_time += at - t.spawned_at;
|
||||
} else {
|
||||
summary.pending_driving_trips += 1;
|
||||
summary.total_driving_trip_time += now - t.spawned_at;
|
||||
}
|
||||
summary.total_driving_trips += 1;
|
||||
} else {
|
||||
if let Some(at) = t.finished_at {
|
||||
summary.total_walking_trip_time += at - t.spawned_at;
|
||||
} else {
|
||||
summary.pending_walking_trips += 1;
|
||||
summary.total_walking_trip_time += now - t.spawned_at;
|
||||
}
|
||||
summary.total_walking_trips += 1;
|
||||
}
|
||||
}
|
||||
summary
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
|
||||
struct Trip {
|
||||
id: TripID,
|
||||
spawned_at: Tick,
|
||||
finished_at: Option<Tick>,
|
||||
uses_car: bool,
|
||||
ped: PedestrianID,
|
||||
start_bldg: BuildingID,
|
||||
goal_bldg: BuildingID,
|
||||
@ -188,3 +248,17 @@ impl TripLeg {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As of a moment in time, not necessarily the end of the simulation
|
||||
#[derive(Debug)]
|
||||
pub struct ScoreSummary {
|
||||
pub pending_walking_trips: usize,
|
||||
pub total_walking_trips: usize,
|
||||
// TODO this is actually a duration
|
||||
pub total_walking_trip_time: Tick,
|
||||
|
||||
pub pending_driving_trips: usize,
|
||||
pub total_driving_trips: usize,
|
||||
// TODO this is actually a duration
|
||||
pub total_driving_trip_time: Tick,
|
||||
}
|
||||
|
@ -10,10 +10,11 @@ use multimap::MultiMap;
|
||||
use parking::ParkingSimState;
|
||||
use std;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use trips::TripManager;
|
||||
use view::{AgentView, WorldView};
|
||||
use {
|
||||
AgentID, Distance, DrawPedestrianInput, Event, InvariantViolated, On, ParkingSpot,
|
||||
PedestrianID, Speed, Time, TIMESTEP,
|
||||
PedestrianID, Speed, Tick, Time, TIMESTEP,
|
||||
};
|
||||
|
||||
// TODO tune these!
|
||||
@ -365,8 +366,10 @@ impl WalkingSimState {
|
||||
&mut self,
|
||||
events: &mut Vec<Event>,
|
||||
delta_time: Time,
|
||||
now: Tick,
|
||||
map: &Map,
|
||||
intersections: &mut IntersectionSimState,
|
||||
trips: &mut TripManager,
|
||||
) -> Result<Vec<(PedestrianID, ParkingSpot)>, Error> {
|
||||
// Could be concurrent, since this is deterministic.
|
||||
let mut requested_moves: Vec<(PedestrianID, Action)> = Vec::new();
|
||||
@ -392,6 +395,8 @@ impl WalkingSimState {
|
||||
.step_cross_path(events, delta_time, map)
|
||||
{
|
||||
self.peds.remove(&id);
|
||||
// TODO Should we return stuff to sim, or do the interaction here?
|
||||
trips.ped_reached_building(*id, now);
|
||||
}
|
||||
}
|
||||
Action::WaitAtBusStop(stop) => {
|
||||
|
Loading…
Reference in New Issue
Block a user