Our team won the F1/10th competition in Torino! Now that the celebrations are over, it’s time to analyze and draw conclusions from the event to prepare ourselves for the upcoming competition in 2019.
The F1/10th is a racing competition for autonomous, electric car models. The organizers (University of Pennsylvania) encourage the participants to develop the fastest (yet, reliable) algorithm, capable of beating a track. The hardware parameters are capped by the rules, and most of the work needs to be devoted to software.
Here’s a short summary video from the event:
The winner of the 3rd F1/10th challenge was chosen based on the number of laps made by the model during two timing sessions. The first sessions lasted 5 minutes, while the second one was shorted and took 3 minutes. If the car needed any kind of assistance, or damaged the track during a lap, the team was required to pause the lap, go back to a previous checkpoint, and continue the lap from there. If the number of laps of two teams were the same, the team with the fastest lap was considered better.
This edition’s track was quite challenging, with multiple turns that proved difficult to all of the teams:
The track started with a simple left turn, followed by two consecutive U-turns. The second U-turn (1) was especially difficult — the cars either tried to go back on this turn, or cut the corner too aggressively.
After beating turn (1) there was a long straight that ended with a tricky turn (2). In the beginning, our car experienced a bit of oversteer, and even turned around. Eventually, we limited the speed of the car in the preceding straight, and narrowed the LIDAR’s scan so that the car wouldn’t be lured to go back.
Then, there’s an easy, slow left turn, followed by the longest straight on the entire track, ending in a left turn (3). On the one hand, we wanted the car to go as fast as possible on this straight, but on the other hand not too fast, so as to fit in the (3)rd corner.
Also, the surface on the entire track was somewhat slippery which also added to the difficulty of the race.
Here’s a video from the first round of the competition (our run starts at 43:00):
We finished 16 laps, and the fastest took 18.29 s.
The organizers shared a Gazebo simulator for prototyping and testing out models. And although there’s a huge gap between how the car behaves in the simulator vs. how it behaves on the actual track, the simulator proved to be a valuable tool for trying out new ideas.
The team and the car
First off: we had very limited time to prepare for the competition. Keep that in mind, and if you’ll run into question like: “Why did they use RaspberryPi instead of the Jetson?” the likely answer is: “We were in a hurry and had to cut corners (pun intended).”
The team members were:
- Łukasz Sztyber
- Karol Majek
- and myself
Three guys passionate about self-driving cars. And that’s all you need to take part in the competition. OK, that’s not all you need — the car is pretty important too.
We started off with the Erle Rover platform, which came with a RaspberryPi3 as the computational unit, and a very simple RGB camera. We first tried to work with the camera but that led to nowhere (even a simple deep learning line-follower model was challenging to train). For one thing, the inference time was too long. But more fundamentally, it became clear that a deep learning approach would not generalize to new tracks, and in particular: the track we would need to beat at the competition. (Which is not to say that a machine learning approach is a dead end — it’s not. But I think it needs to be used for learning “things” that generalize to other tracks.)
Then Łukasz bought the recommended LIDAR, and even the simplest approaches based on a LIDAR seemed promising. It became clear that it’s possible to build a model without SLAM, without path planning and control. This was crucial since at that point we had very little time remaining before the competition started, and we knew that only simple solutions would be achievable.
Once we became more serious about the competition, Łukasz invested in a platform which he dubbed Dzik (pronounced “j ee k”). We used the RaspberryPi3 from the Erle Rover as the computational unit. The organizers recommended NVidia Jetson TX-1, or TX-2, which was faster, but we wouldn’t have time to set it up, configure, and adapt to the rest of our hardware. We stuck to RaspberryPi3, and allocated what time we had left to improving the solution.
Our solution was a variant of the “follow-the-gap” approach for steering, combined with a set of conditions determining the speed of the car. For example, if the optimal direction (the “gap”) is straight ahead, and the distance to the obstacle in that direction is beyond a certain distance (which was one of many parameters of the method), then: go fast. How fast exactly was also controlled by a parameter, and needed adjusting.
Also, it wasn’t actually speed that was controlled, but rather, a Pulse-Width Modulation (PWM) signal passed to the Electronic Speed Controller (ESC), which in turn ran the engine at a particular RPM corresponding to that PWM value. Although we had odometry and tested out a PID controller for speed, it turned out that the ESC was able to reach a stable RPM for a given PWM pretty quickly, so there wasn’t much gain from a PID for speed. That’s all pretty technical, but the gist of this whole paragraph is: we had three PWM values, and we assumed that they correspond to three speeds, and that the engine can quickly switch between these speeds.
In addition to the rules regarding speed, there was a set of rules for stopping. It’s impossible to count all the instances during practice in which we were certain that the LIDAR took the final blow, and that we won’t be able to run in the competition. Therefore, our first line of defense against ‘the blackest scenario’ was a set of conditions in which Dzik just stopped.
Karol says that effectively the rules for going slow/medium/fast form a decision tree. We didn’t train its parameters using observable data, but rather fit it to our intuitions. It’s a bit of a stretch, but you could say we adapted reinforcement learning with an experience-driven optimization algorithm :)
But more seriously, we could have used supervised machine learning, and: record several laps (including minor mishaps) by controlling the car ourselves, fit a machine learning model, validate it on another set of recorded laps (paying close attention to how the model behaves on mishaps), and expect to get a better solution then those hard-coded conditions we used. However, we didn’t go this path, and it’s hard to tell how many problems we would have encountered. In particular: how many laps would be needed? How much time would we need to adapt to a new track? And perhaps the most important unknown: how hard would it be to debug such a model? Remember that our final validation is on the actual track, and if we observe that something goes wrong, it’s very important that we know how to improve upon it. But here, what more can we do other than “get more data”? This is a problem in and of itself. And that’s just speed control, I haven’t even mentioned steering.
So in other words: using an adaptive model seems cool, but it has its drawbacks. Still, it doesn’t mean we won’t try it.
Fundamentally, even if our method found the optimal steering angle locally, it wouldn’t be able (in general) to yield the optimal route globally.
To follow the optimal route, one needs to solve the problems of: localization, planning, and control. Such an approach is admittedly much more complicated, but it holds a promise of reaching a systematic solution, one that can become increasingly better, as the solutions to the individual sub-problems improve.
But a more important reason for not using the follow-the-gap approach again (at least not in its existing form) is actually more simple. Namely: we would like to try something else.
The follow-the-gap approach is fast. The LIDAR (with nominal frequency of 40Hz), produces a new scan every 25ms. The C++ implementation was able to finish its computations in approximately 3ms (on average). That’s quite important during a race, where each stalled millisecond makes the scan increasingly obsolete.
But there’s also another strength: this method is fairly easy to adapt to a new track. The parameters controlling its behavior are intuitive (once you get to know what they actually control), and finding their optimal values for a new track is a matter of several test runs. (Actually, the track in Torino was pretty challenging, so it took 20–30 runs, but still.)
There’s one more strength, but we haven’t really made use of it during this competition. And that is: the follow-the-gap approach can automatically handle moving objects on the road, and avoid them or even overtake them (if it’s an opponent). It may not seem like that big of a deal, but the alternative, systematic approach will probably need more attention in this particular area. And because the time needed to make a decision would inevitably be longer, the algorithm would need to anticipate future positions of the moving objects. So it might get interesting.
The last year’s winners (from Czech Technical University) also used a version of the follow-the-gap approach. And that makes you wonder: perhaps this method is indeed superior to the more systematic ones? Maybe the drawback of following the optimal route is outweighed by the benefit of making locally optimal decisions very quickly? I think the systematic solution is difficult to get right, and that’s one of the main relative advantages of the follow-the-gap approach. But how long will it hold — hard to say.
Plans for the next competition
…are top-secret and I can’t share them here.
But follow the f1tenth.org forum, and meet us at the next competition, and we will gladly share them with you. Right after the race, that is ;)
If you enjoyed this post, please hit the clap button below and follow our publication for more interesting articles about ML & AI.