-
-
Notifications
You must be signed in to change notification settings - Fork 350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamically change lanes if alternative lane is on the same road and available #382
Comments
create a map exemplifying the lane-changing problems of #382.
problems aren't quite captured, but it's a start.
I think so. Looking at https://user-images.githubusercontent.com/899988/98115670-78345780-1ea7-11eb-9cde-a3ac15a059ca.gif again: note that next vehicle enters only after both first crossing is empty AND small road is empty. If car would be able to enter center/left lane then it would not wait until small road is completely empty, right? It should result in doubling (maybe even trippling) capacity. Not sure whatever it would also fix problem with passing crossing one by one (is it possible to have multiple vehicles at crossing at the same time if all are going without conflict between them) |
What
means here? |
To try out the synthetic map and scenario used in the test:
Correct. The main issue here is really bad choice of lane. The secondary issue is the rules for not blocking the intersection. I think I'll try allowing blocking the intersection for these "degenerate" cases where there's just a crosswalk. Pedestrians may suffer, but this is a little closer to reality. And in most cases, there's probably not even a crosswalk there anyway. My experience hand-crafting the synthetic map and trying to specify a traffic pattern that approximates this setup was tedious, and the result still doesn't match. I wonder if there's something more direct we could do. Maybe a way to specify a small test area from an existing map, record the exact timing of vehicles passing through there, and replay it. |
If that is possible without spending an extremely long time on that - it would be an ideal solution. Not sure is there some sane way to record blocked outflow (what is some cases may be important, but not here so it is not some blocker) |
the lane-changing test map. #382 Also abandon sim.run_until_done; the only caller was the LC test, and it uses 30s steps inappropriately anyway.
…ntersections, to help with producing a better test case for #382
…sses the entry point. Otherwise, in the Krakow scenario of interest, the vehicles piling up and making poor LCing decisions actually get blocked by other gridlock and never make it to the exit. #382
Not sure if it would be useful, but I can record video how this crossing behaves with a real traffic. (with potato-level camera, from ground, without a proper stabilization but cars behavior should be visible) |
Hmm, I think I have intuition for how things flow here, but if it's not much trouble, sure! It'd be a neat experiment to see how closely we can get the simulation to look like a sample of real behavior. |
I've been poking at this and wanted to check in and verify my understanding @dabreegster. I think what's happening is: A car's route is a list of lane IDs (and their connections) that will be traversed. A lane ID identifies one segment of road between two particular intersections - so if you pass through an intersection, even if you stay in the right most lane, the Lane ID changes. This is important for later. When a car crosses an intersection, provided the intersection is clear[1], we allow the crossing car to opportunistically switch lanes while crossing the intersection. The car chooses the best lane to switch to based on a scoring of all possible lanes: backed up traffic, a bike in the lane, and changing lanes all count against a lane. A little example: Imagine a one-way road with two lanes and three segments.
Let's say my original planned route is:
However, when I reach the end of 1a, and am about to cross, I see a cyclist in
Notably, I made no updates to the third step — Fortunately, normally when we reach the end of https://user-images.githubusercontent.com/899988/98115670-78345780-1ea7-11eb-9cde-a3ac15a059ca.gif So looking back at our Krakow scenario, the lane that everyone is changing too is indeed part of an uber turn and part of their original travel route. What's happening is drivers approaching the intersection have opportunistically changed lanes before the uber turn. But then once they're in the uber turn, they're prevented from further updating their original plan, which forces them to switch back to their original lane. Does that sound correct and plausible @dabreegster ? If so, some possible solutions might be:
I naively tried this and hit a cascade of asserts - it seems like you had specific and thorough reasons for preventing this.
When changing lanes - update more than just the next step - update the entire trip plan (or some forward looking portion of it).
e.g maybe road based, or "index of lane" based rather than LaneId based (obviously we'd have to handle changing the number of lanes - maybe it could be a float... drive 25% from the right edge 🙃, just throwing stuff at the wall really. Any inclinations? [1]
More specifically - when [2]
I'm not completely sure what this is - it seems to be used in a couple of related but slightly different contexts. One context is surfaced in the UI as a visual tool to visualize arbitrary combinations of turns through intersection. The other context is that an individual intersection can be "in an uber turn". My intuition is that this just happens when a bunch of turns are close together, but I haven't yet verified that. |
For reference, this is a call in If you remove this check, the crash happens because the laggy head clears the lane, tries to wake up its follower, but it's already
Ah, yes! The traffic signal intersection in question is just north of another traffic signal, and that proximity causes uber-turns to be created there. There's a second case forcing uber-turns: turn restrictions -- but there are none defined here.
There is/was a reason, but I need to think about it again to explain it; I'll followup shortly.
This could work, but it's likely just a performance issue. Also, the number of vehicles in a future lane could change by the time we get there, so waiting to decide seems nice. Plus, I'm not sure changing the plan ahead of time would even help in this case -- there's still some reason why lane-changing in the middle of an uber-turn is tricky.
This is a possibility. Also need to think about it a bit more. I'll think through 2 of these ideas more and reply soon. |
Uber-turns and lane-changingBackgroundUT=uber-turn An UT is a sequence of lane->turn->lane->turn...->lane. They get created for two reasons:
For reference, there's another unsolved bug involving editing a lane in/near the end of an UT: #259 Why can't we LC during an UT?I introduced this restriction during the
I started this experiment roughly around #114 (comment). But I don't think it was successful -- I didn't really make progress on the south_seattle map there. And since then, one of the problems -- dealing with unsynchronized traffic signals -- has gotten easier to manually edit, and there's very primitive "autotuning" to line up pairs of signals. In retrospect, SolutionsIdeally we would delete this experiment. I think first you can try your repro scenario passing If this doesn't work, then maybe we still need to reserve an UT, but we can relax and allow LCing inside an UT, removing the restriction from A third option is to allow LCing near an UT in a specific case: when the UT is actually just a single turn. That's the case in this situation; all of the UTs along Aleja Juliusza Słowackiego are just single turns. So the reservation logic wouldn't do anything funny. We could either detect this one-step case in I'm thinking through your third proposal, will write it up too |
One last thing that came up: When do we lock in the Turn we want?As soon as a vehicle hits I'm losing steam, but I think it's worth revisiting the protocol that agents use to communicate with intersections. Should vehicles submit all possible turns they want to do? Should the request they submit be coarser and just at the level of a destination road? Then the "opportunistic LCing" can be lazily evaluated inside |
Thank you for all this excellent feedback! I will consider over night and respond in the AM - I'd like to keep working on this. |
Thanks for the clarity around how doing lane-based pathfinding avoids certain unsupported scenarios (e.g. mid-segment lane changing). I see why it would be complicated to switch to a road-based route. With I'm currently running through some other scenarios to see what the impact is... Honestly though, now that you've explained it more, special handling of compound turns ("uber turns") makes sense to me. And I'm a little hesitant to just completely rip it out. Instead, maybe we could take it a bit further:
I'm wondering if there's a compromise here, where we could opportunistically cascade updates to the path, not for the entire trip, but for entire uber turn, and still require that it all be "locked in" before the car enters the intersection. Since it's (hopefully/usually) only a handful of segments making up the uber turn, it should be more tractable vs. updating their entire trip plan. And since it's locked in at the point of entry, I'm hoping we could keep the bulk of uberturn logic untouched like the "don't get locked in the middle of an uber turn" behavior from #114 |
It looks much better! But all but one of the cars in the video are going straight, so it's still far from realistic -- all 3 lanes should be advancing at the same time, without any switching around...
This is a nice idea! The performance should be fine, and the code complexity shouldn't blow up too much. I am not sure it'll help this specific situation, though -- the uber-turn is only across the traffic signal, not the two little stop signs before and after: I never finished the process of playing around with how to automatically group UTs. Maybe we can take away checks like https://github.com/dabreegster/abstreet/blob/308eb90956be7d963f308957168bf0e91a1c504c/map_model/src/pathfind/uber_turns.rs#L181 and just go by distance. I think I saw some pretty unintuitively large groups when I did that. |
Tweaking the costing function helps this. Currently we'll lane change if there's even just one car in front of us, which is probably not very realistic. If we combine the "number of cars ahead of us" and "number of lanes" into a single score component, we can get more realistic behavior. Note there are still one or two lane changes in the intersection, but from what I can tell they represent reasonable choices for the car - i.e. they eventually need to be in that lane. There is a comment about a desire to "keep it simple" and avoid linear combinations of the score function, but this does seem more realistic. WDYT? @@ -407,7 +407,7 @@ impl Router {
} else {
slow_lane = 0;
}
- let cost = (lt, bike, slow_lane, vehicles, lc);
+ let cost = (lt, bike, slow_lane, vehicles + lc);
(cost, turn1, l, turn2)
}) Alternatively, we could try explicitly penalizing "short lanes" and add that as an explicit cost in our function to exclude lane-changing. (language is tricky here! by exclude lane-changing I mean that in the context of allowing changing the trip plan to avoid changing lanes.) |
Switching to a sum here SGTM. The preference for the lexicographic ordering is understandability. Maybe we shouldn't switch 1 lane over until there's a 2 or 3 vehicle advantage, to add a bit more hysteresis. I'm allergic to parameter tuning, especially when we don't have a convenient way of trying different parameters against a representative sample of traffic loads. But since this little change improves things so much and feels more realistic, seems worth it.
That's also pretty intuitive -- getting stuck behind a slow vehicle for very short distance doesn't matter so much. Where would it get prioritized in this tuple? |
If we went that route, I'd guess pretty early on, something like: I kind of prefer the combination score though - it seems like it would effectively accomplish the same thing (you can't have more than one car in the lane if it's very short) plus it seems like it handles some other real world cases (I think people don't usually lane change to get around just one car). Plus it's easier to implement. ;) |
SGTM! So IIUC, the immediate plan is to:
And the criteria for success are:
|
Sounds right, yes! |
As an update, I'm still working on this. My work in progress (https://github.com/dabreegster/abstreet/compare/master...michaelkirk:mkirk/lane-change-2?expand=1) attempts to update router.rs so that an entire uber turn can be optimized just before the agent enters the turn. However, this has introduced some failures that I'm trying to track down. Here are two that I'm seeing:
Another one like:
Current theory is that something about my change is preventing queues from releasing their reserved length properly. I don't immediately understand why, but still looking. |
Can I repro that on the south_seattle map? I read through your change and it all looks correct to me, not sure what's wrong either. |
south seattle crashes almost immediately for me. I see you added some debugging to the analytics |
Steps changed. Before: vs After:
|
Ah, your bug is |
ah!! thank you!! |
starting an uber-turn) the same as accepted turns, updating the blocked-by graph and allowing a movement to start if a cycle is detected. No effect in montlake or lakeslice, and very slight progress at the Krakow roundabout for #382 -- increasing a tiny 26 trips by 7:20 am.
|
Whoops! I don't regularly test that flag. Do you think it's worth keeping around? Are we likely to want to try simulating without reserving UTs, to see if it helps some particular map? Keeping around all of the optional flags definitely increases code complexity, but we might regret it later if we remove them. |
I think we want to reduce UT via merge, but I don’t believe they will completely go away. One way to find good merge candidates might be to remove UT and watch for jams. If that’s the case, then I think we might want to fix some things. I’m guessing, but I suspect it’s a hand full of asserts or unwraps that need additional filtering. I started to look at how to provide the flag and not crash, but wasn't able to find a clean, clear, way to do it. Instead, I found more "danger ahead" signs, like this in map_model/src/pathfind/mod.rs:
|
- only triggered when a vehicle becomes Queued - Only one adjacent lane, no contraflow (crossing the road's center line) - Don't return to the original lane after passing - Using a static blockage in the old lane (so other vehicles will wait too much) - Only using the new lane to determine position (so visually a car will clip a bike as they pass) Haven't regenerated prebaked data. #382
- only triggered when a vehicle becomes Queued - Only one adjacent lane, no contraflow (crossing the road's center line) - Don't return to the original lane after passing - Using a static blockage in the old lane (so other vehicles will wait too much) - Only using the new lane to determine position (so visually a car will clip a bike as they pass) Haven't regenerated prebaked data. #382
for working on exiting from driveways and lane-changing. #382
for working on exiting from driveways and lane-changing. #382
for working on exiting from driveways and lane-changing. #382
lane that they're stuck behind them. Only record a risk exposure event the first time, but let passing happen anywhere. #382 Also add scenario name to PrebakeSummary, to disambiguate the Poundbury results.
Everyone wants to use right lane, even ones changing lane after crossing to middle one!
Scene from Kraków map, unmodified anything, robot proletariat traffic model.
Drivers in a real-life Kraków could be better, but they are not so bad :)
The text was updated successfully, but these errors were encountered: