making car length vary, and adjusting parking model in response

This commit is contained in:
Dustin Carlino 2018-08-20 11:39:58 -07:00
parent 917c3c3ed5
commit 7f29e5d285
7 changed files with 145 additions and 94 deletions

View File

@ -546,6 +546,7 @@ Some first tests to write:
- car stops for departing car (winds up following it)
- departing car waits for other car (winds up following it)
- a line of cars moving through a stop sign looks jittery right now. correct or not?
- following distances for cars of different lengths
Unclear how to nicely let the test inspect stuff every tick.
@ -556,3 +557,8 @@ Need to associate car length between driving and parking sims.
---> could store this in master Sim; after all, there will be some more permanentish stuff like agent/building/trip/owned car soon
- but soon need to bundle things together and pass less params everywhere
- or stash in parking sim, transfer to driving, and back later
Wait, length of car affects parking pretty critically. A bunch of things plumb
around the precomputed front of the spot, used for drawing and for cars to line
up their front in the sim. I think we need to plumb the true start of the spot
and have a method to interpolate and pick the true front.

View File

@ -4,17 +4,12 @@ use dimensioned::si;
use ezgui::GfxCtx;
use geom::{Angle, Polygon, Pt2D};
use graphics;
use kinematics::Vehicle;
use map_model::{geometry, Map, TurnID};
use std;
use {CarID, Distance};
const CAR_WIDTH: f64 = 2.0;
pub const CAR_LENGTH: Distance = si::Meter {
value_unsafe: 4.5,
_marker: std::marker::PhantomData,
};
// TODO should this live in editor/render?
pub struct DrawCar {
pub id: CarID,
@ -32,6 +27,7 @@ pub struct DrawCar {
impl DrawCar {
pub fn new(
id: CarID,
vehicle: &Vehicle,
waiting_for_turn: Option<TurnID>,
map: &Map,
front: Pt2D,
@ -40,7 +36,7 @@ impl DrawCar {
) -> DrawCar {
let turn_arrow = if let Some(t) = waiting_for_turn {
let angle = map.get_t(t).line.angle();
let arrow_pt = front.project_away(CAR_LENGTH.value_unsafe / 2.0, angle.opposite());
let arrow_pt = front.project_away(vehicle.length.value_unsafe / 2.0, angle.opposite());
Some([arrow_pt.x(), arrow_pt.y(), front.x(), front.y()])
} else {
None
@ -62,7 +58,7 @@ impl DrawCar {
// TODO the rounded corners from graphics::Line::new_round look kind of cool though
body_polygon: geometry::thick_line_from_angle(
CAR_WIDTH,
CAR_LENGTH.value_unsafe,
vehicle.length.value_unsafe,
front,
// find the back of the car relative to the front
angle.opposite(),
@ -83,7 +79,7 @@ impl DrawCar {
front_window_thickness * 0.8,
CAR_WIDTH - 2.0 * front_window_length_gap,
front
.project_away(CAR_LENGTH.value_unsafe - 1.0, angle.opposite())
.project_away(vehicle.length.value_unsafe - 1.0, angle.opposite())
.project_away(
CAR_WIDTH / 2.0 - front_window_length_gap,
angle.rotate_degs(-90.0),

View File

@ -11,9 +11,9 @@ use map_model::{LaneID, Map, TurnID};
use models::{choose_turn, FOLLOWING_DISTANCE};
use multimap::MultiMap;
use ordered_float::NotNaN;
use parking::ParkingSimState;
use parking::{ParkingSimState, ParkingSpot};
use rand::Rng;
use sim::{CarParking, ParkingSpot};
use sim::CarParking;
use std;
use std::collections::{BTreeMap, HashMap, VecDeque};
use {Acceleration, AgentID, CarID, CarState, Distance, InvariantViolated, On, Speed, Tick, Time};
@ -93,24 +93,24 @@ impl Car {
return Action::WorkOnParking;
}
let vehicle = &properties[&self.id];
if self.path.is_empty() && self.speed <= kinematics::EPSILON_SPEED {
if let Some(spot) =
self.find_parking_spot(self.on.as_lane(), self.dist_along, map, parking_sim)
{
if spot.dist_along == self.dist_along {
if spot.dist_along_for_car(vehicle) == self.dist_along {
return Action::StartParking(spot);
}
// This seems to never happen; TODO make it an InvariantViolated
println!(
"uh oh, {} is stopped {} before their parking spot. keep going I guess.",
self.id,
spot.dist_along - self.dist_along
spot.dist_along_for_car(vehicle) - self.dist_along
);
}
}
let vehicle = &properties[&self.id];
// TODO could wrap this state up
let mut current_speed_limit = self.on.speed_limit(map);
// Of course we have to include FOLLOWING_DISTANCE
@ -176,7 +176,7 @@ impl Car {
if let Some(spot) =
self.find_parking_spot(id, current_dist_along, map, parking_sim)
{
spot.dist_along
spot.dist_along_for_car(vehicle)
} else {
need_parking = true;
current_on.length(map)
@ -661,8 +661,11 @@ impl DrivingSimState {
map: &Map,
properties: &BTreeMap<CarID, Vehicle>,
) -> bool {
let vehicle = &properties[&car];
let start = path.pop_front().unwrap();
let dist_along = parking.spot.dist_along;
// TODO this looks like it jumps when the parking and driving lanes are different lengths
// due to diagonals
let dist_along = parking.spot.dist_along_for_car(vehicle);
// If not, we have a parking lane much longer than a driving lane...
assert!(dist_along <= map.get_l(start).length());
@ -682,7 +685,6 @@ impl DrivingSimState {
return false;
}
let vehicle = &properties[&car];
let accel_for_other_to_stop = vehicle.accel_to_follow(
self.cars[&other].speed,
map.get_parent(start).get_speed_limit(),
@ -734,7 +736,8 @@ impl DrivingSimState {
) -> Option<DrawCar> {
let c = self.cars.get(&id)?;
let (base_pos, angle) = c.on.dist_along(c.dist_along, map);
let stopping_dist = properties[&id].stopping_distance(c.speed);
let vehicle = &properties[&id];
let stopping_dist = vehicle.stopping_distance(c.speed);
// TODO arguably, this math might belong in DrawCar.
let pos = if let Some(ref parking) = c.parking {
@ -754,6 +757,7 @@ impl DrivingSimState {
Some(DrawCar::new(
c.id,
vehicle,
c.waiting_for.and_then(|on| on.maybe_turn()),
map,
pos,

View File

@ -10,6 +10,12 @@ pub const EPSILON_SPEED: Speed = si::MeterPerSecond {
_marker: std::marker::PhantomData,
};
// This must be < PARKING_SPOT_LENGTH
pub const MAX_CAR_LENGTH: Distance = si::Meter {
value_unsafe: 6.5,
_marker: std::marker::PhantomData,
};
// TODO unit test all of this
// TODO handle floating point issues uniformly here
@ -21,6 +27,8 @@ pub struct Vehicle {
max_accel: Acceleration,
// < 0
pub max_deaccel: Acceleration,
pub length: Distance,
}
// TODO this is used for verifying sim state determinism, so it should actually check everything.
@ -38,6 +46,8 @@ impl Vehicle {
id,
max_accel: rng.gen_range(2.4, 2.8) * si::MPS2,
max_deaccel: rng.gen_range(-2.8, -2.4) * si::MPS2,
// TODO more realistic to have a few preset lengths and choose between them
length: rng.gen_range(4.5, MAX_CAR_LENGTH.value_unsafe) * si::M,
}
}

View File

@ -206,14 +206,15 @@ impl SimQueue {
}
// TODO base this on actual speed and each vehicle ;)
let stopping_dist =
properties[&self.cars_queue[0]].stopping_distance(self.id.speed_limit(map));
let vehicle = &properties[&self.cars_queue[0]];
let stopping_dist = vehicle.stopping_distance(self.id.speed_limit(map));
let mut results = Vec::new();
let (pos1, angle1, dist_along1) =
sim.cars[&self.cars_queue[0]].get_best_case_pos(time, map);
results.push(DrawCar::new(
self.cars_queue[0],
vehicle,
sim.cars[&self.cars_queue[0]]
.waiting_for
.and_then(|on| on.maybe_turn()),
@ -229,6 +230,7 @@ impl SimQueue {
if dist_along_bound - FOLLOWING_DISTANCE > dist_along {
results.push(DrawCar::new(
*id,
vehicle,
sim.cars[id].waiting_for.and_then(|on| on.maybe_turn()),
map,
pos,
@ -243,6 +245,7 @@ impl SimQueue {
let (pt, angle) = self.id.dist_along(dist_along_bound, map);
results.push(DrawCar::new(
*id,
vehicle,
sim.cars[id].waiting_for.and_then(|on| on.maybe_turn()),
map,
pt,

View File

@ -1,10 +1,11 @@
use dimensioned::si;
use draw_car;
use draw_car::DrawCar;
use geom::{Angle, Pt2D};
use kinematics::Vehicle;
use map_model;
use map_model::{Lane, LaneID, LaneType, Map};
use sim::{CarParking, ParkingSpot};
use sim::CarParking;
use std::collections::BTreeMap;
use std::iter;
use {CarID, Distance};
@ -31,7 +32,7 @@ impl ParkingSimState {
self.lanes[id.0] = ParkingLane {
id: id,
spots: Vec::new(),
spot_fronts: Vec::new(),
occupants: Vec::new(),
};
}
@ -44,27 +45,15 @@ impl ParkingSimState {
}
pub fn get_all_spots(&self, lane: LaneID) -> Vec<ParkingSpot> {
let mut spots: Vec<ParkingSpot> = Vec::new();
for (idx, front) in self.lanes[lane.0].spot_fronts.iter().enumerate() {
spots.push(ParkingSpot {
parking_lane: lane,
spot_idx: idx,
dist_along: front.0,
});
}
spots
self.lanes[lane.0].spots.clone()
}
pub fn get_all_free_spots(&self) -> Vec<ParkingSpot> {
let mut spots: Vec<ParkingSpot> = Vec::new();
for l in &self.lanes {
for (idx, (occupant, front)) in l.spots.iter().zip(l.spot_fronts.iter()).enumerate() {
for (spot, occupant) in l.spots.iter().zip(l.occupants.iter()) {
if occupant.is_none() {
spots.push(ParkingSpot {
parking_lane: l.id,
spot_idx: idx,
dist_along: front.0,
});
spots.push(spot.clone());
}
}
}
@ -78,22 +67,34 @@ impl ParkingSimState {
pub fn add_parked_car(&mut self, p: CarParking) {
assert_eq!(
self.lanes[p.spot.parking_lane.0].spots[p.spot.spot_idx],
self.lanes[p.spot.parking_lane.0].occupants[p.spot.spot_idx],
None
);
self.lanes[p.spot.parking_lane.0].spots[p.spot.spot_idx] = Some(p.car);
self.lanes[p.spot.parking_lane.0].occupants[p.spot.spot_idx] = Some(p.car);
self.total_count += 1;
}
pub fn get_draw_cars(&self, id: LaneID, map: &Map) -> Vec<DrawCar> {
self.lanes[id.0].get_draw_cars(map)
pub fn get_draw_cars(
&self,
id: LaneID,
map: &Map,
properties: &BTreeMap<CarID, Vehicle>,
) -> Vec<DrawCar> {
self.lanes[id.0].get_draw_cars(map, properties)
}
pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCar> {
pub fn get_draw_car(
&self,
id: CarID,
map: &Map,
properties: &BTreeMap<CarID, Vehicle>,
) -> Option<DrawCar> {
// TODO this is so horrendously slow :D
for l in &self.lanes {
if l.spots.contains(&Some(id)) {
return l.get_draw_cars(map).into_iter().find(|c| c.id == id);
if l.occupants.contains(&Some(id)) {
return l.get_draw_cars(map, properties)
.into_iter()
.find(|c| c.id == id);
}
}
None
@ -102,7 +103,7 @@ impl ParkingSimState {
pub fn lane_of_car(&self, id: CarID) -> Option<LaneID> {
// TODO this is so horrendously slow :D
for l in &self.lanes {
if l.spots.contains(&Some(id)) {
if l.occupants.contains(&Some(id)) {
return Some(l.id);
}
}
@ -112,21 +113,17 @@ impl ParkingSimState {
// Of the front of the car
pub fn get_spot_of_car(&self, c: CarID, l: LaneID) -> ParkingSpot {
let idx = self.lanes[l.0]
.spots
.occupants
.iter()
.position(|x| *x == Some(c))
.unwrap();
ParkingSpot {
parking_lane: l,
spot_idx: idx,
dist_along: self.lanes[l.0].spot_fronts[idx].0,
}
self.lanes[l.0].spots[idx].clone()
}
pub fn get_all_cars(&self) -> Vec<(CarID, LaneID)> {
let mut result = Vec::new();
for l in &self.lanes {
for maybe_car in &l.spots {
for maybe_car in &l.occupants {
if let Some(car) = maybe_car {
result.push((*car, l.id));
}
@ -137,29 +134,26 @@ impl ParkingSimState {
pub fn get_first_free_spot(&self, lane: LaneID, dist_along: Distance) -> Option<ParkingSpot> {
let l = &self.lanes[lane.0];
let idx = l.spots
.iter()
.enumerate()
.position(|(idx, x)| x.is_none() && l.spot_fronts[idx].0 >= dist_along)?;
Some(ParkingSpot {
parking_lane: lane,
spot_idx: idx,
dist_along: l.spot_fronts[idx].0,
})
// Just require the car to currently be behind the end of the spot length, so we don't have
// to worry about where in the spot they need to line up.
let idx = l.occupants.iter().enumerate().position(|(idx, x)| {
x.is_none() && l.spots[idx].dist_along + map_model::PARKING_SPOT_LENGTH >= dist_along
})?;
Some(l.spots[idx].clone())
}
}
#[derive(Serialize, Deserialize)]
struct ParkingLane {
id: LaneID,
spots: Vec<Option<CarID>>,
spot_fronts: Vec<(Distance, Pt2D, Angle)>,
spots: Vec<ParkingSpot>,
occupants: Vec<Option<CarID>>,
}
// TODO the f64's prevent derivation
impl PartialEq for ParkingLane {
fn eq(&self, other: &ParkingLane) -> bool {
self.id == other.id && self.spots == other.spots
self.id == other.id && self.occupants == other.occupants
}
}
@ -171,44 +165,88 @@ impl ParkingLane {
return ParkingLane {
id: l.id,
spots: Vec::new(),
spot_fronts: Vec::new(),
occupants: Vec::new(),
};
}
ParkingLane {
id: l.id,
spots: iter::repeat(None).take(l.number_parking_spots()).collect(),
spot_fronts: (0..l.number_parking_spots())
occupants: iter::repeat(None).take(l.number_parking_spots()).collect(),
spots: (0..l.number_parking_spots())
.map(|idx| {
let spot_start = map_model::PARKING_SPOT_LENGTH * (2.0 + idx as f64);
let dist_along =
spot_start - (map_model::PARKING_SPOT_LENGTH - draw_car::CAR_LENGTH) / 2.0;
let (pos, angle) = l.dist_along(dist_along);
(dist_along, pos, angle)
let (pos, angle) = l.dist_along(spot_start);
ParkingSpot {
parking_lane: l.id,
spot_idx: idx,
dist_along: spot_start,
pos,
angle,
}
})
.collect(),
}
}
fn remove_parked_car(&mut self, car: CarID) {
let idx = self.spots.iter().position(|x| *x == Some(car)).unwrap();
self.spots[idx] = None;
let idx = self.occupants.iter().position(|x| *x == Some(car)).unwrap();
self.occupants[idx] = None;
}
fn get_draw_cars(&self, map: &Map) -> Vec<DrawCar> {
self.spots
fn get_draw_cars(&self, map: &Map, properties: &BTreeMap<CarID, Vehicle>) -> Vec<DrawCar> {
self.occupants
.iter()
.enumerate()
.filter_map(|(idx, maybe_id)| {
maybe_id.and_then(|id| {
let (_, front, angle) = self.spot_fronts[idx];
Some(DrawCar::new(id, None, map, front, angle, 0.0 * si::M))
let vehicle = &properties[&id];
let (front, angle) = self.spots[idx].front_of_car(vehicle);
Some(DrawCar::new(
id,
vehicle,
None,
map,
front,
angle,
0.0 * si::M,
))
})
})
.collect()
}
fn is_empty(&self) -> bool {
!self.spots.iter().find(|&&x| x.is_some()).is_some()
!self.occupants.iter().find(|&&x| x.is_some()).is_some()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Eq)]
pub struct ParkingSpot {
pub parking_lane: LaneID,
pub spot_idx: usize,
// These 3 are of the front of the parking spot
#[derivative(PartialEq = "ignore")]
dist_along: Distance,
pos: Pt2D,
#[derivative(PartialEq = "ignore")]
angle: Angle,
}
impl ParkingSpot {
pub fn dist_along_for_car(&self, vehicle: &Vehicle) -> Distance {
// Find the offset to center this particular car in the parking spot
let offset = (map_model::PARKING_SPOT_LENGTH - vehicle.length) / 2.0;
self.dist_along - offset
}
fn front_of_car(&self, vehicle: &Vehicle) -> (Pt2D, Angle) {
// Find the offset to center this particular car in the parking spot
let offset = (map_model::PARKING_SPOT_LENGTH - vehicle.length) / 2.0;
(
self.pos
.project_away(offset.value_unsafe, self.angle.opposite()),
self.angle,
)
}
}

View File

@ -9,14 +9,14 @@ use intersections::{AgentInfo, IntersectionSimState};
use kinematics::Vehicle;
use map_model::{IntersectionID, LaneID, LaneType, Map, Turn, TurnID};
use parametric_driving;
use parking::ParkingSimState;
use parking::{ParkingSimState, ParkingSpot};
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
use spawn::Spawner;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::f64;
use std::time::{Duration, Instant};
use walking::WalkingSimState;
use {CarID, CarState, Distance, InvariantViolated, PedestrianID, Tick, TIMESTEP};
use {CarID, CarState, InvariantViolated, PedestrianID, Tick, TIMESTEP};
#[derive(Serialize, Deserialize, Derivative, PartialEq, Eq)]
pub enum DrivingModel {
@ -317,7 +317,10 @@ impl Sim {
pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCar> {
self.driving_state
.get_draw_car(id, self.time, map, &self.car_properties)
.or_else(|| self.parking_state.get_draw_car(id, map))
.or_else(|| {
self.parking_state
.get_draw_car(id, map, &self.car_properties)
})
}
pub fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option<DrawPedestrian> {
@ -331,7 +334,8 @@ impl Sim {
self.driving_state
.get_draw_cars_on_lane(l, self.time, map, &self.car_properties)
}
LaneType::Parking => self.parking_state.get_draw_cars(l, map),
LaneType::Parking => self.parking_state
.get_draw_cars(l, map, &self.car_properties),
LaneType::Sidewalk => Vec::new(),
LaneType::Biking => Vec::new(),
}
@ -419,16 +423,6 @@ impl Benchmark {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Eq)]
pub struct ParkingSpot {
pub parking_lane: LaneID,
pub spot_idx: usize,
// Of the front of the car
#[derivative(PartialEq = "ignore")]
pub dist_along: Distance,
}
// TODO better name?
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CarParking {