Building an Autonomous Car with FreeRTOS: Chaos, Challenges, and Triumph

Dec 11, 2024

What happens when you throw an Arduino Mega, a bunch of sensors, and FreeRTOS into the mix? You get a clumsy, chaotic, yet incredibly rewarding journey toward building a self-driving car. Here’s a quick look at how we turned a pile of parts into a (mostly) functional autonomous vehicle.

🚗 The Vision: A Mini Autonomous Car

Our goal? Build a car that thinks for itself—well, sort of. It had to:

  • Avoid obstacles using an ultrasonic radar and servo motor.
  • Move with precision powered by four DC motors.
  • Allow manual control via an IR remote.
  • React to light levels with a photoresistor-controlled LED.

Sounds easy, right? Spoiler: It wasn’t.

🛠 The Evolution: Three Versions of Chaos

Version 1: Basic Functionality

This was our “just make it move” phase. Without FreeRTOS, the car could detect obstacles, stop, and turn—but the behavior was clunky, and adding features quickly became messy.

Version 2: Enter FreeRTOS

We introduced FreeRTOS and organized the car’s behavior into tasks. Radar, control logic, motors, and servo movements were each handled by separate tasks, communicating through queues. But this setup was far from perfect:

  • Queue Jams: Too much data overwhelmed the system.
  • Out-of-Sync Tasks: Radar and motor tasks couldn’t keep up with each other, leading to erratic movements.

Version 3: Streamlined with Notifications

Finally, we overhauled the system with these key improvements:

  • Stream Buffers: Replaced queues to reduce data clutter.
  • Task Notifications: Allowed tasks to signal each other directly, ensuring better synchronization.
  • Simplified Tasks: Decoupled logic to avoid tasks stepping on each other’s toes.

🎉 Features That Made It Work

  1. Obstacle Avoidance: The car scanned left and right, picked the best path, or turned around if no options were available.
  2. Remote Control: Switch between autonomous and manual modes with the press of a button.
  3. Auto Lighting: An LED turned on automatically in low light, making it easier to track the car.
  4. Randomized Turns: If all paths were clear, the car added a bit of unpredictability for fun.

🔍 Challenges We Overcame

  1. Power Problems: The motors drained power, causing glitches. Upgrading the battery solved this.
  2. Task Overload: Overusing queues led to inconsistent behavior. Stream buffers simplified things.
  3. Debugging Woes: Deadlocks, timer conflicts, and missed notifications made debugging an adventure in itself.

📸 A Closer Look: Images of the Build

To give you a better sense of how everything came together, here’s a quick walkthrough of our autonomous car setup, complete with some snapshots from different angles:

Front View:

  • The ultrasonic radar is mounted on a piece of cardboard that is glued to the arm of the servo motor.

Back View:

  • The remote IR remote receiver can be seen at the bottom left.

Right side view:

  • The Task RT1 LED can be seen at the bottom left of the Arduino board.

Left side view:

  • The Photoresistor LED can be seen on the breadboard.

Top side view:

🤝 Team Effort

  • Me focused on hardware setup and early prototypes.
  • Partner refined the FreeRTOS integration and added advanced features like xTaskNotify.
  • Both of us tackled debugging, testing,.

🚀 Final Thoughts

Despite the headaches, seeing the car navigate obstacles on its own made it all worthwhile. It wasn’t perfect, but it was a huge leap from where we started. More importantly, we learned how to use FreeRTOS to manage complex tasks in real-time—skills we’ll carry into future projects.

Would we do it all over again? Definitely (but maybe with fewer sleepless nights).

Got your own FreeRTOS project stories? Share them to us—we’d love to hear them!

Sean B. Li