very primitively tracking trip times, only showing in headless

This commit is contained in:
Dustin Carlino 2018-09-27 15:21:40 -07:00
parent c1427a044c
commit 4f3afa3894
7 changed files with 131 additions and 8 deletions

View File

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

View File

@ -80,6 +80,7 @@ fn main() {
}),
);
sim::save_backtraces("call_graph.json");
println!("{:?}", sim.get_score());
}
// TODO This is copied from editor; dedupe how?

View File

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

View File

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

View File

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

View File

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

View File

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