a-b-street / a-b-street/abstreet
Pathfinding v2 / Exit driveways onto any lane
- Dominant language
- Rust
- Stars
- 8.2k
- Forks
- 380
- PR merge metrics
- No merged PRs in 30d
Description
Today, when vehicles leave a building or parking lot's driveway, they always turn onto the rightmost lane of the adjacent road. (Or leftmost in left-handed maps, but for simplicity, I'll just assume right-handed driving here.) This was originally done for simplicity, but the assumption trickles down and affects a whole bunch of other things. I'd like to fix this and start reaping the benefits everywhere.
Some of these notes originally from https://github.com/a-b-street/abstreet/issues/382#issuecomment-730806495
# The problem
In combination with lane-changing only allowed at intersections, exiting onto the rightmost lane forces us to pathfind at the granularity of lanes, not individual roads. Why?

Imagine we're a car pulling out of the driveway of the red house. Right now, the `driving_pos` connection is the rightmost lane, so if our path ideally starts by turning left onto Louisa, we have to follow the green route and loop around the block to get into the proper lane. This is because we can't lane-change in the middle of a lane, only at an intersection.
If we routed by roads, then the first step of the path might say to immediately turn left, following the blue route. If we keep spawning cars in the rightmost lane only, then that wouldn't work. We could maybe try to workaround this by starting the routing from a road that we definitely can reach from the rightmost lane, but there are a few arbitrary choices, and running several pathfinding queries and picking the cheapest feels like a possible performance problem.
# The goal
Allow exiting a driveway onto any lane, sometimes having "lane-changing" immediately happen.
Change **vehicle** pathfinding to operate on `DirectedRoadID`s, not `LaneIDs` -- or equivalently, maybe `IntersectionID`s. This will have various benefits:
- It's a prerequisite to widening roads for #67
- It'll reduce the contraction hierarchy size, because there are less roads than lanes. This cuts down file size and speeds up building the CH and also querying it.
- It simplifies the cost function by removing the lane-changing costs, and having all of that just determined lazily at runtime. Right now we dynamically rewrite the path at the simulation layer, and this is kind of confusing.
# Modeling it
At the map layer, `driving_connection` for buildings and parking lots will instead allow choosing any starting lane.
At the sim layer, we'll pathfind at the road granularity, then figure out the closest starting lane based on the first turn required by the path. How will we physically spawn the car in that lane? `start_car_on_lane` gets more complicated when we detect we have to cross a few lanes. It'll call `get_idx_to_insert_car` on all of the intervening queues to check for room. Only if all of them come back clear, will the car "grab the lock" on all of the queues and begin their exit. I think we'll need a new construct in `Queue`. Instead of just storing an ordered sequence of cars, there's a possibility of inserting a "blockage" in there at a certain index, with a fixed `Distance` of the back of the blockage. Calculating car positions will just use that fixed position as the back of the car, always, until it's cleaned up. When the blockage is deleted, I think we have to wake up the first follower if they were `Queued` and put them back in `Crossing`, same as for deleting a parked car or making a bus depart again.
When should we clean up the blockages, when the car is fully finished unparking? Or as they clear each queue? Probably the former to start, but the latter could work by just assuming the fixed exiting speed and calculating when they'd clear each intermediate lane.
Also this queue "blockage" construct will probably help with later implementing dynamic lane over-taking (#81).
# Queueing on driveways?
Today when a car fails to spawn, it waits `BLIND_RETRY_TO_SPAWN = 5 seconds` and tries again. I think we can continue doing the same thing. If multiple cars are trying to leave at once, that's invisible to the player. There's no internal order -- they'll all just keep retrying and competing. If one of them has less lanes to cut across, they might wind up getting the lock sooner. This seems fine.
# Other cleanup steps
The crux of cutting over to road-based pathfinding seems like it's exiting driveways, but there are lots of other steps too. In no particular order:
- Deciding whether to keep the lane-based `Path` struct, and/or creating some `HighLevelPath` struct with the roads instead
- Drawing paths. When we're previewing a route and we only know roads, we could draw a thick fuzzy band or along centerlines or something. When we're drawing the route that a vehicle has followed so far or in the past, we might want to continue showing exactly their lane changes -- or maybe not, if it's not useful to see that.
- Handling uber-turns -- we still need to do this at the CH level and n the sim to detect entering/exiting an UT. The UT would have to be expressed at the road segment level, not individual lanes and turns.
- Figuring out what to do with pedestrian paths. I think a list of `DirectedRoadID`s is unambiguous for figuring out what side of the road they're on, but need to think more carefully about this.
# Later on
At first, I started writing this issue up to include turning left out of driveways, turning left into driveways, and using center left-turn lanes for driveways. But then I realized the smaller first step. Will revisit this after this issue is done. It has implications for gridlock (very silly paths because vehicles are forced into right turns) and parking blackholes.
# Impl plan
TODO. Maybe a first step is just allowing any start lane, and warping the car there, without doing the queue blockage thing. Figuring out all the path / high-level path API changes is maybe the first step there.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.