make the spawner deal with trips and transitions... big revamp for it

This commit is contained in:
Dustin Carlino 2018-08-23 08:25:16 -07:00
parent 0a944f9864
commit 7745678794
10 changed files with 226 additions and 132 deletions

View File

@ -588,10 +588,10 @@ Time to get even more multi-modal / multi-phase!
- next things
= make ped have a SidewalkSpot as a start.
= make ped have a SidewalkSpot as goal.
- make the spawner orchestrate everything!
- or should there be something else? spawner currently owns IDs. should maybe split out scenario generation and trip FSM management, but... not yet?
- when reaching a parking spot, return up to master sim layer. something needs to make peds at a parking spot become started cars
- something needs to make a newly parked car become a ped
- when a driving car finishes parking, also tell walking sim to spawn a pedestrian
- by the end of this, the spawner should be pretty simple, right?

View File

@ -504,8 +504,8 @@ impl gui::GUI for UI {
}
if input.unimportant_key_pressed(Key::S, "Seed the map with agents") {
self.sim_ctrl.sim.seed_parked_cars(0.5);
self.sim_ctrl.sim.seed_pedestrians(&self.map, 100);
self.sim_ctrl.sim.start_many_parked_cars(&self.map, 100);
self.sim_ctrl.sim.seed_walking_trips(&self.map, 100);
self.sim_ctrl.sim.seed_driving_trips(&self.map, 100);
return gui::EventLoopMode::InputOnly;
}

View File

@ -335,4 +335,8 @@ impl Map {
let parking = road.find_parking_lane(driving)?;
road.find_sidewalk(parking)
}
pub fn get_driving_lane_from_parking(&self, parking: LaneID) -> Option<LaneID> {
self.get_parent(parking).find_driving_lane(parking)
}
}

View File

@ -5,8 +5,10 @@ use {LaneID, Map};
// Returns an inclusive path, aka, [start, ..., end]
pub fn pathfind(map: &Map, start: LaneID, end: LaneID) -> Option<Vec<LaneID>> {
assert_ne!(start, end);
assert_eq!(map.get_l(start).lane_type, map.get_l(end).lane_type);
if start == end {
return Some(vec![start]);
}
// This should be deterministic, since theoretical distance ties would be broken by LaneID.
let mut queue: BinaryHeap<(NotNaN<f64>, LaneID)> = BinaryHeap::new();

View File

@ -141,6 +141,11 @@ impl ParkingSimState {
})?;
Some(l.spots[idx].clone())
}
pub fn get_car_at_spot(&self, spot: ParkingSpot) -> Option<CarParking> {
let l = &self.lanes[spot.parking_lane.0];
l.occupants[spot.spot_idx].and_then(|car| Some(CarParking::new(car, spot)))
}
}
#[derive(Serialize, Deserialize)]

View File

@ -107,8 +107,8 @@ impl Sim {
ids
}
pub fn start_many_parked_cars(&mut self, map: &Map, num_cars: usize) {
self.spawner.start_many_parked_cars(
pub fn seed_driving_trips(&mut self, map: &Map, num_cars: usize) {
self.spawner.seed_driving_trips(
self.time.next(),
map,
num_cars,
@ -143,9 +143,9 @@ impl Sim {
.spawn_pedestrian(self.time.next(), map, sidewalk, &mut self.rng);
}
pub fn seed_pedestrians(&mut self, map: &Map, num: usize) {
pub fn seed_walking_trips(&mut self, map: &Map, num: usize) {
self.spawner
.spawn_many_pedestrians(self.time.next(), map, num, &mut self.rng);
.seed_walking_trips(self.time.next(), map, num, &mut self.rng);
}
// TODO not sure returning info for tests like this is ideal
@ -172,16 +172,21 @@ impl Sim {
) {
Ok(parked_cars) => for p in parked_cars {
cars_parked_this_step.push(p.clone());
self.parking_state.add_parked_car(p);
self.parking_state.add_parked_car(p.clone());
self.spawner.car_reached_parking_spot(self.time, p);
},
Err(e) => panic!("At {}: {}", self.time, e),
};
if let Err(e) = self.walking_state
match self.walking_state
.step(TIMESTEP, map, &mut self.intersection_state)
{
panic!("At {}: {}", self.time, e);
}
Ok(peds_ready_to_drive) => for (ped, spot) in peds_ready_to_drive {
self.spawner
.ped_reached_parking_spot(self.time, ped, spot, &self.parking_state);
},
Err(e) => panic!("At {}: {}", self.time, e),
};
// TODO want to pass self as a lazy QueryCar trait, but intersection_state is mutably
// borrowed :(

View File

@ -2,43 +2,77 @@ use driving::DrivingSimState;
use kinematics::Vehicle;
use map_model;
use map_model::{BuildingID, LaneID, Map};
use parking::ParkingSimState;
use parking::{ParkingSimState, ParkingSpot};
use rand::Rng;
use sim::CarParking;
use std::collections::{BTreeMap, VecDeque};
use std::fmt;
use std::time::Instant;
use walking::{SidewalkSpot, WalkingSimState};
use {AgentID, CarID, PedestrianID, Tick};
use {CarID, PedestrianID, Tick};
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct Command {
at: Tick,
agent: AgentID,
start: BuildingID,
goal: BuildingID,
enum Command {
Walk(Tick, TripID, PedestrianID, SidewalkSpot, SidewalkSpot),
Drive(Tick, TripID, CarParking, BuildingID),
}
// This must get the car/ped IDs correct.
impl Command {
fn at(&self) -> Tick {
match self {
Command::Walk(at, _, _, _, _) => *at,
Command::Drive(at, _, _, _) => *at,
}
}
fn get_pathfinding_lanes(&self, map: &Map) -> (LaneID, LaneID) {
match self {
Command::Walk(_, _, _, spot1, spot2) => {
(spot1.get_sidewalk_pos(map).0, spot2.get_sidewalk_pos(map).0)
}
Command::Drive(_, _, car_parking, goal_bldg) => (
map.get_driving_lane_from_parking(car_parking.spot.parking_lane)
.unwrap(),
map.get_driving_lane_from_bldg(*goal_bldg).unwrap(),
),
}
}
fn retry_next_tick(&self) -> Command {
match self {
Command::Walk(at, trip, ped, spot1, spot2) => {
Command::Walk(at.next(), *trip, *ped, spot1.clone(), spot2.clone())
}
Command::Drive(at, trip, car_parking, goal) => {
Command::Drive(at.next(), *trip, car_parking.clone(), *goal)
}
}
}
}
// This owns car/ped IDs.
#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct Spawner {
// This happens immediately (at the beginning of the simulation in most cases, except for
// interactive UI stuff)
spawn_parked_cars: Vec<CarParking>,
// Ordered by time
commands: VecDeque<Command>,
car_id_counter: usize,
ped_id_counter: usize,
trips: Vec<Trip>,
trip_per_ped: BTreeMap<PedestrianID, TripID>,
trip_per_car: BTreeMap<CarID, TripID>,
}
impl Spawner {
pub fn empty() -> Spawner {
Spawner {
spawn_parked_cars: Vec::new(),
commands: VecDeque::new(),
car_id_counter: 0,
ped_id_counter: 0,
trips: Vec::new(),
trip_per_ped: BTreeMap::new(),
trip_per_car: BTreeMap::new(),
}
}
@ -51,95 +85,62 @@ impl Spawner {
driving_sim: &mut DrivingSimState,
properties: &BTreeMap<CarID, Vehicle>,
) {
for p in self.spawn_parked_cars.drain(0..) {
parking_sim.add_parked_car(p);
}
let mut spawn_agents: Vec<(AgentID, BuildingID, BuildingID)> = Vec::new();
let mut commands: Vec<Command> = Vec::new();
let mut requested_paths: Vec<(LaneID, LaneID)> = Vec::new();
loop {
let pop = if let Some(cmd) = self.commands.front() {
if now == cmd.at {
spawn_agents.push((cmd.agent, cmd.start, cmd.goal));
let (start_lane, goal_lane) = match cmd.agent {
AgentID::Car(_) => (
map.get_driving_lane_from_bldg(cmd.start).unwrap(),
map.get_driving_lane_from_bldg(cmd.goal).unwrap(),
),
AgentID::Pedestrian(_) => (
map.get_b(cmd.start).front_path.sidewalk,
map.get_b(cmd.goal).front_path.sidewalk,
),
};
requested_paths.push((start_lane, goal_lane));
true
} else {
false
}
} else {
false
};
if pop {
self.commands.pop_front();
if self.commands
.front()
.and_then(|cmd| Some(now == cmd.at()))
.unwrap_or(false)
{
let cmd = self.commands.pop_front().unwrap();
requested_paths.push(cmd.get_pathfinding_lanes(map));
commands.push(cmd);
} else {
break;
}
}
if spawn_agents.is_empty() {
if commands.is_empty() {
return;
}
let paths = calculate_paths(&requested_paths, map);
let mut spawned_agents = 0;
for ((agent, start_bldg, goal_bldg), (req, maybe_path)) in spawn_agents
.into_iter()
.zip(requested_paths.iter().zip(paths))
for (cmd, (req, maybe_path)) in commands.into_iter().zip(requested_paths.iter().zip(paths))
{
if let Some(path) = maybe_path {
match agent {
AgentID::Car(car) => {
let driving_lane = path[0];
let parking_lane = map.get_parent(driving_lane)
.find_parking_lane(driving_lane)
.unwrap();
let spot = parking_sim.get_spot_of_car(car, parking_lane);
match cmd {
Command::Drive(_, trip, ref car_parking, _) => {
let car = car_parking.car;
let parking_lane = car_parking.spot.parking_lane;
if driving_sim.start_car_on_lane(
now,
car,
CarParking::new(car, spot),
car_parking.clone(),
VecDeque::from(path),
map,
properties,
) {
self.trip_per_car.insert(car, trip);
parking_sim.remove_parked_car(parking_lane, car);
spawned_agents += 1;
} else {
// Try again next tick. Because we already slurped up all the commands
// for this tick, the front of the queue is the right spot.
self.commands.push_front(Command {
at: now.next(),
agent: agent,
start: start_bldg,
goal: goal_bldg,
});
self.commands.push_front(cmd.retry_next_tick());
}
}
AgentID::Pedestrian(ped) => {
walking_sim.seed_pedestrian(
ped,
SidewalkSpot::Building(start_bldg),
SidewalkSpot::Building(goal_bldg),
map,
VecDeque::from(path),
);
Command::Walk(_, trip, ped, spot1, spot2) => {
self.trip_per_ped.insert(ped, trip);
walking_sim.seed_pedestrian(ped, spot1, spot2, map, VecDeque::from(path));
spawned_agents += 1;
}
};
} else {
println!(
"Couldn't find path from {} to {} for {:?}",
req.0, req.1, agent
req.0, req.1, cmd
);
}
}
@ -150,7 +151,7 @@ impl Spawner {
);
}
// TODO the mut is temporary
// This happens immediately; it isn't scheduled.
pub fn seed_parked_cars<R: Rng + ?Sized>(
&mut self,
percent_capacity_to_fill: f64,
@ -158,7 +159,6 @@ impl Spawner {
rng: &mut R,
) -> Vec<Vehicle> {
assert!(percent_capacity_to_fill >= 0.0 && percent_capacity_to_fill <= 1.0);
assert!(self.spawn_parked_cars.is_empty());
let mut total_capacity = 0;
let mut new_cars: Vec<Vehicle> = Vec::new();
@ -169,7 +169,6 @@ impl Spawner {
// TODO since spawning applies during the next step, lots of stuff breaks without
// this :(
parking_sim.add_parked_car(CarParking::new(id, spot));
//self.spawn_parked_cars.push(CarParking::new(CarID(self.car_id_counter), spot));
new_cars.push(Vehicle::generate_typical_car(id, rng));
self.car_id_counter += 1;
}
@ -190,14 +189,12 @@ impl Spawner {
parking_sim: &mut ParkingSimState,
rng: &mut R,
) -> Vec<Vehicle> {
assert!(self.spawn_parked_cars.is_empty());
let spots = parking_sim.get_all_spots(lane);
spot_indices
.into_iter()
.map(|idx| {
let id = CarID(self.car_id_counter);
parking_sim.add_parked_car(CarParking::new(id, spots[idx].clone()));
// TODO push onto spawn_parked_cars?
self.car_id_counter += 1;
Vehicle::generate_typical_car(id, rng)
})
@ -214,32 +211,44 @@ impl Spawner {
rng: &mut R,
) {
if let Some(cmd) = self.commands.back() {
assert!(at >= cmd.at);
assert!(at >= cmd.at());
}
// Don't add duplicate commands.
if self.commands
.iter()
.find(|cmd| cmd.agent == AgentID::Car(car))
.is_some()
{
if let Some(trip) = self.trips.iter().find(|t| t.use_car == Some(car)) {
println!(
"{} is already scheduled to start, ignoring new request",
car
"{} is already a part of {:?}, ignoring new request",
car, trip
);
return;
}
let parking_lane = parking_sim.lane_of_car(car).expect("Car isn't parked");
let road = map.get_parent(parking_lane);
let driving_lane = road.find_driving_lane(parking_lane)
.expect("Parking lane has no driving lane");
let sidewalk = road.find_sidewalk(parking_lane)
.expect("Parking lane has no sidewalk");
let start_bldg = pick_bldg_from_sidewalk(rng, map, sidewalk);
let goal_bldg = pick_bldg_from_driving_lane(rng, map, goal);
self.commands.push_back(Command {
at,
agent: AgentID::Car(car),
start: pick_bldg_from_driving_lane(rng, map, driving_lane),
goal: pick_bldg_from_driving_lane(rng, map, goal),
let trip_id = TripID(self.trips.len());
let ped_id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
self.trips.push(Trip {
id: trip_id,
ped: ped_id,
start_bldg,
use_car: Some(car),
goal_bldg,
});
self.commands.push_back(Command::Walk(
at,
trip_id,
ped_id,
SidewalkSpot::Building(start_bldg),
SidewalkSpot::ParkingSpot(parking_sim.get_spot_of_car(car, parking_lane)),
));
}
pub fn start_parked_car<R: Rng + ?Sized>(
@ -259,7 +268,7 @@ impl Spawner {
self.start_parked_car_with_goal(at, map, car, parking_sim, goal, rng);
}
pub fn start_many_parked_cars<R: Rng + ?Sized>(
pub fn seed_driving_trips<R: Rng + ?Sized>(
&mut self,
at: Tick,
map: &Map,
@ -302,20 +311,34 @@ impl Spawner {
rng: &mut R,
) {
if let Some(cmd) = self.commands.back() {
assert!(at >= cmd.at);
assert!(at >= cmd.at());
}
assert!(map.get_l(sidewalk).is_sidewalk());
self.commands.push_back(Command {
at,
agent: AgentID::Pedestrian(PedestrianID(self.ped_id_counter)),
start: pick_bldg_from_sidewalk(rng, map, sidewalk),
goal: pick_ped_goal(rng, map, sidewalk),
});
let start_bldg = pick_bldg_from_sidewalk(rng, map, sidewalk);
let goal_bldg = pick_ped_goal(rng, map, sidewalk);
let trip_id = TripID(self.trips.len());
let ped_id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
self.trips.push(Trip {
id: trip_id,
ped: ped_id,
start_bldg,
use_car: None,
goal_bldg,
});
self.commands.push_back(Command::Walk(
at,
trip_id,
ped_id,
SidewalkSpot::Building(start_bldg),
SidewalkSpot::Building(goal_bldg),
));
}
pub fn spawn_many_pedestrians<R: Rng + ?Sized>(
pub fn seed_walking_trips<R: Rng + ?Sized>(
&mut self,
at: Tick,
map: &Map,
@ -334,6 +357,34 @@ impl Spawner {
self.spawn_pedestrian(at, map, start, rng);
}
}
// Trip transitions
pub fn car_reached_parking_spot(&mut self, at: Tick, p: CarParking) {
let trip = &self.trips[self.trip_per_car.remove(&p.car).unwrap().0];
self.commands.push_back(Command::Walk(
at.next(),
trip.id,
trip.ped,
SidewalkSpot::ParkingSpot(p.spot),
SidewalkSpot::Building(trip.goal_bldg),
));
}
pub fn ped_reached_parking_spot(
&mut self,
at: Tick,
ped: PedestrianID,
spot: ParkingSpot,
parking_sim: &ParkingSimState,
) {
let trip = &self.trips[self.trip_per_ped.remove(&ped).unwrap().0];
self.commands.push_back(Command::Drive(
at.next(),
trip.id,
parking_sim.get_car_at_spot(spot).unwrap(),
trip.goal_bldg,
));
}
}
fn pick_car_goal<R: Rng + ?Sized>(rng: &mut R, map: &Map, start: LaneID) -> LaneID {
@ -400,3 +451,22 @@ fn pick_bldg_from_driving_lane<R: Rng + ?Sized>(
) -> BuildingID {
pick_bldg_from_sidewalk(rng, map, map.get_sidewalk_from_driving_lane(start).unwrap())
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct TripID(pub usize);
impl fmt::Display for TripID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TripID({0})", self.0)
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct Trip {
id: TripID,
ped: PedestrianID,
start_bldg: BuildingID,
// Later, this could be an enum of mode choices, or something even more complicated
use_car: Option<CarID>,
goal_bldg: BuildingID,
}

View File

@ -21,14 +21,14 @@ const SPEED: Speed = si::MeterPerSecond {
// A pedestrian can start from a parking spot (after driving and parking) or at a building.
// A pedestrian can end at a parking spot (to start driving) or at a building.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SidewalkSpot {
ParkingSpot(ParkingSpot),
Building(BuildingID),
}
impl SidewalkSpot {
fn get_sidewalk_pos(&self, map: &Map) -> (LaneID, Distance) {
pub fn get_sidewalk_pos(&self, map: &Map) -> (LaneID, Distance) {
match self {
SidewalkSpot::ParkingSpot(spot) => (
map.get_parent(spot.parking_lane)
@ -281,12 +281,13 @@ impl WalkingSimState {
// No-op
}
// Return all the pedestrians that have reached a parking spot.
pub fn step(
&mut self,
delta_time: Time,
map: &Map,
intersections: &mut IntersectionSimState,
) -> Result<(), InvariantViolated> {
) -> Result<Vec<(PedestrianID, ParkingSpot)>, InvariantViolated> {
// Could be concurrent, since this is deterministic.
let mut requested_moves: Vec<(PedestrianID, Action)> = Vec::new();
for p in self.peds.values() {
@ -296,6 +297,8 @@ impl WalkingSimState {
// In AORTA, there was a split here -- react vs step phase. We're still following the same
// thing, but it might be slightly more clear to express it differently?
let mut results = Vec::new();
// Apply moves. This can also be concurrent, since there are no possible conflicts.
for (id, act) in &requested_moves {
match *act {
@ -305,13 +308,12 @@ impl WalkingSimState {
.unwrap()
.step_cross_path(delta_time, map)
{
// TODO return to sim that id has reached spot
self.peds.remove(&id);
}
}
Action::StartParkedCar(ref _spot) => {
Action::StartParkedCar(ref spot) => {
self.peds.remove(&id);
// TODO return something up to sim
results.push((*id, spot.clone()));
}
Action::StartCrossingPath(bldg) => {
let p = self.peds.get_mut(&id).unwrap();
@ -349,7 +351,7 @@ impl WalkingSimState {
};
}
Ok(())
Ok(results)
}
pub fn debug_ped(&self, id: PedestrianID) {
@ -406,7 +408,9 @@ impl WalkingSimState {
let start_lane = path.pop_front().unwrap();
let (spot_start_lane, start_dist) = start.get_sidewalk_pos(map);
assert_eq!(start_lane, spot_start_lane);
assert_eq!(*path.back().unwrap(), goal.get_sidewalk_pos(map).0);
if !path.is_empty() {
assert_eq!(*path.back().unwrap(), goal.get_sidewalk_pos(map).0);
}
let front_path = if let SidewalkSpot::Building(id) = start {
Some(CrossingFrontPath {
bldg: id,
@ -417,7 +421,11 @@ impl WalkingSimState {
None
};
let contraflow = is_contraflow(map, start_lane, path[0]);
let contraflow = if path.is_empty() {
start_dist > goal.get_sidewalk_pos(map).1
} else {
is_contraflow(map, start_lane, path[0])
};
self.peds.insert(
id,
Pedestrian {

View File

@ -14,9 +14,9 @@ fn aorta_model_completes() {
let control_map = control::ControlMap::new(&map);
let mut sim = sim::Sim::new(&map, Some(rng_seed));
sim.seed_pedestrians(&map, spawn_count);
sim.seed_parked_cars(0.5);
sim.start_many_parked_cars(&map, spawn_count);
sim.seed_walking_trips(&map, spawn_count);
sim.seed_driving_trips(&map, spawn_count);
loop {
sim.step(&map, &control_map);

View File

@ -13,9 +13,9 @@ fn serialization() {
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
let mut sim = sim::Sim::new(&map, Some(rng_seed));
sim.seed_pedestrians(&map, spawn_count);
sim.seed_parked_cars(0.5);
sim.start_many_parked_cars(&map, spawn_count);
sim.seed_walking_trips(&map, spawn_count);
sim.seed_driving_trips(&map, spawn_count);
// Does savestating produce the same string?
let save1 = abstutil::to_json(&sim);
@ -36,12 +36,12 @@ fn from_scratch() {
let mut sim1 = sim::Sim::new(&map, Some(rng_seed));
let mut sim2 = sim::Sim::new(&map, Some(rng_seed));
sim1.seed_pedestrians(&map, spawn_count);
sim1.seed_parked_cars(0.5);
sim1.start_many_parked_cars(&map, spawn_count);
sim2.seed_pedestrians(&map, spawn_count);
sim1.seed_walking_trips(&map, spawn_count);
sim1.seed_driving_trips(&map, spawn_count);
sim2.seed_parked_cars(0.5);
sim2.start_many_parked_cars(&map, spawn_count);
sim2.seed_walking_trips(&map, spawn_count);
sim2.seed_driving_trips(&map, spawn_count);
for _ in 1..600 {
if sim1 != sim2 {
@ -72,12 +72,12 @@ fn with_savestating() {
let mut sim1 = sim::Sim::new(&map, Some(rng_seed));
let mut sim2 = sim::Sim::new(&map, Some(rng_seed));
sim1.seed_pedestrians(&map, spawn_count);
sim1.seed_parked_cars(0.5);
sim1.start_many_parked_cars(&map, spawn_count);
sim2.seed_pedestrians(&map, spawn_count);
sim1.seed_walking_trips(&map, spawn_count);
sim1.seed_driving_trips(&map, spawn_count);
sim2.seed_parked_cars(0.5);
sim2.start_many_parked_cars(&map, spawn_count);
sim2.seed_walking_trips(&map, spawn_count);
sim2.seed_driving_trips(&map, spawn_count);
for _ in 1..600 {
sim1.step(&map, &control_map);